diff --git a/Jakefile.js b/Jakefile.js deleted file mode 100644 index faef1289..00000000 --- a/Jakefile.js +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Jake build script - */ -var jake = require('jake'), - browserify = require('browserify'), - wrench = require('wrench'), - CleanCSS = require('clean-css'), - fs = require('fs'); - -require('jake-utils'); - -// constants -var DIST = './dist'; -var VIS = DIST + '/vis.js'; -var VIS_CSS = DIST + '/vis.css'; -var VIS_TMP = DIST + '/vis.js.tmp'; -var VIS_MIN = DIST + '/vis.min.js'; -var VIS_MIN_CSS = DIST + '/vis.min.css'; - -/** - * default task - */ -desc('Default task: build all libraries'); -task('default', ['build', 'minify'], function () { - console.log('done'); -}); - -/** - * build the visualization library vis.js - */ -desc('Build the visualization library vis.js'); -task('build', {async: true}, function () { - jake.mkdirP(DIST); - jake.mkdirP(DIST + '/img'); - - // concatenate and stringify the css files - concat({ - src: [ - './lib/timeline/component/css/timeline.css', - './lib/timeline/component/css/panel.css', - './lib/timeline/component/css/labelset.css', - './lib/timeline/component/css/itemset.css', - './lib/timeline/component/css/item.css', - './lib/timeline/component/css/timeaxis.css', - './lib/timeline/component/css/currenttime.css', - './lib/timeline/component/css/customtime.css', - './lib/timeline/component/css/animation.css', - - './lib/timeline/component/css/dataaxis.css', - './lib/timeline/component/css/pathStyles.css', - - './lib/network/css/network-manipulation.css', - './lib/network/css/network-navigation.css' - ], - dest: VIS_CSS, - separator: '\n' - }); - console.log('created ' + VIS_CSS); - - // concatenate the script files - concat({ - dest: VIS_TMP, - src: [ - './lib/module/imports.js', - - './lib/shim.js', - './lib/util.js', - './lib/DOMutil.js', - './lib/DataSet.js', - './lib/DataView.js', - - './lib/timeline/component/GraphGroup.js', - './lib/timeline/component/Legend.js', - './lib/timeline/component/DataAxis.js', - './lib/timeline/component/LineGraph.js', - './lib/timeline/DataStep.js', - - './lib/timeline/Stack.js', - './lib/timeline/TimeStep.js', - './lib/timeline/Range.js', - './lib/timeline/component/Component.js', - './lib/timeline/component/TimeAxis.js', - './lib/timeline/component/CurrentTime.js', - './lib/timeline/component/CustomTime.js', - './lib/timeline/component/ItemSet.js', - './lib/timeline/component/item/*.js', - './lib/timeline/component/Group.js', - './lib/timeline/Timeline.js', - './lib/timeline/Graph2d.js', - - './lib/network/dotparser.js', - './lib/network/shapes.js', - './lib/network/Node.js', - './lib/network/Edge.js', - './lib/network/Popup.js', - './lib/network/Groups.js', - './lib/network/Images.js', - './lib/network/networkMixins/physics/PhysicsMixin.js', - './lib/network/networkMixins/physics/HierarchialRepulsion.js', - './lib/network/networkMixins/physics/BarnesHut.js', - './lib/network/networkMixins/physics/Repulsion.js', - './lib/network/networkMixins/HierarchicalLayoutMixin.js', - './lib/network/networkMixins/ManipulationMixin.js', - './lib/network/networkMixins/SectorsMixin.js', - './lib/network/networkMixins/ClusterMixin.js', - './lib/network/networkMixins/SelectionMixin.js', - './lib/network/networkMixins/NavigationMixin.js', - './lib/network/networkMixins/MixinLoader.js', - './lib/network/Network.js', - - './lib/graph3d/Graph3d.js', - - './lib/module/exports.js' - ], - - separator: '\n' - }); - - // copy images - wrench.copyDirSyncRecursive('./lib/network/img', DIST + '/img/network', { - forceDelete: true - }); - wrench.copyDirSyncRecursive('./lib/timeline/img', DIST + '/img/timeline', { - forceDelete: true - }); - - var timeStart = Date.now(); - // bundle the concatenated script and dependencies into one file - var b = browserify(); - b.add(VIS_TMP); - b.bundle({ - standalone: 'vis' - }, function (err, code) { - if(err) { - throw err; - } - console.log("browserify",Date.now() - timeStart); timeStart = Date.now(); - // add header and footer - var lib = read('./lib/module/header.js') + code; - - // write bundled file - write(VIS, lib); - console.log('created js' + VIS); - - // remove temporary file - fs.unlinkSync(VIS_TMP); - - // update version number and stuff in the javascript files - replacePlaceholders(VIS); - - complete(); - }); -}); - -/** - * minify the visualization library vis.js - */ -desc('Minify the visualization library vis.js'); -task('minify', {async: true}, function () { - // minify javascript - minify({ - src: VIS, - dest: VIS_MIN, - header: read('./lib/module/header.js') - }); - - // update version number and stuff in the javascript files - replacePlaceholders(VIS_MIN); - - console.log('created minified ' + VIS_MIN); - - var minified = new CleanCSS().minify(read(VIS_CSS)); - write(VIS_MIN_CSS, minified); - console.log('created minified ' + VIS_MIN_CSS); -}); - -/** - * test task - */ -desc('Test the library'); -task('test', function () { - // TODO: use a testing suite for testing: nodeunit, mocha, tap, ... - var filelist = new jake.FileList(); - filelist.include([ - './test/**/*.js' - ]); - - var files = filelist.toArray(); - files.forEach(function (file) { - require('./' + file); - }); - - console.log('Executed ' + files.length + ' test files successfully'); -}); - -/** - * replace version, date, and name placeholders in the provided file - * @param {String} filename - */ -var replacePlaceholders = function (filename) { - replace({ - replacements: [ - {pattern: '@@date', replacement: today()}, - {pattern: '@@version', replacement: version()} - ], - src: filename - }); -}; diff --git a/README.md b/README.md index f2727a89..027cca7d 100644 --- a/README.md +++ b/README.md @@ -124,15 +124,9 @@ To build the library from source, clone the project from github git clone git://github.com/almende/vis.git -The project uses [jake](https://github.com/mde/jake) as build tool. -The build script uses [Browserify](http://browserify.org/) to -bundle the source code into a library, -and uses [UglifyJS](http://lisperator.net/uglifyjs/) to minify the code. The source code uses the module style of node (require and module.exports) to -organize dependencies. - -To install all dependencies and build the library, run `npm install` in the -root of the project. +organize dependencies. To install all dependencies and build the library, +run `npm install` in the root of the project. cd vis npm install diff --git a/dist/vis.css b/dist/vis.css old mode 100644 new mode 100755 index 2a5d8a24..260d73e2 --- a/dist/vis.css +++ b/dist/vis.css @@ -703,4 +703,4 @@ div.network-navigation.zoomExtends { background-image: url("img/network/zoomExtends.png"); bottom:50px; right:15px; -} +} \ No newline at end of file diff --git a/dist/vis.js b/dist/vis.js index bfad88c2..df27110a 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 3.0.1-SNAPSHOT - * @date 2014-07-07 + * @date 2014-07-08 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -22,441 +22,312 @@ * License for the specific language governing permissions and limitations under * the License. */ -!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.vis=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o>> 0; - - // 4. If IsCallable(callback) is false, throw a TypeError exception. - // See: http://es5.github.com/#x9.11 - if (typeof callback !== "function") { - throw new TypeError(callback + " is not a function"); - } - - // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. - if (thisArg) { - T = thisArg; - } - - // 6. Let A be a new array created as if by the expression new Array(len) where Array is - // the standard built-in constructor with that name and len is the value of len. - A = new Array(len); - - // 7. Let k be 0 - k = 0; - - // 8. Repeat, while k < len - while(k < len) { - - var kValue, mappedValue; - - // a. Let Pk be ToString(k). - // This is implicit for LHS operands of the in operator - // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. - // This step can be combined with c - // c. If kPresent is true, then - if (k in O) { + // Network + exports.Network = __webpack_require__(26); + exports.network = { + Edge: __webpack_require__(27), + Groups: __webpack_require__(28), + Images: __webpack_require__(29), + Node: __webpack_require__(30), + Popup: __webpack_require__(31), + dotparser: __webpack_require__(32) + }; - // i. Let kValue be the result of calling the Get internal method of O with argument Pk. - kValue = O[ k ]; + // Deprecated since v3.0.0 + exports.Graph = function () { + throw new Error('Graph is renamed to Network. Please create a graph as new vis.Network(...)'); + }; - // ii. Let mappedValue be the result of calling the Call internal method of callback - // with T as the this value and argument list containing kValue, k, and O. - mappedValue = callback.call(T, kValue, k, O); - // iii. Call the DefineOwnProperty internal method of A with arguments - // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true}, - // and false. +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { - // In browsers that support Object.defineProperty, use the following: - // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true }); + // utility functions - // For best browser support, use the following: - A[ k ] = mappedValue; - } - // d. Increase k by 1. - k++; - } + // first check if moment.js is already loaded in the browser window, if so, + // use this instance. Else, load via commonjs. + var Hammer = __webpack_require__(38); + var moment = __webpack_require__(39); - // 9. return A - return A; + /** + * Test whether given object is a number + * @param {*} object + * @return {Boolean} isNumber + */ + exports.isNumber = function(object) { + return (object instanceof Number || typeof object == 'number'); }; -} - -// Internet Explorer 8 and older does not support Array.filter, so we define it -// here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter -if (!Array.prototype.filter) { - Array.prototype.filter = function(fun /*, thisp */) { - "use strict"; - if (this == null) { - throw new TypeError(); - } + /** + * Test whether given object is a string + * @param {*} object + * @return {Boolean} isString + */ + exports.isString = function(object) { + return (object instanceof String || typeof object == 'string'); + }; - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun != "function") { - throw new TypeError(); + /** + * Test whether given object is a Date, or a String containing a Date + * @param {Date | String} object + * @return {Boolean} isDate + */ + exports.isDate = function(object) { + if (object instanceof Date) { + return true; } - - var res = []; - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in t) { - var val = t[i]; // in case fun mutates this - if (fun.call(thisp, val, i, t)) - res.push(val); + else if (exports.isString(object)) { + // test whether this string contains a date + var match = ASPDateRegex.exec(object); + if (match) { + return true; + } + else if (!isNaN(Date.parse(object))) { + return true; } } - return res; + return false; }; -} - -// Internet Explorer 8 and older does not support Object.keys, so we define it -// here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys -if (!Object.keys) { - Object.keys = (function () { - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ], - dontEnumsLength = dontEnums.length; - - return function (obj) { - if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) { - throw new TypeError('Object.keys called on non-object'); - } + /** + * Test whether given object is an instance of google.visualization.DataTable + * @param {*} object + * @return {Boolean} isDataTable + */ + exports.isDataTable = function(object) { + return (typeof (google) !== 'undefined') && + (google.visualization) && + (google.visualization.DataTable) && + (object instanceof google.visualization.DataTable); + }; - var result = []; + /** + * Create a semi UUID + * source: http://stackoverflow.com/a/105074/1262753 + * @return {String} uuid + */ + exports.randomUUID = function() { + var S4 = function () { + return Math.floor( + Math.random() * 0x10000 /* 65536 */ + ).toString(16); + }; - for (var prop in obj) { - if (hasOwnProperty.call(obj, prop)) result.push(prop); - } + return ( + S4() + S4() + '-' + + S4() + '-' + + S4() + '-' + + S4() + '-' + + S4() + S4() + S4() + ); + }; - if (hasDontEnumBug) { - for (var i=0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]); + /** + * Extend object a with the properties of object b or a series of objects + * Only properties with defined values are copied + * @param {Object} a + * @param {... Object} b + * @return {Object} a + */ + exports.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)) { + a[prop] = other[prop]; } } - return result; } - })() -} -// Internet Explorer 8 and older does not support Array.isArray, -// so we define it here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray -if(!Array.isArray) { - Array.isArray = function (vArg) { - return Object.prototype.toString.call(vArg) === "[object Array]"; + return a; }; -} -// Internet Explorer 8 and older does not support Function.bind, -// so we define it here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + /** + * 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 + */ + exports.selectiveExtend = function (props, a, b) { + if (!Array.isArray(props)) { + throw new Error('Array with property names expected as first argument'); } - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; -} + for (var i = 2; i < arguments.length; i++) { + var other = arguments[i]; -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create -if (!Object.create) { - Object.create = function (o) { - if (arguments.length > 1) { - throw new Error('Object.create implementation only accepts the first parameter.'); - } - function F() {} - F.prototype = o; - return new F(); - }; -} - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + for (var p = 0; p < props.length; p++) { + var prop = props[p]; + if (other.hasOwnProperty(prop)) { + a[prop] = other[prop]; + } + } } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; + return a; }; -} - -/** - * utility functions - */ -var util = {}; - -/** - * Test whether given object is a number - * @param {*} object - * @return {Boolean} isNumber - */ -util.isNumber = function(object) { - return (object instanceof Number || typeof object == 'number'); -}; - -/** - * Test whether given object is a string - * @param {*} object - * @return {Boolean} isString - */ -util.isString = function(object) { - return (object instanceof String || typeof object == 'string'); -}; - -/** - * Test whether given object is a Date, or a String containing a Date - * @param {Date | String} object - * @return {Boolean} isDate - */ -util.isDate = function(object) { - if (object instanceof Date) { - return true; - } - else if (util.isString(object)) { - // test whether this string contains a date - var match = ASPDateRegex.exec(object); - if (match) { - return true; - } - else if (!isNaN(Date.parse(object))) { - return true; - } - } - - return false; -}; - -/** - * Test whether given object is an instance of google.visualization.DataTable - * @param {*} object - * @return {Boolean} isDataTable - */ -util.isDataTable = function(object) { - return (typeof (google) !== 'undefined') && - (google.visualization) && - (google.visualization.DataTable) && - (object instanceof google.visualization.DataTable); -}; -/** - * Create a semi UUID - * source: http://stackoverflow.com/a/105074/1262753 - * @return {String} uuid - */ -util.randomUUID = function() { - var S4 = function () { - return Math.floor( - Math.random() * 0x10000 /* 65536 */ - ).toString(16); - }; - - return ( - S4() + S4() + '-' + - S4() + '-' + - S4() + '-' + - S4() + '-' + - S4() + S4() + S4() - ); -}; + /** + * 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 + */ + exports.selectiveDeepExtend = function (props, a, b) { + // TODO: add support for Arrays to deepExtend + if (Array.isArray(b)) { + throw new TypeError('Arrays are not supported by deepExtend'); + } + for (var i = 2; i < arguments.length; i++) { + var other = arguments[i]; + for (var p = 0; p < props.length; p++) { + var prop = props[p]; + if (other.hasOwnProperty(prop)) { + if (b[prop] && b[prop].constructor === Object) { + if (a[prop] === undefined) { + a[prop] = {}; + } + if (a[prop].constructor === Object) { + exports.deepExtend(a[prop], b[prop]); + } + else { + a[prop] = b[prop]; + } + } else if (Array.isArray(b[prop])) { + throw new TypeError('Arrays are not supported by deepExtend'); + } else { + a[prop] = b[prop]; + } -/** - * Extend object a with the properties of object b or a series of objects - * Only properties with defined values are copied - * @param {Object} a - * @param {... Object} b - * @return {Object} a - */ -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)) { - 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 = 2; i < arguments.length; i++) { - var other = arguments[i]; + return a; + }; - for (var p = 0; p < props.length; p++) { - var prop = props[p]; - if (other.hasOwnProperty(prop)) { - a[prop] = other[prop]; - } + /** + * Deep extend an object a with the properties of object b + * @param {Object} a + * @param {Object} b + * @returns {Object} + */ + exports.deepExtend = function(a, b) { + // TODO: add support for Arrays to deepExtend + if (Array.isArray(b)) { + throw new TypeError('Arrays are not supported by deepExtend'); } - } - 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.selectiveDeepExtend = function (props, a, b) { - // TODO: add support for Arrays to deepExtend - if (Array.isArray(b)) { - throw new TypeError('Arrays are not supported by deepExtend'); - } - for (var i = 2; i < arguments.length; i++) { - var other = arguments[i]; - for (var p = 0; p < props.length; p++) { - var prop = props[p]; - if (other.hasOwnProperty(prop)) { + for (var prop in b) { + if (b.hasOwnProperty(prop)) { if (b[prop] && b[prop].constructor === Object) { if (a[prop] === undefined) { a[prop] = {}; } if (a[prop].constructor === Object) { - util.deepExtend(a[prop], b[prop]); + exports.deepExtend(a[prop], b[prop]); } else { a[prop] = b[prop]; @@ -466,29721 +337,36939 @@ util.selectiveDeepExtend = function (props, a, b) { } else { a[prop] = b[prop]; } - } } - } - return a; -}; + return a; + }; -/** - * Deep extend an object a with the properties of object b - * @param {Object} a - * @param {Object} b - * @returns {Object} - */ -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'); - } + /** + * Test whether all elements in two arrays are equal. + * @param {Array} a + * @param {Array} b + * @return {boolean} Returns true if both arrays have the same length and same + * elements. + */ + exports.equalArray = function (a, b) { + if (a.length != b.length) return false; - for (var prop in b) { - if (b.hasOwnProperty(prop)) { - if (b[prop] && b[prop].constructor === Object) { - if (a[prop] === undefined) { - a[prop] = {}; - } - if (a[prop].constructor === Object) { - util.deepExtend(a[prop], b[prop]); - } - else { - a[prop] = b[prop]; - } - } else if (Array.isArray(b[prop])) { - throw new TypeError('Arrays are not supported by deepExtend'); - } else { - a[prop] = b[prop]; - } + for (var i = 0, len = a.length; i < len; i++) { + if (a[i] != b[i]) return false; } - } - return a; -}; -/** - * Test whether all elements in two arrays are equal. - * @param {Array} a - * @param {Array} b - * @return {boolean} Returns true if both arrays have the same length and same - * elements. - */ -util.equalArray = function (a, b) { - if (a.length != b.length) return false; - - for (var i = 0, len = a.length; i < len; i++) { - if (a[i] != b[i]) return false; - } - - return true; -}; - -/** - * Convert an object to another type - * @param {Boolean | Number | String | Date | Moment | Null | undefined} object - * @param {String | undefined} type Name of the type. Available types: - * 'Boolean', 'Number', 'String', - * 'Date', 'Moment', ISODate', 'ASPDate'. - * @return {*} object - * @throws Error - */ -util.convert = function(object, type) { - var match; - - if (object === undefined) { - return undefined; - } - if (object === null) { - return null; - } - - if (!type) { - return object; - } - if (!(typeof type === 'string') && !(type instanceof String)) { - throw new Error('Type must be a string'); - } + return true; + }; - //noinspection FallthroughInSwitchStatementJS - switch (type) { - case 'boolean': - case 'Boolean': - return Boolean(object); + /** + * Convert an object to another type + * @param {Boolean | Number | String | Date | Moment | Null | undefined} object + * @param {String | undefined} type Name of the type. Available types: + * 'Boolean', 'Number', 'String', + * 'Date', 'Moment', ISODate', 'ASPDate'. + * @return {*} object + * @throws Error + */ + exports.convert = function(object, type) { + var match; - case 'number': - case 'Number': - return Number(object.valueOf()); + if (object === undefined) { + return undefined; + } + if (object === null) { + return null; + } - case 'string': - case 'String': - return String(object); + if (!type) { + return object; + } + if (!(typeof type === 'string') && !(type instanceof String)) { + throw new Error('Type must be a string'); + } - case 'Date': - if (util.isNumber(object)) { - return new Date(object); - } - if (object instanceof Date) { - return new Date(object.valueOf()); - } - else if (moment.isMoment(object)) { - return new Date(object.valueOf()); - } - if (util.isString(object)) { - match = ASPDateRegex.exec(object); - if (match) { - // object is an ASP date - return new Date(Number(match[1])); // parse number + //noinspection FallthroughInSwitchStatementJS + switch (type) { + case 'boolean': + case 'Boolean': + return Boolean(object); + + case 'number': + case 'Number': + return Number(object.valueOf()); + + case 'string': + case 'String': + return String(object); + + case 'Date': + if (exports.isNumber(object)) { + return new Date(object); + } + if (object instanceof Date) { + return new Date(object.valueOf()); + } + else if (moment.isMoment(object)) { + return new Date(object.valueOf()); + } + if (exports.isString(object)) { + match = ASPDateRegex.exec(object); + if (match) { + // object is an ASP date + return new Date(Number(match[1])); // parse number + } + else { + return moment(object).toDate(); // parse string + } } else { - return moment(object).toDate(); // parse string + throw new Error( + 'Cannot convert object of type ' + exports.getType(object) + + ' to type Date'); } - } - else { - throw new Error( - 'Cannot convert object of type ' + util.getType(object) + - ' to type Date'); - } - case 'Moment': - if (util.isNumber(object)) { - return moment(object); - } - if (object instanceof Date) { - return moment(object.valueOf()); - } - else if (moment.isMoment(object)) { - return moment(object); - } - if (util.isString(object)) { - match = ASPDateRegex.exec(object); - if (match) { - // object is an ASP date - return moment(Number(match[1])); // parse number + case 'Moment': + if (exports.isNumber(object)) { + return moment(object); + } + if (object instanceof Date) { + return moment(object.valueOf()); + } + else if (moment.isMoment(object)) { + return moment(object); + } + if (exports.isString(object)) { + match = ASPDateRegex.exec(object); + if (match) { + // object is an ASP date + return moment(Number(match[1])); // parse number + } + else { + return moment(object); // parse string + } } else { - return moment(object); // parse string + throw new Error( + 'Cannot convert object of type ' + exports.getType(object) + + ' to type Date'); } - } - else { - throw new Error( - 'Cannot convert object of type ' + util.getType(object) + - ' to type Date'); - } - case 'ISODate': - if (util.isNumber(object)) { - return new Date(object); - } - else if (object instanceof Date) { - return object.toISOString(); - } - else if (moment.isMoment(object)) { - return object.toDate().toISOString(); - } - else if (util.isString(object)) { - match = ASPDateRegex.exec(object); - if (match) { - // object is an ASP date - return new Date(Number(match[1])).toISOString(); // parse number + case 'ISODate': + if (exports.isNumber(object)) { + return new Date(object); + } + else if (object instanceof Date) { + return object.toISOString(); + } + else if (moment.isMoment(object)) { + return object.toDate().toISOString(); + } + else if (exports.isString(object)) { + match = ASPDateRegex.exec(object); + if (match) { + // object is an ASP date + return new Date(Number(match[1])).toISOString(); // parse number + } + else { + return new Date(object).toISOString(); // parse string + } } else { - return new Date(object).toISOString(); // parse string + throw new Error( + 'Cannot convert object of type ' + exports.getType(object) + + ' to type ISODate'); } - } - else { - throw new Error( - 'Cannot convert object of type ' + util.getType(object) + - ' to type ISODate'); - } - case 'ASPDate': - if (util.isNumber(object)) { - return '/Date(' + object + ')/'; - } - else if (object instanceof Date) { - return '/Date(' + object.valueOf() + ')/'; - } - else if (util.isString(object)) { - match = ASPDateRegex.exec(object); - var value; - if (match) { - // object is an ASP date - value = new Date(Number(match[1])).valueOf(); // parse number + case 'ASPDate': + if (exports.isNumber(object)) { + return '/Date(' + object + ')/'; + } + else if (object instanceof Date) { + return '/Date(' + object.valueOf() + ')/'; + } + else if (exports.isString(object)) { + match = ASPDateRegex.exec(object); + var value; + if (match) { + // object is an ASP date + value = new Date(Number(match[1])).valueOf(); // parse number + } + else { + value = new Date(object).valueOf(); // parse string + } + return '/Date(' + value + ')/'; } else { - value = new Date(object).valueOf(); // parse string + throw new Error( + 'Cannot convert object of type ' + exports.getType(object) + + ' to type ASPDate'); } - return '/Date(' + value + ')/'; - } - else { - throw new Error( - 'Cannot convert object of type ' + util.getType(object) + - ' to type ASPDate'); - } - default: - throw new Error('Unknown type "' + type + '"'); - } -}; + default: + throw new Error('Unknown type "' + type + '"'); + } + }; -// parse ASP.Net Date pattern, -// for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/' -// code from http://momentjs.com/ -var ASPDateRegex = /^\/?Date\((\-?\d+)/i; + // parse ASP.Net Date pattern, + // for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/' + // code from http://momentjs.com/ + var ASPDateRegex = /^\/?Date\((\-?\d+)/i; -/** - * Get the type of an object, for example util.getType([]) returns 'Array' - * @param {*} object - * @return {String} type - */ -util.getType = function(object) { - var type = typeof object; + /** + * Get the type of an object, for example exports.getType([]) returns 'Array' + * @param {*} object + * @return {String} type + */ + exports.getType = function(object) { + var type = typeof object; - if (type == 'object') { - if (object == null) { - return 'null'; - } - if (object instanceof Boolean) { - return 'Boolean'; + if (type == 'object') { + if (object == null) { + return 'null'; + } + if (object instanceof Boolean) { + return 'Boolean'; + } + if (object instanceof Number) { + return 'Number'; + } + if (object instanceof String) { + return 'String'; + } + if (object instanceof Array) { + return 'Array'; + } + if (object instanceof Date) { + return 'Date'; + } + return 'Object'; } - if (object instanceof Number) { + else if (type == 'number') { return 'Number'; } - if (object instanceof String) { - return 'String'; - } - if (object instanceof Array) { - return 'Array'; + else if (type == 'boolean') { + return 'Boolean'; } - if (object instanceof Date) { - return 'Date'; + else if (type == 'string') { + return 'String'; } - return 'Object'; - } - else if (type == 'number') { - return 'Number'; - } - else if (type == 'boolean') { - return 'Boolean'; - } - else if (type == 'string') { - return 'String'; - } - - return type; -}; -/** - * Retrieve the absolute left value of a DOM element - * @param {Element} elem A dom element, for example a div - * @return {number} left The absolute left position of this element - * in the browser page. - */ -util.getAbsoluteLeft = function(elem) { - var doc = document.documentElement; - var body = document.body; - - var left = elem.offsetLeft; - var e = elem.offsetParent; - while (e != null && e != body && e != doc) { - left += e.offsetLeft; - left -= e.scrollLeft; - e = e.offsetParent; - } - return left; -}; + return type; + }; -/** - * Retrieve the absolute top value of a DOM element - * @param {Element} elem A dom element, for example a div - * @return {number} top The absolute top position of this element - * in the browser page. - */ -util.getAbsoluteTop = function(elem) { - var doc = document.documentElement; - var body = document.body; - - var top = elem.offsetTop; - var e = elem.offsetParent; - while (e != null && e != body && e != doc) { - top += e.offsetTop; - top -= e.scrollTop; - e = e.offsetParent; - } - return top; -}; + /** + * Retrieve the absolute left value of a DOM element + * @param {Element} elem A dom element, for example a div + * @return {number} left The absolute left position of this element + * in the browser page. + */ + exports.getAbsoluteLeft = function(elem) { + var doc = document.documentElement; + var body = document.body; -/** - * Get the absolute, vertical mouse position from an event. - * @param {Event} event - * @return {Number} pageY - */ -util.getPageY = function(event) { - if ('pageY' in event) { - return event.pageY; - } - else { - var clientY; - if (('targetTouches' in event) && event.targetTouches.length) { - clientY = event.targetTouches[0].clientY; - } - else { - clientY = event.clientY; + var left = elem.offsetLeft; + var e = elem.offsetParent; + while (e != null && e != body && e != doc) { + left += e.offsetLeft; + left -= e.scrollLeft; + e = e.offsetParent; } + return left; + }; + /** + * Retrieve the absolute top value of a DOM element + * @param {Element} elem A dom element, for example a div + * @return {number} top The absolute top position of this element + * in the browser page. + */ + exports.getAbsoluteTop = function(elem) { var doc = document.documentElement; var body = document.body; - return clientY + - ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } -}; -/** - * Get the absolute, horizontal mouse position from an event. - * @param {Event} event - * @return {Number} pageX - */ -util.getPageX = function(event) { - if ('pageY' in event) { - return event.pageX; - } - else { - var clientX; - if (('targetTouches' in event) && event.targetTouches.length) { - clientX = event.targetTouches[0].clientX; + var top = elem.offsetTop; + var e = elem.offsetParent; + while (e != null && e != body && e != doc) { + top += e.offsetTop; + top -= e.scrollTop; + e = e.offsetParent; + } + return top; + }; + + /** + * Get the absolute, vertical mouse position from an event. + * @param {Event} event + * @return {Number} pageY + */ + exports.getPageY = function(event) { + if ('pageY' in event) { + return event.pageY; } else { - clientX = event.clientX; + var clientY; + if (('targetTouches' in event) && event.targetTouches.length) { + clientY = event.targetTouches[0].clientY; + } + else { + clientY = event.clientY; + } + + var doc = document.documentElement; + var body = document.body; + return clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); } + }; - var doc = document.documentElement; - var body = document.body; - return clientX + - ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - } -}; + /** + * Get the absolute, horizontal mouse position from an event. + * @param {Event} event + * @return {Number} pageX + */ + exports.getPageX = function(event) { + if ('pageY' in event) { + return event.pageX; + } + else { + var clientX; + if (('targetTouches' in event) && event.targetTouches.length) { + clientX = event.targetTouches[0].clientX; + } + else { + clientX = event.clientX; + } -/** - * add a className to the given elements style - * @param {Element} elem - * @param {String} 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 - elem.className = classes.join(' '); - } -}; + var doc = document.documentElement; + var body = document.body; + return clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + } + }; -/** - * add a className to the given elements style - * @param {Element} elem - * @param {String} className - */ -util.removeClassName = function(elem, className) { - var classes = elem.className.split(' '); - var index = classes.indexOf(className); - if (index != -1) { - classes.splice(index, 1); // remove the class from the array - elem.className = classes.join(' '); - } -}; + /** + * add a className to the given elements style + * @param {Element} elem + * @param {String} className + */ + exports.addClassName = function(elem, className) { + var classes = elem.className.split(' '); + if (classes.indexOf(className) == -1) { + classes.push(className); // add the class to the array + elem.className = classes.join(' '); + } + }; -/** - * For each method for both arrays and objects. - * In case of an array, the built-in Array.forEach() is applied. - * In case of an Object, the method loops over all properties of the object. - * @param {Object | Array} object An Object or Array - * @param {function} callback Callback method, called for each item in - * the object or array with three parameters: - * callback(value, index, object) - */ -util.forEach = function(object, callback) { - var i, - len; - if (object instanceof Array) { - // array - for (i = 0, len = object.length; i < len; i++) { - callback(object[i], i, object); + /** + * add a className to the given elements style + * @param {Element} elem + * @param {String} className + */ + exports.removeClassName = function(elem, className) { + var classes = elem.className.split(' '); + var index = classes.indexOf(className); + if (index != -1) { + classes.splice(index, 1); // remove the class from the array + elem.className = classes.join(' '); } - } - else { - // object - for (i in object) { - if (object.hasOwnProperty(i)) { + }; + + /** + * For each method for both arrays and objects. + * In case of an array, the built-in Array.forEach() is applied. + * In case of an Object, the method loops over all properties of the object. + * @param {Object | Array} object An Object or Array + * @param {function} callback Callback method, called for each item in + * the object or array with three parameters: + * callback(value, index, object) + */ + exports.forEach = function(object, callback) { + var i, + len; + if (object instanceof Array) { + // array + for (i = 0, len = object.length; i < len; i++) { callback(object[i], i, object); } } - } -}; - -/** - * Convert an object into an array: all objects properties are put into the - * array. The resulting array is unordered. - * @param {Object} object - * @param {Array} array - */ -util.toArray = function(object) { - var array = []; + else { + // object + for (i in object) { + if (object.hasOwnProperty(i)) { + callback(object[i], i, object); + } + } + } + }; - for (var prop in object) { - if (object.hasOwnProperty(prop)) array.push(object[prop]); - } + /** + * Convert an object into an array: all objects properties are put into the + * array. The resulting array is unordered. + * @param {Object} object + * @param {Array} array + */ + exports.toArray = function(object) { + var array = []; - return array; -} + for (var prop in object) { + if (object.hasOwnProperty(prop)) array.push(object[prop]); + } -/** - * Update a property in an object - * @param {Object} object - * @param {String} key - * @param {*} value - * @return {Boolean} changed - */ -util.updateProperty = function(object, key, value) { - if (object[key] !== value) { - object[key] = value; - return true; - } - else { - return false; + return array; } -}; - -/** - * Add and event listener. Works for all browsers - * @param {Element} element An html element - * @param {string} action The action, for example "click", - * without the prefix "on" - * @param {function} listener The callback function to be executed - * @param {boolean} [useCapture] - */ -util.addEventListener = function(element, action, listener, useCapture) { - if (element.addEventListener) { - if (useCapture === undefined) - useCapture = false; - if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { - action = "DOMMouseScroll"; // For Firefox + /** + * Update a property in an object + * @param {Object} object + * @param {String} key + * @param {*} value + * @return {Boolean} changed + */ + exports.updateProperty = function(object, key, value) { + if (object[key] !== value) { + object[key] = value; + return true; } + else { + return false; + } + }; - element.addEventListener(action, listener, useCapture); - } else { - element.attachEvent("on" + action, listener); // IE browsers - } -}; + /** + * Add and event listener. Works for all browsers + * @param {Element} element An html element + * @param {string} action The action, for example "click", + * without the prefix "on" + * @param {function} listener The callback function to be executed + * @param {boolean} [useCapture] + */ + exports.addEventListener = function(element, action, listener, useCapture) { + if (element.addEventListener) { + if (useCapture === undefined) + useCapture = false; -/** - * Remove an event listener from an element - * @param {Element} element An html dom element - * @param {string} action The name of the event, for example "mousedown" - * @param {function} listener The listener function - * @param {boolean} [useCapture] - */ -util.removeEventListener = function(element, action, listener, useCapture) { - if (element.removeEventListener) { - // non-IE browsers - if (useCapture === undefined) - useCapture = false; + if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { + action = "DOMMouseScroll"; // For Firefox + } - if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { - action = "DOMMouseScroll"; // For Firefox + element.addEventListener(action, listener, useCapture); + } else { + element.attachEvent("on" + action, listener); // IE browsers } + }; - element.removeEventListener(action, listener, useCapture); - } else { - // IE browsers - element.detachEvent("on" + action, listener); - } -}; + /** + * Remove an event listener from an element + * @param {Element} element An html dom element + * @param {string} action The name of the event, for example "mousedown" + * @param {function} listener The listener function + * @param {boolean} [useCapture] + */ + exports.removeEventListener = function(element, action, listener, useCapture) { + if (element.removeEventListener) { + // non-IE browsers + if (useCapture === undefined) + useCapture = false; + if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { + action = "DOMMouseScroll"; // For Firefox + } -/** - * Get HTML element which is the target of the event - * @param {Event} event - * @return {Element} target element - */ -util.getTarget = function(event) { - // code from http://www.quirksmode.org/js/events_properties.html - if (!event) { - event = window.event; - } + element.removeEventListener(action, listener, useCapture); + } else { + // IE browsers + element.detachEvent("on" + action, listener); + } + }; - var target; - if (event.target) { - target = event.target; - } - else if (event.srcElement) { - target = event.srcElement; - } + /** + * Get HTML element which is the target of the event + * @param {Event} event + * @return {Element} target element + */ + exports.getTarget = function(event) { + // code from http://www.quirksmode.org/js/events_properties.html + if (!event) { + event = window.event; + } - if (target.nodeType != undefined && target.nodeType == 3) { - // defeat Safari bug - target = target.parentNode; - } + var target; - return target; -}; + if (event.target) { + target = event.target; + } + else if (event.srcElement) { + target = event.srcElement; + } -/** - * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent - * @param {Element} element - * @param {Event} event - */ -util.fakeGesture = function(element, event) { - var eventType = null; + if (target.nodeType != undefined && target.nodeType == 3) { + // defeat Safari bug + target = target.parentNode; + } - // for hammer.js 1.0.5 - var gesture = Hammer.event.collectEventData(this, eventType, event); + return target; + }; - // for hammer.js 1.0.6 - //var touches = Hammer.event.getTouchList(event, eventType); - // var gesture = Hammer.event.collectEventData(this, eventType, touches, event); + /** + * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent + * @param {Element} element + * @param {Event} event + */ + exports.fakeGesture = function(element, event) { + var eventType = null; - // on IE in standards mode, no touches are recognized by hammer.js, - // resulting in NaN values for center.pageX and center.pageY - if (isNaN(gesture.center.pageX)) { - gesture.center.pageX = event.pageX; - } - if (isNaN(gesture.center.pageY)) { - gesture.center.pageY = event.pageY; - } + // for hammer.js 1.0.5 + var gesture = Hammer.event.collectEventData(this, eventType, event); - return gesture; -}; + // for hammer.js 1.0.6 + //var touches = Hammer.event.getTouchList(event, eventType); + // var gesture = Hammer.event.collectEventData(this, eventType, touches, event); -util.option = {}; + // on IE in standards mode, no touches are recognized by hammer.js, + // resulting in NaN values for center.pageX and center.pageY + if (isNaN(gesture.center.pageX)) { + gesture.center.pageX = event.pageX; + } + if (isNaN(gesture.center.pageY)) { + gesture.center.pageY = event.pageY; + } -/** - * Convert a value into a boolean - * @param {Boolean | function | undefined} value - * @param {Boolean} [defaultValue] - * @returns {Boolean} bool - */ -util.option.asBoolean = function (value, defaultValue) { - if (typeof value == 'function') { - value = value(); - } + return gesture; + }; - if (value != null) { - return (value != false); - } + exports.option = {}; - return defaultValue || null; -}; + /** + * Convert a value into a boolean + * @param {Boolean | function | undefined} value + * @param {Boolean} [defaultValue] + * @returns {Boolean} bool + */ + exports.option.asBoolean = function (value, defaultValue) { + if (typeof value == 'function') { + value = value(); + } -/** - * Convert a value into a number - * @param {Boolean | function | undefined} value - * @param {Number} [defaultValue] - * @returns {Number} number - */ -util.option.asNumber = function (value, defaultValue) { - if (typeof value == 'function') { - value = value(); - } + if (value != null) { + return (value != false); + } - if (value != null) { - return Number(value) || defaultValue || null; - } + return defaultValue || null; + }; - return defaultValue || null; -}; + /** + * Convert a value into a number + * @param {Boolean | function | undefined} value + * @param {Number} [defaultValue] + * @returns {Number} number + */ + exports.option.asNumber = function (value, defaultValue) { + if (typeof value == 'function') { + value = value(); + } -/** - * Convert a value into a string - * @param {String | function | undefined} value - * @param {String} [defaultValue] - * @returns {String} str - */ -util.option.asString = function (value, defaultValue) { - if (typeof value == 'function') { - value = value(); - } + if (value != null) { + return Number(value) || defaultValue || null; + } - if (value != null) { - return String(value); - } + return defaultValue || null; + }; - return defaultValue || null; -}; + /** + * Convert a value into a string + * @param {String | function | undefined} value + * @param {String} [defaultValue] + * @returns {String} str + */ + exports.option.asString = function (value, defaultValue) { + if (typeof value == 'function') { + value = value(); + } -/** - * Convert a size or location into a string with pixels or a percentage - * @param {String | Number | function | undefined} value - * @param {String} [defaultValue] - * @returns {String} size - */ -util.option.asSize = function (value, defaultValue) { - if (typeof value == 'function') { - value = value(); - } + if (value != null) { + return String(value); + } - if (util.isString(value)) { - return value; - } - else if (util.isNumber(value)) { - return value + 'px'; - } - else { return defaultValue || null; - } -}; - -/** - * Convert a value into a DOM element - * @param {HTMLElement | function | undefined} value - * @param {HTMLElement} [defaultValue] - * @returns {HTMLElement | null} dom - */ -util.option.asElement = function (value, defaultValue) { - if (typeof value == 'function') { - value = value(); - } - - return value || defaultValue || null; -}; - - - -util.GiveDec = function(Hex) { - var Value; - - if (Hex == "A") - Value = 10; - else if (Hex == "B") - Value = 11; - else if (Hex == "C") - Value = 12; - else if (Hex == "D") - Value = 13; - else if (Hex == "E") - Value = 14; - else if (Hex == "F") - Value = 15; - else - Value = eval(Hex); - - return Value; -}; - -util.GiveHex = function(Dec) { - var Value; - - if(Dec == 10) - Value = "A"; - else if (Dec == 11) - Value = "B"; - else if (Dec == 12) - Value = "C"; - else if (Dec == 13) - Value = "D"; - else if (Dec == 14) - Value = "E"; - else if (Dec == 15) - Value = "F"; - else - Value = "" + Dec; - - return Value; -}; + }; -/** - * Parse a color property into an object with border, background, and - * highlight colors - * @param {Object | String} color - * @return {Object} colorObject - */ -util.parseColor = function(color) { - var c; - if (util.isString(color)) { - if (util.isValidHex(color)) { - var hsv = util.hexToHSV(color); - var lighterColorHSV = {h:hsv.h,s:hsv.s * 0.45,v:Math.min(1,hsv.v * 1.05)}; - var darkerColorHSV = {h:hsv.h,s:Math.min(1,hsv.v * 1.25),v:hsv.v*0.6}; - var darkerColorHex = util.HSVToHex(darkerColorHSV.h ,darkerColorHSV.h ,darkerColorHSV.v); - var lighterColorHex = util.HSVToHex(lighterColorHSV.h,lighterColorHSV.s,lighterColorHSV.v); - - c = { - background: color, - border:darkerColorHex, - highlight: { - background:lighterColorHex, - border:darkerColorHex - }, - hover: { - background:lighterColorHex, - border:darkerColorHex - } - }; - } - else { - c = { - background:color, - border:color, - highlight: { - background:color, - border:color - }, - hover: { - background:color, - border:color - } - }; + /** + * Convert a size or location into a string with pixels or a percentage + * @param {String | Number | function | undefined} value + * @param {String} [defaultValue] + * @returns {String} size + */ + exports.option.asSize = function (value, defaultValue) { + if (typeof value == 'function') { + value = value(); } - } - else { - c = {}; - c.background = color.background || 'white'; - c.border = color.border || c.background; - if (util.isString(color.highlight)) { - c.highlight = { - border: color.highlight, - background: color.highlight - } + if (exports.isString(value)) { + return value; + } + else if (exports.isNumber(value)) { + return value + 'px'; } else { - c.highlight = {}; - c.highlight.background = color.highlight && color.highlight.background || c.background; - c.highlight.border = color.highlight && color.highlight.border || c.border; + return defaultValue || null; } + }; - if (util.isString(color.hover)) { - c.hover = { - border: color.hover, - background: color.hover - } - } - else { - c.hover = {}; - c.hover.background = color.hover && color.hover.background || c.background; - c.hover.border = color.hover && color.hover.border || c.border; + /** + * Convert a value into a DOM element + * @param {HTMLElement | function | undefined} value + * @param {HTMLElement} [defaultValue] + * @returns {HTMLElement | null} dom + */ + exports.option.asElement = function (value, defaultValue) { + if (typeof value == 'function') { + value = value(); } - } - return c; -}; + return value || defaultValue || null; + }; -/** - * http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php - * - * @param {String} hex - * @returns {{r: *, g: *, b: *}} - */ -util.hexToRGB = function(hex) { - hex = hex.replace("#","").toUpperCase(); - var a = util.GiveDec(hex.substring(0, 1)); - var b = util.GiveDec(hex.substring(1, 2)); - var c = util.GiveDec(hex.substring(2, 3)); - var d = util.GiveDec(hex.substring(3, 4)); - var e = util.GiveDec(hex.substring(4, 5)); - var f = util.GiveDec(hex.substring(5, 6)); - var r = (a * 16) + b; - var g = (c * 16) + d; - var b = (e * 16) + f; + exports.GiveDec = function(Hex) { + var Value; - return {r:r,g:g,b:b}; -}; + if (Hex == "A") + Value = 10; + else if (Hex == "B") + Value = 11; + else if (Hex == "C") + Value = 12; + else if (Hex == "D") + Value = 13; + else if (Hex == "E") + Value = 14; + else if (Hex == "F") + Value = 15; + else + Value = eval(Hex); -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)); - var d = util.GiveHex(green % 16); - var e = util.GiveHex(Math.floor(blue / 16)); - var f = util.GiveHex(blue % 16); + return Value; + }; - var hex = a + b + c + d + e + f; - return "#" + hex; -}; + exports.GiveHex = function(Dec) { + var Value; + + if(Dec == 10) + Value = "A"; + else if (Dec == 11) + Value = "B"; + else if (Dec == 12) + Value = "C"; + else if (Dec == 13) + Value = "D"; + else if (Dec == 14) + Value = "E"; + else if (Dec == 15) + Value = "F"; + else + Value = "" + Dec; + + return Value; + }; + /** + * Parse a color property into an object with border, background, and + * highlight colors + * @param {Object | String} color + * @return {Object} colorObject + */ + exports.parseColor = function(color) { + var c; + if (exports.isString(color)) { + if (exports.isValidHex(color)) { + var hsv = exports.hexToHSV(color); + var lighterColorHSV = {h:hsv.h,s:hsv.s * 0.45,v:Math.min(1,hsv.v * 1.05)}; + var darkerColorHSV = {h:hsv.h,s:Math.min(1,hsv.v * 1.25),v:hsv.v*0.6}; + var darkerColorHex = exports.HSVToHex(darkerColorHSV.h ,darkerColorHSV.h ,darkerColorHSV.v); + var lighterColorHex = exports.HSVToHex(lighterColorHSV.h,lighterColorHSV.s,lighterColorHSV.v); + + c = { + background: color, + border:darkerColorHex, + highlight: { + background:lighterColorHex, + border:darkerColorHex + }, + hover: { + background:lighterColorHex, + border:darkerColorHex + } + }; + } + else { + c = { + background:color, + border:color, + highlight: { + background:color, + border:color + }, + hover: { + background:color, + border:color + } + }; + } + } + else { + c = {}; + c.background = color.background || 'white'; + c.border = color.border || c.background; -/** - * http://www.javascripter.net/faq/rgb2hsv.htm - * - * @param red - * @param green - * @param blue - * @returns {*} - * @constructor - */ -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)); - - // Black-gray-white - if (minRGB == maxRGB) { - return {h:0,s:0,v:minRGB}; - } + if (exports.isString(color.highlight)) { + c.highlight = { + border: color.highlight, + background: color.highlight + } + } + else { + c.highlight = {}; + c.highlight.background = color.highlight && color.highlight.background || c.background; + c.highlight.border = color.highlight && color.highlight.border || c.border; + } - // Colors other than black-gray-white: - var d = (red==minRGB) ? green-blue : ((blue==minRGB) ? red-green : blue-red); - var h = (red==minRGB) ? 3 : ((blue==minRGB) ? 1 : 5); - var hue = 60*(h - d/(maxRGB - minRGB))/360; - var saturation = (maxRGB - minRGB)/maxRGB; - var value = maxRGB; - return {h:hue,s:saturation,v:value}; -}; + if (exports.isString(color.hover)) { + c.hover = { + border: color.hover, + background: color.hover + } + } + else { + c.hover = {}; + c.hover.background = color.hover && color.hover.background || c.background; + c.hover.border = color.hover && color.hover.border || c.border; + } + } + return c; + }; -/** - * https://gist.github.com/mjijackson/5311256 - * @param hue - * @param saturation - * @param value - * @returns {{r: number, g: number, b: number}} - * @constructor - */ -util.HSVToRGB = function(h, s, v) { - var r, g, b; - - var i = Math.floor(h * 6); - var f = h * 6 - i; - var p = v * (1 - s); - var q = v * (1 - f * s); - var t = v * (1 - (1 - f) * s); - - switch (i % 6) { - case 0: r = v, g = t, b = p; break; - case 1: r = q, g = v, b = p; break; - case 2: r = p, g = v, b = t; break; - case 3: r = p, g = q, b = v; break; - case 4: r = t, g = p, b = v; break; - case 5: r = v, g = p, b = q; break; - } + /** + * http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php + * + * @param {String} hex + * @returns {{r: *, g: *, b: *}} + */ + exports.hexToRGB = function(hex) { + hex = hex.replace("#","").toUpperCase(); - return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) }; -}; + var a = exports.GiveDec(hex.substring(0, 1)); + var b = exports.GiveDec(hex.substring(1, 2)); + var c = exports.GiveDec(hex.substring(2, 3)); + var d = exports.GiveDec(hex.substring(3, 4)); + var e = exports.GiveDec(hex.substring(4, 5)); + var f = exports.GiveDec(hex.substring(5, 6)); -util.HSVToHex = function(h, s, v) { - var rgb = util.HSVToRGB(h, s, v); - return util.RGBToHex(rgb.r, rgb.g, rgb.b); -}; + var r = (a * 16) + b; + var g = (c * 16) + d; + var b = (e * 16) + f; -util.hexToHSV = function(hex) { - var rgb = util.hexToRGB(hex); - return util.RGBToHSV(rgb.r, rgb.g, rgb.b); -}; + return {r:r,g:g,b:b}; + }; -util.isValidHex = function(hex) { - var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex); - return isOk; -}; + exports.RGBToHex = function(red,green,blue) { + var a = exports.GiveHex(Math.floor(red / 16)); + var b = exports.GiveHex(red % 16); + var c = exports.GiveHex(Math.floor(green / 16)); + var d = exports.GiveHex(green % 16); + var e = exports.GiveHex(Math.floor(blue / 16)); + var f = exports.GiveHex(blue % 16); + var hex = a + b + c + d + e + f; + return "#" + hex; + }; -/** - * This recursively redirects the prototype of JSON objects to the referenceObject - * This is used for default options. - * - * @param referenceObject - * @returns {*} - */ -util.selectiveBridgeObject = function(fields, referenceObject) { - if (typeof referenceObject == "object") { - var objectTo = Object.create(referenceObject); - for (var i = 0; i < fields.length; i++) { - if (referenceObject.hasOwnProperty(fields[i])) { - if (typeof referenceObject[fields[i]] == "object") { - objectTo[fields[i]] = util.bridgeObject(referenceObject[fields[i]]); - } - } - } - return objectTo; - } - else { - return null; - } -}; -/** - * This recursively redirects the prototype of JSON objects to the referenceObject - * This is used for default options. - * - * @param referenceObject - * @returns {*} - */ -util.bridgeObject = function(referenceObject) { - if (typeof referenceObject == "object") { - var objectTo = Object.create(referenceObject); - for (var i in referenceObject) { - if (referenceObject.hasOwnProperty(i)) { - if (typeof referenceObject[i] == "object") { - objectTo[i] = util.bridgeObject(referenceObject[i]); + /** + * http://www.javascripter.net/faq/rgb2hsv.htm + * + * @param red + * @param green + * @param blue + * @returns {*} + * @constructor + */ + exports.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)); + + // Black-gray-white + if (minRGB == maxRGB) { + return {h:0,s:0,v:minRGB}; + } + + // Colors other than black-gray-white: + var d = (red==minRGB) ? green-blue : ((blue==minRGB) ? red-green : blue-red); + var h = (red==minRGB) ? 3 : ((blue==minRGB) ? 1 : 5); + var hue = 60*(h - d/(maxRGB - minRGB))/360; + var saturation = (maxRGB - minRGB)/maxRGB; + var value = maxRGB; + return {h:hue,s:saturation,v:value}; + }; + + + /** + * https://gist.github.com/mjijackson/5311256 + * @param hue + * @param saturation + * @param value + * @returns {{r: number, g: number, b: number}} + * @constructor + */ + exports.HSVToRGB = function(h, s, v) { + var r, g, b; + + var i = Math.floor(h * 6); + var f = h * 6 - i; + var p = v * (1 - s); + var q = v * (1 - f * s); + var t = v * (1 - (1 - f) * s); + + switch (i % 6) { + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + } + + return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) }; + }; + + exports.HSVToHex = function(h, s, v) { + var rgb = exports.HSVToRGB(h, s, v); + return exports.RGBToHex(rgb.r, rgb.g, rgb.b); + }; + + exports.hexToHSV = function(hex) { + var rgb = exports.hexToRGB(hex); + return exports.RGBToHSV(rgb.r, rgb.g, rgb.b); + }; + + exports.isValidHex = function(hex) { + var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex); + return isOk; + }; + + + /** + * This recursively redirects the prototype of JSON objects to the referenceObject + * This is used for default options. + * + * @param referenceObject + * @returns {*} + */ + exports.selectiveBridgeObject = function(fields, referenceObject) { + if (typeof referenceObject == "object") { + var objectTo = Object.create(referenceObject); + for (var i = 0; i < fields.length; i++) { + if (referenceObject.hasOwnProperty(fields[i])) { + if (typeof referenceObject[fields[i]] == "object") { + objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]); + } } } + return objectTo; } - return objectTo; - } - else { - return null; - } -}; - + else { + return null; + } + }; -/** - * this is used to set the options of subobjects in the options object. A requirement of these subobjects - * is that they have an 'enabled' element which is optional for the user but mandatory for the program. - * - * @param [object] mergeTarget | this is either this.options or the options used for the groups. - * @param [object] options | options - * @param [String] option | this is the option key in the options argument - * @private - */ -util.mergeOptions = function (mergeTarget, options, option) { - if (options[option] !== undefined) { - if (typeof options[option] == 'boolean') { - mergeTarget[option].enabled = options[option]; + /** + * This recursively redirects the prototype of JSON objects to the referenceObject + * This is used for default options. + * + * @param referenceObject + * @returns {*} + */ + exports.bridgeObject = function(referenceObject) { + if (typeof referenceObject == "object") { + var objectTo = Object.create(referenceObject); + for (var i in referenceObject) { + if (referenceObject.hasOwnProperty(i)) { + if (typeof referenceObject[i] == "object") { + objectTo[i] = exports.bridgeObject(referenceObject[i]); + } + } + } + return objectTo; } else { - mergeTarget[option].enabled = true; - for (prop in options[option]) { - if (options[option].hasOwnProperty(prop)) { - mergeTarget[option][prop] = options[option][prop]; + return null; + } + }; + + + /** + * this is used to set the options of subobjects in the options object. A requirement of these subobjects + * is that they have an 'enabled' element which is optional for the user but mandatory for the program. + * + * @param [object] mergeTarget | this is either this.options or the options used for the groups. + * @param [object] options | options + * @param [String] option | this is the option key in the options argument + * @private + */ + exports.mergeOptions = function (mergeTarget, options, option) { + if (options[option] !== undefined) { + if (typeof options[option] == 'boolean') { + mergeTarget[option].enabled = options[option]; + } + else { + mergeTarget[option].enabled = true; + for (prop in options[option]) { + if (options[option].hasOwnProperty(prop)) { + mergeTarget[option][prop] = options[option][prop]; + } } } } } -} -/** - * this is used to set the options of subobjects in the options object. A requirement of these subobjects - * is that they have an 'enabled' element which is optional for the user but mandatory for the program. - * - * @param [object] mergeTarget | this is either this.options or the options used for the groups. - * @param [object] options | options - * @param [String] option | this is the option key in the options argument - * @private - */ -util.mergeOptions = function (mergeTarget, options, option) { - if (options[option] !== undefined) { - if (typeof options[option] == 'boolean') { - mergeTarget[option].enabled = options[option]; - } - else { - mergeTarget[option].enabled = true; - for (prop in options[option]) { - if (options[option].hasOwnProperty(prop)) { - mergeTarget[option][prop] = options[option][prop]; + /** + * this is used to set the options of subobjects in the options object. A requirement of these subobjects + * is that they have an 'enabled' element which is optional for the user but mandatory for the program. + * + * @param [object] mergeTarget | this is either this.options or the options used for the groups. + * @param [object] options | options + * @param [String] option | this is the option key in the options argument + * @private + */ + exports.mergeOptions = function (mergeTarget, options, option) { + if (options[option] !== undefined) { + if (typeof options[option] == 'boolean') { + mergeTarget[option].enabled = options[option]; + } + else { + mergeTarget[option].enabled = true; + for (prop in options[option]) { + if (options[option].hasOwnProperty(prop)) { + mergeTarget[option][prop] = options[option][prop]; + } } } } } -} -/** - * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd - * arrays. This is done by giving a boolean value true if you want to use the byEnd. - * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check - * if the time we selected (start or end) is within the current range). - * - * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is - * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest, - * either the start OR end time has to be in the range. - * - * @param {{byStart: Item[], byEnd: Item[]}} orderedItems - * @param {{start: number, end: number}} range - * @param {Boolean} byEnd - * @returns {number} - * @private - */ -util.binarySearch = function(orderedItems, range, field, field2) { - var array = orderedItems; - var interval = range.end - range.start; - - var found = false; - var low = 0; - var high = array.length; - var guess = Math.floor(0.5*(high+low)); - var newGuess; - var value; - - if (high == 0) {guess = -1;} - else if (high == 1) { - value = field2 === undefined ? array[guess][field] : array[guess][field][field2]; - if ((value > range.start - interval) && (value < range.end)) { - guess = 0; - } - else { - guess = -1; - } - } - else { - high -= 1; - while (found == false) { + /** + * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd + * arrays. This is done by giving a boolean value true if you want to use the byEnd. + * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check + * if the time we selected (start or end) is within the current range). + * + * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is + * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest, + * either the start OR end time has to be in the range. + * + * @param {{byStart: Item[], byEnd: Item[]}} orderedItems + * @param {{start: number, end: number}} range + * @param {Boolean} byEnd + * @returns {number} + * @private + */ + exports.binarySearch = function(orderedItems, range, field, field2) { + var array = orderedItems; + var interval = range.end - range.start; + + var found = false; + var low = 0; + var high = array.length; + var guess = Math.floor(0.5*(high+low)); + var newGuess; + var value; + + if (high == 0) {guess = -1;} + else if (high == 1) { value = field2 === undefined ? array[guess][field] : array[guess][field][field2]; if ((value > range.start - interval) && (value < range.end)) { - found = true; + guess = 0; } else { - if (value < range.start - interval) { // it is too small --> increase low - low = Math.floor(0.5*(high+low)); - } - else { // it is too big --> decrease high - high = Math.floor(0.5*(high+low)); - } - newGuess = Math.floor(0.5*(high+low)); - // not in list; - if (guess == newGuess) { - guess = -1; + guess = -1; + } + } + else { + high -= 1; + while (found == false) { + value = field2 === undefined ? array[guess][field] : array[guess][field][field2]; + if ((value > range.start - interval) && (value < range.end)) { found = true; } else { - guess = newGuess; + if (value < range.start - interval) { // it is too small --> increase low + low = Math.floor(0.5*(high+low)); + } + else { // it is too big --> decrease high + high = Math.floor(0.5*(high+low)); + } + newGuess = Math.floor(0.5*(high+low)); + // not in list; + if (guess == newGuess) { + guess = -1; + found = true; + } + else { + guess = newGuess; + } } } } - } - return guess; -}; + return guess; + }; -/** - * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd - * arrays. This is done by giving a boolean value true if you want to use the byEnd. - * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check - * if the time we selected (start or end) is within the current range). - * - * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is - * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest, - * either the start OR end time has to be in the range. - * - * @param {Array} orderedItems - * @param {{start: number, end: number}} target - * @param {Boolean} byEnd - * @returns {number} - * @private - */ -util.binarySearchGeneric = function(orderedItems, target, field, sidePreference) { - var array = orderedItems; - var found = false; - var low = 0; - var high = array.length; - var guess = Math.floor(0.5*(high+low)); - var newGuess; - var prevValue, value, nextValue; - - if (high == 0) {guess = -1;} - else if (high == 1) { - value = array[guess][field]; - if (value == target) { - guess = 0; + /** + * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd + * arrays. This is done by giving a boolean value true if you want to use the byEnd. + * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check + * if the time we selected (start or end) is within the current range). + * + * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is + * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest, + * either the start OR end time has to be in the range. + * + * @param {Array} orderedItems + * @param {{start: number, end: number}} target + * @param {Boolean} byEnd + * @returns {number} + * @private + */ + exports.binarySearchGeneric = function(orderedItems, target, field, sidePreference) { + var array = orderedItems; + var found = false; + var low = 0; + var high = array.length; + var guess = Math.floor(0.5*(high+low)); + var newGuess; + var prevValue, value, nextValue; + + if (high == 0) {guess = -1;} + else if (high == 1) { + value = array[guess][field]; + if (value == target) { + guess = 0; + } + else { + guess = -1; + } } else { - guess = -1; - } - } - else { - high -= 1; - while (found == false) { - prevValue = array[Math.max(0,guess - 1)][field]; - value = array[guess][field]; - nextValue = array[Math.min(array.length-1,guess + 1)][field]; - - if (value == target || prevValue < target && value > target || value < target && nextValue > target) { - found = true; - if (value != target) { - if (sidePreference == 'before') { - if (prevValue < target && value > target) { - guess = Math.max(0,guess - 1); + high -= 1; + while (found == false) { + prevValue = array[Math.max(0,guess - 1)][field]; + value = array[guess][field]; + nextValue = array[Math.min(array.length-1,guess + 1)][field]; + + if (value == target || prevValue < target && value > target || value < target && nextValue > target) { + found = true; + if (value != target) { + if (sidePreference == 'before') { + if (prevValue < target && value > target) { + guess = Math.max(0,guess - 1); + } } - } - else { - if (value < target && nextValue > target) { - guess = Math.min(array.length-1,guess + 1); + else { + if (value < target && nextValue > target) { + guess = Math.min(array.length-1,guess + 1); + } } } } - } - else { - if (value < target) { // it is too small --> increase low - low = Math.floor(0.5*(high+low)); - } - else { // it is too big --> decrease high - high = Math.floor(0.5*(high+low)); - } - newGuess = Math.floor(0.5*(high+low)); - // not in list; - if (guess == newGuess) { - guess = -2; - found = true; - } else { - guess = newGuess; + if (value < target) { // it is too small --> increase low + low = Math.floor(0.5*(high+low)); + } + else { // it is too big --> decrease high + high = Math.floor(0.5*(high+low)); + } + newGuess = Math.floor(0.5*(high+low)); + // not in list; + if (guess == newGuess) { + guess = -2; + found = true; + } + else { + guess = newGuess; + } } } } - } - return guess; -}; -/** - * Created by Alex on 6/20/14. - */ + return guess; + }; -var DOMutil = {}; +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { -/** - * this prepares the JSON container for allocating SVG elements - * @param JSONcontainer - * @private - */ -DOMutil.prepareElements = function(JSONcontainer) { - // cleanup the redundant svgElements; - for (var elementType in JSONcontainer) { - if (JSONcontainer.hasOwnProperty(elementType)) { - JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; - JSONcontainer[elementType].used = []; + // DOM utility methods + + /** + * this prepares the JSON container for allocating SVG elements + * @param JSONcontainer + * @private + */ + exports.prepareElements = function(JSONcontainer) { + // cleanup the redundant svgElements; + for (var elementType in JSONcontainer) { + if (JSONcontainer.hasOwnProperty(elementType)) { + JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; + JSONcontainer[elementType].used = []; + } } - } -}; + }; -/** - * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from - * which to remove the redundant elements. - * - * @param JSONcontainer - * @private - */ -DOMutil.cleanupElements = function(JSONcontainer) { - // cleanup the redundant svgElements; - for (var elementType in JSONcontainer) { - if (JSONcontainer.hasOwnProperty(elementType)) { - if (JSONcontainer[elementType].redundant) { - for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { - JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); + /** + * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from + * which to remove the redundant elements. + * + * @param JSONcontainer + * @private + */ + exports.cleanupElements = function(JSONcontainer) { + // cleanup the redundant svgElements; + for (var elementType in JSONcontainer) { + if (JSONcontainer.hasOwnProperty(elementType)) { + if (JSONcontainer[elementType].redundant) { + for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { + JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); + } + JSONcontainer[elementType].redundant = []; } - JSONcontainer[elementType].redundant = []; } } - } -}; + }; -/** - * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer - * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. - * - * @param elementType - * @param JSONcontainer - * @param svgContainer - * @returns {*} - * @private - */ -DOMutil.getSVGElement = function (elementType, JSONcontainer, svgContainer) { - var element; - // allocate SVG element, if it doesnt yet exist, create one. - if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before - // check if there is an redundant element - if (JSONcontainer[elementType].redundant.length > 0) { - element = JSONcontainer[elementType].redundant[0]; - JSONcontainer[elementType].redundant.shift(); + /** + * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer + * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. + * + * @param elementType + * @param JSONcontainer + * @param svgContainer + * @returns {*} + * @private + */ + exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { + var element; + // allocate SVG element, if it doesnt yet exist, create one. + if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before + // check if there is an redundant element + if (JSONcontainer[elementType].redundant.length > 0) { + element = JSONcontainer[elementType].redundant[0]; + JSONcontainer[elementType].redundant.shift(); + } + else { + // create a new element and add it to the SVG + element = document.createElementNS('http://www.w3.org/2000/svg', elementType); + svgContainer.appendChild(element); + } } else { - // create a new element and add it to the SVG + // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. element = document.createElementNS('http://www.w3.org/2000/svg', elementType); + JSONcontainer[elementType] = {used: [], redundant: []}; svgContainer.appendChild(element); } - } - else { - // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. - element = document.createElementNS('http://www.w3.org/2000/svg', elementType); - JSONcontainer[elementType] = {used: [], redundant: []}; - svgContainer.appendChild(element); - } - JSONcontainer[elementType].used.push(element); - return element; -}; + JSONcontainer[elementType].used.push(element); + return element; + }; -/** - * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer - * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. - * - * @param elementType - * @param JSONcontainer - * @param DOMContainer - * @returns {*} - * @private - */ -DOMutil.getDOMElement = function (elementType, JSONcontainer, DOMContainer) { - var element; - // allocate SVG element, if it doesnt yet exist, create one. - if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before - // check if there is an redundant element - if (JSONcontainer[elementType].redundant.length > 0) { - element = JSONcontainer[elementType].redundant[0]; - JSONcontainer[elementType].redundant.shift(); + /** + * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer + * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. + * + * @param elementType + * @param JSONcontainer + * @param DOMContainer + * @returns {*} + * @private + */ + exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer) { + var element; + // allocate SVG element, if it doesnt yet exist, create one. + if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before + // check if there is an redundant element + if (JSONcontainer[elementType].redundant.length > 0) { + element = JSONcontainer[elementType].redundant[0]; + JSONcontainer[elementType].redundant.shift(); + } + else { + // create a new element and add it to the SVG + element = document.createElement(elementType); + DOMContainer.appendChild(element); + } } else { - // create a new element and add it to the SVG + // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. element = document.createElement(elementType); + JSONcontainer[elementType] = {used: [], redundant: []}; DOMContainer.appendChild(element); } - } - else { - // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. - element = document.createElement(elementType); - JSONcontainer[elementType] = {used: [], redundant: []}; - DOMContainer.appendChild(element); - } - JSONcontainer[elementType].used.push(element); - return element; -}; + JSONcontainer[elementType].used.push(element); + return element; + }; -/** - * draw a point object. this is a seperate function because it can also be called by the legend. - * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions - * as well. - * - * @param x - * @param y - * @param group - * @param JSONcontainer - * @param svgContainer - * @returns {*} - */ -DOMutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { - var point; - if (group.options.drawPoints.style == 'circle') { - point = DOMutil.getSVGElement('circle',JSONcontainer,svgContainer); - point.setAttributeNS(null, "cx", x); - point.setAttributeNS(null, "cy", y); - point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); - point.setAttributeNS(null, "class", group.className + " point"); - } - else { - point = DOMutil.getSVGElement('rect',JSONcontainer,svgContainer); - point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); - point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); - point.setAttributeNS(null, "width", group.options.drawPoints.size); - point.setAttributeNS(null, "height", group.options.drawPoints.size); - point.setAttributeNS(null, "class", group.className + " point"); - } - return point; -}; + /** + * draw a point object. this is a seperate function because it can also be called by the legend. + * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions + * as well. + * + * @param x + * @param y + * @param group + * @param JSONcontainer + * @param svgContainer + * @returns {*} + */ + exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { + var point; + if (group.options.drawPoints.style == 'circle') { + point = exports.getSVGElement('circle',JSONcontainer,svgContainer); + point.setAttributeNS(null, "cx", x); + point.setAttributeNS(null, "cy", y); + point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); + point.setAttributeNS(null, "class", group.className + " point"); + } + else { + point = exports.getSVGElement('rect',JSONcontainer,svgContainer); + point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); + point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); + point.setAttributeNS(null, "width", group.options.drawPoints.size); + point.setAttributeNS(null, "height", group.options.drawPoints.size); + point.setAttributeNS(null, "class", group.className + " point"); + } + return point; + }; -/** - * draw a bar SVG element centered on the X coordinate - * - * @param x - * @param y - * @param className - */ -DOMutil.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { - var rect = DOMutil.getSVGElement('rect',JSONcontainer, svgContainer); - rect.setAttributeNS(null, "x", x - 0.5 * width); - rect.setAttributeNS(null, "y", y); - rect.setAttributeNS(null, "width", width); - rect.setAttributeNS(null, "height", height); - rect.setAttributeNS(null, "class", className); -}; -/** - * DataSet - * - * Usage: - * var dataSet = new DataSet({ - * fieldId: '_id', - * type: { - * // ... - * } - * }); - * - * dataSet.add(item); - * dataSet.add(data); - * dataSet.update(item); - * dataSet.update(data); - * dataSet.remove(id); - * dataSet.remove(ids); - * var data = dataSet.get(); - * var data = dataSet.get(id); - * var data = dataSet.get(ids); - * var data = dataSet.get(ids, options, data); - * dataSet.clear(); - * - * A data set can: - * - add/remove/update data - * - gives triggers upon changes in the data - * - can import/export data in various data formats - * - * @param {Array | DataTable} [data] Optional array with initial data - * @param {Object} [options] Available options: - * {String} fieldId Field name of the id in the - * items, 'id' by default. - * {Object.} [type] - * {String[]} [fields] field names to be returned - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * {Array | DataTable} [data] If provided, items will be appended to this - * array or table. Required in case of Google - * DataTable. - * - * @throws Error - */ -DataSet.prototype.get = function (args) { - var me = this; - - // parse the arguments - var id, ids, options, data; - var firstType = util.getType(arguments[0]); - if (firstType == 'String' || firstType == 'Number') { - // get(id [, options] [, data]) - id = arguments[0]; - options = arguments[1]; - data = arguments[2]; - } - else if (firstType == 'Array') { - // get(ids [, options] [, data]) - ids = arguments[0]; - options = arguments[1]; - data = arguments[2]; - } - else { - // get([, options] [, data]) - options = arguments[0]; - data = arguments[1]; - } + return addedIds.concat(updatedIds); + }; - // determine the return type - var returnType; - if (options && options.returnType) { - returnType = (options.returnType == 'DataTable') ? 'DataTable' : 'Array'; + /** + * Get a data item or multiple items. + * + * Usage: + * + * get() + * get(options: Object) + * get(options: Object, data: Array | DataTable) + * + * get(id: Number | String) + * get(id: Number | String, options: Object) + * get(id: Number | String, options: Object, data: Array | DataTable) + * + * get(ids: Number[] | String[]) + * get(ids: Number[] | String[], options: Object) + * get(ids: Number[] | String[], options: Object, data: Array | DataTable) + * + * Where: + * + * {Number | String} id The id of an item + * {Number[] | String{}} ids An array with ids of items + * {Object} options An Object with options. Available options: + * {String} [returnType] Type of data to be + * returned. Can be 'DataTable' or 'Array' (default) + * {Object.} [type] + * {String[]} [fields] field names to be returned + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * {Array | DataTable} [data] If provided, items will be appended to this + * array or table. Required in case of Google + * DataTable. + * + * @throws Error + */ + DataSet.prototype.get = function (args) { + var me = this; - if (data && (returnType != util.getType(data))) { - throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' + - 'does not correspond with specified options.type (' + options.type + ')'); + // parse the arguments + var id, ids, options, data; + var firstType = util.getType(arguments[0]); + if (firstType == 'String' || firstType == 'Number') { + // get(id [, options] [, data]) + id = arguments[0]; + options = arguments[1]; + data = arguments[2]; + } + else if (firstType == 'Array') { + // get(ids [, options] [, data]) + ids = arguments[0]; + options = arguments[1]; + data = arguments[2]; } - if (returnType == 'DataTable' && !util.isDataTable(data)) { - throw new Error('Parameter "data" must be a DataTable ' + - 'when options.type is "DataTable"'); + else { + // get([, options] [, data]) + options = arguments[0]; + data = arguments[1]; } - } - else if (data) { - returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array'; - } - else { - returnType = 'Array'; - } - // build options - var type = options && options.type || this._options.type; - var filter = options && options.filter; - var items = [], item, itemId, i, len; + // determine the return type + var returnType; + if (options && options.returnType) { + returnType = (options.returnType == 'DataTable') ? 'DataTable' : 'Array'; - // convert items - if (id != undefined) { - // return a single item - item = me._getItem(id, type); - if (filter && !filter(item)) { - item = null; + if (data && (returnType != util.getType(data))) { + throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' + + 'does not correspond with specified options.type (' + options.type + ')'); + } + if (returnType == 'DataTable' && !util.isDataTable(data)) { + throw new Error('Parameter "data" must be a DataTable ' + + 'when options.type is "DataTable"'); + } } - } - else if (ids != undefined) { - // return a subset of items - for (i = 0, len = ids.length; i < len; i++) { - item = me._getItem(ids[i], type); - if (!filter || filter(item)) { - items.push(item); + else if (data) { + returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array'; + } + else { + returnType = 'Array'; + } + + // build options + var type = options && options.type || this._options.type; + var filter = options && options.filter; + var items = [], item, itemId, i, len; + + // convert items + if (id != undefined) { + // return a single item + item = me._getItem(id, type); + if (filter && !filter(item)) { + item = null; } } - } - else { - // return all items - for (itemId in this._data) { - if (this._data.hasOwnProperty(itemId)) { - item = me._getItem(itemId, type); + else if (ids != undefined) { + // return a subset of items + for (i = 0, len = ids.length; i < len; i++) { + item = me._getItem(ids[i], type); if (!filter || filter(item)) { items.push(item); } } } - } - - // order the results - if (options && options.order && id == undefined) { - this._sort(items, options.order); - } - - // filter fields of the items - if (options && options.fields) { - var fields = options.fields; - if (id != undefined) { - item = this._filterFields(item, fields); - } else { - for (i = 0, len = items.length; i < len; i++) { - items[i] = this._filterFields(items[i], fields); + // return all items + for (itemId in this._data) { + if (this._data.hasOwnProperty(itemId)) { + item = me._getItem(itemId, type); + if (!filter || filter(item)) { + items.push(item); + } + } } } - } - // return the results - if (returnType == 'DataTable') { - var columns = this._getColumnNames(data); - if (id != undefined) { - // append a single item to the data table - me._appendRow(data, columns, item); + // order the results + if (options && options.order && id == undefined) { + this._sort(items, options.order); } - else { - // copy the items to the provided data table - for (i = 0, len = items.length; i < len; i++) { - me._appendRow(data, columns, items[i]); + + // filter fields of the items + if (options && options.fields) { + var fields = options.fields; + if (id != undefined) { + item = this._filterFields(item, fields); + } + else { + for (i = 0, len = items.length; i < len; i++) { + items[i] = this._filterFields(items[i], fields); + } } } - return data; - } - else { - // return an array - if (id != undefined) { - // a single item - return item; - } - else { - // multiple items - if (data) { - // copy the items to the provided array + + // return the results + if (returnType == 'DataTable') { + var columns = this._getColumnNames(data); + if (id != undefined) { + // append a single item to the data table + me._appendRow(data, columns, item); + } + else { + // copy the items to the provided data table for (i = 0, len = items.length; i < len; i++) { - data.push(items[i]); + me._appendRow(data, columns, items[i]); } - return data; + } + return data; + } + else { + // return an array + if (id != undefined) { + // a single item + return item; } else { - // just return our array - return items; + // multiple items + if (data) { + // copy the items to the provided array + for (i = 0, len = items.length; i < len; i++) { + data.push(items[i]); + } + return data; + } + else { + // just return our array + return items; + } } } - } -}; - -/** - * Get ids of all items or from a filtered set of items. - * @param {Object} [options] An Object with options. Available options: - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Array} ids - */ -DataSet.prototype.getIds = function (options) { - var data = this._data, - filter = options && options.filter, - order = options && options.order, - type = options && options.type || this._options.type, - i, - len, - id, - item, - items, - ids = []; + }; - if (filter) { - // get filtered items - if (order) { - // create ordered list - items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - items.push(item); + /** + * Get ids of all items or from a filtered set of items. + * @param {Object} [options] An Object with options. Available options: + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Array} ids + */ + DataSet.prototype.getIds = function (options) { + var data = this._data, + filter = options && options.filter, + order = options && options.order, + type = options && options.type || this._options.type, + i, + len, + id, + item, + items, + ids = []; + + if (filter) { + // get filtered items + if (order) { + // create ordered list + items = []; + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (filter(item)) { + items.push(item); + } } } - } - this._sort(items, order); + this._sort(items, order); - for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; + for (i = 0, len = items.length; i < len; i++) { + ids[i] = items[i][this._fieldId]; + } + } + else { + // create unordered list + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (filter(item)) { + ids.push(item[this._fieldId]); + } + } + } } } else { - // create unordered list - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - ids.push(item[this._fieldId]); + // get all items + if (order) { + // create an ordered list + items = []; + for (id in data) { + if (data.hasOwnProperty(id)) { + items.push(data[id]); } } + + this._sort(items, order); + + for (i = 0, len = items.length; i < len; i++) { + ids[i] = items[i][this._fieldId]; + } } - } - } - else { - // get all items - if (order) { - // create an ordered list - items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - items.push(data[id]); + else { + // create unordered list + for (id in data) { + if (data.hasOwnProperty(id)) { + item = data[id]; + ids.push(item[this._fieldId]); + } } } + } + + return ids; + }; + + /** + * Returns the DataSet itself. Is overwritten for example by the DataView, + * which returns the DataSet it is connected to instead. + */ + DataSet.prototype.getDataSet = function () { + return this; + }; - this._sort(items, order); + /** + * Execute a callback function for every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + */ + DataSet.prototype.forEach = function (callback, options) { + var filter = options && options.filter, + type = options && options.type || this._options.type, + data = this._data, + item, + id; - for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; + if (options && options.order) { + // execute forEach on ordered list + var items = this.get(options); + + for (var i = 0, len = items.length; i < len; i++) { + item = items[i]; + id = item[this._fieldId]; + callback(item, id); } } else { - // create unordered list + // unordered for (id in data) { if (data.hasOwnProperty(id)) { - item = data[id]; - ids.push(item[this._fieldId]); + item = this._getItem(id, type); + if (!filter || filter(item)) { + callback(item, id); + } } } } - } - - return ids; -}; - -/** - * Returns the DataSet itself. Is overwritten for example by the DataView, - * which returns the DataSet it is connected to instead. - */ -DataSet.prototype.getDataSet = function () { - return this; -}; + }; -/** - * Execute a callback function for every item in the dataset. - * @param {function} callback - * @param {Object} [options] Available options: - * {Object.} [type] - * {String[]} [fields] filter fields - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - */ -DataSet.prototype.forEach = function (callback, options) { - var filter = options && options.filter, - type = options && options.type || this._options.type, - data = this._data, - item, - id; - - if (options && options.order) { - // execute forEach on ordered list - var items = this.get(options); - - for (var i = 0, len = items.length; i < len; i++) { - item = items[i]; - id = item[this._fieldId]; - callback(item, id); - } - } - else { - // unordered - for (id in data) { + /** + * Map every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Object[]} mappedItems + */ + DataSet.prototype.map = function (callback, options) { + var filter = options && options.filter, + type = options && options.type || this._options.type, + mappedItems = [], + data = this._data, + item; + + // convert and filter items + for (var id in data) { if (data.hasOwnProperty(id)) { item = this._getItem(id, type); if (!filter || filter(item)) { - callback(item, id); + mappedItems.push(callback(item, id)); } } } - } -}; - -/** - * Map every item in the dataset. - * @param {function} callback - * @param {Object} [options] Available options: - * {Object.} [type] - * {String[]} [fields] filter fields - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Object[]} mappedItems - */ -DataSet.prototype.map = function (callback, options) { - var filter = options && options.filter, - type = options && options.type || this._options.type, - mappedItems = [], - data = this._data, - item; - // convert and filter items - for (var id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - mappedItems.push(callback(item, id)); - } + // order items + if (options && options.order) { + this._sort(mappedItems, options.order); } - } - // order items - if (options && options.order) { - this._sort(mappedItems, options.order); - } - - return mappedItems; -}; + return mappedItems; + }; -/** - * Filter the fields of an item - * @param {Object} item - * @param {String[]} fields Field names - * @return {Object} filteredItem - * @private - */ -DataSet.prototype._filterFields = function (item, fields) { - var filteredItem = {}; + /** + * Filter the fields of an item + * @param {Object} item + * @param {String[]} fields Field names + * @return {Object} filteredItem + * @private + */ + DataSet.prototype._filterFields = function (item, fields) { + var filteredItem = {}; - for (var field in item) { - if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { - filteredItem[field] = item[field]; + for (var field in item) { + if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { + filteredItem[field] = item[field]; + } } - } - return filteredItem; -}; + return filteredItem; + }; -/** - * Sort the provided array with items - * @param {Object[]} items - * @param {String | function} order A field name or custom sort function. - * @private - */ -DataSet.prototype._sort = function (items, order) { - if (util.isString(order)) { - // order by provided field name - var name = order; // field name - items.sort(function (a, b) { - var av = a[name]; - var bv = b[name]; - return (av > bv) ? 1 : ((av < bv) ? -1 : 0); - }); - } - else if (typeof order === 'function') { - // order by sort function - items.sort(order); - } - // TODO: extend order by an Object {field:String, direction:String} - // where direction can be 'asc' or 'desc' - else { - throw new TypeError('Order must be a function or a string'); - } -}; + /** + * Sort the provided array with items + * @param {Object[]} items + * @param {String | function} order A field name or custom sort function. + * @private + */ + DataSet.prototype._sort = function (items, order) { + if (util.isString(order)) { + // order by provided field name + var name = order; // field name + items.sort(function (a, b) { + var av = a[name]; + var bv = b[name]; + return (av > bv) ? 1 : ((av < bv) ? -1 : 0); + }); + } + else if (typeof order === 'function') { + // order by sort function + items.sort(order); + } + // TODO: extend order by an Object {field:String, direction:String} + // where direction can be 'asc' or 'desc' + else { + throw new TypeError('Order must be a function or a string'); + } + }; -/** - * Remove an object by pointer or by id - * @param {String | Number | Object | Array} id Object or id, or an array with - * objects or ids to be removed - * @param {String} [senderId] Optional sender id - * @return {Array} removedIds - */ -DataSet.prototype.remove = function (id, senderId) { - var removedIds = [], - i, len, removedId; + /** + * Remove an object by pointer or by id + * @param {String | Number | Object | Array} id Object or id, or an array with + * objects or ids to be removed + * @param {String} [senderId] Optional sender id + * @return {Array} removedIds + */ + DataSet.prototype.remove = function (id, senderId) { + var removedIds = [], + i, len, removedId; - if (Array.isArray(id)) { - for (i = 0, len = id.length; i < len; i++) { - removedId = this._remove(id[i]); + if (Array.isArray(id)) { + for (i = 0, len = id.length; i < len; i++) { + removedId = this._remove(id[i]); + if (removedId != null) { + removedIds.push(removedId); + } + } + } + else { + removedId = this._remove(id); if (removedId != null) { removedIds.push(removedId); } } - } - else { - removedId = this._remove(id); - if (removedId != null) { - removedIds.push(removedId); - } - } - if (removedIds.length) { - this._trigger('remove', {items: removedIds}, senderId); - } + if (removedIds.length) { + this._trigger('remove', {items: removedIds}, senderId); + } - return removedIds; -}; + return removedIds; + }; -/** - * Remove an item by its id - * @param {Number | String | Object} id id or item - * @returns {Number | String | null} id - * @private - */ -DataSet.prototype._remove = function (id) { - if (util.isNumber(id) || util.isString(id)) { - if (this._data[id]) { - delete this._data[id]; - return id; + /** + * Remove an item by its id + * @param {Number | String | Object} id id or item + * @returns {Number | String | null} id + * @private + */ + DataSet.prototype._remove = function (id) { + if (util.isNumber(id) || util.isString(id)) { + if (this._data[id]) { + delete this._data[id]; + return id; + } } - } - else if (id instanceof Object) { - var itemId = id[this._fieldId]; - if (itemId && this._data[itemId]) { - delete this._data[itemId]; - return itemId; + else if (id instanceof Object) { + var itemId = id[this._fieldId]; + if (itemId && this._data[itemId]) { + delete this._data[itemId]; + return itemId; + } } - } - return null; -}; + return null; + }; -/** - * Clear the data - * @param {String} [senderId] Optional sender id - * @return {Array} removedIds The ids of all removed items - */ -DataSet.prototype.clear = function (senderId) { - var ids = Object.keys(this._data); + /** + * Clear the data + * @param {String} [senderId] Optional sender id + * @return {Array} removedIds The ids of all removed items + */ + DataSet.prototype.clear = function (senderId) { + var ids = Object.keys(this._data); - this._data = {}; + this._data = {}; - this._trigger('remove', {items: ids}, senderId); + this._trigger('remove', {items: ids}, senderId); - return ids; -}; + return ids; + }; -/** - * Find the item with maximum value of a specified field - * @param {String} field - * @return {Object | null} item Item containing max value, or null if no items - */ -DataSet.prototype.max = function (field) { - var data = this._data, - max = null, - maxField = null; + /** + * Find the item with maximum value of a specified field + * @param {String} field + * @return {Object | null} item Item containing max value, or null if no items + */ + DataSet.prototype.max = function (field) { + var data = this._data, + max = null, + maxField = null; - for (var id in data) { - if (data.hasOwnProperty(id)) { - var item = data[id]; - var itemField = item[field]; - if (itemField != null && (!max || itemField > maxField)) { - max = item; - maxField = itemField; + for (var id in data) { + if (data.hasOwnProperty(id)) { + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!max || itemField > maxField)) { + max = item; + maxField = itemField; + } } } - } - return max; -}; + return max; + }; -/** - * Find the item with minimum value of a specified field - * @param {String} field - * @return {Object | null} item Item containing max value, or null if no items - */ -DataSet.prototype.min = function (field) { - var data = this._data, - min = null, - minField = null; + /** + * Find the item with minimum value of a specified field + * @param {String} field + * @return {Object | null} item Item containing max value, or null if no items + */ + DataSet.prototype.min = function (field) { + var data = this._data, + min = null, + minField = null; - for (var id in data) { - if (data.hasOwnProperty(id)) { - var item = data[id]; - var itemField = item[field]; - if (itemField != null && (!min || itemField < minField)) { - min = item; - minField = itemField; + for (var id in data) { + if (data.hasOwnProperty(id)) { + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!min || itemField < minField)) { + min = item; + minField = itemField; + } } } - } - return min; -}; + return min; + }; -/** - * Find all distinct values of a specified field - * @param {String} field - * @return {Array} values Array containing all distinct values. If data items - * do not contain the specified field are ignored. - * The returned array is unordered. - */ -DataSet.prototype.distinct = function (field) { - var data = this._data; - var values = []; - var fieldType = this._options.type && this._options.type[field] || null; - var count = 0; - var i; - - for (var prop in data) { - if (data.hasOwnProperty(prop)) { - var item = data[prop]; - var value = item[field]; - var exists = false; - for (i = 0; i < count; i++) { - if (values[i] == value) { - exists = true; - break; + /** + * Find all distinct values of a specified field + * @param {String} field + * @return {Array} values Array containing all distinct values. If data items + * do not contain the specified field are ignored. + * The returned array is unordered. + */ + DataSet.prototype.distinct = function (field) { + var data = this._data; + var values = []; + var fieldType = this._options.type && this._options.type[field] || null; + var count = 0; + var i; + + for (var prop in data) { + if (data.hasOwnProperty(prop)) { + var item = data[prop]; + var value = item[field]; + var exists = false; + for (i = 0; i < count; i++) { + if (values[i] == value) { + exists = true; + break; + } + } + if (!exists && (value !== undefined)) { + values[count] = value; + count++; } - } - if (!exists && (value !== undefined)) { - values[count] = value; - count++; } } - } - if (fieldType) { - for (i = 0; i < values.length; i++) { - values[i] = util.convert(values[i], fieldType); + if (fieldType) { + for (i = 0; i < values.length; i++) { + values[i] = util.convert(values[i], fieldType); + } } - } - return values; -}; + return values; + }; -/** - * Add a single item. Will fail when an item with the same id already exists. - * @param {Object} item - * @return {String} id - * @private - */ -DataSet.prototype._addItem = function (item) { - var id = item[this._fieldId]; + /** + * Add a single item. Will fail when an item with the same id already exists. + * @param {Object} item + * @return {String} id + * @private + */ + DataSet.prototype._addItem = function (item) { + var id = item[this._fieldId]; - if (id != undefined) { - // check whether this id is already taken - if (this._data[id]) { - // item already exists - throw new Error('Cannot add item: item with id ' + id + ' already exists'); + if (id != undefined) { + // check whether this id is already taken + if (this._data[id]) { + // item already exists + throw new Error('Cannot add item: item with id ' + id + ' already exists'); + } + } + else { + // generate an id + id = util.randomUUID(); + item[this._fieldId] = id; } - } - else { - // generate an id - id = util.randomUUID(); - item[this._fieldId] = id; - } - var d = {}; - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); + var d = {}; + for (var field in item) { + if (item.hasOwnProperty(field)) { + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); + } } - } - this._data[id] = d; + this._data[id] = d; - return id; -}; + return id; + }; -/** - * Get an item. Fields can be converted to a specific type - * @param {String} id - * @param {Object.} [types] field types to convert - * @return {Object | null} item - * @private - */ -DataSet.prototype._getItem = function (id, types) { - var field, value; + /** + * Get an item. Fields can be converted to a specific type + * @param {String} id + * @param {Object.} [types] field types to convert + * @return {Object | null} item + * @private + */ + DataSet.prototype._getItem = function (id, types) { + var field, value; - // get the item from the dataset - var raw = this._data[id]; - if (!raw) { - return null; - } + // get the item from the dataset + var raw = this._data[id]; + if (!raw) { + return null; + } - // convert the items field types - var converted = {}; - if (types) { - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = util.convert(value, types[field]); + // convert the items field types + var converted = {}; + if (types) { + for (field in raw) { + if (raw.hasOwnProperty(field)) { + value = raw[field]; + converted[field] = util.convert(value, types[field]); + } } } - } - else { - // no field types specified, no converting needed - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = value; + else { + // no field types specified, no converting needed + for (field in raw) { + if (raw.hasOwnProperty(field)) { + value = raw[field]; + converted[field] = value; + } } } - } - return converted; -}; + return converted; + }; -/** - * Update a single item: merge with existing item. - * Will fail when the item has no id, or when there does not exist an item - * with the same id. - * @param {Object} item - * @return {String} id - * @private - */ -DataSet.prototype._updateItem = function (item) { - var id = item[this._fieldId]; - if (id == undefined) { - throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); - } - var d = this._data[id]; - if (!d) { - // item doesn't exist - throw new Error('Cannot update item: no item with id ' + id + ' found'); - } + /** + * Update a single item: merge with existing item. + * Will fail when the item has no id, or when there does not exist an item + * with the same id. + * @param {Object} item + * @return {String} id + * @private + */ + DataSet.prototype._updateItem = function (item) { + var id = item[this._fieldId]; + if (id == undefined) { + throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); + } + var d = this._data[id]; + if (!d) { + // item doesn't exist + throw new Error('Cannot update item: no item with id ' + id + ' found'); + } - // merge with current item - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); + // merge with current item + for (var field in item) { + if (item.hasOwnProperty(field)) { + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); + } } - } - return id; -}; + return id; + }; -/** - * Get an array with the column names of a Google DataTable - * @param {DataTable} dataTable - * @return {String[]} columnNames - * @private - */ -DataSet.prototype._getColumnNames = function (dataTable) { - var columns = []; - for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) { - columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col); - } - return columns; -}; + /** + * Get an array with the column names of a Google DataTable + * @param {DataTable} dataTable + * @return {String[]} columnNames + * @private + */ + DataSet.prototype._getColumnNames = function (dataTable) { + var columns = []; + for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) { + columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col); + } + return columns; + }; -/** - * Append an item as a row to the dataTable - * @param dataTable - * @param columns - * @param item - * @private - */ -DataSet.prototype._appendRow = function (dataTable, columns, item) { - var row = dataTable.addRow(); + /** + * Append an item as a row to the dataTable + * @param dataTable + * @param columns + * @param item + * @private + */ + DataSet.prototype._appendRow = function (dataTable, columns, item) { + var row = dataTable.addRow(); - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - dataTable.setValue(row, col, item[field]); - } -}; + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + dataTable.setValue(row, col, item[field]); + } + }; -/** - * DataView - * - * a dataview offers a filtered view on a dataset or an other dataview. - * - * @param {DataSet | DataView} data - * @param {Object} [options] Available options: see method get - * - * @constructor DataView - */ -function DataView (data, options) { - this._data = null; - this._ids = {}; // ids of the items currently in memory (just contains a boolean true) - this._options = options || {}; - this._fieldId = 'id'; // name of the field containing id - this._subscribers = {}; // event subscribers + module.exports = DataSet; - var me = this; - this.listener = function () { - me._onEvent.apply(me, arguments); - }; - this.setData(data); -} +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { -// TODO: implement a function .config() to dynamically update things like configured filter -// and trigger changes accordingly + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); -/** - * Set a data source for the view - * @param {DataSet | DataView} data - */ -DataView.prototype.setData = function (data) { - var ids, i, len; + /** + * DataView + * + * a dataview offers a filtered view on a dataset or an other dataview. + * + * @param {DataSet | DataView} data + * @param {Object} [options] Available options: see method get + * + * @constructor DataView + */ + function DataView (data, options) { + this._data = null; + this._ids = {}; // ids of the items currently in memory (just contains a boolean true) + this._options = options || {}; + this._fieldId = 'id'; // name of the field containing id + this._subscribers = {}; // event subscribers - if (this._data) { - // unsubscribe from current dataset - if (this._data.unsubscribe) { - this._data.unsubscribe('*', this.listener); - } + var me = this; + this.listener = function () { + me._onEvent.apply(me, arguments); + }; - // trigger a remove of all items in memory - ids = []; - for (var id in this._ids) { - if (this._ids.hasOwnProperty(id)) { - ids.push(id); - } - } - this._ids = {}; - this._trigger('remove', {items: ids}); + this.setData(data); } - this._data = data; + // TODO: implement a function .config() to dynamically update things like configured filter + // and trigger changes accordingly + + /** + * Set a data source for the view + * @param {DataSet | DataView} data + */ + DataView.prototype.setData = function (data) { + var ids, i, len; - if (this._data) { - // update fieldId - this._fieldId = this._options.fieldId || - (this._data && this._data.options && this._data.options.fieldId) || - 'id'; + if (this._data) { + // unsubscribe from current dataset + if (this._data.unsubscribe) { + this._data.unsubscribe('*', this.listener); + } - // trigger an add of all added items - ids = this._data.getIds({filter: this._options && this._options.filter}); - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - this._ids[id] = true; + // trigger a remove of all items in memory + ids = []; + for (var id in this._ids) { + if (this._ids.hasOwnProperty(id)) { + ids.push(id); + } + } + this._ids = {}; + this._trigger('remove', {items: ids}); } - this._trigger('add', {items: ids}); - // subscribe to new dataset - if (this._data.on) { - this._data.on('*', this.listener); + this._data = data; + + if (this._data) { + // update 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}); + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + this._ids[id] = true; + } + this._trigger('add', {items: ids}); + + // subscribe to new dataset + if (this._data.on) { + this._data.on('*', this.listener); + } } - } -}; + }; -/** - * Get data from the data view - * - * Usage: - * - * get() - * get(options: Object) - * get(options: Object, data: Array | DataTable) - * - * get(id: Number) - * get(id: Number, options: Object) - * get(id: Number, options: Object, data: Array | DataTable) - * - * get(ids: Number[]) - * get(ids: Number[], options: Object) - * get(ids: Number[], options: Object, data: Array | DataTable) - * - * Where: - * - * {Number | String} id The id of an item - * {Number[] | String{}} ids An array with ids of items - * {Object} options An Object with options. Available options: - * {String} [type] Type of data to be returned. Can - * be 'DataTable' or 'Array' (default) - * {Object.} [convert] - * {String[]} [fields] field names to be returned - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * {Array | DataTable} [data] If provided, items will be appended to this - * array or table. Required in case of Google - * DataTable. - * @param args - */ -DataView.prototype.get = function (args) { - var me = this; - - // parse the arguments - var ids, options, data; - var firstType = util.getType(arguments[0]); - if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { - // get(id(s) [, options] [, data]) - ids = arguments[0]; // can be a single id or an array with ids - options = arguments[1]; - data = arguments[2]; - } - else { - // get([, options] [, data]) - options = arguments[0]; - data = arguments[1]; - } + /** + * Get data from the data view + * + * Usage: + * + * get() + * get(options: Object) + * get(options: Object, data: Array | DataTable) + * + * get(id: Number) + * get(id: Number, options: Object) + * get(id: Number, options: Object, data: Array | DataTable) + * + * get(ids: Number[]) + * get(ids: Number[], options: Object) + * get(ids: Number[], options: Object, data: Array | DataTable) + * + * Where: + * + * {Number | String} id The id of an item + * {Number[] | String{}} ids An array with ids of items + * {Object} options An Object with options. Available options: + * {String} [type] Type of data to be returned. Can + * be 'DataTable' or 'Array' (default) + * {Object.} [convert] + * {String[]} [fields] field names to be returned + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * {Array | DataTable} [data] If provided, items will be appended to this + * array or table. Required in case of Google + * DataTable. + * @param args + */ + DataView.prototype.get = function (args) { + var me = this; + + // parse the arguments + var ids, options, data; + var firstType = util.getType(arguments[0]); + if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { + // get(id(s) [, options] [, data]) + ids = arguments[0]; // can be a single id or an array with ids + options = arguments[1]; + data = arguments[2]; + } + else { + // get([, options] [, data]) + options = arguments[0]; + data = arguments[1]; + } - // extend the options with the default options and provided options - var viewOptions = util.extend({}, this._options, options); + // extend the options with the default options and provided options + var viewOptions = util.extend({}, this._options, options); - // create a combined filter method when needed - if (this._options.filter && options && options.filter) { - viewOptions.filter = function (item) { - return me._options.filter(item) && options.filter(item); + // create a combined filter method when needed + if (this._options.filter && options && options.filter) { + viewOptions.filter = function (item) { + return me._options.filter(item) && options.filter(item); + } } - } - // build up the call to the linked data set - var getArguments = []; - if (ids != undefined) { - getArguments.push(ids); - } - getArguments.push(viewOptions); - getArguments.push(data); + // build up the call to the linked data set + var getArguments = []; + if (ids != undefined) { + getArguments.push(ids); + } + getArguments.push(viewOptions); + getArguments.push(data); - return this._data && this._data.get.apply(this._data, getArguments); -}; + return this._data && this._data.get.apply(this._data, getArguments); + }; -/** - * Get ids of all items or from a filtered set of items. - * @param {Object} [options] An Object with options. Available options: - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Array} ids - */ -DataView.prototype.getIds = function (options) { - var ids; + /** + * Get ids of all items or from a filtered set of items. + * @param {Object} [options] An Object with options. Available options: + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Array} ids + */ + DataView.prototype.getIds = function (options) { + var ids; - if (this._data) { - var defaultFilter = this._options.filter; - var filter; + if (this._data) { + var defaultFilter = this._options.filter; + var filter; - if (options && options.filter) { - if (defaultFilter) { - filter = function (item) { - return defaultFilter(item) && options.filter(item); + if (options && options.filter) { + if (defaultFilter) { + filter = function (item) { + return defaultFilter(item) && options.filter(item); + } + } + else { + filter = options.filter; } } else { - filter = options.filter; + filter = defaultFilter; } + + ids = this._data.getIds({ + filter: filter, + order: options && options.order + }); } else { - filter = defaultFilter; + ids = []; } - ids = this._data.getIds({ - filter: filter, - order: options && options.order - }); - } - else { - ids = []; - } - - return ids; -}; + return ids; + }; -/** - * Get the DataSet to which this DataView is connected. In case there is a chain - * of multiple DataViews, the root DataSet of this chain is returned. - * @return {DataSet} dataSet - */ -DataView.prototype.getDataSet = function () { - var dataSet = this; - while (dataSet instanceof DataView) { - dataSet = dataSet._data; - } - return dataSet || null; -}; + /** + * Get the DataSet to which this DataView is connected. In case there is a chain + * of multiple DataViews, the root DataSet of this chain is returned. + * @return {DataSet} dataSet + */ + DataView.prototype.getDataSet = function () { + var dataSet = this; + while (dataSet instanceof DataView) { + dataSet = dataSet._data; + } + return dataSet || null; + }; -/** - * Event listener. Will propagate all events from the connected data set to - * the subscribers of the DataView, but will filter the items and only trigger - * when there are changes in the filtered data set. - * @param {String} event - * @param {Object | null} params - * @param {String} senderId - * @private - */ -DataView.prototype._onEvent = function (event, params, senderId) { - var i, len, id, item, - ids = params && params.items, - data = this._data, - added = [], - updated = [], - removed = []; - - if (ids && data) { - switch (event) { - case 'add': - // filter the ids of the added items - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - item = this.get(id); - if (item) { - this._ids[id] = true; - added.push(id); + /** + * Event listener. Will propagate all events from the connected data set to + * the subscribers of the DataView, but will filter the items and only trigger + * when there are changes in the filtered data set. + * @param {String} event + * @param {Object | null} params + * @param {String} senderId + * @private + */ + DataView.prototype._onEvent = function (event, params, senderId) { + var i, len, id, item, + ids = params && params.items, + data = this._data, + added = [], + updated = [], + removed = []; + + if (ids && data) { + switch (event) { + case 'add': + // filter the ids of the added items + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + item = this.get(id); + if (item) { + this._ids[id] = true; + added.push(id); + } } - } - break; + break; - case 'update': - // determine the event from the views viewpoint: an updated - // item can be added, updated, or removed from this view. - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - item = this.get(id); + case 'update': + // determine the event from the views viewpoint: an updated + // item can be added, updated, or removed from this view. + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + item = this.get(id); - if (item) { - if (this._ids[id]) { - updated.push(id); + if (item) { + if (this._ids[id]) { + updated.push(id); + } + else { + this._ids[id] = true; + added.push(id); + } } else { - this._ids[id] = true; - added.push(id); + if (this._ids[id]) { + delete this._ids[id]; + removed.push(id); + } + else { + // nothing interesting for me :-( + } } } - else { + + break; + + case 'remove': + // filter the ids of the removed items + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; if (this._ids[id]) { delete this._ids[id]; removed.push(id); } - else { - // nothing interesting for me :-( - } - } - } - - break; - - case 'remove': - // filter the ids of the removed items - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - if (this._ids[id]) { - delete this._ids[id]; - removed.push(id); } - } - break; - } + break; + } - if (added.length) { - this._trigger('add', {items: added}, senderId); - } - if (updated.length) { - this._trigger('update', {items: updated}, senderId); - } - if (removed.length) { - this._trigger('remove', {items: removed}, senderId); + if (added.length) { + this._trigger('add', {items: added}, senderId); + } + if (updated.length) { + this._trigger('update', {items: updated}, senderId); + } + if (removed.length) { + this._trigger('remove', {items: removed}, senderId); + } } - } -}; - -// copy subscription functionality from DataSet -DataView.prototype.on = DataSet.prototype.on; -DataView.prototype.off = DataSet.prototype.off; -DataView.prototype._trigger = DataSet.prototype._trigger; + }; -// TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) -DataView.prototype.subscribe = DataView.prototype.on; -DataView.prototype.unsubscribe = DataView.prototype.off; + // copy subscription functionality from DataSet + DataView.prototype.on = DataSet.prototype.on; + DataView.prototype.off = DataSet.prototype.off; + DataView.prototype._trigger = DataSet.prototype._trigger; -/** - * @constructor Group - * @param {Number | String} groupId - * @param {Object} data - * @param {ItemSet} itemSet - */ -function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { - this.id = groupId; - var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] - this.options = util.selectiveBridgeObject(fields,options); - this.usingDefaultStyle = group.className === undefined; - this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; - this.zeroPosition = 0; - this.update(group); - if (this.usingDefaultStyle == true) { - this.groupsUsingDefaultStyles[0] += 1; - } - this.itemsData = []; -} + // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) + DataView.prototype.subscribe = DataView.prototype.on; + DataView.prototype.unsubscribe = DataView.prototype.off; -GraphGroup.prototype.setItems = function(items) { - if (items != null) { - this.itemsData = items; - if (this.options.sort == true) { - this.itemsData.sort(function (a,b) {return a.x - b.x;}) - } - } - else { - this.itemsData = []; - } -} + module.exports = DataView; -GraphGroup.prototype.setZeroPosition = function(pos) { - this.zeroPosition = pos; -} +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { -GraphGroup.prototype.setOptions = function(options) { - if (options !== undefined) { - var fields = ['sampling','style','sort','yAxisOrientation','barChart']; - util.selectiveDeepExtend(fields, this.options, options); + var Emitter = __webpack_require__(41); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Point3d = __webpack_require__(33); + var Point2d = __webpack_require__(34); + var Filter = __webpack_require__(35); + var StepNumber = __webpack_require__(36); - util.mergeOptions(this.options, options,'catmullRom'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); + /** + * @constructor Graph3d + * Graph3d displays data in 3d. + * + * Graph3d is developed in javascript as a Google Visualization Chart. + * + * @param {Element} container The DOM element in which the Graph3d will + * be created. Normally a div element. + * @param {DataSet | DataView | Array} [data] + * @param {Object} [options] + */ + function Graph3d(container, data, options) { + if (!(this instanceof Graph3d)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + // create variables and set default values + this.containerElement = container; + this.width = '400px'; + this.height = '400px'; + this.margin = 10; // px + this.defaultXCenter = '55%'; + this.defaultYCenter = '50%'; + + this.xLabel = 'x'; + this.yLabel = 'y'; + this.zLabel = 'z'; + this.filterLabel = 'time'; + this.legendLabel = 'value'; + + this.style = Graph3d.STYLE.DOT; + this.showPerspective = true; + this.showGrid = true; + this.keepAspectRatio = true; + this.showShadow = false; + this.showGrayBottom = false; // TODO: this does not work correctly + this.showTooltip = false; + this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube' + + this.animationInterval = 1000; // milliseconds + this.animationPreload = false; + + this.camera = new Graph3d.Camera(); + this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? + + this.dataTable = null; // The original data table + this.dataPoints = null; // The table with point objects + + // the column indexes + this.colX = undefined; + this.colY = undefined; + this.colZ = undefined; + this.colValue = undefined; + this.colFilter = undefined; + + this.xMin = 0; + this.xStep = undefined; // auto by default + this.xMax = 1; + this.yMin = 0; + this.yStep = undefined; // auto by default + this.yMax = 1; + this.zMin = 0; + this.zStep = undefined; // auto by default + this.zMax = 1; + this.valueMin = 0; + this.valueMax = 1; + this.xBarWidth = 1; + this.yBarWidth = 1; + // TODO: customize axis range + + // constants + this.colorAxis = '#4D4D4D'; + this.colorGrid = '#D3D3D3'; + this.colorDot = '#7DC1FF'; + this.colorDotBorder = '#3267D2'; + + // create a frame and canvas + this.create(); + + // apply options (also when undefined) + this.setOptions(options); - if (options.catmullRom) { - if (typeof options.catmullRom == 'object') { - if (options.catmullRom.parametrization) { - if (options.catmullRom.parametrization == 'uniform') { - this.options.catmullRom.alpha = 0; - } - else if (options.catmullRom.parametrization == 'chordal') { - this.options.catmullRom.alpha = 1.0; - } - else { - this.options.catmullRom.parametrization = 'centripetal'; - this.options.catmullRom.alpha = 0.5; - } - } - } + // apply data + if (data) { + this.setData(data); } } -}; - -GraphGroup.prototype.update = function(group) { - this.group = group; - this.content = group.content || 'graph'; - this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10; - this.setOptions(group.options); -}; - -GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { - var fillHeight = iconHeight * 0.5; - var path, fillPath; - - var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); - outline.setAttributeNS(null, "x", x); - outline.setAttributeNS(null, "y", y - fillHeight); - outline.setAttributeNS(null, "width", iconWidth); - outline.setAttributeNS(null, "height", 2*fillHeight); - outline.setAttributeNS(null, "class", "outline"); - - if (this.options.style == 'line') { - path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - path.setAttributeNS(null, "class", this.className); - path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); - if (this.options.shaded.enabled == true) { - fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - if (this.options.shaded.orientation == 'top') { - fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + - "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); - } - else { - fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + - "L"+x+"," + (y + fillHeight) + " " + - "L"+ (x + iconWidth) + "," + (y + fillHeight) + - "L"+ (x + iconWidth) + ","+y); - } - fillPath.setAttributeNS(null, "class", this.className + " iconFill"); - } - if (this.options.drawPoints.enabled == true) { - DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); - } - } - else { - var barWidth = Math.round(0.3 * iconWidth); - var bar1Height = Math.round(0.4 * iconHeight); - var bar2Height = Math.round(0.75 * iconHeight); + // Extend Graph3d with an Emitter mixin + Emitter(Graph3d.prototype); - var offset = Math.round((iconWidth - (2 * barWidth))/3); + /** + * @class Camera + * The camera is mounted on a (virtual) camera arm. The camera arm can rotate + * The camera is always looking in the direction of the origin of the arm. + * This way, the camera always rotates around one fixed point, the location + * of the camera arm. + * + * Documentation: + * http://en.wikipedia.org/wiki/3D_projection + */ + Graph3d.Camera = function () { + this.armLocation = new Point3d(); + this.armRotation = {}; + this.armRotation.horizontal = 0; + this.armRotation.vertical = 0; + this.armLength = 1.7; - DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); - DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); - } -} + this.cameraLocation = new Point3d(); + this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); -/** - * Created by Alex on 6/17/14. - */ -function Legend(body, options, side) { - this.body = body; - this.defaultOptions = { - enabled: true, - icons: true, - iconSize: 20, - iconSpacing: 6, - left: { - visible: true, - position: 'top-left' // top/bottom - left,center,right - }, - right: { - visible: true, - position: 'top-left' // top/bottom - left,center,right - } - } - this.side = side; - this.options = util.extend({},this.defaultOptions); - - this.svgElements = {}; - this.dom = {}; - this.groups = {}; - this.amountOfGroups = 0; - this._create(); + this.calculateCameraOrientation(); + }; - this.setOptions(options); -}; + /** + * Set the location (origin) of the arm + * @param {Number} x Normalized value of x + * @param {Number} y Normalized value of y + * @param {Number} z Normalized value of z + */ + Graph3d.Camera.prototype.setArmLocation = function(x, y, z) { + this.armLocation.x = x; + this.armLocation.y = y; + this.armLocation.z = z; -Legend.prototype = new Component(); + this.calculateCameraOrientation(); + }; + /** + * Set the rotation of the camera arm + * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. + */ + Graph3d.Camera.prototype.setArmRotation = function(horizontal, vertical) { + if (horizontal !== undefined) { + this.armRotation.horizontal = horizontal; + } -Legend.prototype.addGroup = function(label, graphOptions) { - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; - } - this.amountOfGroups += 1; -}; + if (vertical !== undefined) { + this.armRotation.vertical = vertical; + if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; + if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; + } -Legend.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; -}; + if (horizontal !== undefined || vertical !== undefined) { + this.calculateCameraOrientation(); + } + }; -Legend.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; - } -}; + /** + * Retrieve the current arm rotation + * @return {object} An object with parameters horizontal and vertical + */ + Graph3d.Camera.prototype.getArmRotation = function() { + var rot = {}; + rot.horizontal = this.armRotation.horizontal; + rot.vertical = this.armRotation.vertical; -Legend.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.className = 'legend'; - this.dom.frame.style.position = "absolute"; - this.dom.frame.style.top = "10px"; - this.dom.frame.style.display = "block"; + return rot; + }; - this.dom.textArea = document.createElement('div'); - this.dom.textArea.className = 'legendText'; - this.dom.textArea.style.position = "relative"; - this.dom.textArea.style.top = "0px"; + /** + * Set the (normalized) length of the camera arm. + * @param {Number} length A length between 0.71 and 5.0 + */ + Graph3d.Camera.prototype.setArmLength = function(length) { + if (length === undefined) + return; - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = 'absolute'; - this.svg.style.top = 0 +'px'; - this.svg.style.width = this.options.iconSize + 5 + 'px'; + this.armLength = length; - this.dom.frame.appendChild(this.svg); - this.dom.frame.appendChild(this.dom.textArea); -} + // Radius must be larger than the corner of the graph, + // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the + // graph + if (this.armLength < 0.71) this.armLength = 0.71; + if (this.armLength > 5.0) this.armLength = 5.0; -/** - * Hide the component from the DOM - */ -Legend.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } -}; + this.calculateCameraOrientation(); + }; -/** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed - */ -Legend.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); - } -}; + /** + * Retrieve the arm length + * @return {Number} length + */ + Graph3d.Camera.prototype.getArmLength = function() { + return this.armLength; + }; -Legend.prototype.setOptions = function(options) { - var fields = ['enabled','orientation','icons','left','right']; - util.selectiveDeepExtend(fields, this.options, options); -} + /** + * Retrieve the camera location + * @return {Point3d} cameraLocation + */ + Graph3d.Camera.prototype.getCameraLocation = function() { + return this.cameraLocation; + }; -Legend.prototype.redraw = function() { - if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false) { - this.hide(); - } - else { - this.show(); - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { - this.dom.frame.style.left = '4px'; - this.dom.frame.style.textAlign = "left"; - this.dom.textArea.style.textAlign = "left"; - this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.right = ''; - this.svg.style.left = 0 +'px'; - this.svg.style.right = ''; - } - else { - this.dom.frame.style.right = '4px'; - this.dom.frame.style.textAlign = "right"; - this.dom.textArea.style.textAlign = "right"; - this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.left = ''; - this.svg.style.right = 0 +'px'; - this.svg.style.left = ''; - } + /** + * Retrieve the camera rotation + * @return {Point3d} cameraRotation + */ + Graph3d.Camera.prototype.getCameraRotation = function() { + return this.cameraRotation; + }; - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { - this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.bottom = ''; - } - else { - this.dom.frame.style.bottom = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.top = ''; - } + /** + * Calculate the location and rotation of the camera based on the + * position and orientation of the camera arm + */ + Graph3d.Camera.prototype.calculateCameraOrientation = function() { + // calculate location of the camera + this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); + + // calculate rotation of the camera + this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; + this.cameraRotation.y = 0; + this.cameraRotation.z = -this.armRotation.horizontal; + }; - if (this.options.icons == false) { - this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; - this.dom.textArea.style.right = ''; - this.dom.textArea.style.left = ''; - this.svg.style.width = '0px'; - } - else { - this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' - this.drawLegendIcons(); - } + /** + * Calculate the scaling values, dependent on the range in x, y, and z direction + */ + Graph3d.prototype._setScale = function() { + this.scale = new Point3d(1 / (this.xMax - this.xMin), + 1 / (this.yMax - this.yMin), + 1 / (this.zMax - this.zMin)); - var content = ""; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - content += this.groups[groupId].content + '
'; + // keep aspect ration between x and y scale if desired + if (this.keepAspectRatio) { + if (this.scale.x < this.scale.y) { + //noinspection JSSuspiciousNameCombination + this.scale.y = this.scale.x; + } + else { + //noinspection JSSuspiciousNameCombination + this.scale.x = this.scale.y; } } - this.dom.textArea.innerHTML = content; - this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; - } -} -Legend.prototype.drawLegendIcons = function() { - if (this.dom.frame.parentNode) { - DOMutil.prepareElements(this.svgElements); - var padding = window.getComputedStyle(this.dom.frame).paddingTop; - var iconOffset = Number(padding.replace("px",'')); - var x = iconOffset; - var iconWidth = this.options.iconSize; - var iconHeight = 0.75 * this.options.iconSize; - var y = iconOffset + 0.5 * iconHeight + 3; + // scale the vertical axis + this.scale.z *= this.verticalRatio; + // TODO: can this be automated? verticalRatio? - this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; + // determine scale for (optional) value + this.scale.value = 1 / (this.valueMax - this.valueMin); - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + this.options.iconSpacing; - } - } + // position the camera arm + var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x; + var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y; + var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z; + this.camera.setArmLocation(xCenter, yCenter, zCenter); + }; - DOMutil.cleanupElements(this.svgElements); - } -} -/** - * A horizontal time axis - * @param {Object} [options] See DataAxis.setOptions for the available - * options. - * @constructor DataAxis - * @extends Component - * @param body - */ -function DataAxis (body, options, svg) { - this.id = util.randomUUID(); - this.body = body; - this.defaultOptions = { - orientation: 'left', // supported: 'left', 'right' - showMinorLabels: true, - showMajorLabels: true, - icons: true, - majorLinesOffset: 7, - minorLinesOffset: 4, - labelOffsetX: 10, - labelOffsetY: 2, - iconWidth: 20, - width: '40px', - visible: true + /** + * Convert a 3D location to a 2D location on screen + * http://en.wikipedia.org/wiki/3D_projection + * @param {Point3d} point3d A 3D point with parameters x, y, z + * @return {Point2d} point2d A 2D point with parameters x, y + */ + Graph3d.prototype._convert3Dto2D = function(point3d) { + var translation = this._convertPointToTranslation(point3d); + return this._convertTranslationToScreen(translation); }; - this.linegraphSVG = svg; - this.props = {}; - this.DOMelements = { // dynamic elements - lines: {}, - labels: {} + /** + * Convert a 3D location its translation seen from the camera + * http://en.wikipedia.org/wiki/3D_projection + * @param {Point3d} point3d A 3D point with parameters x, y, z + * @return {Point3d} translation A 3D point with parameters x, y, z This is + * the translation of the point, seen from the + * camera + */ + Graph3d.prototype._convertPointToTranslation = function(point3d) { + var ax = point3d.x * this.scale.x, + ay = point3d.y * this.scale.y, + az = point3d.z * this.scale.z, + + cx = this.camera.getCameraLocation().x, + cy = this.camera.getCameraLocation().y, + cz = this.camera.getCameraLocation().z, + + // calculate angles + sinTx = Math.sin(this.camera.getCameraRotation().x), + cosTx = Math.cos(this.camera.getCameraRotation().x), + sinTy = Math.sin(this.camera.getCameraRotation().y), + cosTy = Math.cos(this.camera.getCameraRotation().y), + sinTz = Math.sin(this.camera.getCameraRotation().z), + cosTz = Math.cos(this.camera.getCameraRotation().z), + + // calculate translation + dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), + dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)), + dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx)); + + return new Point3d(dx, dy, dz); }; - this.dom = {}; - - this.range = {start:0, end:0}; + /** + * Convert a translation point to a point on the screen + * @param {Point3d} translation A 3D point with parameters x, y, z This is + * the translation of the point, seen from the + * camera + * @return {Point2d} point2d A 2D point with parameters x, y + */ + Graph3d.prototype._convertTranslationToScreen = function(translation) { + var ex = this.eye.x, + ey = this.eye.y, + ez = this.eye.z, + dx = translation.x, + dy = translation.y, + dz = translation.z; + + // calculate position on screen from translation + var bx; + var by; + if (this.showPerspective) { + bx = (dx - ex) * (ez / dz); + by = (dy - ey) * (ez / dz); + } + else { + bx = dx * -(ez / this.camera.getArmLength()); + by = dy * -(ez / this.camera.getArmLength()); + } - this.options = util.extend({}, this.defaultOptions); - this.conversionFactor = 1; + // shift and scale the point to the center of the screen + // use the width of the graph to scale both horizontally and vertically. + return new Point2d( + this.xcenter + bx * this.frame.canvas.clientWidth, + this.ycenter - by * this.frame.canvas.clientWidth); + }; - this.setOptions(options); - this.width = Number(('' + this.options.width).replace("px","")); - this.minWidth = this.width; - this.height = this.linegraphSVG.offsetHeight; + /** + * Set the background styling for the graph + * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor + */ + Graph3d.prototype._setBackgroundColor = function(backgroundColor) { + var fill = 'white'; + var stroke = 'gray'; + var strokeWidth = 1; - this.stepPixels = 25; - this.stepPixelsForced = 25; - this.lineOffset = 0; - this.master = true; - this.svgElements = {}; + if (typeof(backgroundColor) === 'string') { + fill = backgroundColor; + stroke = 'none'; + strokeWidth = 0; + } + else if (typeof(backgroundColor) === 'object') { + if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; + if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; + if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; + } + else if (backgroundColor === undefined) { + // use use defaults + } + else { + throw 'Unsupported type of backgroundColor'; + } + this.frame.style.backgroundColor = fill; + this.frame.style.borderColor = stroke; + this.frame.style.borderWidth = strokeWidth + 'px'; + this.frame.style.borderStyle = 'solid'; + }; - this.groups = {}; - this.amountOfGroups = 0; - // create the HTML DOM - this._create(); -} + /// enumerate the available styles + Graph3d.STYLE = { + BAR: 0, + BARCOLOR: 1, + BARSIZE: 2, + DOT : 3, + DOTLINE : 4, + DOTCOLOR: 5, + DOTSIZE: 6, + GRID : 7, + LINE: 8, + SURFACE : 9 + }; -DataAxis.prototype = new Component(); + /** + * Retrieve the style index from given styleName + * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' + * @return {Number} styleNumber Enumeration value representing the style, or -1 + * when not found + */ + Graph3d.prototype._getStyleNumber = function(styleName) { + switch (styleName) { + case 'dot': return Graph3d.STYLE.DOT; + case 'dot-line': return Graph3d.STYLE.DOTLINE; + case 'dot-color': return Graph3d.STYLE.DOTCOLOR; + case 'dot-size': return Graph3d.STYLE.DOTSIZE; + case 'line': return Graph3d.STYLE.LINE; + case 'grid': return Graph3d.STYLE.GRID; + case 'surface': return Graph3d.STYLE.SURFACE; + case 'bar': return Graph3d.STYLE.BAR; + case 'bar-color': return Graph3d.STYLE.BARCOLOR; + case 'bar-size': return Graph3d.STYLE.BARSIZE; + } + return -1; + }; + /** + * Determine the indexes of the data columns, based on the given style and data + * @param {DataSet} data + * @param {Number} style + */ + Graph3d.prototype._determineColumnIndexes = function(data, style) { + if (this.style === Graph3d.STYLE.DOT || + this.style === Graph3d.STYLE.DOTLINE || + this.style === Graph3d.STYLE.LINE || + this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE || + this.style === Graph3d.STYLE.BAR) { + // 3 columns expected, and optionally a 4th with filter values + this.colX = 0; + this.colY = 1; + this.colZ = 2; + this.colValue = undefined; + + if (data.getNumberOfColumns() > 3) { + this.colFilter = 3; + } + } + else if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + // 4 columns expected, and optionally a 5th with filter values + this.colX = 0; + this.colY = 1; + this.colZ = 2; + this.colValue = 3; + + if (data.getNumberOfColumns() > 4) { + this.colFilter = 4; + } + } + else { + throw 'Unknown style "' + this.style + '"'; + } + }; -DataAxis.prototype.addGroup = function(label, graphOptions) { - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; + Graph3d.prototype.getNumberOfRows = function(data) { + return data.length; } - this.amountOfGroups += 1; -}; -DataAxis.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; -}; -DataAxis.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; - } -}; - - -DataAxis.prototype.setOptions = function (options) { - if (options) { - var redraw = false; - if (this.options.orientation != options.orientation && options.orientation !== undefined) { - redraw = true; - } - var fields = [ - 'orientation', - 'showMinorLabels', - 'showMajorLabels', - 'icons', - 'majorLinesOffset', - 'minorLinesOffset', - 'labelOffsetX', - 'labelOffsetY', - 'iconWidth', - 'width', - 'visible']; - util.selectiveExtend(fields, this.options, options); - - this.minWidth = Number(('' + this.options.width).replace("px","")); - - if (redraw == true && this.dom.frame) { - this.hide(); - this.show(); + Graph3d.prototype.getNumberOfColumns = function(data) { + var counter = 0; + for (var column in data[0]) { + if (data[0].hasOwnProperty(column)) { + counter++; + } } + return counter; } -}; -/** - * Create the HTML DOM for the DataAxis - */ -DataAxis.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.style.width = this.options.width; - this.dom.frame.style.height = this.height; - - this.dom.lineContainer = document.createElement('div'); - this.dom.lineContainer.style.width = '100%'; - this.dom.lineContainer.style.height = this.height; - - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = "absolute"; - this.svg.style.top = '0px'; - this.svg.style.height = '100%'; - this.svg.style.width = '100%'; - this.svg.style.display = "block"; - this.dom.frame.appendChild(this.svg); -}; - -DataAxis.prototype._redrawGroupIcons = function () { - DOMutil.prepareElements(this.svgElements); - - var x; - var iconWidth = this.options.iconWidth; - var iconHeight = 15; - var iconOffset = 4; - var y = iconOffset + 0.5 * iconHeight; - - if (this.options.orientation == 'left') { - x = iconOffset; - } - else { - x = this.width - iconWidth - iconOffset; - } - - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + iconOffset; + Graph3d.prototype.getDistinctValues = function(data, column) { + var distinctValues = []; + for (var i = 0; i < data.length; i++) { + if (distinctValues.indexOf(data[i][column]) == -1) { + distinctValues.push(data[i][column]); + } } + return distinctValues; } - DOMutil.cleanupElements(this.svgElements); -}; -/** - * Create the HTML DOM for the DataAxis - */ -DataAxis.prototype.show = function() { - if (!this.dom.frame.parentNode) { - if (this.options.orientation == 'left') { - this.body.dom.left.appendChild(this.dom.frame); - } - else { - this.body.dom.right.appendChild(this.dom.frame); + Graph3d.prototype.getColumnRange = function(data,column) { + var minMax = {min:data[0][column],max:data[0][column]}; + for (var i = 0; i < data.length; i++) { + if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } + if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } } - } + return minMax; + }; - if (!this.dom.lineContainer.parentNode) { - this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); - } -}; + /** + * Initialize the data from the data table. Calculate minimum and maximum values + * and column index values + * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph. + * @param {Number} style Style Number + */ + Graph3d.prototype._dataInitialize = function (rawData, style) { + var me = this; -/** - * Create the HTML DOM for the DataAxis - */ -DataAxis.prototype.hide = function() { - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } + // unsubscribe from the dataTable + if (this.dataSet) { + this.dataSet.off('*', this._onChange); + } - if (this.dom.lineContainer.parentNode) { - this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); - } -}; + if (rawData === undefined) + return; -/** - * Set a range (start and end) - * @param end - * @param start - * @param end - */ -DataAxis.prototype.setRange = function (start, end) { - this.range.start = start; - this.range.end = end; -}; + if (Array.isArray(rawData)) { + rawData = new DataSet(rawData); + } -/** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ -DataAxis.prototype.redraw = function () { - var changeCalled = false; - if (this.amountOfGroups == 0) { - this.hide(); - } - else { - this.show(); - this.height = Number(this.linegraphSVG.style.height.replace("px","")); - // svg offsetheight did not work in firefox and explorer... + var data; + if (rawData instanceof DataSet || rawData instanceof DataView) { + data = rawData.get(); + } + else { + throw new Error('Array, DataSet, or DataView expected'); + } - this.dom.lineContainer.style.height = this.height + 'px'; - this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; + if (data.length == 0) + return; - var props = this.props; - var frame = this.dom.frame; + this.dataSet = rawData; + this.dataTable = data; - // update classname - frame.className = 'dataaxis'; + // subscribe to changes in the dataset + this._onChange = function () { + me.setData(me.dataSet); + }; + this.dataSet.on('*', this._onChange); - // calculate character width and height - this._calculateCharSize(); + // _determineColumnIndexes + // getNumberOfRows (points) + // getNumberOfColumns (x,y,z,v,t,t1,t2...) + // getDistinctValues (unique values?) + // getColumnRange - var orientation = this.options.orientation; - var showMinorLabels = this.options.showMinorLabels; - var showMajorLabels = this.options.showMajorLabels; + // determine the location of x,y,z,value,filter columns + this.colX = 'x'; + this.colY = 'y'; + this.colZ = 'z'; + this.colValue = 'style'; + this.colFilter = 'filter'; - // determine the width and height of the elemens for the axis - props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; - props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; - props.minorLineHeight = 1; - props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; - props.majorLineHeight = 1; - // take frame offline while updating (is almost twice as fast) - if (orientation == 'left') { - frame.style.top = '0'; - frame.style.left = '0'; - frame.style.bottom = ''; - frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; - } - else { // right - frame.style.top = ''; - frame.style.bottom = '0'; - frame.style.left = '0'; - frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; - } - changeCalled = this._redrawLabels(); - if (this.options.icons == true) { - this._redrawGroupIcons(); + // check if a filter column is provided + if (data[0].hasOwnProperty('filter')) { + if (this.dataFilter === undefined) { + this.dataFilter = new Filter(rawData, this.colFilter, this); + this.dataFilter.setOnLoadCallback(function() {me.redraw();}); + } } - } - return changeCalled; -}; -/** - * Repaint major and minor text labels and vertical grid lines - * @private - */ -DataAxis.prototype._redrawLabels = function () { - DOMutil.prepareElements(this.DOMelements); - var orientation = this.options['orientation']; + var withBars = this.style == Graph3d.STYLE.BAR || + this.style == Graph3d.STYLE.BARCOLOR || + this.style == Graph3d.STYLE.BARSIZE; - // calculate range and step (step such that we have space for 7 characters per label) - var minimumStep = this.master ? this.props.majorCharHeight || 10 : this.stepPixelsForced; - var step = new DataStep(this.range.start, this.range.end, minimumStep, this.dom.frame.offsetHeight); - this.step = step; - step.first(); + // determine barWidth from data + if (withBars) { + if (this.defaultXBarWidth !== undefined) { + this.xBarWidth = this.defaultXBarWidth; + } + else { + var dataX = this.getDistinctValues(data,this.colX); + this.xBarWidth = (dataX[1] - dataX[0]) || 1; + } - // get the distance in pixels for a step - var stepPixels = this.dom.frame.offsetHeight / ((step.marginRange / step.step) + 1); - this.stepPixels = stepPixels; + if (this.defaultYBarWidth !== undefined) { + this.yBarWidth = this.defaultYBarWidth; + } + else { + var dataY = this.getDistinctValues(data,this.colY); + this.yBarWidth = (dataY[1] - dataY[0]) || 1; + } + } - var amountOfSteps = this.height / stepPixels; - var stepDifference = 0; + // calculate minimums and maximums + var xRange = this.getColumnRange(data,this.colX); + if (withBars) { + xRange.min -= this.xBarWidth / 2; + xRange.max += this.xBarWidth / 2; + } + this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min; + this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max; + if (this.xMax <= this.xMin) this.xMax = this.xMin + 1; + this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5; - if (this.master == false) { - stepPixels = this.stepPixelsForced; - stepDifference = Math.round((this.height / stepPixels) - amountOfSteps); - for (var i = 0; i < 0.5 * stepDifference; i++) { - step.previous(); + var yRange = this.getColumnRange(data,this.colY); + if (withBars) { + yRange.min -= this.yBarWidth / 2; + yRange.max += this.yBarWidth / 2; } - amountOfSteps = this.height / stepPixels; - } + this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min; + this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max; + if (this.yMax <= this.yMin) this.yMax = this.yMin + 1; + this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5; + var zRange = this.getColumnRange(data,this.colZ); + this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min; + this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max; + if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; + this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; - this.valueAtZero = step.marginEnd; - var marginStartPos = 0; + if (this.colValue !== undefined) { + var valueRange = this.getColumnRange(data,this.colValue); + this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; + this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; + if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; + } - // do not draw the first label - var max = 1; - step.next(); + // set the scale dependent on the ranges. + this._setScale(); + }; - this.maxLabelSize = 0; - var y = 0; - while (max < Math.round(amountOfSteps)) { - y = Math.round(max * stepPixels); - marginStartPos = max * stepPixels; - var isMajor = step.isMajor(); - if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) { - this._redrawLabel(y - 2, step.getCurrent(), orientation, 'yAxis minor', this.props.minorCharHeight); - } + /** + * Filter the data based on the current filter + * @param {Array} data + * @return {Array} dataPoints Array with point objects which can be drawn on screen + */ + Graph3d.prototype._getDataPoints = function (data) { + // TODO: store the created matrix dataPoints in the filters instead of reloading each time + var x, y, i, z, obj, point; - if (isMajor && this.options['showMajorLabels'] && this.master == true || - this.options['showMinorLabels'] == false && this.master == false && isMajor == true) { + var dataPoints = []; - if (y >= 0) { - this._redrawLabel(y - 2, step.getCurrent(), orientation, 'yAxis major', this.props.majorCharHeight); + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + // copy all values from the google data table to a matrix + // the provided values are supposed to form a grid of (x,y) positions + + // create two lists with all present x and y values + var dataX = []; + var dataY = []; + for (i = 0; i < this.getNumberOfRows(data); i++) { + x = data[i][this.colX] || 0; + y = data[i][this.colY] || 0; + + if (dataX.indexOf(x) === -1) { + dataX.push(x); + } + if (dataY.indexOf(y) === -1) { + dataY.push(y); + } } - this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); - } - else { - this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); - } - step.next(); - max++; - } + function sortNumber(a, b) { + return a - b; + } + dataX.sort(sortNumber); + dataY.sort(sortNumber); - this.conversionFactor = marginStartPos/((amountOfSteps-1) * step.step); + // create a grid, a 2d matrix, with all values. + var dataMatrix = []; // temporary data matrix + for (i = 0; i < data.length; i++) { + x = data[i][this.colX] || 0; + y = data[i][this.colY] || 0; + z = data[i][this.colZ] || 0; - var offset = this.options.icons == true ? this.options.iconWidth + this.options.labelOffsetX + 15 : this.options.labelOffsetX + 15; - // this will resize the yAxis to accomodate the labels. - if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { - this.width = this.maxLabelSize + offset; - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements); - this.redraw(); - return true; - } - // this will resize the yAxis if it is too big for the labels. - else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { - this.width = Math.max(this.minWidth,this.maxLabelSize + offset); - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements); - this.redraw(); - return true; - } - else { - DOMutil.cleanupElements(this.DOMelements); - return false; - } -}; + var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer + var yIndex = dataY.indexOf(y); -/** - * Create a label for the axis at position x - * @private - * @param y - * @param text - * @param orientation - * @param className - * @param characterHeight - */ -DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { - // reuse redundant label - var label = DOMutil.getDOMElement('div',this.DOMelements, this.dom.frame); //this.dom.redundant.labels.shift(); - label.className = className; - label.innerHTML = text; - - if (orientation == 'left') { - label.style.left = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "right"; - } - else { - label.style.right = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "left"; - } + if (dataMatrix[xIndex] === undefined) { + dataMatrix[xIndex] = []; + } - label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; + var point3d = new Point3d(); + point3d.x = x; + point3d.y = y; + point3d.z = z; - text += ''; + obj = {}; + obj.point = point3d; + obj.trans = undefined; + obj.screen = undefined; + obj.bottom = new Point3d(x, y, this.zMin); - var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); - if (this.maxLabelSize < text.length * largestWidth) { - this.maxLabelSize = text.length * largestWidth; - } -}; + dataMatrix[xIndex][yIndex] = obj; -/** - * Create a minor line for the axis at position y - * @param y - * @param orientation - * @param className - * @param offset - * @param width - */ -DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { - if (this.master == true) { - var line = DOMutil.getDOMElement('div',this.DOMelements, this.dom.lineContainer);//this.dom.redundant.lines.shift(); - line.className = className; - line.innerHTML = ''; + dataPoints.push(obj); + } - if (orientation == 'left') { - line.style.left = (this.width - offset) + 'px'; - } - else { - line.style.right = (this.width - offset) + 'px'; + // fill in the pointers to the neighbors. + for (x = 0; x < dataMatrix.length; x++) { + for (y = 0; y < dataMatrix[x].length; y++) { + if (dataMatrix[x][y]) { + dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined; + dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined; + dataMatrix[x][y].pointCross = + (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ? + dataMatrix[x+1][y+1] : + undefined; + } + } + } } + else { // 'dot', 'dot-line', etc. + // copy all values from the google data table to a list with Point3d objects + for (i = 0; i < data.length; i++) { + point = new Point3d(); + point.x = data[i][this.colX] || 0; + point.y = data[i][this.colY] || 0; + point.z = data[i][this.colZ] || 0; - line.style.width = width + 'px'; - line.style.top = y + 'px'; - } -}; - + if (this.colValue !== undefined) { + point.value = data[i][this.colValue] || 0; + } -DataAxis.prototype.convertValue = function (value) { - var invertedValue = this.valueAtZero - value; - var convertedValue = invertedValue * this.conversionFactor; - return convertedValue; // the -2 is to compensate for the borders -}; + obj = {}; + obj.point = point; + obj.bottom = new Point3d(point.x, point.y, this.zMin); + obj.trans = undefined; + obj.screen = undefined; + dataPoints.push(obj); + } + } -/** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. - * @private - */ -DataAxis.prototype._calculateCharSize = function () { - // determine the char width and height on the minor axis - if (!('minorCharHeight' in this.props)) { + return dataPoints; + }; - var textMinor = document.createTextNode('0'); - var measureCharMinor = document.createElement('DIV'); - measureCharMinor.className = 'yAxis minor measure'; - measureCharMinor.appendChild(textMinor); - this.dom.frame.appendChild(measureCharMinor); + /** + * Create the main frame for the Graph3d. + * This function is executed once when a Graph3d object is created. The frame + * contains a canvas, and this canvas contains all objects like the axis and + * nodes. + */ + Graph3d.prototype.create = function () { + // remove all elements from the container element. + while (this.containerElement.hasChildNodes()) { + this.containerElement.removeChild(this.containerElement.firstChild); + } - this.props.minorCharHeight = measureCharMinor.clientHeight; - this.props.minorCharWidth = measureCharMinor.clientWidth; + this.frame = document.createElement('div'); + this.frame.style.position = 'relative'; + this.frame.style.overflow = 'hidden'; - this.dom.frame.removeChild(measureCharMinor); - } + // create the graph canvas (HTML canvas element) + this.frame.canvas = document.createElement( 'canvas' ); + this.frame.canvas.style.position = 'relative'; + this.frame.appendChild(this.frame.canvas); + //if (!this.frame.canvas.getContext) { + { + var noCanvas = document.createElement( 'DIV' ); + noCanvas.style.color = 'red'; + noCanvas.style.fontWeight = 'bold' ; + noCanvas.style.padding = '10px'; + noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; + this.frame.canvas.appendChild(noCanvas); + } + + this.frame.filter = document.createElement( 'div' ); + this.frame.filter.style.position = 'absolute'; + this.frame.filter.style.bottom = '0px'; + this.frame.filter.style.left = '0px'; + this.frame.filter.style.width = '100%'; + this.frame.appendChild(this.frame.filter); + + // add event listeners to handle moving and zooming the contents + var me = this; + var onmousedown = function (event) {me._onMouseDown(event);}; + var ontouchstart = function (event) {me._onTouchStart(event);}; + var onmousewheel = function (event) {me._onWheel(event);}; + var ontooltip = function (event) {me._onTooltip(event);}; + // TODO: these events are never cleaned up... can give a 'memory leakage' + + G3DaddEventListener(this.frame.canvas, 'keydown', onkeydown); + G3DaddEventListener(this.frame.canvas, 'mousedown', onmousedown); + G3DaddEventListener(this.frame.canvas, 'touchstart', ontouchstart); + G3DaddEventListener(this.frame.canvas, 'mousewheel', onmousewheel); + G3DaddEventListener(this.frame.canvas, 'mousemove', ontooltip); + + // add the new graph to the container element + this.containerElement.appendChild(this.frame); + }; - if (!('majorCharHeight' in this.props)) { - var textMajor = document.createTextNode('0'); - var measureCharMajor = document.createElement('DIV'); - measureCharMajor.className = 'yAxis major measure'; - measureCharMajor.appendChild(textMajor); - this.dom.frame.appendChild(measureCharMajor); - this.props.majorCharHeight = measureCharMajor.clientHeight; - this.props.majorCharWidth = measureCharMajor.clientWidth; + /** + * Set a new size for the graph + * @param {string} width Width in pixels or percentage (for example '800px' + * or '50%') + * @param {string} height Height in pixels or percentage (for example '400px' + * or '30%') + */ + Graph3d.prototype.setSize = function(width, height) { + this.frame.style.width = width; + this.frame.style.height = height; - this.dom.frame.removeChild(measureCharMajor); - } -}; + this._resizeCanvas(); + }; -/** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ -DataAxis.prototype.snap = function(date) { - return this.step.snap(date); -}; + /** + * Resize the canvas to the current size of the frame + */ + Graph3d.prototype._resizeCanvas = function() { + this.frame.canvas.style.width = '100%'; + this.frame.canvas.style.height = '100%'; -var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items + this.frame.canvas.width = this.frame.canvas.clientWidth; + this.frame.canvas.height = this.frame.canvas.clientHeight; -/** - * This is the constructor of the LineGraph. It requires a Timeline body and options. - * - * @param body - * @param options - * @constructor - */ -function LineGraph(body, options) { - this.id = util.randomUUID(); - this.body = body; - - this.defaultOptions = { - yAxisOrientation: 'left', - defaultGroup: 'default', - sort: true, - sampling: true, - graphHeight: '400px', - shaded: { - enabled: false, - orientation: 'bottom' // top, bottom - }, - style: 'line', // line, bar - barChart: { - width: 50, - align: 'center' // left, center, right - }, - catmullRom: { - enabled: true, - parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) - alpha: 0.5 - }, - drawPoints: { - enabled: true, - size: 6, - style: 'square' // square, circle - }, - dataAxis: { - showMinorLabels: true, - showMajorLabels: true, - icons: false, - width: '40px', - visible: true - }, - legend: { - enabled: false, - icons: true, - left: { - visible: true, - position: 'top-left' // top/bottom - left,right - }, - right: { - visible: true, - position: 'top-right' // top/bottom - left,right - } - } + // adjust with for margin + this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px'; }; - // options is shared by this ItemSet and all its items - this.options = util.extend({}, this.defaultOptions); - this.dom = {}; - this.props = {}; - this.hammer = null; - this.groups = {}; - - var me = this; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + /** + * Start animation + */ + Graph3d.prototype.animationStart = function() { + if (!this.frame.filter || !this.frame.filter.slider) + throw 'No animation available'; - // listeners for the DataSet of the items - this.itemListeners = { - 'add': function (event, params, senderId) { - me._onAdd(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdate(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemove(params.items); - } + this.frame.filter.slider.play(); }; - // listeners for the DataSet of the groups - this.groupListeners = { - 'add': function (event, params, senderId) { - me._onAddGroups(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdateGroups(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemoveGroups(params.items); - } - }; - - this.items = {}; // object with an Item for every data item - this.selection = []; // list with the ids of all selected nodes - this.lastStart = this.body.range.start; - this.touchParams = {}; // stores properties while dragging - - this.svgElements = {}; - this.setOptions(options); - this.groupsUsingDefaultStyles = [0]; - - this.body.emitter.on("rangechange",function() { - if (me.lastStart != 0) { - var offset = me.body.range.start - me.lastStart; - var range = me.body.range.end - me.body.range.start; - if (me.width != 0) { - var rangePerPixelInv = me.width/range; - var xOffset = offset * rangePerPixelInv; - me.svg.style.left = (-me.width - xOffset) + "px"; - } - } - }); - this.body.emitter.on("rangechanged", function() { - me.lastStart = me.body.range.start; - me.svg.style.left = util.option.asSize(-me.width); - me._updateGraph.apply(me); - }); - - // create the HTML DOM - this._create(); - this.body.emitter.emit("change"); -} - -LineGraph.prototype = new Component(); - -/** - * Create the HTML DOM for the ItemSet - */ -LineGraph.prototype._create = function(){ - var frame = document.createElement('div'); - frame.className = 'LineGraph'; - this.dom.frame = frame; - - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = "relative"; - this.svg.style.height = ('' + this.options.graphHeight).replace("px",'') + 'px'; - this.svg.style.display = "block"; - frame.appendChild(this.svg); - - // data axis - this.options.dataAxis.orientation = 'left'; - this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg); - - this.options.dataAxis.orientation = 'right'; - this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg); - delete this.options.dataAxis.orientation; - // legends - this.legendLeft = new Legend(this.body, this.options.legend, 'left'); - this.legendRight = new Legend(this.body, this.options.legend, 'right'); + /** + * Stop animation + */ + Graph3d.prototype.animationStop = function() { + if (!this.frame.filter || !this.frame.filter.slider) return; - this.show(); -}; + this.frame.filter.slider.stop(); + }; -/** - * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. - * @param options - */ -LineGraph.prototype.setOptions = function(options) { - if (options) { - var fields = ['sampling','defaultGroup','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort']; - util.selectiveDeepExtend(fields, this.options, options); - util.mergeOptions(this.options, options,'catmullRom'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); - util.mergeOptions(this.options, options,'legend'); - if (options.catmullRom) { - if (typeof options.catmullRom == 'object') { - if (options.catmullRom.parametrization) { - if (options.catmullRom.parametrization == 'uniform') { - this.options.catmullRom.alpha = 0; - } - else if (options.catmullRom.parametrization == 'chordal') { - this.options.catmullRom.alpha = 1.0; - } - else { - this.options.catmullRom.parametrization = 'centripetal'; - this.options.catmullRom.alpha = 0.5; - } - } - } + /** + * Resize the center position based on the current values in this.defaultXCenter + * and this.defaultYCenter (which are strings with a percentage or a value + * in pixels). The center positions are the variables this.xCenter + * and this.yCenter + */ + Graph3d.prototype._resizeCenter = function() { + // calculate the horizontal center position + if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') { + this.xcenter = + parseFloat(this.defaultXCenter) / 100 * + this.frame.canvas.clientWidth; } - - if (this.yAxisLeft) { - if (options.dataAxis !== undefined) { - this.yAxisLeft.setOptions(this.options.dataAxis); - this.yAxisRight.setOptions(this.options.dataAxis); - } + else { + this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px } - if (this.legendLeft) { - if (options.legend !== undefined) { - this.legendLeft.setOptions(this.options.legend); - this.legendRight.setOptions(this.options.legend); - } + // calculate the vertical center position + if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') { + this.ycenter = + parseFloat(this.defaultYCenter) / 100 * + (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); } - - if (this.groups.hasOwnProperty(UNGROUPED)) { - this.groups[UNGROUPED].setOptions(options); + else { + this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px } - } - if (this.dom.frame) { - this._updateGraph(); - } -}; - -/** - * Hide the component from the DOM - */ -LineGraph.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } -}; - -/** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed - */ -LineGraph.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); - } -}; + }; + /** + * Set the rotation and distance of the camera + * @param {Object} pos An object with the camera position. The object + * contains three parameters: + * - horizontal {Number} + * The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * - vertical {Number} + * The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. + * - distance {Number} + * The (normalized) distance of the camera to the + * center of the graph, a value between 0.71 and 5.0. + * Optional, can be left undefined. + */ + Graph3d.prototype.setCameraPosition = function(pos) { + if (pos === undefined) { + return; + } -/** - * Set items - * @param {vis.DataSet | null} items - */ -LineGraph.prototype.setItems = function(items) { - var me = this, - ids, - oldItemsData = this.itemsData; - - // replace the dataset - if (!items) { - this.itemsData = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - this.itemsData = items; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } + if (pos.horizontal !== undefined && pos.vertical !== undefined) { + this.camera.setArmRotation(pos.horizontal, pos.vertical); + } - if (oldItemsData) { - // unsubscribe from old dataset - util.forEach(this.itemListeners, function (callback, event) { - oldItemsData.off(event, callback); - }); + if (pos.distance !== undefined) { + this.camera.setArmLength(pos.distance); + } - // remove all drawn items - ids = oldItemsData.getIds(); - this._onRemove(ids); - } + this.redraw(); + }; - if (this.itemsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.itemListeners, function (callback, event) { - me.itemsData.on(event, callback, id); - }); - // add all new items - ids = this.itemsData.getIds(); - this._onAdd(ids); - } - this._updateUngrouped(); - this._updateGraph(); - this.redraw(); -}; + /** + * Retrieve the current camera rotation + * @return {object} An object with parameters horizontal, vertical, and + * distance + */ + Graph3d.prototype.getCameraPosition = function() { + var pos = this.camera.getArmRotation(); + pos.distance = this.camera.getArmLength(); + return pos; + }; -/** - * Set groups - * @param {vis.DataSet} groups - */ -LineGraph.prototype.setGroups = function(groups) { - var me = this, - ids; - - // unsubscribe from current dataset - if (this.groupsData) { - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.unsubscribe(event, callback); - }); + /** + * Load data into the 3D Graph + */ + Graph3d.prototype._readData = function(data) { + // read the data + this._dataInitialize(data, this.style); - // remove all drawn groups - ids = this.groupsData.getIds(); - this.groupsData = null; - this._onRemoveGroups(ids); // note: this will cause a redraw - } - // replace the dataset - if (!groups) { - this.groupsData = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - this.groupsData = groups; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } + if (this.dataFilter) { + // apply filtering + this.dataPoints = this.dataFilter._getDataPoints(); + } + else { + // no filtering. load all data + this.dataPoints = this._getDataPoints(this.dataTable); + } - if (this.groupsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.on(event, callback, id); - }); + // draw the filter + this._redrawFilter(); + }; - // draw all ms - ids = this.groupsData.getIds(); - this._onAddGroups(ids); - } - this._onUpdate(); -}; - - - -LineGraph.prototype._onUpdate = function(ids) { - this._updateUngrouped(); - this._updateAllGroupData(); - this._updateGraph(); - this.redraw(); -}; -LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; -LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; -LineGraph.prototype._onUpdateGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - var group = this.groupsData.get(groupIds[i]); - this._updateGroup(group, groupIds[i]); - } + /** + * Replace the dataset of the Graph3d + * @param {Array | DataSet | DataView} data + */ + Graph3d.prototype.setData = function (data) { + this._readData(data); + this.redraw(); - this._updateGraph(); - this.redraw(); -}; -LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; + // start animation when option is true + if (this.animationAutoStart && this.dataFilter) { + this.animationStart(); + } + }; -LineGraph.prototype._onRemoveGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - if (!this.groups.hasOwnProperty(groupIds[i])) { - if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { - this.yAxisRight.removeGroup(groupIds[i]); - this.legendRight.removeGroup(groupIds[i]); - this.legendRight.redraw(); + /** + * Update the options. Options will be merged with current options + * @param {Object} options + */ + Graph3d.prototype.setOptions = function (options) { + var cameraPosition = undefined; + + this.animationStop(); + + if (options !== undefined) { + // retrieve parameter values + if (options.width !== undefined) this.width = options.width; + if (options.height !== undefined) this.height = options.height; + + if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter; + if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter; + + if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel; + if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel; + if (options.xLabel !== undefined) this.xLabel = options.xLabel; + if (options.yLabel !== undefined) this.yLabel = options.yLabel; + if (options.zLabel !== undefined) this.zLabel = options.zLabel; + + if (options.style !== undefined) { + var styleNumber = this._getStyleNumber(options.style); + if (styleNumber !== -1) { + this.style = styleNumber; + } + } + if (options.showGrid !== undefined) this.showGrid = options.showGrid; + if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective; + if (options.showShadow !== undefined) this.showShadow = options.showShadow; + if (options.tooltip !== undefined) this.showTooltip = options.tooltip; + if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls; + if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio; + if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio; + + if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval; + if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload; + if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart; + + if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; + if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; + + if (options.xMin !== undefined) this.defaultXMin = options.xMin; + if (options.xStep !== undefined) this.defaultXStep = options.xStep; + if (options.xMax !== undefined) this.defaultXMax = options.xMax; + if (options.yMin !== undefined) this.defaultYMin = options.yMin; + if (options.yStep !== undefined) this.defaultYStep = options.yStep; + if (options.yMax !== undefined) this.defaultYMax = options.yMax; + if (options.zMin !== undefined) this.defaultZMin = options.zMin; + if (options.zStep !== undefined) this.defaultZStep = options.zStep; + if (options.zMax !== undefined) this.defaultZMax = options.zMax; + if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; + if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; + + if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; + + if (cameraPosition !== undefined) { + this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); + this.camera.setArmLength(cameraPosition.distance); } else { - this.yAxisLeft.removeGroup(groupIds[i]); - this.legendLeft.removeGroup(groupIds[i]); - this.legendLeft.redraw(); + this.camera.setArmRotation(1.0, 0.5); + this.camera.setArmLength(1.7); } - delete this.groups[groupIds[i]]; } - } - this._updateUngrouped(); - this._updateGraph(); - this.redraw(); -}; -/** - * update a group object - * - * @param group - * @param groupId - * @private - */ -LineGraph.prototype._updateGroup = function (group, groupId) { - if (!this.groups.hasOwnProperty(groupId)) { - this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); - if (this.groups[groupId].options.yAxisOrientation == 'right') { - this.yAxisRight.addGroup(groupId, this.groups[groupId]); - this.legendRight.addGroup(groupId, this.groups[groupId]); - } - else { - this.yAxisLeft.addGroup(groupId, this.groups[groupId]); - this.legendLeft.addGroup(groupId, this.groups[groupId]); + this._setBackgroundColor(options && options.backgroundColor); + + this.setSize(this.width, this.height); + + // re-load the data + if (this.dataTable) { + this.setData(this.dataTable); } - } - else { - this.groups[groupId].update(group); - if (this.groups[groupId].options.yAxisOrientation == 'right') { - this.yAxisRight.updateGroup(groupId, this.groups[groupId]); - this.legendRight.updateGroup(groupId, this.groups[groupId]); + + // start animation when option is true + if (this.animationAutoStart && this.dataFilter) { + this.animationStart(); } - else { - this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); - this.legendLeft.updateGroup(groupId, this.groups[groupId]); + }; + + /** + * Redraw the Graph. + */ + Graph3d.prototype.redraw = function() { + if (this.dataPoints === undefined) { + throw 'Error: graph data not initialized'; } - } - this.legendLeft.redraw(); - this.legendRight.redraw(); -}; -LineGraph.prototype._updateAllGroupData = function () { - if (this.itemsData != null) { - // ~450 ms @ 500k + this._resizeCanvas(); + this._resizeCenter(); + this._redrawSlider(); + this._redrawClear(); + this._redrawAxis(); - var groupsContent = {}; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - groupsContent[groupId] = []; - } + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + this._redrawDataGrid(); } - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - item.x = util.convert(item.x,"Date"); - groupsContent[item.group].push(item); - } + else if (this.style === Graph3d.STYLE.LINE) { + this._redrawDataLine(); } - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - this.groups[groupId].setItems(groupsContent[groupId]); - } - } -// // ~4500ms @ 500k -// for (var groupId in this.groups) { -// if (this.groups.hasOwnProperty(groupId)) { -// this.groups[groupId].setItems(this.itemsData.get({filter: -// function (item) { -// return (item.group == groupId); -// }, type:{x:"Date"}} -// )); -// } -// } - } -}; - -/** - * Create or delete the group holding all ungrouped items. This group is used when - * there are no groups specified. This anonymous group is called 'graph'. - * @protected - */ -LineGraph.prototype._updateUngrouped = function() { - if (this.itemsData != null) { -// var t0 = new Date(); - var group = {id: UNGROUPED, content: this.options.defaultGroup}; - this._updateGroup(group, UNGROUPED); - var ungroupedCounter = 0; - if (this.itemsData) { - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (item != undefined) { - if (item.hasOwnProperty('group')) { - if (item.group === undefined) { - item.group = UNGROUPED; - } - } - else { - item.group = UNGROUPED; - } - ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; - } - } - } + else if (this.style === Graph3d.STYLE.BAR || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + this._redrawDataBar(); } - - // much much slower -// var datapoints = this.itemsData.get({ -// filter: function (item) {return item.group === undefined;}, -// showInternalIds:true -// }); -// if (datapoints.length > 0) { -// var updateQuery = []; -// for (var i = 0; i < datapoints.length; i++) { -// updateQuery.push({id:datapoints[i].id, group: UNGROUPED}); -// } -// this.itemsData.update(updateQuery, true); -// } -// var t1 = new Date(); -// var pointInUNGROUPED = this.itemsData.get({filter: function (item) {return item.group == UNGROUPED;}}); - if (ungroupedCounter == 0) { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); + else { + // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE + this._redrawDataDot(); } -// console.log("getting amount ungrouped",new Date() - t1); -// console.log("putting in ungrouped",new Date() - t0); - } - else { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); - } - this.legendLeft.redraw(); - this.legendRight.redraw(); -}; + this._redrawInfo(); + this._redrawLegend(); + }; + /** + * Clear the canvas before redrawing + */ + Graph3d.prototype._redrawClear = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); -/** - * Redraw the component, mandatory function - * @return {boolean} Returns true if the component is resized - */ -LineGraph.prototype.redraw = function() { - var resized = false; + ctx.clearRect(0, 0, canvas.width, canvas.height); + }; - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; - if (this.lastWidth === undefined && this.width || this.lastWidth != this.width) { - resized = true; - } - // check if this component is resized - resized = this._isResized() || resized; - // check whether zoomed (in that case we need to re-stack everything) - var visibleInterval = this.body.range.end - this.body.range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth); - this.lastVisibleInterval = visibleInterval; - this.lastWidth = this.width; - - // calculate actual size and position - this.width = this.dom.frame.offsetWidth; - - // the svg element is three times as big as the width, this allows for fully dragging left and right - // without reloading the graph. the controls for this are bound to events in the constructor - if (resized == true) { - this.svg.style.width = util.option.asSize(3*this.width); - this.svg.style.left = util.option.asSize(-this.width); - } - if (zoomed == true) { - this._updateGraph(); - } - this.legendLeft.redraw(); - this.legendRight.redraw(); + /** + * Redraw the legend showing the colors + */ + Graph3d.prototype._redrawLegend = function() { + var y; - return resized; -}; + if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE) { -/** - * Update and redraw the graph. - * - */ -LineGraph.prototype._updateGraph = function () { - // reset the svg elements - DOMutil.prepareElements(this.svgElements); -// // very slow... -// groupData = group.itemsData.get({filter: -// function (item) { -// return (item.x > minDate && item.x < maxDate); -// }} -// ); - - - if (this.width != 0 && this.itemsData != null) { - var group, groupData, preprocessedGroup, i; - var preprocessedGroupData = []; - var processedGroupData = []; - var groupRanges = []; - var changeCalled = false; + var dotSize = this.frame.clientWidth * 0.02; - // getting group Ids - var groupIds = []; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - groupIds.push(groupId); + var widthMin, widthMax; + if (this.style === Graph3d.STYLE.DOTSIZE) { + widthMin = dotSize / 2; // px + widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function + } + else { + widthMin = 20; // px + widthMax = 20; // px } + + var height = Math.max(this.frame.clientHeight * 0.25, 100); + var top = this.margin; + var right = this.frame.clientWidth - this.margin; + var left = right - widthMax; + var bottom = top + height; } - // this is the range of the SVG canvas - var minDate = this.body.util.toGlobalTime(- this.body.domProps.root.width); - var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + ctx.lineWidth = 1; + ctx.font = '14px arial'; // TODO: put in options - // first select and preprocess the data from the datasets. - // the groups have their preselection of data, we now loop over this data to see - // what data we need to draw. Sorted data is much faster. - // more optimization is possible by doing the sampling before and using the binary search - // to find the end date to determine the increment. - if (groupIds.length > 0) { - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - groupData = []; - // optimization for sorted data - if (group.options.sort == true) { - var guess = Math.max(0,util.binarySearchGeneric(group.itemsData, minDate, 'x', 'before')); - - for (var j = guess; j < group.itemsData.length; j++) { - var item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > maxDate) { - groupData.push(item); - break; - } - else { - groupData.push(item); - } - } - } - } - else { - for (var j = 0; j < group.itemsData.length; j++) { - var item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > minDate && item.x < maxDate) { - groupData.push(item); - } - } - } - } - // preprocess, split into ranges and data - preprocessedGroup = this._preprocessData(groupData, group); - groupRanges.push({min: preprocessedGroup.min, max: preprocessedGroup.max}); - preprocessedGroupData.push(preprocessedGroup.data); - } + if (this.style === Graph3d.STYLE.DOTCOLOR) { + // draw the color bar + var ymin = 0; + var ymax = height; // Todo: make height customizable + for (y = ymin; y < ymax; y++) { + var f = (y - ymin) / (ymax - ymin); - // update the Y axis first, we use this data to draw at the correct Y points - // changeCalled is required to clean the SVG on a change emit. - changeCalled = this._updateYAxis(groupIds, groupRanges); - if (changeCalled == true) { - DOMutil.cleanupElements(this.svgElements); - this.body.emitter.emit("change"); - return; - } + //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function + var hue = f * 240; + var color = this._hsv2rgb(hue, 1, 1); - // with the yAxis scaled correctly, use this to get the Y values of the points. - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - processedGroupData.push(this._convertYvalues(preprocessedGroupData[i],group)) + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.moveTo(left, top + y); + ctx.lineTo(right, top + y); + ctx.stroke(); } - // draw the groups - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - if (group.options.style == 'line') { - this._drawLineGraph(processedGroupData[i], group); - } - else { - this._drawBarGraph (processedGroupData[i], group); - } - } + ctx.strokeStyle = this.colorAxis; + ctx.strokeRect(left, top, widthMax, height); } - } - // cleanup unused svg elements - DOMutil.cleanupElements(this.svgElements); -}; + if (this.style === Graph3d.STYLE.DOTSIZE) { + // draw border around color bar + ctx.strokeStyle = this.colorAxis; + ctx.fillStyle = this.colorDot; + ctx.beginPath(); + ctx.moveTo(left, top); + ctx.lineTo(right, top); + ctx.lineTo(right - widthMax + widthMin, bottom); + ctx.lineTo(left, bottom); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } -/** - * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. - * @param {array} groupIds - * @private - */ -LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { - var changeCalled = false; - var yAxisLeftUsed = false; - var yAxisRightUsed = false; - var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; - var orientation = 'left'; - - // if groups are present - if (groupIds.length > 0) { - for (var i = 0; i < groupIds.length; i++) { - orientation = 'left'; - var group = this.groups[groupIds[i]]; - if (group.options.yAxisOrientation == 'right') { - orientation = 'right'; + if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE) { + // print values along the color bar + var gridLineLen = 5; // px + var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true); + step.start(); + if (step.getCurrent() < this.valueMin) { + step.next(); } + while (!step.end()) { + y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height; - minVal = groupRanges[i].min; - maxVal = groupRanges[i].max; + ctx.beginPath(); + ctx.moveTo(left - gridLineLen, y); + ctx.lineTo(left, y); + ctx.stroke(); - if (orientation == 'left') { - yAxisLeftUsed = true; - minLeft = minLeft > minVal ? minVal : minLeft; - maxLeft = maxLeft < maxVal ? maxVal : maxLeft; - } - else { - yAxisRightUsed = true; - minRight = minRight > minVal ? minVal : minRight; - maxRight = maxRight < maxVal ? maxVal : maxRight; + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); + + step.next(); } + + ctx.textAlign = 'right'; + ctx.textBaseline = 'top'; + var label = this.legendLabel; + ctx.fillText(label, right, bottom + this.margin); } - if (yAxisLeftUsed == true) { - this.yAxisLeft.setRange(minLeft, maxLeft); - } - if (yAxisRightUsed == true) { - this.yAxisRight.setRange(minRight, maxRight); - } - } + }; - changeCalled = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || changeCalled; - changeCalled = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || changeCalled; + /** + * Redraw the filter + */ + Graph3d.prototype._redrawFilter = function() { + this.frame.filter.innerHTML = ''; - if (yAxisRightUsed == true && yAxisLeftUsed == true) { - this.yAxisLeft.drawIcons = true; - this.yAxisRight.drawIcons = true; - } - else { - this.yAxisLeft.drawIcons = false; - this.yAxisRight.drawIcons = false; - } + if (this.dataFilter) { + var options = { + 'visible': this.showAnimationControls + }; + var slider = new Slider(this.frame.filter, options); + this.frame.filter.slider = slider; - this.yAxisRight.master = !yAxisLeftUsed; + // TODO: css here is not nice here... + this.frame.filter.style.padding = '10px'; + //this.frame.filter.style.backgroundColor = '#EFEFEF'; - if (this.yAxisRight.master == false) { - if (yAxisRightUsed == true) { - this.yAxisLeft.lineOffset = this.yAxisRight.width; - } - changeCalled = this.yAxisLeft.redraw() || changeCalled; - this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; - changeCalled = this.yAxisRight.redraw() || changeCalled; - } - else { - changeCalled = this.yAxisRight.redraw() || changeCalled; - } - return changeCalled; -}; + slider.setValues(this.dataFilter.values); + slider.setPlayInterval(this.animationInterval); -/** - * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function - * - * @param {boolean} axisUsed - * @returns {boolean} - * @private - * @param axis - */ -LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { - var changed = false; - if (axisUsed == false) { - if (axis.dom.frame.parentNode) { - axis.hide(); - changed = true; + // create an event handler + var me = this; + var onchange = function () { + var index = slider.getIndex(); + + me.dataFilter.selectValue(index); + me.dataPoints = me.dataFilter._getDataPoints(); + + me.redraw(); + }; + slider.setOnChangeCallback(onchange); } - } - else { - if (!axis.dom.frame.parentNode) { - axis.show(); - changed = true; + else { + this.frame.filter.slider = undefined; } - } - return changed; -}; + }; + /** + * Redraw the slider + */ + Graph3d.prototype._redrawSlider = function() { + if ( this.frame.filter.slider !== undefined) { + this.frame.filter.slider.redraw(); + } + }; -/** - * draw a bar graph - * @param datapoints - * @param group - */ -LineGraph.prototype._drawBarGraph = function (dataset, group) { - if (dataset != null) { - if (dataset.length > 0) { - var coreDistance; - var minWidth = 0.1 * group.options.barChart.width; - var offset = 0; - var width = group.options.barChart.width; - if (group.options.barChart.align == 'left') {offset -= 0.5*width;} - else if (group.options.barChart.align == 'right') {offset += 0.5*width;} + /** + * Redraw common information + */ + Graph3d.prototype._redrawInfo = function() { + if (this.dataFilter) { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + + ctx.font = '14px arial'; // TODO: put in options + ctx.lineStyle = 'gray'; + ctx.fillStyle = 'gray'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; - for (var i = 0; i < dataset.length; i++) { - // dynammically downscale the width so there is no overlap up to 1/10th the original width - if (i+1 < dataset.length) {coreDistance = Math.abs(dataset[i+1].x - dataset[i].x);} - if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(dataset[i-1].x - dataset[i].x));} - if (coreDistance < width) {width = coreDistance < minWidth ? minWidth : coreDistance;} + var x = this.margin; + var y = this.margin; + ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y); + } + }; - DOMutil.drawBar(dataset[i].x + offset, dataset[i].y, width, group.zeroPosition - dataset[i].y, group.className + ' bar', this.svgElements, this.svg); - } - // draw points - if (group.options.drawPoints.enabled == true) { - this._drawPoints(dataset, group, this.svgElements, this.svg, offset); - } + /** + * Redraw the axis + */ + Graph3d.prototype._redrawAxis = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + from, to, step, prettyStep, + text, xText, yText, zText, + offset, xOffset, yOffset, + xMin2d, xMax2d; + + // TODO: get the actual rendered style of the containerElement + //ctx.font = this.containerElement.style.font; + ctx.font = 24 / this.camera.getArmLength() + 'px arial'; + + // calculate the length for the short grid lines + var gridLenX = 0.025 / this.scale.x; + var gridLenY = 0.025 / this.scale.y; + var textMargin = 5 / this.camera.getArmLength(); // px + var armAngle = this.camera.getArmRotation().horizontal; + + // draw x-grid lines + ctx.lineWidth = 1; + prettyStep = (this.defaultXStep === undefined); + step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep); + step.start(); + if (step.getCurrent() < this.xMin) { + step.next(); } - } -}; + while (!step.end()) { + var x = step.getCurrent(); - -/** - * draw a line graph - * - * @param datapoints - * @param group - */ -LineGraph.prototype._drawLineGraph = function (dataset, group) { - if (dataset != null) { - if (dataset.length > 0) { - var path, d; - var svgHeight = Number(this.svg.style.height.replace("px","")); - path = DOMutil.getSVGElement('path', this.svgElements, this.svg); - path.setAttributeNS(null, "class", group.className); - - // construct path from dataset - if (group.options.catmullRom.enabled == true) { - d = this._catmullRom(dataset, group); + if (this.showGrid) { + from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); } else { - d = this._linear(dataset); - } + from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - // append with points for fill and finalize the path - if (group.options.shaded.enabled == true) { - var fillPath = DOMutil.getSVGElement('path',this.svgElements, this.svg); - var dFill; - if (group.options.shaded.orientation == 'top') { - dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0; - } - else { - dFill = "M" + dataset[0].x + "," + svgHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + svgHeight; - } - fillPath.setAttributeNS(null, "class", group.className + " fill"); - fillPath.setAttributeNS(null, "d", dFill); + from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); } - // copy properties to path for drawing. - path.setAttributeNS(null, "d", "M" + d); - // draw points - if (group.options.drawPoints.enabled == true) { - this._drawPoints(dataset, group, this.svgElements, this.svg); + yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax; + text = this._convert3Dto2D(new Point3d(x, yText, this.zMin)); + if (Math.cos(armAngle * 2) > 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + text.y += textMargin; } - } - } -}; - -/** - * draw the data points - * - * @param dataset - * @param JSONcontainer - * @param svg - * @param group - */ -LineGraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg, offset) { - if (offset === undefined) {offset = 0;} - for (var i = 0; i < dataset.length; i++) { - DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, JSONcontainer, svg); - } -}; - + else if (Math.sin(armAngle * 2) < 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(' ' + step.getCurrent() + ' ', text.x, text.y); + step.next(); + } -/** - * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the - * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for - * the yAxis. - * - * @param datapoints - * @returns {Array} - * @private - */ -LineGraph.prototype._preprocessData = function (datapoints, group) { - var extractedData = []; - var xValue, yValue; - var toScreen = this.body.util.toScreen; - - var increment = 1; - var amountOfPoints = datapoints.length; - - var yMin = datapoints[0].y; - var yMax = datapoints[0].y; - - // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop - // of width changing of the yAxis. - if (group.options.sampling == true) { - var xDistance = this.body.util.toGlobalScreen(datapoints[datapoints.length-1].x) - this.body.util.toGlobalScreen(datapoints[0].x); - var pointsPerPixel = amountOfPoints/xDistance; - increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1,Math.round(pointsPerPixel))); - } + // draw y-grid lines + ctx.lineWidth = 1; + prettyStep = (this.defaultYStep === undefined); + step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep); + step.start(); + if (step.getCurrent() < this.yMin) { + step.next(); + } + while (!step.end()) { + if (this.showGrid) { + from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } + else { + from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - for (var i = 0; i < amountOfPoints; i += increment) { - xValue = toScreen(datapoints[i].x) + this.width - 1; - yValue = datapoints[i].y; - extractedData.push({x: xValue, y: yValue}); - yMin = yMin > yValue ? yValue : yMin; - yMax = yMax < yValue ? yValue : yMax; - } + from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } - // extractedData.sort(function (a,b) {return a.x - b.x;}); - return {min: yMin, max: yMax, data: extractedData}; -}; + xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax; + text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin)); + if (Math.cos(armAngle * 2) < 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + text.y += textMargin; + } + else if (Math.sin(armAngle * 2) > 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(' ' + step.getCurrent() + ' ', text.x, text.y); -/** - * This uses the DataAxis object to generate the correct Y coordinate on the SVG window. It uses the - * util function toScreen to get the x coordinate from the timestamp. - * - * @param datapoints - * @param options - * @returns {Array} - * @private - */ -LineGraph.prototype._convertYvalues = function (datapoints, group) { - var extractedData = []; - var xValue, yValue; - var axis = this.yAxisLeft; - var svgHeight = Number(this.svg.style.height.replace("px","")); - - if (group.options.yAxisOrientation == 'right') { - axis = this.yAxisRight; - } + step.next(); + } - for (var i = 0; i < datapoints.length; i++) { - xValue = datapoints[i].x; - yValue = Math.round(axis.convertValue(datapoints[i].y)); - extractedData.push({x: xValue, y: yValue}); - } + // draw z-grid lines and axis + ctx.lineWidth = 1; + prettyStep = (this.defaultZStep === undefined); + step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep); + step.start(); + if (step.getCurrent() < this.zMin) { + step.next(); + } + xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; + yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; + while (!step.end()) { + // TODO: make z-grid lines really 3d? + from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent())); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(from.x - textMargin, from.y); + ctx.stroke(); - group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(step.getCurrent() + ' ', from.x - 5, from.y); - // extractedData.sort(function (a,b) {return a.x - b.x;}); - return extractedData; -}; + step.next(); + } + ctx.lineWidth = 1; + from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + // draw x-axis + ctx.lineWidth = 1; + // line at yMin + xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); + xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(xMin2d.x, xMin2d.y); + ctx.lineTo(xMax2d.x, xMax2d.y); + ctx.stroke(); + // line at ymax + xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); + xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(xMin2d.x, xMin2d.y); + ctx.lineTo(xMax2d.x, xMax2d.y); + ctx.stroke(); -/** - * This uses an uniform parametrization of the CatmullRom algorithm: - * "On the Parameterization of Catmull-Rom Curves" by Cem Yuksel et al. - * @param data - * @returns {string} - * @private - */ -LineGraph.prototype._catmullRomUniform = function(data) { - // catmull rom - var p0, p1, p2, p3, bp1, bp2; - var d = Math.round(data[0].x) + "," + Math.round(data[0].y) + " "; - var normalization = 1/6; - var length = data.length; - for (var i = 0; i < length - 1; i++) { - - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; - - - // Catmull-Rom to Cubic Bezier conversion matrix - // 0 1 0 0 - // -1/6 1 1/6 0 - // 0 1/6 1 -1/6 - // 0 0 1 0 - - // bp0 = { x: p1.x, y: p1.y }; - bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; - bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; - // bp0 = { x: p2.x, y: p2.y }; - - d += "C" + - bp1.x + "," + - bp1.y + " " + - bp2.x + "," + - bp2.y + " " + - p2.x + "," + - p2.y + " "; - } + // draw y-axis + ctx.lineWidth = 1; + // line at xMin + from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + // line at xMax + from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - return d; -}; + // draw x-label + var xLabel = this.xLabel; + if (xLabel.length > 0) { + yOffset = 0.1 / this.scale.y; + xText = (this.xMin + this.xMax) / 2; + yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset; + text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + if (Math.cos(armAngle * 2) > 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + } + else if (Math.sin(armAngle * 2) < 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(xLabel, text.x, text.y); + } -/** - * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. - * By default, the centripetal parameterization is used because this gives the nicest results. - * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. - * - * One optimization can be used to reuse distances since this is a sliding window approach. - * @param data - * @returns {string} - * @private - */ -LineGraph.prototype._catmullRom = function(data, group) { - var alpha = group.options.catmullRom.alpha; - if (alpha == 0 || alpha === undefined) { - return this._catmullRomUniform(data); - } - else { - var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; - var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; - var d = Math.round(data[0].x) + "," + Math.round(data[0].y) + " "; - var length = data.length; - for (var i = 0; i < length - 1; i++) { + // draw y-label + var yLabel = this.yLabel; + if (yLabel.length > 0) { + xOffset = 0.1 / this.scale.x; + xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset; + yText = (this.yMin + this.yMax) / 2; + text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + if (Math.cos(armAngle * 2) < 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + } + else if (Math.sin(armAngle * 2) > 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(yLabel, text.x, text.y); + } - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; + // draw z-label + var zLabel = this.zLabel; + if (zLabel.length > 0) { + offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? + xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; + yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; + zText = (this.zMin + this.zMax) / 2; + text = this._convert3Dto2D(new Point3d(xText, yText, zText)); + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(zLabel, text.x - offset, text.y); + } + }; - d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); - d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); - d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); + /** + * Calculate the color based on the given value. + * @param {Number} H Hue, a value be between 0 and 360 + * @param {Number} S Saturation, a value between 0 and 1 + * @param {Number} V Value, a value between 0 and 1 + */ + Graph3d.prototype._hsv2rgb = function(H, S, V) { + var R, G, B, C, Hi, X; - // Catmull-Rom to Cubic Bezier conversion matrix - // - // A = 2d1^2a + 3d1^a * d2^a + d3^2a - // B = 2d3^2a + 3d3^a * d2^a + d2^2a - // - // [ 0 1 0 0 ] - // [ -d2^2a/N A/N d1^2a/N 0 ] - // [ 0 d3^2a/M B/M -d2^2a/M ] - // [ 0 0 1 0 ] - - // [ 0 1 0 0 ] - // [ -d2pow2a/N A/N d1pow2a/N 0 ] - // [ 0 d3pow2a/M B/M -d2pow2a/M ] - // [ 0 0 1 0 ] - - d3powA = Math.pow(d3, alpha); - d3pow2A = Math.pow(d3,2*alpha); - d2powA = Math.pow(d2, alpha); - d2pow2A = Math.pow(d2,2*alpha); - d1powA = Math.pow(d1, alpha); - d1pow2A = Math.pow(d1,2*alpha); - - A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; - B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; - N = 3*d1powA * (d1powA + d2powA); - if (N > 0) {N = 1 / N;} - M = 3*d3powA * (d3powA + d2powA); - if (M > 0) {M = 1 / M;} - - bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), - y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; - - bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), - y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; - - if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} - if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} - d += "C" + - bp1.x + "," + - bp1.y + " " + - bp2.x + "," + - bp2.y + " " + - p2.x + "," + - p2.y + " "; - } + C = V * S; + Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5 + X = C * (1 - Math.abs(((H/60) % 2) - 1)); - return d; - } -}; + switch (Hi) { + case 0: R = C; G = X; B = 0; break; + case 1: R = X; G = C; B = 0; break; + case 2: R = 0; G = C; B = X; break; + case 3: R = 0; G = X; B = C; break; + case 4: R = X; G = 0; B = C; break; + case 5: R = C; G = 0; B = X; break; -/** - * this generates the SVG path for a linear drawing between datapoints. - * @param data - * @returns {string} - * @private - */ -LineGraph.prototype._linear = function(data) { - // linear - var d = ""; - for (var i = 0; i < data.length; i++) { - if (i == 0) { - d += data[i].x + "," + data[i].y; + default: R = 0; G = 0; B = 0; break; } - else { - d += " " + data[i].x + "," + data[i].y; - } - } - return d; -}; - + return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')'; + }; + /** + * Draw all datapoints as a grid + * This function can be used when the style is 'grid' + */ + Graph3d.prototype._redrawDataGrid = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + point, right, top, cross, + i, + topSideVisible, fillStyle, strokeStyle, lineWidth, + h, s, v, zAvg; -/** - * @constructor DataStep - * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an - * end data point. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 - * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ -function DataStep(start, end, minimumStep, containerHeight, forcedStepSize) { - // variables - this.current = 0; + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? - this.autoScale = true; - this.stepIndex = 0; - this.step = 1; - this.scale = 1; + // calculate the translations and screen position of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); - this.marginStart; - this.marginEnd; + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; - this.majorSteps = [1, 2, 5, 10]; - this.minorSteps = [0.25, 0.5, 1, 2]; + // calculate the translation of the point at the bottom (needed for sorting) + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + } - this.setRange(start, end, minimumStep, containerHeight, forcedStepSize); -} + // sort the points on depth of their (x,y) position (not on z) + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); + + if (this.style === Graph3d.STYLE.SURFACE) { + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; + cross = this.dataPoints[i].pointCross; + + if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { + + if (this.showGrayBottom || this.showShadow) { + // calculate the cross product of the two vectors from center + // to left and right, in order to know whether we are looking at the + // bottom or at the top side. We can also use the cross product + // for calculating light intensity + var aDiff = Point3d.subtract(cross.trans, point.trans); + var bDiff = Point3d.subtract(top.trans, right.trans); + var crossproduct = Point3d.crossProduct(aDiff, bDiff); + var len = crossproduct.length(); + // FIXME: there is a bug with determining the surface side (shadow or colored) + + topSideVisible = (crossproduct.z > 0); + } + else { + topSideVisible = true; + } + if (topSideVisible) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + s = 1; // saturation + if (this.showShadow) { + v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale + fillStyle = this._hsv2rgb(h, s, v); + strokeStyle = fillStyle; + } + else { + v = 1; + fillStyle = this._hsv2rgb(h, s, v); + strokeStyle = this.colorAxis; + } + } + else { + fillStyle = 'gray'; + strokeStyle = this.colorAxis; + } + lineWidth = 0.5; + + ctx.lineWidth = lineWidth; + ctx.fillStyle = fillStyle; + ctx.strokeStyle = strokeStyle; + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.lineTo(cross.screen.x, cross.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } + } + } + else { // grid style + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; + + if (point !== undefined) { + if (this.showPerspective) { + lineWidth = 2 / -point.trans.z; + } + else { + lineWidth = 2 * -(this.eye.z / this.camera.getArmLength()); + } + } -/** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Number} [start] The start date and time. - * @param {Number} [end] The end date and time. - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ -DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, forcedStepSize) { - this._start = start; - this._end = end; + if (point !== undefined && right !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - if (this.autoScale) { - this.setMinimumStep(minimumStep, containerHeight, forcedStepSize); - } - this.setFirst(); -}; + ctx.lineWidth = lineWidth; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.stroke(); + } -/** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds - */ -DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { - // round to floor - var size = this._end - this._start; - var safeSize = size * 1.1; - var minimumStepValue = minimumStep * (safeSize / containerHeight); - var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); - - var minorStepIdx = -1; - var magnitudefactor = Math.pow(10,orderOfMagnitude); - - var start = 0; - if (orderOfMagnitude < 0) { - start = orderOfMagnitude; - } + if (point !== undefined && top !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + top.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - var solutionFound = false; - for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { - magnitudefactor = Math.pow(10,i); - for (var j = 0; j < this.minorSteps.length; j++) { - var stepSize = magnitudefactor * this.minorSteps[j]; - if (stepSize >= minimumStepValue) { - solutionFound = true; - minorStepIdx = j; - break; + ctx.lineWidth = lineWidth; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.stroke(); + } } } - if (solutionFound == true) { - break; - } - } - this.stepIndex = minorStepIdx; - this.scale = magnitudefactor; - this.step = magnitudefactor * this.minorSteps[minorStepIdx]; -}; + }; -/** - * Set the range iterator to the start date. - */ -DataStep.prototype.first = function() { - this.setFirst(); -}; + /** + * Draw all datapoints as dots. + * This function can be used when the style is 'dot' or 'dot-line' + */ + Graph3d.prototype._redrawDataDot = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + var i; -/** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date - */ -DataStep.prototype.setFirst = function() { - var niceStart = this._start - (this.scale * this.minorSteps[this.stepIndex]); - var niceEnd = this._end + (this.scale * this.minorSteps[this.stepIndex]); + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? - this.marginEnd = this.roundToMinor(niceEnd); - this.marginStart = this.roundToMinor(niceStart); - this.marginRange = this.marginEnd - this.marginStart; + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; - this.current = this.marginEnd; + // calculate the distance from the point at the bottom to the camera + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + } -}; + // order the translated points by depth + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); -DataStep.prototype.roundToMinor = function(value) { - var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); - if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { - return rounded + (this.scale * this.minorSteps[this.stepIndex]); - } - else { - return rounded; - } -} + // draw the datapoints as colored circles + var dotSize = this.frame.clientWidth * 0.02; // px + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; + + if (this.style === Graph3d.STYLE.DOTLINE) { + // draw a vertical line from the bottom to the graph value + //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin)); + var from = this._convert3Dto2D(point.bottom); + ctx.lineWidth = 1; + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(point.screen.x, point.screen.y); + ctx.stroke(); + } + // calculate radius for the circle + var size; + if (this.style === Graph3d.STYLE.DOTSIZE) { + size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin); + } + else { + size = dotSize; + } -/** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date - */ -DataStep.prototype.hasNext = function () { - return (this.current >= this.marginStart); -}; + var radius; + if (this.showPerspective) { + radius = size / -point.trans.z; + } + else { + radius = size * -(this.eye.z / this.camera.getArmLength()); + } + if (radius < 0) { + radius = 0; + } -/** - * Do the next step - */ -DataStep.prototype.next = function() { - var prev = this.current; - this.current -= this.step; + var hue, color, borderColor; + if (this.style === Graph3d.STYLE.DOTCOLOR ) { + // calculate the color based on the value + hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } + else if (this.style === Graph3d.STYLE.DOTSIZE) { + color = this.colorDot; + borderColor = this.colorDotBorder; + } + else { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } - // safety mechanism: if current time is still unchanged, move to the end - if (this.current == prev) { - this.current = this._end; - } -}; + // draw the circle + ctx.lineWidth = 1.0; + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true); + ctx.fill(); + ctx.stroke(); + } + }; -/** - * Do the next step - */ -DataStep.prototype.previous = function() { - this.current += this.step; - this.marginEnd += this.step; - this.marginRange = this.marginEnd - this.marginStart; -}; + /** + * Draw all datapoints as bars. + * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' + */ + Graph3d.prototype._redrawDataBar = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + var i, j, surface, corners; + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; -/** - * Get the current datetime - * @return {Number} current The current date - */ -DataStep.prototype.getCurrent = function() { - var toPrecision = '' + Number(this.current).toPrecision(5); - for (var i = toPrecision.length-1; i > 0; i--) { - if (toPrecision[i] == "0") { - toPrecision = toPrecision.slice(0,i); - } - else if (toPrecision[i] == "." || toPrecision[i] == ",") { - toPrecision = toPrecision.slice(0,i); - break; + // calculate the distance from the point at the bottom to the camera + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; } - else{ - break; - } - } + // order the translated points by depth + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); - return toPrecision; -}; - + // draw the datapoints as bars + var xWidth = this.xBarWidth / 2; + var yWidth = this.yBarWidth / 2; + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; + // determine color + var hue, color, borderColor; + if (this.style === Graph3d.STYLE.BARCOLOR ) { + // calculate the color based on the value + hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } + else if (this.style === Graph3d.STYLE.BARSIZE) { + color = this.colorDot; + borderColor = this.colorDotBorder; + } + else { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } -/** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ -DataStep.prototype.snap = function(date) { + // calculate size for the bar + if (this.style === Graph3d.STYLE.BARSIZE) { + xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + } -}; + // calculate all corner points + var me = this; + var point3d = point.point; + var top = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} + ]; + var bottom = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} + ]; + + // calculate screen location of the points + top.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); + bottom.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); -/** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. - */ -DataStep.prototype.isMajor = function() { - return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); -}; + // create five sides, calculate both corner points and center points + var surfaces = [ + {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, + {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, + {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, + {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, + {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} + ]; + point.surfaces = surfaces; + + // calculate the distance of each of the surface centers to the camera + for (j = 0; j < surfaces.length; j++) { + surface = surfaces[j]; + var transCenter = this._convertPointToTranslation(surface.center); + surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; + // TODO: this dept calculation doesn't work 100% of the cases due to perspective, + // but the current solution is fast/simple and works in 99.9% of all cases + // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) + } + + // order the surfaces by their (translated) depth + surfaces.sort(function (a, b) { + var diff = b.dist - a.dist; + if (diff) return diff; + + // if equal depth, sort the top surface last + if (a.corners === top) return 1; + if (b.corners === top) return -1; + + // both are equal + return 0; + }); -/** - * Utility functions for ordering and stacking of items - */ -var stack = {}; + // draw the ordered surfaces + ctx.lineWidth = 1; + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside + for (j = 2; j < surfaces.length; j++) { + surface = surfaces[j]; + corners = surface.corners; + ctx.beginPath(); + ctx.moveTo(corners[3].screen.x, corners[3].screen.y); + ctx.lineTo(corners[0].screen.x, corners[0].screen.y); + ctx.lineTo(corners[1].screen.x, corners[1].screen.y); + ctx.lineTo(corners[2].screen.x, corners[2].screen.y); + ctx.lineTo(corners[3].screen.x, corners[3].screen.y); + ctx.fill(); + ctx.stroke(); + } + } + }; -/** - * Order items by their start data - * @param {Item[]} items - */ -stack.orderByStart = function(items) { - items.sort(function (a, b) { - return a.data.start - b.data.start; - }); -}; -/** - * Order items by their end date. If they have no end date, their start date - * is used. - * @param {Item[]} items - */ -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; + /** + * Draw a line through all datapoints. + * This function can be used when the style is 'line' + */ + Graph3d.prototype._redrawDataLine = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + point, i; - return aTime - bTime; - }); -}; + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? -/** - * Adjust vertical positions of the items such that they don't overlap each - * other. - * @param {Item[]} items - * All visible items - * @param {{item: number, axis: number}} margin - * Margins between items and between items and the axis. - * @param {boolean} [force=false] - * If true, all items will be repositioned. If false (default), only - * items having a top===null will be re-stacked - */ -stack.stack = function(items, margin, force) { - var i, iMax; + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); - if (force) { - // reset top position of all items - for (i = 0, iMax = items.length; i < iMax; i++) { - items[i].top = null; + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; } - } - // calculate new, non-overlapping positions - for (i = 0, iMax = items.length; i < iMax; i++) { - var item = items[i]; - if (item.top === null) { - // initialize top position - item.top = margin.axis; - - do { - // TODO: optimize checking for overlap. when there is a gap without items, - // you only need to check for items from the next item on, not from zero - var collidingItem = null; - for (var j = 0, jj = items.length; j < jj; j++) { - var other = items[j]; - if (other.top !== null && other !== item && stack.collision(item, other, margin.item)) { - collidingItem = other; - break; - } - } + // start the line + if (this.dataPoints.length > 0) { + point = this.dataPoints[0]; - if (collidingItem != null) { - // There is a collision. Reposition the items above the colliding element - item.top = collidingItem.top + collidingItem.height + margin.item; - } - } while (collidingItem); + ctx.lineWidth = 1; // TODO: make customizable + ctx.strokeStyle = 'blue'; // TODO: make customizable + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); } - } -}; -/** - * Adjust vertical positions of the items without stacking them - * @param {Item[]} items - * All visible items - * @param {{item: number, axis: number}} margin - * Margins between items and between items and the axis. - */ -stack.nostack = function(items, margin) { - var i, iMax; + // draw the datapoints as colored circles + for (i = 1; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + ctx.lineTo(point.screen.x, point.screen.y); + } - // reset top position of all items - for (i = 0, iMax = items.length; i < iMax; i++) { - items[i].top = margin.axis; - } -}; + // finish the line + if (this.dataPoints.length > 0) { + ctx.stroke(); + } + }; -/** - * Test if the two provided items collide - * The items must have parameters left, width, top, and height. - * @param {Item} a The first item - * @param {Item} b The second item - * @param {Number} margin A minimum required margin. - * If margin is provided, the two items will be - * marked colliding when they overlap or - * when the margin between the two is smaller than - * the requested margin. - * @return {boolean} true if a and b collide, else false - */ -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) && - (a.top + a.height + margin) > b.top); -}; + /** + * Start a moving operation inside the provided parent element + * @param {Event} event The event that occurred (required for + * retrieving the mouse position) + */ + Graph3d.prototype._onMouseDown = function(event) { + event = event || window.event; -/** - * @constructor TimeStep - * The class TimeStep is an iterator for dates. You provide a start date and an - * end date. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 - * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ -function TimeStep(start, end, minimumStep) { - // variables - this.current = new Date(); - this._start = new Date(); - this._end = new Date(); - - this.autoScale = true; - this.scale = TimeStep.SCALE.DAY; - this.step = 1; - - // initialize the range - this.setRange(start, end, minimumStep); -} - -/// enum scale -TimeStep.SCALE = { - MILLISECOND: 1, - SECOND: 2, - MINUTE: 3, - HOUR: 4, - DAY: 5, - WEEKDAY: 6, - MONTH: 7, - YEAR: 8 -}; + // check if mouse is still down (may be up when focus is lost for example + // in an iframe) + if (this.leftButtonDown) { + this._onMouseUp(event); + } + // only react on left mouse button down + this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); + if (!this.leftButtonDown && !this.touchDown) return; -/** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Date} [start] The start date and time. - * @param {Date} [end] The end date and time. - * @param {int} [minimumStep] Optional. Minimum step size in milliseconds - */ -TimeStep.prototype.setRange = function(start, end, minimumStep) { - if (!(start instanceof Date) || !(end instanceof Date)) { - throw "No legal start or end date in method setRange"; - } + // get mouse position (different code for IE and all other browsers) + this.startMouseX = getMouseX(event); + this.startMouseY = getMouseY(event); - this._start = (start != undefined) ? new Date(start.valueOf()) : new Date(); - this._end = (end != undefined) ? new Date(end.valueOf()) : new Date(); + this.startStart = new Date(this.start); + this.startEnd = new Date(this.end); + this.startArmRotation = this.camera.getArmRotation(); - if (this.autoScale) { - this.setMinimumStep(minimumStep); - } -}; + this.frame.style.cursor = 'move'; -/** - * Set the range iterator to the start date. - */ -TimeStep.prototype.first = function() { - this.current = new Date(this._start.valueOf()); - this.roundToMinor(); -}; + // add event listeners to handle moving the contents + // we store the function onmousemove and onmouseup in the graph, so we can + // remove the eventlisteners lateron in the function mouseUp() + var me = this; + this.onmousemove = function (event) {me._onMouseMove(event);}; + this.onmouseup = function (event) {me._onMouseUp(event);}; + G3DaddEventListener(document, 'mousemove', me.onmousemove); + G3DaddEventListener(document, 'mouseup', me.onmouseup); + G3DpreventDefault(event); + }; -/** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date - */ -TimeStep.prototype.roundToMinor = function() { - // round to floor - // IMPORTANT: we have no breaks in this switch! (this is no bug) - //noinspection FallthroughInSwitchStatementJS - switch (this.scale) { - case 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: // intentional fall through - 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); - //case TimeStep.SCALE.MILLISECOND: // nothing to do for milliseconds - } - if (this.step != 1) { - // round down to the first minor value that is a multiple of the current step size - switch (this.scale) { - case 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: // intentional fall through - 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); break; - default: break; - } - } -}; + /** + * Perform moving operating. + * This function activated from within the funcion Graph.mouseDown(). + * @param {Event} event Well, eehh, the event + */ + Graph3d.prototype._onMouseMove = function (event) { + event = event || window.event; -/** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date - */ -TimeStep.prototype.hasNext = function () { - return (this.current.valueOf() <= this._end.valueOf()); -}; + // calculate change in mouse position + var diffX = parseFloat(getMouseX(event)) - this.startMouseX; + var diffY = parseFloat(getMouseY(event)) - this.startMouseY; -/** - * Do the next step - */ -TimeStep.prototype.next = function() { - var prev = this.current.valueOf(); + var horizontalNew = this.startArmRotation.horizontal + diffX / 200; + var verticalNew = this.startArmRotation.vertical + diffY / 200; - // Two cases, needed to prevent issues with switching daylight savings - // (end of March and end of October) - if (this.current.getMonth() < 6) { - switch (this.scale) { - case TimeStep.SCALE.MILLISECOND: + var snapAngle = 4; // degrees + var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); - this.current = new Date(this.current.valueOf() + this.step); break; - case TimeStep.SCALE.SECOND: this.current = new Date(this.current.valueOf() + this.step * 1000); break; - case TimeStep.SCALE.MINUTE: this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break; - case TimeStep.SCALE.HOUR: - this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60); - // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...) - var h = this.current.getHours(); - this.current.setHours(h - (h % this.step)); - break; - case TimeStep.SCALE.WEEKDAY: // intentional fall through - 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); break; - default: break; + // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... + // the -0.001 is to take care that the vertical axis is always drawn at the left front corner + if (Math.abs(Math.sin(horizontalNew)) < snapValue) { + horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001; } - } - else { - 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: // intentional fall through - 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); break; - default: break; + if (Math.abs(Math.cos(horizontalNew)) < snapValue) { + horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001; } - } - if (this.step != 1) { - // round down to the correct major value - switch (this.scale) { - case TimeStep.SCALE.MILLISECOND: if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break; - case TimeStep.SCALE.SECOND: if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break; - case TimeStep.SCALE.MINUTE: if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break; - case TimeStep.SCALE.HOUR: if(this.current.getHours() < this.step) this.current.setHours(0); break; - case TimeStep.SCALE.WEEKDAY: // intentional fall through - case TimeStep.SCALE.DAY: if(this.current.getDate() < this.step+1) this.current.setDate(1); break; - case TimeStep.SCALE.MONTH: if(this.current.getMonth() < this.step) this.current.setMonth(0); break; - case TimeStep.SCALE.YEAR: break; // nothing to do for year - default: break; + // snap vertically to nice angles + if (Math.abs(Math.sin(verticalNew)) < snapValue) { + verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI; + } + if (Math.abs(Math.cos(verticalNew)) < snapValue) { + verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI; } - } - - // safety mechanism: if current time is still unchanged, move to the end - if (this.current.valueOf() == prev) { - this.current = new Date(this._end.valueOf()); - } -}; + this.camera.setArmRotation(horizontalNew, verticalNew); + this.redraw(); -/** - * Get the current datetime - * @return {Date} current The current date - */ -TimeStep.prototype.getCurrent = function() { - return this.current; -}; + // fire a cameraPositionChange event + var parameters = this.getCameraPosition(); + this.emit('cameraPositionChange', parameters); -/** - * Set a custom scale. Autoscaling will be disabled. - * For example setScale(SCALE.MINUTES, 5) will result - * in minor steps of 5 minutes, and major steps of an hour. - * - * @param {TimeStep.SCALE} newScale - * A scale. Choose from SCALE.MILLISECOND, - * SCALE.SECOND, SCALE.MINUTE, SCALE.HOUR, - * SCALE.WEEKDAY, SCALE.DAY, SCALE.MONTH, - * SCALE.YEAR. - * @param {Number} newStep A step size, by default 1. Choose for - * example 1, 2, 5, or 10. - */ -TimeStep.prototype.setScale = function(newScale, newStep) { - this.scale = newScale; + G3DpreventDefault(event); + }; - if (newStep > 0) { - this.step = newStep; - } - this.autoScale = false; -}; + /** + * Stop moving operating. + * This function activated from within the funcion Graph.mouseDown(). + * @param {event} event The event + */ + Graph3d.prototype._onMouseUp = function (event) { + this.frame.style.cursor = 'auto'; + this.leftButtonDown = false; + + // remove event listeners here + G3DremoveEventListener(document, 'mousemove', this.onmousemove); + G3DremoveEventListener(document, 'mouseup', this.onmouseup); + G3DpreventDefault(event); + }; -/** - * Enable or disable autoscaling - * @param {boolean} enable If true, autoascaling is set true - */ -TimeStep.prototype.setAutoScale = function (enable) { - this.autoScale = enable; -}; + /** + * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point + * @param {Event} event A mouse move event + */ + Graph3d.prototype._onTooltip = function (event) { + var delay = 300; // ms + var mouseX = getMouseX(event) - getAbsoluteLeft(this.frame); + var mouseY = getMouseY(event) - getAbsoluteTop(this.frame); + if (!this.showTooltip) { + return; + } -/** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds - */ -TimeStep.prototype.setMinimumStep = function(minimumStep) { - if (minimumStep == undefined) { - return; - } + if (this.tooltipTimeout) { + clearTimeout(this.tooltipTimeout); + } - var stepYear = (1000 * 60 * 60 * 24 * 30 * 12); - var stepMonth = (1000 * 60 * 60 * 24 * 30); - var stepDay = (1000 * 60 * 60 * 24); - var stepHour = (1000 * 60 * 60); - var stepMinute = (1000 * 60); - var stepSecond = (1000); - var stepMillisecond= (1); - - // find the smallest step that is larger than the provided minimumStep - if (stepYear*1000 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1000;} - if (stepYear*500 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 500;} - if (stepYear*100 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 100;} - if (stepYear*50 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 50;} - if (stepYear*10 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 10;} - if (stepYear*5 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 5;} - if (stepYear > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1;} - if (stepMonth*3 > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 3;} - if (stepMonth > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 1;} - if (stepDay*5 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 5;} - if (stepDay*2 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 2;} - if (stepDay > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 1;} - if (stepDay/2 > minimumStep) {this.scale = TimeStep.SCALE.WEEKDAY; this.step = 1;} - if (stepHour*4 > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 4;} - if (stepHour > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 1;} - if (stepMinute*15 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 15;} - if (stepMinute*10 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 10;} - if (stepMinute*5 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 5;} - if (stepMinute > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 1;} - if (stepSecond*15 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 15;} - if (stepSecond*10 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 10;} - if (stepSecond*5 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 5;} - if (stepSecond > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 1;} - if (stepMillisecond*200 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 200;} - if (stepMillisecond*100 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 100;} - if (stepMillisecond*50 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 50;} - if (stepMillisecond*10 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 10;} - if (stepMillisecond*5 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 5;} - if (stepMillisecond > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 1;} -}; + // (delayed) display of a tooltip only if no mouse button is down + if (this.leftButtonDown) { + this._hideTooltip(); + return; + } -/** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ -TimeStep.prototype.snap = function(date) { - var clone = new Date(date.valueOf()); - - if (this.scale == TimeStep.SCALE.YEAR) { - var year = clone.getFullYear() + Math.round(clone.getMonth() / 12); - clone.setFullYear(Math.round(year / this.step) * this.step); - clone.setMonth(0); - clone.setDate(0); - clone.setHours(0); - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == TimeStep.SCALE.MONTH) { - if (clone.getDate() > 15) { - clone.setDate(1); - clone.setMonth(clone.getMonth() + 1); - // important: first set Date to 1, after that change the month. + if (this.tooltip && this.tooltip.dataPoint) { + // tooltip is currently visible + var dataPoint = this._dataPointFromXY(mouseX, mouseY); + if (dataPoint !== this.tooltip.dataPoint) { + // datapoint changed + if (dataPoint) { + this._showTooltip(dataPoint); + } + else { + this._hideTooltip(); + } + } } else { - clone.setDate(1); - } + // tooltip is currently not visible + var me = this; + this.tooltipTimeout = setTimeout(function () { + me.tooltipTimeout = null; - clone.setHours(0); - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == TimeStep.SCALE.DAY) { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 5: - case 2: - clone.setHours(Math.round(clone.getHours() / 24) * 24); break; - default: - clone.setHours(Math.round(clone.getHours() / 12) * 12); break; - } - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == TimeStep.SCALE.WEEKDAY) { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 5: - case 2: - clone.setHours(Math.round(clone.getHours() / 12) * 12); break; - default: - clone.setHours(Math.round(clone.getHours() / 6) * 6); break; - } - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == TimeStep.SCALE.HOUR) { - switch (this.step) { - case 4: - clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break; - default: - clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break; - } - clone.setSeconds(0); - clone.setMilliseconds(0); - } else if (this.scale == TimeStep.SCALE.MINUTE) { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 15: - case 10: - clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5); - clone.setSeconds(0); - break; - case 5: - clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break; - default: - clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break; - } - clone.setMilliseconds(0); - } - else if (this.scale == TimeStep.SCALE.SECOND) { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 15: - case 10: - clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5); - clone.setMilliseconds(0); - break; - case 5: - clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break; - default: - clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break; + // show a tooltip if we have a data point + var dataPoint = me._dataPointFromXY(mouseX, mouseY); + if (dataPoint) { + me._showTooltip(dataPoint); + } + }, delay); } - } - else if (this.scale == TimeStep.SCALE.MILLISECOND) { - var step = this.step > 5 ? this.step / 2 : 1; - clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step); - } - - return clone; -}; - -/** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. - */ -TimeStep.prototype.isMajor = function() { - switch (this.scale) { - case TimeStep.SCALE.MILLISECOND: - return (this.current.getMilliseconds() == 0); - case TimeStep.SCALE.SECOND: - return (this.current.getSeconds() == 0); - case TimeStep.SCALE.MINUTE: - return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); - // Note: this is no bug. Major label is equal for both minute and hour scale - case TimeStep.SCALE.HOUR: - return (this.current.getHours() == 0); - case TimeStep.SCALE.WEEKDAY: // intentional fall through - case TimeStep.SCALE.DAY: - return (this.current.getDate() == 1); - case TimeStep.SCALE.MONTH: - return (this.current.getMonth() == 0); - case TimeStep.SCALE.YEAR: - return false; - default: - return false; - } -}; - - -/** - * Returns formatted text for the minor axislabel, depending on the current - * date and the scale. For example when scale is MINUTE, the current time is - * formatted as "hh:mm". - * @param {Date} [date] custom date. if not provided, current date is taken - */ -TimeStep.prototype.getLabelMinor = function(date) { - if (date == undefined) { - date = this.current; - } - - switch (this.scale) { - case TimeStep.SCALE.MILLISECOND: return moment(date).format('SSS'); - case TimeStep.SCALE.SECOND: return moment(date).format('s'); - case TimeStep.SCALE.MINUTE: return moment(date).format('HH:mm'); - case TimeStep.SCALE.HOUR: return moment(date).format('HH:mm'); - case TimeStep.SCALE.WEEKDAY: return moment(date).format('ddd D'); - case TimeStep.SCALE.DAY: return moment(date).format('D'); - case TimeStep.SCALE.MONTH: return moment(date).format('MMM'); - case TimeStep.SCALE.YEAR: return moment(date).format('YYYY'); - default: return ''; - } -}; - - -/** - * Returns formatted text for the major axis label, depending on the current - * date and the scale. For example when scale is MINUTE, the major scale is - * hours, and the hour will be formatted as "hh". - * @param {Date} [date] custom date. if not provided, current date is taken - */ -TimeStep.prototype.getLabelMajor = function(date) { - if (date == undefined) { - date = this.current; - } - - //noinspection FallthroughInSwitchStatementJS - switch (this.scale) { - case TimeStep.SCALE.MILLISECOND:return moment(date).format('HH:mm:ss'); - case TimeStep.SCALE.SECOND: return moment(date).format('D MMMM HH:mm'); - case TimeStep.SCALE.MINUTE: - case TimeStep.SCALE.HOUR: return moment(date).format('ddd D MMMM'); - case TimeStep.SCALE.WEEKDAY: - case TimeStep.SCALE.DAY: return moment(date).format('MMMM YYYY'); - case TimeStep.SCALE.MONTH: return moment(date).format('YYYY'); - case TimeStep.SCALE.YEAR: return ''; - default: return ''; - } -}; + }; -/** - * @constructor Range - * A Range controls a numeric range with a start and end value. - * The Range adjusts the range based on mouse events or programmatic changes, - * and triggers events when the range is changing or has been changed. - * @param {{dom: Object, domProps: Object, emitter: Emitter}} body - * @param {Object} [options] See description at Range.setOptions - */ -function Range(body, options) { - var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); - this.start = now.clone().add('days', -3).valueOf(); // Number - this.end = now.clone().add('days', 4).valueOf(); // Number + /** + * Event handler for touchstart event on mobile devices + */ + Graph3d.prototype._onTouchStart = function(event) { + this.touchDown = true; - this.body = body; + var me = this; + this.ontouchmove = function (event) {me._onTouchMove(event);}; + this.ontouchend = function (event) {me._onTouchEnd(event);}; + G3DaddEventListener(document, 'touchmove', me.ontouchmove); + G3DaddEventListener(document, 'touchend', me.ontouchend); - // 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._onMouseDown(event); }; - this.options = util.extend({}, this.defaultOptions); - this.props = { - touch: {} + /** + * Event handler for touchmove event on mobile devices + */ + Graph3d.prototype._onTouchMove = function(event) { + this._onMouseMove(event); }; - // drag listeners for dragging - this.body.emitter.on('dragstart', this._onDragStart.bind(this)); - this.body.emitter.on('drag', this._onDrag.bind(this)); - this.body.emitter.on('dragend', this._onDragEnd.bind(this)); + /** + * Event handler for touchend event on mobile devices + */ + Graph3d.prototype._onTouchEnd = function(event) { + this.touchDown = false; - // ignore dragging when holding - this.body.emitter.on('hold', this._onHold.bind(this)); + G3DremoveEventListener(document, 'touchmove', this.ontouchmove); + G3DremoveEventListener(document, 'touchend', this.ontouchend); - // mouse wheel for zooming - this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); - this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF + this._onMouseUp(event); + }; - // pinch to zoom - this.body.emitter.on('touch', this._onTouch.bind(this)); - this.body.emitter.on('pinch', this._onPinch.bind(this)); - this.setOptions(options); -} + /** + * Event handler for mouse wheel event, used to zoom the graph + * Code from http://adomas.org/javascript-mouse-wheel/ + * @param {event} event The event + */ + Graph3d.prototype._onWheel = function(event) { + if (!event) /* For IE. */ + event = window.event; -Range.prototype = new Component(); + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta/120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail/3; + } -/** - * Set options for the range controller - * @param {Object} options Available options: - * {Number | Date | String} start Start date for the range - * {Number | Date | String} end End date for the range - * {Number} min Minimum value for start - * {Number} max Maximum value for end - * {Number} zoomMin Set a minimum value for - * (end - start). - * {Number} zoomMax Set a maximum value for - * (end - start). - * {Boolean} moveable Enable moving of the range - * by dragging. True by default - * {Boolean} zoomable Enable zooming of the range - * by pinching/scrolling. True by default - */ -Range.prototype.setOptions = function (options) { - if (options) { - // copy the options that we know - var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable']; - util.selectiveExtend(fields, this.options, options); + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + var oldLength = this.camera.getArmLength(); + var newLength = oldLength * (1 - delta / 10); - if ('start' in options || 'end' in options) { - // apply a new range. both start and end are optional - this.setRange(options.start, options.end); + this.camera.setArmLength(newLength); + this.redraw(); + + this._hideTooltip(); } - } -}; -/** - * Test whether direction has a valid value - * @param {String} direction 'horizontal' or 'vertical' - */ -function validateDirection (direction) { - if (direction != 'horizontal' && direction != 'vertical') { - throw new TypeError('Unknown direction "' + direction + '". ' + - 'Choose "horizontal" or "vertical".'); - } -} + // fire a cameraPositionChange event + var parameters = this.getCameraPosition(); + this.emit('cameraPositionChange', parameters); -/** - * Set a new start and end range - * @param {Number} [start] - * @param {Number} [end] - */ -Range.prototype.setRange = function(start, end) { - var changed = this._applyRange(start, end); - if (changed) { - var params = { - start: new Date(this.start), - end: new Date(this.end) - }; - this.body.emitter.emit('rangechange', params); - this.body.emitter.emit('rangechanged', params); - } -}; + // Prevent default actions caused by mouse wheel. + // That might be ugly, but we handle scrolls somehow + // anyway, so don't bother here.. + G3DpreventDefault(event); + }; -/** - * Set a new start and end range. This method is the same as setRange, but - * does not trigger a range change and range changed event, and it returns - * true when the range is changed - * @param {Number} [start] - * @param {Number} [end] - * @return {Boolean} changed - * @private - */ -Range.prototype._applyRange = function(start, end) { - var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start, - newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end, - max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null, - min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null, - diff; - - // check for valid number - if (isNaN(newStart) || newStart === null) { - throw new Error('Invalid start "' + start + '"'); - } - if (isNaN(newEnd) || newEnd === null) { - throw new Error('Invalid end "' + end + '"'); - } + /** + * Test whether a point lies inside given 2D triangle + * @param {Point2d} point + * @param {Point2d[]} triangle + * @return {boolean} Returns true if given point lies inside or on the edge of the triangle + * @private + */ + Graph3d.prototype._insideTriangle = function (point, triangle) { + var a = triangle[0], + b = triangle[1], + c = triangle[2]; - // prevent start < end - if (newEnd < newStart) { - newEnd = newStart; - } + function sign (x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; + } - // prevent start < min - if (min !== null) { - if (newStart < min) { - diff = (min - newStart); - newStart += diff; - newEnd += diff; + var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); + var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); + var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); - // prevent end > max - if (max != null) { - if (newEnd > max) { - newEnd = max; + // each of the three signs must be either equal to each other or zero + return (as == 0 || bs == 0 || as == bs) && + (bs == 0 || cs == 0 || bs == cs) && + (as == 0 || cs == 0 || as == cs); + }; + + /** + * Find a data point close to given screen position (x, y) + * @param {Number} x + * @param {Number} y + * @return {Object | null} The closest data point or null if not close to any data point + * @private + */ + Graph3d.prototype._dataPointFromXY = function (x, y) { + var i, + distMax = 100, // px + dataPoint = null, + closestDataPoint = null, + closestDist = null, + center = new Point2d(x, y); + + if (this.style === Graph3d.STYLE.BAR || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + // the data points are ordered from far away to closest + for (i = this.dataPoints.length - 1; i >= 0; i--) { + dataPoint = this.dataPoints[i]; + var surfaces = dataPoint.surfaces; + if (surfaces) { + for (var s = surfaces.length - 1; s >= 0; s--) { + // split each surface in two triangles, and see if the center point is inside one of these + var surface = surfaces[s]; + var corners = surface.corners; + var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; + var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; + if (this._insideTriangle(center, triangle1) || + this._insideTriangle(center, triangle2)) { + // return immediately at the first hit + return dataPoint; + } + } } } } - } - - // prevent end > max - if (max !== null) { - if (newEnd > max) { - diff = (newEnd - max); - newStart -= diff; - newEnd -= diff; - - // prevent start < min - if (min != null) { - if (newStart < min) { - newStart = min; + else { + // find the closest data point, using distance to the center of the point on 2d screen + for (i = 0; i < this.dataPoints.length; i++) { + dataPoint = this.dataPoints[i]; + var point = dataPoint.screen; + if (point) { + var distX = Math.abs(x - point.x); + var distY = Math.abs(y - point.y); + var dist = Math.sqrt(distX * distX + distY * distY); + + if ((closestDist === null || dist < closestDist) && dist < distMax) { + closestDist = dist; + closestDataPoint = dataPoint; + } } } } - } - // prevent (end-start) < zoomMin - if (this.options.zoomMin !== null) { - var zoomMin = parseFloat(this.options.zoomMin); - if (zoomMin < 0) { - zoomMin = 0; + + return closestDataPoint; + }; + + /** + * Display a tooltip for given data point + * @param {Object} dataPoint + * @private + */ + Graph3d.prototype._showTooltip = function (dataPoint) { + var content, line, dot; + + if (!this.tooltip) { + content = document.createElement('div'); + content.style.position = 'absolute'; + content.style.padding = '10px'; + content.style.border = '1px solid #4d4d4d'; + content.style.color = '#1a1a1a'; + content.style.background = 'rgba(255,255,255,0.7)'; + content.style.borderRadius = '2px'; + content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)'; + + line = document.createElement('div'); + line.style.position = 'absolute'; + line.style.height = '40px'; + line.style.width = '0'; + line.style.borderLeft = '1px solid #4d4d4d'; + + dot = document.createElement('div'); + dot.style.position = 'absolute'; + dot.style.height = '0'; + dot.style.width = '0'; + dot.style.border = '5px solid #4d4d4d'; + dot.style.borderRadius = '5px'; + + this.tooltip = { + dataPoint: null, + dom: { + content: content, + line: line, + dot: dot + } + }; } - if ((newEnd - newStart) < zoomMin) { - if ((this.end - this.start) === zoomMin) { - // ignore this action, we are already zoomed to the minimum - newStart = this.start; - newEnd = this.end; - } - else { - // zoom to the minimum - diff = (zoomMin - (newEnd - newStart)); - newStart -= diff / 2; - newEnd += diff / 2; - } + else { + content = this.tooltip.dom.content; + line = this.tooltip.dom.line; + dot = this.tooltip.dom.dot; } - } - // prevent (end-start) > zoomMax - if (this.options.zoomMax !== null) { - var zoomMax = parseFloat(this.options.zoomMax); - if (zoomMax < 0) { - zoomMax = 0; + this._hideTooltip(); + + this.tooltip.dataPoint = dataPoint; + if (typeof this.showTooltip === 'function') { + content.innerHTML = this.showTooltip(dataPoint.point); } - if ((newEnd - newStart) > zoomMax) { - if ((this.end - this.start) === zoomMax) { - // ignore this action, we are already zoomed to the maximum - newStart = this.start; - newEnd = this.end; - } - else { - // zoom to the maximum - diff = ((newEnd - newStart) - zoomMax); - newStart += diff / 2; - newEnd -= diff / 2; + else { + content.innerHTML = '' + + '' + + '' + + '' + + '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; + } + + content.style.left = '0'; + content.style.top = '0'; + this.frame.appendChild(content); + this.frame.appendChild(line); + this.frame.appendChild(dot); + + // calculate sizes + var contentWidth = content.offsetWidth; + var contentHeight = content.offsetHeight; + var lineHeight = line.offsetHeight; + var dotWidth = dot.offsetWidth; + var dotHeight = dot.offsetHeight; + + var left = dataPoint.screen.x - contentWidth / 2; + left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); + + line.style.left = dataPoint.screen.x + 'px'; + line.style.top = (dataPoint.screen.y - lineHeight) + 'px'; + content.style.left = left + 'px'; + content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px'; + dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px'; + dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px'; + }; + + /** + * Hide the tooltip when displayed + * @private + */ + Graph3d.prototype._hideTooltip = function () { + if (this.tooltip) { + this.tooltip.dataPoint = null; + + for (var prop in this.tooltip.dom) { + if (this.tooltip.dom.hasOwnProperty(prop)) { + var elem = this.tooltip.dom[prop]; + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); + } + } } } - } + }; - var changed = (this.start != newStart || this.end != newEnd); - this.start = newStart; - this.end = newEnd; + /** + * Add and event listener. Works for all browsers + * @param {Element} element An html element + * @param {string} action The action, for example 'click', + * without the prefix 'on' + * @param {function} listener The callback function to be executed + * @param {boolean} useCapture + */ + G3DaddEventListener = function(element, action, listener, useCapture) { + if (element.addEventListener) { + if (useCapture === undefined) + useCapture = false; - return changed; -}; + if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) { + action = 'DOMMouseScroll'; // For Firefox + } -/** - * Retrieve the current range. - * @return {Object} An object with start and end properties - */ -Range.prototype.getRange = function() { - return { - start: this.start, - end: this.end + element.addEventListener(action, listener, useCapture); + } else { + element.attachEvent('on' + action, listener); // IE browsers + } }; -}; -/** - * Calculate the conversion offset and scale for current range, based on - * the provided width - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ -Range.prototype.conversion = function (width) { - return Range.conversion(this.start, this.end, width); -}; + /** + * Remove an event listener from an element + * @param {Element} element An html dom element + * @param {string} action The name of the event, for example 'mousedown' + * @param {function} listener The listener function + * @param {boolean} useCapture + */ + G3DremoveEventListener = function(element, action, listener, useCapture) { + if (element.removeEventListener) { + // non-IE browsers + if (useCapture === undefined) + useCapture = false; -/** - * Static method to calculate the conversion offset and scale for a range, - * based on the provided start, end, and width - * @param {Number} start - * @param {Number} end - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ -Range.conversion = function (start, end, width) { - if (width != 0 && (end - start != 0)) { - return { - offset: start, - scale: width / (end - start) + if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) { + action = 'DOMMouseScroll'; // For Firefox + } + + element.removeEventListener(action, listener, useCapture); + } else { + // IE browsers + element.detachEvent('on' + action, listener); } - } - else { - return { - offset: 0, - scale: 1 - }; - } -}; - -/** - * 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 (!this.props.touch.allowDragging) return; + /** + * Stop event propagation + */ + G3DstopPropagation = function(event) { + if (!event) + event = window.event; - this.props.touch.start = this.start; - this.props.touch.end = this.end; + if (event.stopPropagation) { + event.stopPropagation(); // non-IE browsers + } + else { + event.cancelBubble = true; // IE browsers + } + }; - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'move'; - } -}; -/** - * 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); - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; - var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY, - 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(this.props.touch.start + diffRange, this.props.touch.end + diffRange); - this.body.emitter.emit('rangechange', { - start: new Date(this.start), - end: new Date(this.end) - }); -}; + /** + * Cancels the event if it is cancelable, without stopping further propagation of the event. + */ + G3DpreventDefault = function (event) { + if (!event) + event = window.event; -/** - * Stop dragging operation - * @param {event} event - * @private - */ -Range.prototype._onDragEnd = function (event) { - // only allow dragging when configured as movable - if (!this.options.moveable) return; + if (event.preventDefault) { + event.preventDefault(); // non-IE browsers + } + else { + event.returnValue = false; // IE browsers + } + }; - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + /** + * @constructor Slider + * + * An html slider control with start/stop/prev/next buttons + * @param {Element} container The element where the slider will be created + * @param {Object} options Available options: + * {boolean} visible If true (default) the + * slider is visible. + */ + function Slider(container, options) { + if (container === undefined) { + throw 'Error: No container element defined'; + } + this.container = container; + this.visible = (options && options.visible != undefined) ? options.visible : true; + + if (this.visible) { + this.frame = document.createElement('DIV'); + //this.frame.style.backgroundColor = '#E5E5E5'; + this.frame.style.width = '100%'; + this.frame.style.position = 'relative'; + this.container.appendChild(this.frame); + + this.frame.prev = document.createElement('INPUT'); + this.frame.prev.type = 'BUTTON'; + this.frame.prev.value = 'Prev'; + this.frame.appendChild(this.frame.prev); + + this.frame.play = document.createElement('INPUT'); + this.frame.play.type = 'BUTTON'; + this.frame.play.value = 'Play'; + this.frame.appendChild(this.frame.play); + + this.frame.next = document.createElement('INPUT'); + this.frame.next.type = 'BUTTON'; + this.frame.next.value = 'Next'; + this.frame.appendChild(this.frame.next); + + this.frame.bar = document.createElement('INPUT'); + this.frame.bar.type = 'BUTTON'; + this.frame.bar.style.position = 'absolute'; + this.frame.bar.style.border = '1px solid red'; + this.frame.bar.style.width = '100px'; + this.frame.bar.style.height = '6px'; + this.frame.bar.style.borderRadius = '2px'; + this.frame.bar.style.MozBorderRadius = '2px'; + this.frame.bar.style.border = '1px solid #7F7F7F'; + this.frame.bar.style.backgroundColor = '#E5E5E5'; + this.frame.appendChild(this.frame.bar); + + this.frame.slide = document.createElement('INPUT'); + this.frame.slide.type = 'BUTTON'; + this.frame.slide.style.margin = '0px'; + this.frame.slide.value = ' '; + this.frame.slide.style.position = 'relative'; + this.frame.slide.style.left = '-100px'; + this.frame.appendChild(this.frame.slide); + + // create events + var me = this; + this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);}; + this.frame.prev.onclick = function (event) {me.prev(event);}; + this.frame.play.onclick = function (event) {me.togglePlay(event);}; + this.frame.next.onclick = function (event) {me.next(event);}; + } - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'auto'; - } + this.onChangeCallback = undefined; - // fire a rangechanged event - this.body.emitter.emit('rangechanged', { - start: new Date(this.start), - end: new Date(this.end) - }); -}; + this.values = []; + this.index = undefined; -/** - * Event handler for mouse wheel event, used to zoom - * Code from http://adomas.org/javascript-mouse-wheel/ - * @param {Event} event - * @private - */ -Range.prototype._onMouseWheel = function(event) { - // only allow zooming when configured as zoomable and moveable - if (!(this.options.zoomable && this.options.moveable)) return; - - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta / 120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail / 3; + this.playTimeout = undefined; + this.playInterval = 1000; // milliseconds + this.playLoop = true; } - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - // perform the zoom action. Delta is normally 1 or -1 - - // adjust a negative delta such that zooming in with delta 0.1 - // equals zooming out with a delta -0.1 - var scale; - if (delta < 0) { - scale = 1 - (delta / 5); - } - else { - scale = 1 / (1 + (delta / 5)) ; + /** + * Select the previous index + */ + Slider.prototype.prev = function() { + var index = this.getIndex(); + if (index > 0) { + index--; + this.setIndex(index); } + }; - // calculate center, the date to zoom around - var gesture = util.fakeGesture(this, event), - pointer = getPointer(gesture.center, this.body.dom.center), - pointerDate = this._pointerToDate(pointer); - - this.zoom(scale, pointerDate); - } + /** + * Select the next index + */ + Slider.prototype.next = function() { + var index = this.getIndex(); + if (index < this.values.length - 1) { + index++; + this.setIndex(index); + } + }; - // Prevent default actions caused by mouse wheel - // (else the page and timeline both zoom and scroll) - event.preventDefault(); -}; + /** + * Select the next index + */ + Slider.prototype.playNext = function() { + var start = new Date(); -/** - * Start of a touch gesture - * @private - */ -Range.prototype._onTouch = function (event) { - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.allowDragging = true; - this.props.touch.center = null; -}; + var index = this.getIndex(); + if (index < this.values.length - 1) { + index++; + this.setIndex(index); + } + else if (this.playLoop) { + // jump to the start + index = 0; + this.setIndex(index); + } -/** - * On start of a hold gesture - * @private - */ -Range.prototype._onHold = function () { - this.props.touch.allowDragging = false; -}; + var end = new Date(); + var diff = (end - start); -/** - * Handle pinch event - * @param {Event} event - * @private - */ -Range.prototype._onPinch = function (event) { - // only allow zooming when configured as zoomable and moveable - if (!(this.options.zoomable && this.options.moveable)) return; + // calculate how much time it to to set the index and to execute the callback + // function. + var interval = Math.max(this.playInterval - diff, 0); + // document.title = diff // TODO: cleanup - this.props.touch.allowDragging = false; + var me = this; + this.playTimeout = setTimeout(function() {me.playNext();}, interval); + }; - if (event.gesture.touches.length > 1) { - if (!this.props.touch.center) { - this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center); + /** + * Toggle start or stop playing + */ + Slider.prototype.togglePlay = function() { + if (this.playTimeout === undefined) { + this.play(); + } else { + this.stop(); } + }; - var scale = 1 / event.gesture.scale, - initDate = this._pointerToDate(this.props.touch.center); - - // calculate new start and end - var newStart = parseInt(initDate + (this.props.touch.start - initDate) * scale); - var newEnd = parseInt(initDate + (this.props.touch.end - initDate) * scale); + /** + * Start playing + */ + Slider.prototype.play = function() { + // Test whether already playing + if (this.playTimeout) return; - // apply new range - this.setRange(newStart, newEnd); - } -}; + this.playNext(); -/** - * Helper function to calculate the center date for zooming - * @param {{x: Number, y: Number}} pointer - * @return {number} date - * @private - */ -Range.prototype._pointerToDate = function (pointer) { - var conversion; - var direction = this.options.direction; + if (this.frame) { + this.frame.play.value = 'Stop'; + } + }; - validateDirection(direction); + /** + * Stop playing + */ + Slider.prototype.stop = function() { + clearInterval(this.playTimeout); + this.playTimeout = undefined; - if (direction == 'horizontal') { - var width = this.body.domProps.center.width; - conversion = this.conversion(width); - return pointer.x / conversion.scale + conversion.offset; - } - else { - var height = this.body.domProps.center.height; - conversion = this.conversion(height); - return pointer.y / conversion.scale + conversion.offset; - } -}; + if (this.frame) { + this.frame.play.value = 'Play'; + } + }; -/** - * Get the pointer location relative to the location of the dom element - * @param {{pageX: Number, pageY: Number}} touch - * @param {Element} element HTML DOM element - * @return {{x: Number, y: Number}} pointer - * @private - */ -function getPointer (touch, element) { - return { - x: touch.pageX - vis.util.getAbsoluteLeft(element), - y: touch.pageY - vis.util.getAbsoluteTop(element) + /** + * Set a callback function which will be triggered when the value of the + * slider bar has changed. + */ + Slider.prototype.setOnChangeCallback = function(callback) { + this.onChangeCallback = callback; }; -} -/** - * Zoom the range the given scale in or out. Start and end date will - * be adjusted, and the timeline will be redrawn. You can optionally give a - * date around which to zoom. - * For example, try scale = 0.9 or 1.1 - * @param {Number} scale Scaling factor. Values above 1 will zoom out, - * values below 1 will zoom in. - * @param {Number} [center] Value representing a date around which will - * be zoomed. - */ -Range.prototype.zoom = function(scale, center) { - // if centerDate is not provided, take it half between start Date and end Date - if (center == null) { - center = (this.start + this.end) / 2; - } + /** + * Set the interval for playing the list + * @param {Number} interval The interval in milliseconds + */ + Slider.prototype.setPlayInterval = function(interval) { + this.playInterval = interval; + }; - // calculate new start and end - var newStart = center + (this.start - center) * scale; - var newEnd = center + (this.end - center) * scale; + /** + * Retrieve the current play interval + * @return {Number} interval The interval in milliseconds + */ + Slider.prototype.getPlayInterval = function(interval) { + return this.playInterval; + }; - this.setRange(newStart, newEnd); -}; + /** + * Set looping on or off + * @pararm {boolean} doLoop If true, the slider will jump to the start when + * the end is passed, and will jump to the end + * when the start is passed. + */ + Slider.prototype.setPlayLoop = function(doLoop) { + this.playLoop = doLoop; + }; -/** - * Move the range with a given delta to the left or right. Start and end - * value will be adjusted. For example, try delta = 0.1 or -0.1 - * @param {Number} delta Moving amount. Positive value will move right, - * negative value will move left - */ -Range.prototype.move = function(delta) { - // zoom start Date and end Date relative to the centerDate - var diff = (this.end - this.start); - // apply new values - var newStart = this.start + diff * delta; - var newEnd = this.end + diff * delta; + /** + * Execute the onchange callback function + */ + Slider.prototype.onChange = function() { + if (this.onChangeCallback !== undefined) { + this.onChangeCallback(); + } + }; - // TODO: reckon with min and max range + /** + * redraw the slider on the correct place + */ + Slider.prototype.redraw = function() { + if (this.frame) { + // resize the bar + this.frame.bar.style.top = (this.frame.clientHeight/2 - + this.frame.bar.offsetHeight/2) + 'px'; + this.frame.bar.style.width = (this.frame.clientWidth - + this.frame.prev.clientWidth - + this.frame.play.clientWidth - + this.frame.next.clientWidth - 30) + 'px'; + + // position the slider button + var left = this.indexToLeft(this.index); + this.frame.slide.style.left = (left) + 'px'; + } + }; - this.start = newStart; - this.end = newEnd; -}; -/** - * Move the range to a new center point - * @param {Number} moveTo New center point of the range - */ -Range.prototype.moveTo = function(moveTo) { - var center = (this.start + this.end) / 2; + /** + * Set the list with values for the slider + * @param {Array} values A javascript array with values (any type) + */ + Slider.prototype.setValues = function(values) { + this.values = values; - var diff = center - moveTo; + if (this.values.length > 0) + this.setIndex(0); + else + this.index = undefined; + }; - // calculate new start and end - var newStart = this.start - diff; - var newEnd = this.end - diff; + /** + * Select a value by its index + * @param {Number} index + */ + Slider.prototype.setIndex = function(index) { + if (index < this.values.length) { + this.index = index; - this.setRange(newStart, newEnd); -}; + this.redraw(); + this.onChange(); + } + else { + throw 'Error: index out of range'; + } + }; -/** - * Prototype for visual components - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] - * @param {Object} [options] - */ -function Component (body, options) { - this.options = null; - this.props = null; -} + /** + * retrieve the index of the currently selected vaue + * @return {Number} index + */ + Slider.prototype.getIndex = function() { + return this.index; + }; -/** - * Set options for the component. The new options will be merged into the - * current options. - * @param {Object} options - */ -Component.prototype.setOptions = function(options) { - if (options) { - util.extend(this.options, options); - } -}; -/** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ -Component.prototype.redraw = function() { - // should be implemented by the component - return false; -}; + /** + * retrieve the currently selected value + * @return {*} value + */ + Slider.prototype.get = function() { + return this.values[this.index]; + }; -/** - * Destroy the component. Cleanup DOM and event listeners - */ -Component.prototype.destroy = function() { - // should be implemented by the component -}; -/** - * Test whether the component is resized since the last time _isResized() was - * called. - * @return {Boolean} Returns true if the component is resized - * @protected - */ -Component.prototype._isResized = function() { - var resized = (this.props._previousWidth !== this.props.width || - this.props._previousHeight !== this.props.height); + Slider.prototype._onMouseDown = function(event) { + // only react on left mouse button down + var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); + if (!leftButtonDown) return; - this.props._previousWidth = this.props.width; - this.props._previousHeight = this.props.height; + this.startClientX = event.clientX; + this.startSlideX = parseFloat(this.frame.slide.style.left); - return resized; -}; + this.frame.style.cursor = 'move'; -/** - * 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 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 + // add event listeners to handle moving the contents + // we store the function onmousemove and onmouseup in the graph, so we can + // remove the eventlisteners lateron in the function mouseUp() + var me = this; + this.onmousemove = function (event) {me._onMouseMove(event);}; + this.onmouseup = function (event) {me._onMouseUp(event);}; + G3DaddEventListener(document, 'mousemove', this.onmousemove); + G3DaddEventListener(document, 'mouseup', this.onmouseup); + G3DpreventDefault(event); }; - this.defaultOptions = { - orientation: 'bottom', // supported: 'top', 'bottom' - // TODO: implement timeaxis orientations 'left' and 'right' - showMinorLabels: true, - showMajorLabels: true - }; - this.options = util.extend({}, this.defaultOptions); - this.body = body; + Slider.prototype.leftToIndex = function (left) { + var width = parseFloat(this.frame.bar.style.width) - + this.frame.slide.clientWidth - 10; + var x = left - 3; - // create the HTML DOM - this._create(); + var index = Math.round(x / width * (this.values.length-1)); + if (index < 0) index = 0; + if (index > this.values.length-1) index = this.values.length-1; - this.setOptions(options); -} + return index; + }; -TimeAxis.prototype = new Component(); + Slider.prototype.indexToLeft = function (index) { + var width = parseFloat(this.frame.bar.style.width) - + this.frame.slide.clientWidth - 10; -/** - * Set options for the TimeAxis. - * Parameters will be merged in current options. - * @param {Object} options Available options: - * {string} [orientation] - * {boolean} [showMinorLabels] - * {boolean} [showMajorLabels] - */ -TimeAxis.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels'], this.options, options); - } -}; + var x = index / (this.values.length-1) * width; + var left = x + 3; -/** - * Create the HTML DOM for the TimeAxis - */ -TimeAxis.prototype._create = function() { - this.dom.foreground = document.createElement('div'); - this.dom.background = document.createElement('div'); + return left; + }; - this.dom.foreground.className = 'timeaxis foreground'; - this.dom.background.className = 'timeaxis background'; -}; -/** - * Destroy the TimeAxis - */ -TimeAxis.prototype.destroy = function() { - // remove from DOM - if (this.dom.foreground.parentNode) { - this.dom.foreground.parentNode.removeChild(this.dom.foreground); - } - if (this.dom.background.parentNode) { - this.dom.background.parentNode.removeChild(this.dom.background); - } - this.body = null; -}; + Slider.prototype._onMouseMove = function (event) { + var diff = event.clientX - this.startClientX; + var x = this.startSlideX + diff; -/** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ -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); - - // calculate character width and height - this._calculateCharSize(); - - // TODO: recalculate sizes only needed when parent is resized or options is changed - var orientation = this.options.orientation, - showMinorLabels = this.options.showMinorLabels, - showMajorLabels = this.options.showMajorLabels; - - // determine the width and height of the elemens for the axis - props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; - props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - props.height = props.minorLabelHeight + props.majorLabelHeight; - props.width = foreground.offsetWidth; - - props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - - (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); - props.minorLineWidth = 1; // TODO: really calculate width - props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; - props.majorLineWidth = 1; // TODO: really calculate width - - // take foreground and background offline while updating (is almost twice as fast) - var foregroundNextSibling = foreground.nextSibling; - var backgroundNextSibling = background.nextSibling; - foreground.parentNode && foreground.parentNode.removeChild(foreground); - background.parentNode && background.parentNode.removeChild(background); - - foreground.style.height = this.props.height + 'px'; - - this._repaintLabels(); - - // put DOM online again (at the same place) - if (foregroundNextSibling) { - parent.insertBefore(foreground, foregroundNextSibling); - } - else { - parent.appendChild(foreground) - } - if (backgroundNextSibling) { - this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); - } - else { - this.body.dom.backgroundVertical.appendChild(background) - } + var index = this.leftToIndex(x); - return this._isResized() || parentChanged; -}; + this.setIndex(index); -/** - * Repaint major and minor text labels and vertical grid lines - * @private - */ -TimeAxis.prototype._repaintLabels = function () { - var orientation = this.options.orientation; - - // calculate range and step (step such that we have space for 7 characters per label) - var start = util.convert(this.body.range.start, 'Number'), - 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; - - // Move all DOM elements to a "redundant" list, where they - // can be picked for re-use, and clear the lists with lines and texts. - // At the end of the function _repaintLabels, left over elements will be cleaned up - var dom = this.dom; - dom.redundant.majorLines = dom.majorLines; - dom.redundant.majorTexts = dom.majorTexts; - dom.redundant.minorLines = dom.minorLines; - dom.redundant.minorTexts = dom.minorTexts; - dom.majorLines = []; - dom.majorTexts = []; - dom.minorLines = []; - dom.minorTexts = []; - - step.first(); - var xFirstMajorLabel = undefined; - var max = 0; - while (step.hasNext() && max < 1000) { - max++; - var cur = step.getCurrent(), - x = this.body.util.toScreen(cur), - isMajor = step.isMajor(); - - // TODO: lines must have a width, such that we can create css backgrounds - - if (this.options.showMinorLabels) { - this._repaintMinorText(x, step.getLabelMinor(), orientation); - } - - if (isMajor && this.options.showMajorLabels) { - if (x > 0) { - if (xFirstMajorLabel == undefined) { - xFirstMajorLabel = x; - } - this._repaintMajorText(x, step.getLabelMajor(), orientation); - } - this._repaintMajorLine(x, orientation); - } - else { - this._repaintMinorLine(x, orientation); - } + G3DpreventDefault(); + }; - step.next(); - } - // create a major label on the left when needed - if (this.options.showMajorLabels) { - var leftTime = this.body.util.toTime(0), - leftText = step.getLabelMajor(leftTime), - widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation + Slider.prototype._onMouseUp = function (event) { + this.frame.style.cursor = 'auto'; - if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { - this._repaintMajorText(0, leftText, orientation); - } - } + // remove event listeners + G3DremoveEventListener(document, 'mousemove', this.onmousemove); + G3DremoveEventListener(document, 'mouseup', this.onmouseup); - // Cleanup leftover DOM elements from the redundant list - util.forEach(this.dom.redundant, function (arr) { - while (arr.length) { - var elem = arr.pop(); - if (elem && elem.parentNode) { - elem.parentNode.removeChild(elem); - } - } - }); -}; + G3DpreventDefault(); + }; -/** - * Create a minor label for the axis at position x - * @param {Number} x - * @param {String} text - * @param {String} orientation "top" or "bottom" (default) - * @private - */ -TimeAxis.prototype._repaintMinorText = function (x, text, orientation) { - // reuse redundant label - var label = this.dom.redundant.minorTexts.shift(); - - if (!label) { - // create new label - var content = document.createTextNode(''); - label = document.createElement('div'); - label.appendChild(content); - label.className = 'text minor'; - this.dom.foreground.appendChild(label); - } - this.dom.minorTexts.push(label); - label.childNodes[0].nodeValue = text; - label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; - label.style.left = x + 'px'; - //label.title = title; // TODO: this is a heavy operation -}; + /**--------------------------------------------------------------------------**/ -/** - * Create a Major label for the axis at position x - * @param {Number} x - * @param {String} text - * @param {String} orientation "top" or "bottom" (default) - * @private - */ -TimeAxis.prototype._repaintMajorText = function (x, text, orientation) { - // reuse redundant label - var label = this.dom.redundant.majorTexts.shift(); - - if (!label) { - // create label - var content = document.createTextNode(text); - label = document.createElement('div'); - label.className = 'text major'; - label.appendChild(content); - this.dom.foreground.appendChild(label); - } - this.dom.majorTexts.push(label); - label.childNodes[0].nodeValue = text; - //label.title = title; // TODO: this is a heavy operation - label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); - label.style.left = x + 'px'; -}; + /** + * Retrieve the absolute left value of a DOM element + * @param {Element} elem A dom element, for example a div + * @return {Number} left The absolute left position of this element + * in the browser page. + */ + getAbsoluteLeft = function(elem) { + var left = 0; + while( elem !== null ) { + left += elem.offsetLeft; + left -= elem.scrollLeft; + elem = elem.offsetParent; + } + return left; + }; -/** - * Create a minor line for the axis at position x - * @param {Number} x - * @param {String} orientation "top" or "bottom" (default) - * @private - */ -TimeAxis.prototype._repaintMinorLine = function (x, orientation) { - // reuse redundant line - var line = this.dom.redundant.minorLines.shift(); - - if (!line) { - // create vertical line - line = document.createElement('div'); - line.className = 'grid vertical minor'; - this.dom.background.appendChild(line); - } - this.dom.minorLines.push(line); + /** + * Retrieve the absolute top value of a DOM element + * @param {Element} elem A dom element, for example a div + * @return {Number} top The absolute top position of this element + * in the browser page. + */ + getAbsoluteTop = function(elem) { + var top = 0; + while( elem !== null ) { + top += elem.offsetTop; + top -= elem.scrollTop; + elem = elem.offsetParent; + } + return top; + }; - var props = this.props; - if (orientation == 'top') { - line.style.top = props.majorLabelHeight + 'px'; - } - else { - line.style.top = this.body.domProps.top.height + 'px'; - } - line.style.height = props.minorLineHeight + 'px'; - line.style.left = (x - props.minorLineWidth / 2) + 'px'; -}; + /** + * Get the horizontal mouse position from a mouse event + * @param {Event} event + * @return {Number} mouse x + */ + getMouseX = function(event) { + if ('clientX' in event) return event.clientX; + return event.targetTouches[0] && event.targetTouches[0].clientX || 0; + }; -/** - * Create a Major line for the axis at position x - * @param {Number} x - * @param {String} orientation "top" or "bottom" (default) - * @private - */ -TimeAxis.prototype._repaintMajorLine = function (x, orientation) { - // reuse redundant line - var line = this.dom.redundant.majorLines.shift(); - - if (!line) { - // create vertical line - line = document.createElement('DIV'); - line.className = 'grid vertical major'; - this.dom.background.appendChild(line); - } - this.dom.majorLines.push(line); + /** + * Get the vertical mouse position from a mouse event + * @param {Event} event + * @return {Number} mouse y + */ + getMouseY = function(event) { + if ('clientY' in event) return event.clientY; + return event.targetTouches[0] && event.targetTouches[0].clientY || 0; + }; - var props = this.props; - if (orientation == 'top') { - line.style.top = '0'; - } - else { - line.style.top = this.body.domProps.top.height + 'px'; - } - line.style.left = (x - props.majorLineWidth / 2) + 'px'; - line.style.height = props.majorLineHeight + 'px'; -}; + module.exports = Graph3d; -/** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. - * @private - */ -TimeAxis.prototype._calculateCharSize = function () { - // Note: We calculate char size with every redraw. Size may change, for - // example when any of the timelines parents had display:none for example. - - // determine the char width and height on the minor axis - if (!this.dom.measureCharMinor) { - this.dom.measureCharMinor = document.createElement('DIV'); - this.dom.measureCharMinor.className = 'text minor measure'; - this.dom.measureCharMinor.style.position = 'absolute'; - - this.dom.measureCharMinor.appendChild(document.createTextNode('0')); - this.dom.foreground.appendChild(this.dom.measureCharMinor); - } - this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; - this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; - // determine the char width and height on the major axis - if (!this.dom.measureCharMajor) { - this.dom.measureCharMajor = document.createElement('DIV'); - this.dom.measureCharMajor.className = 'text minor measure'; - this.dom.measureCharMajor.style.position = 'absolute'; +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { - this.dom.measureCharMajor.appendChild(document.createTextNode('0')); - this.dom.foreground.appendChild(this.dom.measureCharMajor); - } - this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; - this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; -}; + var Emitter = __webpack_require__(41); + var Hammer = __webpack_require__(49); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Range = __webpack_require__(9); + var TimeAxis = __webpack_require__(21); + var CurrentTime = __webpack_require__(13); + var CustomTime = __webpack_require__(14); + var ItemSet = __webpack_require__(18); -/** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ -TimeAxis.prototype.snap = function(date) { - return this.step.snap(date); -}; + /** + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {Object} [options] See Timeline.setOptions for the available options. + * @constructor + */ + function Timeline (container, items, options) { + if (!(this instanceof Timeline)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } -/** - * A current time bar - * @param {{range: Range, dom: Object, domProps: Object}} body - * @param {Object} [options] Available parameters: - * {Boolean} [showCurrentTime] - * @constructor CurrentTime - * @extends Component - */ + var me = this; + this.defaultOptions = { + start: null, + end: null, -function CurrentTime (body, options) { - this.body = body; + autoResize: true, - // default options - this.defaultOptions = { - showCurrentTime: true - }; - this.options = util.extend({}, this.defaultOptions); + orientation: 'bottom', + width: null, + height: null, + maxHeight: null, + minHeight: null + }; + this.options = util.deepExtend({}, this.defaultOptions); - this._create(); + // Create the DOM, props, and emitter + this._create(container); - this.setOptions(options); -} + // all components listed here will be repainted automatically + this.components = []; -CurrentTime.prototype = new Component(); + this.body = { + dom: this.dom, + domProps: this.props, + emitter: { + on: this.on.bind(this), + off: this.off.bind(this), + emit: this.emit.bind(this) + }, + util: { + snap: null, // will be specified after TimeAxis is created + toScreen: me._toScreen.bind(me), + toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width + toTime: me._toTime.bind(me), + toGlobalTime : me._toGlobalTime.bind(me) + } + }; -/** - * Create the HTML DOM for the current time bar - * @private - */ -CurrentTime.prototype._create = function() { - var bar = document.createElement('div'); - bar.className = 'currenttime'; - bar.style.position = 'absolute'; - bar.style.top = '0px'; - bar.style.height = '100%'; + // range + this.range = new Range(this.body); + this.components.push(this.range); + this.body.range = this.range; - this.bar = bar; -}; + // time axis + this.timeAxis = new TimeAxis(this.body); + this.components.push(this.timeAxis); + this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); -/** - * Destroy the CurrentTime bar - */ -CurrentTime.prototype.destroy = function () { - this.options.showCurrentTime = false; - this.redraw(); // will remove the bar from the DOM and stop refreshing + // current time bar + this.currentTime = new CurrentTime(this.body); + this.components.push(this.currentTime); - this.body = null; -}; + // custom time bar + // Note: time bar will be attached in this.setOptions when selected + this.customTime = new CustomTime(this.body); + this.components.push(this.customTime); -/** - * Set options for the component. Options will be merged in current options. - * @param {Object} options Available parameters: - * {boolean} [showCurrentTime] - */ -CurrentTime.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend(['showCurrentTime'], this.options, options); - } -}; + // item set + this.itemSet = new ItemSet(this.body); + this.components.push(this.itemSet); -/** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ -CurrentTime.prototype.redraw = function() { - if (this.options.showCurrentTime) { - var parent = this.body.dom.backgroundVertical; - if (this.bar.parentNode != parent) { - // attach to the dom - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - parent.appendChild(this.bar); + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - this.start(); + // apply options + if (options) { + this.setOptions(options); } - 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); + // create itemset + if (items) { + this.setItems(items); + } + else { + this.redraw(); } - this.stop(); } - return false; -}; - -/** - * Start auto refreshing the current time bar - */ -CurrentTime.prototype.start = function() { - var me = this; + // turn Timeline into an event emitter + Emitter(Timeline.prototype); - function update () { - me.stop(); + /** + * 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._create = function (container) { + this.dom = {}; - // determine interval to refresh - var scale = me.body.range.conversion(me.body.domProps.center.width).scale; - var interval = 1 / scale / 10; - if (interval < 30) interval = 30; - if (interval > 1000) interval = 1000; + this.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)); + + // 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 = {}; - me.redraw(); + 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; + }); - // start a timer to adjust for the new time - me.currentTimeTimer = setTimeout(update, interval); - } + // 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 - update(); -}; + // attach the root panel to the provided container + if (!container) throw new Error('No container provided'); + container.appendChild(this.dom.root); + }; -/** - * Stop auto refreshing the current time bar - */ -CurrentTime.prototype.stop = function() { - if (this.currentTimeTimer !== undefined) { - clearTimeout(this.currentTimeTimer); - delete this.currentTimeTimer; - } -}; - -/** - * A custom time bar - * @param {{range: Range, dom: Object}} body - * @param {Object} [options] Available parameters: - * {Boolean} [showCustomTime] - * @constructor CustomTime - * @extends Component - */ - -function CustomTime (body, options) { - this.body = body; - - // default options - this.defaultOptions = { - showCustomTime: false - }; - this.options = util.extend({}, this.defaultOptions); + /** + * Destroy the Timeline, clean up all DOM elements and event listeners. + */ + Timeline.prototype.destroy = function () { + // unbind datasets + this.clear(); - this.customTime = new Date(); - this.eventParams = {}; // stores state parameters while dragging the bar + // remove all event listeners + this.off(); - // create the DOM - this._create(); + // stop checking for changed size + this._stopAutoResize(); - this.setOptions(options); -} + // remove from DOM + if (this.dom.root.parentNode) { + this.dom.root.parentNode.removeChild(this.dom.root); + } + this.dom = null; -CustomTime.prototype = new Component(); + // cleanup hammer touch events + for (var event in this.listeners) { + if (this.listeners.hasOwnProperty(event)) { + delete this.listeners[event]; + } + } + this.listeners = null; + this.hammer = null; -/** - * 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); - } -}; + // give all components the opportunity to cleanup + this.components.forEach(function (component) { + component.destroy(); + }); -/** - * Create the DOM for the custom time - * @private - */ -CustomTime.prototype._create = function() { - var bar = document.createElement('div'); - bar.className = 'customtime'; - bar.style.position = 'absolute'; - bar.style.top = '0px'; - bar.style.height = '100%'; - this.bar = bar; - - var drag = document.createElement('div'); - drag.style.position = 'relative'; - drag.style.top = '0px'; - drag.style.left = '-10px'; - drag.style.height = '100%'; - drag.style.width = '20px'; - bar.appendChild(drag); - - // attach event listeners - this.hammer = Hammer(bar, { - prevent_default: true - }); - this.hammer.on('dragstart', this._onDragStart.bind(this)); - this.hammer.on('drag', this._onDrag.bind(this)); - this.hammer.on('dragend', this._onDragEnd.bind(this)); -}; + this.body = null; + }; -/** - * Destroy the CustomTime bar - */ -CustomTime.prototype.destroy = function () { - this.options.showCustomTime = false; - this.redraw(); // will remove the bar from the DOM + /** + * 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); - this.hammer.enable(false); - this.hammer = null; + // enable/disable autoResize + this._initAutoResize(); + } - this.body = null; -}; + // propagate options to all components + this.components.forEach(function (component) { + component.setOptions(options); + }); -/** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ -CustomTime.prototype.redraw = function () { - if (this.options.showCustomTime) { - var parent = this.body.dom.backgroundVertical; - if (this.bar.parentNode != parent) { - // attach to the dom - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - parent.appendChild(this.bar); + // 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.'); } - var x = this.body.util.toScreen(this.customTime); + // redraw everything + this.redraw(); + }; - 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); + /** + * Set a custom time bar + * @param {Date} time + */ + Timeline.prototype.setCustomTime = function (time) { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); } - } - return false; -}; - -/** - * Set custom time. - * @param {Date} time - */ -CustomTime.prototype.setCustomTime = function(time) { - this.customTime = new Date(time.valueOf()); - this.redraw(); -}; + this.customTime.setCustomTime(time); + }; -/** - * Retrieve the current custom time. - * @return {Date} customTime - */ -CustomTime.prototype.getCustomTime = function() { - return new Date(this.customTime.valueOf()); -}; + /** + * Retrieve the current custom time. + * @return {Date} customTime + */ + Timeline.prototype.getCustomTime = function() { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); + } -/** - * Start moving horizontally - * @param {Event} event - * @private - */ -CustomTime.prototype._onDragStart = function(event) { - this.eventParams.dragging = true; - this.eventParams.customTime = this.customTime; + return this.customTime.getCustomTime(); + }; - event.stopPropagation(); - event.preventDefault(); -}; + /** + * Set items + * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + */ + Timeline.prototype.setItems = function(items) { + var initialLoad = (this.itemsData == null); -/** - * Perform moving operating. - * @param {Event} event - * @private - */ -CustomTime.prototype._onDrag = function (event) { - if (!this.eventParams.dragging) return; + // convert to type DataSet when needed + var newDataSet; + if (!items) { + newDataSet = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + newDataSet = items; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(items, { + type: { + start: 'Date', + end: 'Date' + } + }); + } - var deltaX = event.gesture.deltaX, - x = this.body.util.toScreen(this.eventParams.customTime) + deltaX, - time = this.body.util.toTime(x); + // set items + this.itemsData = newDataSet; + this.itemSet && this.itemSet.setItems(newDataSet); - this.setCustomTime(time); + if (initialLoad && ('start' in this.options || 'end' in this.options)) { + this.fit(); - // fire a timechange event - this.body.emitter.emit('timechange', { - time: new Date(this.customTime.valueOf()) - }); + 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; - event.stopPropagation(); - event.preventDefault(); -}; + this.setWindow(start, end); + } + }; -/** - * Stop moving operating. - * @param {event} event - * @private - */ -CustomTime.prototype._onDragEnd = function (event) { - if (!this.eventParams.dragging) return; + /** + * Set groups + * @param {vis.DataSet | Array | google.visualization.DataTable} groups + */ + Timeline.prototype.setGroups = function(groups) { + // convert to type DataSet when needed + var newDataSet; + if (!groups) { + newDataSet = null; + } + else if (groups instanceof DataSet || groups instanceof DataView) { + newDataSet = groups; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(groups); + } - // fire a timechanged event - this.body.emitter.emit('timechanged', { - time: new Date(this.customTime.valueOf()) - }); + this.groupsData = newDataSet; + this.itemSet.setGroups(newDataSet); + }; - event.stopPropagation(); - event.preventDefault(); -}; + /** + * Clear the Timeline. By Default, items, groups and options are cleared. + * Example usage: + * + * timeline.clear(); // clear items, groups, and options + * timeline.clear({options: true}); // clear options only + * + * @param {Object} [what] Optionally specify what to clear. By default: + * {items: true, groups: true, options: true} + */ + Timeline.prototype.clear = function(what) { + // clear items + if (!what || what.items) { + this.setItems(null); + } -var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items + // clear groups + if (!what || what.groups) { + this.setGroups(null); + } -/** - * An ItemSet holds a set of items and ranges which can be displayed in a - * range. The width is determined by the parent of the ItemSet, and the height - * is determined by the size of the items. - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body - * @param {Object} [options] See ItemSet.setOptions for the available options. - * @constructor ItemSet - * @extends Component - */ -function ItemSet(body, options) { - this.body = body; - - this.defaultOptions = { - type: null, // 'box', 'point', 'range' - 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 - - // listeners for the DataSet of the items - this.itemListeners = { - 'add': function (event, params, senderId) { - me._onAdd(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdate(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemove(params.items); - } - }; - - // listeners for the DataSet of the groups - this.groupListeners = { - 'add': function (event, params, senderId) { - me._onAddGroups(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdateGroups(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemoveGroups(params.items); - } - }; - - this.items = {}; // object with an Item for every data item - this.groups = {}; // Group object for every group - this.groupIds = []; - - this.selection = []; // list with the ids of all selected nodes - this.stackDirty = true; // if true, all items will be restacked on next redraw - - this.touchParams = {}; // stores properties while dragging - // create the HTML DOM - - this._create(); - - this.setOptions(options); -} - -ItemSet.prototype = new Component(); - -// available item types will be registered here -ItemSet.types = { - box: ItemBox, - range: ItemRange, - point: ItemPoint -}; + // clear options of timeline and of each of the components + if (!what || what.options) { + this.components.forEach(function (component) { + component.setOptions(component.defaultOptions); + }); -/** - * Create the HTML DOM for the ItemSet - */ -ItemSet.prototype._create = function(){ - var frame = document.createElement('div'); - frame.className = 'itemset'; - frame['timeline-itemset'] = this; - this.dom.frame = frame; - - // create background panel - var background = document.createElement('div'); - background.className = 'background'; - frame.appendChild(background); - this.dom.background = background; - - // create foreground panel - var foreground = document.createElement('div'); - foreground.className = 'foreground'; - frame.appendChild(foreground); - this.dom.foreground = foreground; - - // create axis panel - var axis = document.createElement('div'); - axis.className = 'axis'; - this.dom.axis = axis; - - // create labelset - var labelSet = document.createElement('div'); - labelSet.className = 'labelset'; - this.dom.labelSet = labelSet; - - // create ungrouped Group - this._updateUngrouped(); - - // attach event listeners - // Note: we bind to the centerContainer for the case where the height - // of the center container is larger than of the ItemSet, so we - // can click in the empty area to create a new item or deselect an item. - this.hammer = Hammer(this.body.dom.centerContainer, { - 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(); -}; + this.setOptions(this.defaultOptions); // this will also do a redraw + } + }; -/** - * Set options for the ItemSet. Existing options will be extended/overwritten. - * @param {Object} [options] The following options are available: - * {String} type - * Default type for the items. Choose from 'box' - * (default), 'point', or 'range'. The default - * Style can be overwritten by individual items. - * {String} align - * Alignment for the items, only applicable for - * ItemBox. Choose 'center' (default), 'left', or - * 'right'. - * {String} orientation - * Orientation of the item set. Choose 'top' or - * 'bottom' (default). - * {Function} groupOrder - * A sorting function for ordering groups - * {Boolean} stack - * If true (deafult), items will be stacked on - * top of each other. - * {Number} margin.axis - * Margin between the axis and the items in pixels. - * Default is 20. - * {Number} margin.item - * 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. - * {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); + /** + * Set Timeline window such that it fits all items + */ + Timeline.prototype.fit = function() { + // apply the data range as range + var dataRange = this.getItemRange(); - 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); + // add 5% space on both sides + var start = dataRange.min; + var end = dataRange.max; + if (start != null && end != null) { + var interval = (end.valueOf() - start.valueOf()); + if (interval <= 0) { + // prevent an empty interval + interval = 24 * 60 * 60 * 1000; // 1 day } + start = new Date(start.valueOf() - interval * 0.05); + end = new Date(end.valueOf() + interval * 0.05); } - 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); - } + // skip range set if there is no start and end date + if (start === null && end === null) { + return; } - // 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.range.setRange(start, end); + }; + + /** + * Get the data range of the item set. + * @returns {{min: Date, max: Date}} range A range with a start and end Date. + * When no minimum is found, min==null + * When no maximum is found, max==null + */ + Timeline.prototype.getItemRange = function() { + // calculate min from start filed + var dataset = this.itemsData.getDataSet(), + min = null, + max = null; + + if (dataset) { + // calculate the minimum value of the field 'start' + var minItem = dataset.min('start'); + min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; + // Note: we convert first to Date and then to number because else + // a conversion from ISODate to Number will fail + + // calculate maximum value of fields 'start' and 'end' + var maxStartItem = dataset.max('start'); + if (maxStartItem) { + max = util.convert(maxStartItem.start, 'Date').valueOf(); + } + var maxEndItem = dataset.max('end'); + if (maxEndItem) { + if (max == null) { + max = util.convert(maxEndItem.end, 'Date').valueOf(); + } + else { + max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); } - 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(); - } -}; + return { + min: (min != null) ? new Date(min) : null, + max: (max != null) ? new Date(max) : null + }; + }; -/** - * Mark the ItemSet dirty so it will refresh everything with next redraw - */ -ItemSet.prototype.markDirty = function() { - this.groupIds = []; - this.stackDirty = true; -}; + /** + * Set selected items by their id. Replaces the current selection + * Unknown id's are silently ignored. + * @param {Array} [ids] An array with zero or more id's of the items to be + * selected. If ids is an empty array, all items will be + * unselected. + */ + Timeline.prototype.setSelection = function(ids) { + this.itemSet && this.itemSet.setSelection(ids); + }; -/** - * Destroy the ItemSet - */ -ItemSet.prototype.destroy = function() { - this.hide(); - this.setItems(null); - this.setGroups(null); + /** + * Get the selected items by their id + * @return {Array} ids The ids of the selected items + */ + Timeline.prototype.getSelection = function() { + return this.itemSet && this.itemSet.getSelection() || []; + }; - this.hammer = null; + /** + * Set the visible window. Both parameters are optional, you can change only + * start or only end. Syntax: + * + * TimeLine.setWindow(start, end) + * TimeLine.setWindow(range) + * + * Where start and end can be a Date, number, or string, and range is an + * object with properties start and end. + * + * @param {Date | Number | String | Object} [start] Start date of visible window + * @param {Date | Number | String} [end] End date of visible window + */ + Timeline.prototype.setWindow = function(start, end) { + if (arguments.length == 1) { + var range = arguments[0]; + this.range.setRange(range.start, range.end); + } + else { + this.range.setRange(start, end); + } + }; - this.body = null; - this.conversion = null; -}; + /** + * Get the visible window + * @return {{start: Date, end: Date}} Visible range + */ + Timeline.prototype.getWindow = function() { + var range = this.range.getRange(); + return { + start: new Date(range.start), + end: new Date(range.end) + }; + }; -/** - * Hide the component from the DOM - */ -ItemSet.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } + /** + * Force a redraw of the Timeline. Can be useful to manually redraw when + * option autoResize=false + */ + 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 - + this.props.border.top - this.props.border.bottom, 0); + } + dom.center.style.left = '0'; + dom.center.style.top = offset + 'px'; + dom.left.style.left = '0'; + dom.left.style.top = offset + 'px'; + dom.right.style.left = '0'; + dom.right.style.top = offset + 'px'; + + // show shadows when vertical scrolling is available + var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; + var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; + dom.shadowTop.style.visibility = visibilityTop; + dom.shadowBottom.style.visibility = visibilityBottom; + dom.shadowTopLeft.style.visibility = visibilityTop; + dom.shadowBottomLeft.style.visibility = visibilityBottom; + dom.shadowTopRight.style.visibility = visibilityTop; + dom.shadowBottomRight.style.visibility = visibilityBottom; + + // redraw all components + this.components.forEach(function (component) { + resized = component.redraw() || resized; + }); + if (resized) { + // keep repainting until all sizes are settled + this.redraw(); + } + }; - // remove the axis with dots - if (this.dom.axis.parentNode) { - this.dom.axis.parentNode.removeChild(this.dom.axis); - } + // TODO: deprecated since version 1.1.0, remove some day + Timeline.prototype.repaint = function () { + throw new Error('Function repaint is deprecated. Use redraw instead.'); + }; - // remove the labelset containing all group labels - if (this.dom.labelSet.parentNode) { - this.dom.labelSet.parentNode.removeChild(this.dom.labelSet); - } -}; + /** + * Convert a position on screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x + * @private + */ + // TODO: move this function to Range + Timeline.prototype._toTime = function(x) { + var conversion = this.range.conversion(this.props.center.width); + return new Date(x / conversion.scale + conversion.offset); + }; -/** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed - */ -ItemSet.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); - } - // show axis with dots - if (!this.dom.axis.parentNode) { - this.body.dom.backgroundVertical.appendChild(this.dom.axis); - } + /** + * Convert a position on the global screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x + * @private + */ + // TODO: move this function to Range + Timeline.prototype._toGlobalTime = function(x) { + var conversion = this.range.conversion(this.props.root.width); + return new Date(x / conversion.scale + conversion.offset); + }; - // show labelset containing labels - if (!this.dom.labelSet.parentNode) { - this.body.dom.left.appendChild(this.dom.labelSet); - } -}; + /** + * 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; + }; -/** - * Set selected items by their id. Replaces the current selection - * Unknown id's are silently ignored. - * @param {Array} [ids] An array with zero or more id's of the items to be - * selected. If ids is an empty array, all items will be - * unselected. - */ -ItemSet.prototype.setSelection = function(ids) { - var i, ii, id, item; - if (ids) { - if (!Array.isArray(ids)) { - throw new TypeError('Array expected'); - } + /** + * Convert a datetime (Date object) into a position on the root + * This is used to get the pixel density estimate for the screen, not the center panel + * @param {Date} time A date + * @return {int} x The position on root in pixels which corresponds + * with the given date. + * @private + */ + // TODO: move this function to Range + Timeline.prototype._toGlobalScreen = function(time) { + var conversion = this.range.conversion(this.props.root.width); + return (time.valueOf() - conversion.offset) * conversion.scale; + }; - // unselect currently selected items - for (i = 0, ii = this.selection.length; i < ii; i++) { - id = this.selection[i]; - item = this.items[id]; - if (item) item.unselect(); - } - // select items - this.selection = []; - for (i = 0, ii = ids.length; i < ii; i++) { - id = ids[i]; - item = this.items[id]; - if (item) { - this.selection.push(id); - item.select(); - } + /** + * Initialize watching when option autoResize is true + * @private + */ + Timeline.prototype._initAutoResize = function () { + if (this.options.autoResize == true) { + this._startAutoResize(); } - } -}; - -/** - * Get the selected items by their id - * @return {Array} ids The ids of the selected items - */ -ItemSet.prototype.getSelection = function() { - return this.selection.concat([]); -}; - -/** - * Deselect a selected item - * @param {String | Number} id - * @private - */ -ItemSet.prototype._deselect = function(id) { - var selection = this.selection; - for (var i = 0, ii = selection.length; i < ii; i++) { - if (selection[i] == id) { // non-strict comparison! - selection.splice(i, 1); - break; + else { + this._stopAutoResize(); } - } -}; - -/** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ -ItemSet.prototype.redraw = function() { - var margin = this.options.margin, - range = this.body.range, - asSize = util.option.asSize, - options = this.options, - orientation = options.orientation, - resized = false, - frame = this.dom.frame, - editable = options.editable.updateTime || options.editable.updateGroup; - - // 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 = range.end - range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth); - if (zoomed) this.stackDirty = true; - this.lastVisibleInterval = visibleInterval; - this.props.lastWidth = this.props.width; - - // redraw all groups - var restack = this.stackDirty, - firstGroup = this._firstGroup(), - firstMargin = { - item: margin.item, - axis: margin.axis - }, - nonFirstMargin = { - item: margin.item, - axis: margin.item / 2 - }, - height = 0, - minHeight = margin.axis + margin.item; - util.forEach(this.groups, function (group) { - var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin; - var groupResized = group.redraw(range, groupMargin, restack); - resized = groupResized || resized; - height += group.height; - }); - height = Math.max(height, minHeight); - this.stackDirty = false; - - // update frame height - frame.style.height = asSize(height); - - // calculate actual size and position - 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.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; - - return resized; -}; + }; -/** - * Get the first group, aligned with the axis - * @return {Group | null} firstGroup - * @private - */ -ItemSet.prototype._firstGroup = function() { - var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1); - var firstGroupId = this.groupIds[firstGroupIndex]; - var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED]; + /** + * Watch for changes in the size of the container. On resize, the Panel will + * automatically redraw itself. + * @private + */ + Timeline.prototype._startAutoResize = function () { + var me = this; - return firstGroup || null; -}; + this._stopAutoResize(); -/** - * Create or delete the group holding all ungrouped items. This group is used when - * there are no groups specified. - * @protected - */ -ItemSet.prototype._updateUngrouped = function() { - var ungrouped = this.groups[UNGROUPED]; + this._onResize = function() { + if (me.options.autoResize != true) { + // stop watching when the option autoResize is changed to false + me._stopAutoResize(); + return; + } - if (this.groupsData) { - // remove the group holding all ungrouped items - if (ungrouped) { - ungrouped.hide(); - delete this.groups[UNGROUPED]; - } - } - else { - // create a group holding all (unfiltered) items - if (!ungrouped) { - var id = null; - var data = null; - ungrouped = new Group(id, data, this); - this.groups[UNGROUPED] = ungrouped; + 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; - for (var itemId in this.items) { - if (this.items.hasOwnProperty(itemId)) { - ungrouped.add(this.items[itemId]); + me.emit('change'); } } + }; - ungrouped.show(); - } - } -}; - -/** - * Get the element for the labelset - * @return {HTMLElement} labelSet - */ -ItemSet.prototype.getLabelSet = function() { - return this.dom.labelSet; -}; + // add event listener to window resize + util.addEventListener(window, 'resize', this._onResize); -/** - * Set items - * @param {vis.DataSet | null} items - */ -ItemSet.prototype.setItems = function(items) { - var me = this, - ids, - oldItemsData = this.itemsData; + this.watchTimer = setInterval(this._onResize, 1000); + }; - // replace the dataset - if (!items) { - this.itemsData = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - this.itemsData = items; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } + /** + * Stop watching for a resize of the frame. + * @private + */ + Timeline.prototype._stopAutoResize = function () { + if (this.watchTimer) { + clearInterval(this.watchTimer); + this.watchTimer = undefined; + } - if (oldItemsData) { - // unsubscribe from old dataset - util.forEach(this.itemListeners, function (callback, event) { - oldItemsData.off(event, callback); - }); + // remove event listener on window.resize + util.removeEventListener(window, 'resize', this._onResize); + this._onResize = null; + }; - // remove all drawn items - ids = oldItemsData.getIds(); - this._onRemove(ids); - } + /** + * Start moving the timeline vertically + * @param {Event} event + * @private + */ + Timeline.prototype._onTouch = function (event) { + this.touch.allowDragging = true; + }; - if (this.itemsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.itemListeners, function (callback, event) { - me.itemsData.on(event, callback, id); - }); + /** + * Start moving the timeline vertically + * @param {Event} event + * @private + */ + Timeline.prototype._onPinch = function (event) { + this.touch.allowDragging = false; + }; - // add all new items - ids = this.itemsData.getIds(); - this._onAdd(ids); + /** + * Start moving the timeline vertically + * @param {Event} event + * @private + */ + Timeline.prototype._onDragStart = function (event) { + this.touch.initialScrollTop = this.props.scrollTop; + }; - // update the group holding all ungrouped items - this._updateUngrouped(); - } -}; + /** + * 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; -/** - * Get the current items - * @returns {vis.DataSet | null} - */ -ItemSet.prototype.getItems = function() { - return this.itemsData; -}; + var delta = event.gesture.deltaY; -/** - * Set groups - * @param {vis.DataSet} groups - */ -ItemSet.prototype.setGroups = function(groups) { - var me = this, - ids; + var oldScrollTop = this._getScrollTop(); + var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); - // unsubscribe from current dataset - if (this.groupsData) { - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.unsubscribe(event, callback); - }); + if (newScrollTop != oldScrollTop) { + this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already + } + }; - // remove all drawn groups - ids = this.groupsData.getIds(); - this.groupsData = null; - this._onRemoveGroups(ids); // note: this will cause a redraw - } + /** + * Apply a scrollTop + * @param {Number} scrollTop + * @returns {Number} scrollTop Returns the applied scrollTop + * @private + */ + Timeline.prototype._setScrollTop = function (scrollTop) { + this.props.scrollTop = scrollTop; + this._updateScrollTop(); + return this.props.scrollTop; + }; - // replace the dataset - if (!groups) { - this.groupsData = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - this.groupsData = groups; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } + /** + * Update the current scrollTop when the height of the containers has been changed + * @returns {Number} scrollTop Returns the applied scrollTop + * @private + */ + 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; + } - if (this.groupsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.on(event, callback, id); - }); + // 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; - // draw all ms - ids = this.groupsData.getIds(); - this._onAddGroups(ids); - } + return this.props.scrollTop; + }; - // update the group holding all ungrouped items - this._updateUngrouped(); + /** + * Get the current scrollTop + * @returns {number} scrollTop + * @private + */ + Timeline.prototype._getScrollTop = function () { + return this.props.scrollTop; + }; - // update the order of all items in each group - this._order(); + module.exports = Timeline; - this.body.emitter.emit('change'); -}; -/** - * Get the current groups - * @returns {vis.DataSet | null} groups - */ -ItemSet.prototype.getGroups = function() { - return this.groupsData; -}; +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { -/** - * Remove an item by its id - * @param {String | Number} id - */ -ItemSet.prototype.removeItem = function(id) { - var item = this.itemsData.get(id), - dataset = this.itemsData.getDataSet(); + var Emitter = __webpack_require__(41); + var Hammer = __webpack_require__(49); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Range = __webpack_require__(9); + var TimeAxis = __webpack_require__(21); + var CurrentTime = __webpack_require__(13); + var CustomTime = __webpack_require__(14); + var LineGraph = __webpack_require__(20); - if (item) { - // confirm deletion - this.options.onRemove(item, function (item) { - if (item) { - // remove by id here, it is possible that an item has no id defined - // itself, so better not delete by the item itself - dataset.remove(id); - } - }); - } -}; + /** + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {Object} [options] See Graph2d.setOptions for the available options. + * @constructor + */ + function Graph2d (container, items, options, groups) { + var me = this; + this.defaultOptions = { + start: null, + end: null, -/** - * Handle updated items - * @param {Number[]} ids - * @protected - */ -ItemSet.prototype._onUpdate = function(ids) { - var me = this; + autoResize: true, - ids.forEach(function (id) { - var itemData = me.itemsData.get(id, me.itemOptions), - item = me.items[id], - type = itemData.type || me.options.type || (itemData.end ? 'range' : 'box'); + orientation: 'bottom', + width: null, + height: null, + maxHeight: null, + minHeight: null + }; + this.options = util.deepExtend({}, this.defaultOptions); - var constructor = ItemSet.types[type]; + // Create the DOM, props, and emitter + this._create(container); - if (item) { - // update item - if (!constructor || !(item instanceof constructor)) { - // item type has changed, delete the item and recreate it - me._removeItem(item); - item = null; - } - else { - me._updateItem(item, itemData); - } - } + // all components listed here will be repainted automatically + this.components = []; - if (!item) { - // create item - if (constructor) { - item = new constructor(itemData, me.conversion, me.options); - item.id = id; // TODO: not so nice setting id afterwards - me._addItem(item); - } - else if (type == 'rangeoverflow') { - // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day - throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + - '.vis.timeline .item.range .content {overflow: visible;}'); - } - else { - throw new TypeError('Unknown item type "' + type + '"'); + this.body = { + dom: this.dom, + domProps: this.props, + emitter: { + on: this.on.bind(this), + off: this.off.bind(this), + emit: this.emit.bind(this) + }, + util: { + snap: null, // will be specified after TimeAxis is created + toScreen: me._toScreen.bind(me), + toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width + toTime: me._toTime.bind(me), + toGlobalTime : me._toGlobalTime.bind(me) } - } - }); - - this._order(); - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change'); -}; - -/** - * Handle added items - * @param {Number[]} ids - * @protected - */ -ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate; - -/** - * Handle removed items - * @param {Number[]} ids - * @protected - */ -ItemSet.prototype._onRemove = function(ids) { - var count = 0; - var me = this; - ids.forEach(function (id) { - var item = me.items[id]; - if (item) { - count++; - me._removeItem(item); - } - }); - - if (count) { - // update order - this._order(); - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change'); - } -}; + }; -/** - * Update the order of item in all groups - * @private - */ -ItemSet.prototype._order = function() { - // reorder the items in all groups - // TODO: optimization: only reorder groups affected by the changed items - util.forEach(this.groups, function (group) { - group.order(); - }); -}; + // range + this.range = new Range(this.body); + this.components.push(this.range); + this.body.range = this.range; -/** - * Handle updated groups - * @param {Number[]} ids - * @private - */ -ItemSet.prototype._onUpdateGroups = function(ids) { - this._onAddGroups(ids); -}; + // time axis + this.timeAxis = new TimeAxis(this.body); + this.components.push(this.timeAxis); + this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); -/** - * Handle changed groups - * @param {Number[]} ids - * @private - */ -ItemSet.prototype._onAddGroups = function(ids) { - var me = this; + // current time bar + this.currentTime = new CurrentTime(this.body); + this.components.push(this.currentTime); - ids.forEach(function (id) { - var groupData = me.groupsData.get(id); - var group = me.groups[id]; + // custom time bar + // Note: time bar will be attached in this.setOptions when selected + this.customTime = new CustomTime(this.body); + this.components.push(this.customTime); - if (!group) { - // check for reserved ids - if (id == UNGROUPED) { - throw new Error('Illegal group id. ' + id + ' is a reserved id.'); - } + // item set + this.linegraph = new LineGraph(this.body); + this.components.push(this.linegraph); - var groupOptions = Object.create(me.options); - util.extend(groupOptions, { - height: null - }); + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - group = new Group(id, groupData, me); - me.groups[id] = group; + // apply options + if (options) { + this.setOptions(options); + } - // add items with this groupId to the new group - for (var itemId in me.items) { - if (me.items.hasOwnProperty(itemId)) { - var item = me.items[itemId]; - if (item.data.group == id) { - group.add(item); - } - } - } + // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! + if (groups) { + this.setGroups(groups); + } - group.order(); - group.show(); + // create itemset + if (items) { + this.setItems(items); } else { - // update group - group.setData(groupData); - } - }); - - this.body.emitter.emit('change'); -}; - -/** - * Handle removed groups - * @param {Number[]} ids - * @private - */ -ItemSet.prototype._onRemoveGroups = function(ids) { - var groups = this.groups; - ids.forEach(function (id) { - var group = groups[id]; - - if (group) { - group.hide(); - delete groups[id]; + this.redraw(); } - }); + } - this.markDirty(); + // turn Graph2d into an event emitter + Emitter(Graph2d.prototype); - this.body.emitter.emit('change'); -}; + /** + * Create the main DOM for the Graph2d: a root panel containing left, right, + * top, bottom, content, and background panel. + * @param {Element} container The container element where the Graph2d will + * be attached. + * @private + */ + Graph2d.prototype._create = function (container) { + this.dom = {}; -/** - * Reorder the groups if needed - * @return {boolean} changed - * @private - */ -ItemSet.prototype._orderGroups = function () { - if (this.groupsData) { - // reorder the groups - var groupIds = this.groupsData.getIds({ - order: this.options.groupOrder + this.dom.root = document.createElement('div'); + this.dom.background = document.createElement('div'); + this.dom.backgroundVertical = document.createElement('div'); + this.dom.backgroundHorizontalContainer = document.createElement('div'); + this.dom.centerContainer = document.createElement('div'); + this.dom.leftContainer = document.createElement('div'); + this.dom.rightContainer = document.createElement('div'); + this.dom.backgroundHorizontal = 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.backgroundHorizontalContainer.className = 'vispanel background horizontal'; + 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.backgroundHorizontalContainer); + 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.backgroundHorizontalContainer.appendChild(this.dom.backgroundHorizontal); + 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)); + + // 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 = {}; - var changed = !util.equalArray(groupIds, this.groupIds); - if (changed) { - // hide all groups, removes them from the DOM - var groups = this.groups; - groupIds.forEach(function (groupId) { - groups[groupId].hide(); - }); - - // show the groups again, attach them to the DOM in correct order - groupIds.forEach(function (groupId) { - groups[groupId].show(); - }); - - this.groupIds = groupIds; - } + 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; + }); - return changed; - } - else { - return false; - } -}; + // 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 -/** - * Add a new item - * @param {Item} item - * @private - */ -ItemSet.prototype._addItem = function(item) { - this.items[item.id] = item; + // attach the root panel to the provided container + if (!container) throw new Error('No container provided'); + container.appendChild(this.dom.root); + }; - // add to group - var groupId = this.groupsData ? item.data.group : UNGROUPED; - var group = this.groups[groupId]; - if (group) group.add(item); -}; + /** + * Destroy the Graph2d, clean up all DOM elements and event listeners. + */ + Graph2d.prototype.destroy = function () { + // unbind datasets + this.clear(); -/** - * Update an existing item - * @param {Item} item - * @param {Object} itemData - * @private - */ -ItemSet.prototype._updateItem = function(item, itemData) { - var oldGroupId = item.data.group; + // remove all event listeners + this.off(); - item.data = itemData; - if (item.displayed) { - item.redraw(); - } + // stop checking for changed size + this._stopAutoResize(); - // update group - if (oldGroupId != item.data.group) { - var oldGroup = this.groups[oldGroupId]; - if (oldGroup) oldGroup.remove(item); + // remove from DOM + if (this.dom.root.parentNode) { + this.dom.root.parentNode.removeChild(this.dom.root); + } + this.dom = null; - var groupId = this.groupsData ? item.data.group : UNGROUPED; - var group = this.groups[groupId]; - if (group) group.add(item); - } -}; + // cleanup hammer touch events + for (var event in this.listeners) { + if (this.listeners.hasOwnProperty(event)) { + delete this.listeners[event]; + } + } + this.listeners = null; + this.hammer = null; -/** - * Delete an item from the ItemSet: remove it from the DOM, from the map - * with items, and from the map with visible items, and from the selection - * @param {Item} item - * @private - */ -ItemSet.prototype._removeItem = function(item) { - // remove from DOM - item.hide(); + // give all components the opportunity to cleanup + this.components.forEach(function (component) { + component.destroy(); + }); - // remove from items - delete this.items[item.id]; + this.body = null; + }; - // remove from selection - var index = this.selection.indexOf(item.id); - if (index != -1) this.selection.splice(index, 1); + /** + * Set options. Options will be passed to all components loaded in the Graph2d. + * @param {Object} [options] + * {String} orientation + * Vertical orientation for the Graph2d, + * 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 Graph2d, a number in pixels or + * a css string like '400px' or '75%'. If undefined, + * The Graph2d will automatically size such that + * its contents fit. + * {String | Number} minHeight + * Minimum height for the Graph2d, a number in pixels or + * a css string like '400px' or '75%'. + * {String | Number} maxHeight + * Maximum height for the Graph2d, 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 + */ + Graph2d.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); - // remove from group - var groupId = this.groupsData ? item.data.group : UNGROUPED; - var group = this.groups[groupId]; - if (group) group.remove(item); -}; + // enable/disable autoResize + this._initAutoResize(); + } -/** - * Create an array containing all items being a range (having an end date) - * @param array - * @returns {Array} - * @private - */ -ItemSet.prototype._constructByEndArray = function(array) { - var endArray = []; + // propagate options to all components + this.components.forEach(function (component) { + component.setOptions(options); + }); - for (var i = 0; i < array.length; i++) { - if (array[i] instanceof ItemRange) { - endArray.push(array[i]); + // 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.'); } - } - return endArray; -}; -/** - * Register the clicked item on touch, before dragStart is initiated. - * - * dragStart is initiated from a mousemove event, which can have left the item - * already resulting in an item == null - * - * @param {Event} event - * @private - */ -ItemSet.prototype._onTouch = function (event) { - // store the touched item, used in _onDragStart - this.touchParams.item = ItemSet.itemFromTarget(event); -}; + // redraw everything + this.redraw(); + }; -/** - * Start dragging the selected events - * @param {Event} event - * @private - */ -ItemSet.prototype._onDragStart = function (event) { - if (!this.options.editable.updateTime && !this.options.editable.updateGroup) { - return; - } + /** + * Set a custom time bar + * @param {Date} time + */ + Graph2d.prototype.setCustomTime = function (time) { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); + } - var item = this.touchParams.item || null, - me = this, - props; + this.customTime.setCustomTime(time); + }; - if (item && item.selected) { - var dragLeftItem = event.target.dragLeftItem; - var dragRightItem = event.target.dragRightItem; + /** + * Retrieve the current custom time. + * @return {Date} customTime + */ + Graph2d.prototype.getCustomTime = function() { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); + } - if (dragLeftItem) { - props = { - item: dragLeftItem - }; + return this.customTime.getCustomTime(); + }; - if (me.options.editable.updateTime) { - props.start = item.data.start.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; - } + /** + * Set items + * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + */ + Graph2d.prototype.setItems = function(items) { + var initialLoad = (this.itemsData == null); - this.touchParams.itemProps = [props]; + // convert to type DataSet when needed + var newDataSet; + if (!items) { + newDataSet = null; } - else if (dragRightItem) { - props = { - item: dragRightItem - }; - - if (me.options.editable.updateTime) { - props.end = item.data.end.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; - } - - this.touchParams.itemProps = [props]; + else if (items instanceof DataSet || items instanceof DataView) { + newDataSet = items; } else { - this.touchParams.itemProps = this.getSelection().map(function (id) { - var item = me.items[id]; - var props = { - item: item - }; - - if (me.options.editable.updateTime) { - if ('start' in item.data) props.start = item.data.start.valueOf(); - if ('end' in item.data) props.end = item.data.end.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; + // turn an array into a dataset + newDataSet = new DataSet(items, { + type: { + start: 'Date', + end: 'Date' } - - return props; }); } - event.stopPropagation(); - } -}; - -/** - * Drag selected items - * @param {Event} event - * @private - */ -ItemSet.prototype._onDrag = function (event) { - if (this.touchParams.itemProps) { - var range = this.body.range, - snap = this.body.util.snap || null, - deltaX = event.gesture.deltaX, - scale = (this.props.width / (range.end - range.start)), - offset = deltaX / scale; - - // move - this.touchParams.itemProps.forEach(function (props) { - if ('start' in props) { - var start = new Date(props.start + offset); - props.item.data.start = snap ? snap(start) : start; - } + // set items + this.itemsData = newDataSet; + this.linegraph && this.linegraph.setItems(newDataSet); - if ('end' in props) { - var end = new Date(props.end + offset); - props.item.data.end = snap ? snap(end) : end; - } + if (initialLoad && ('start' in this.options || 'end' in this.options)) { + this.fit(); - if ('group' in props) { - // drag from one group to another - var group = ItemSet.groupFromTarget(event); - if (group && group.groupId != props.item.data.group) { - var oldGroup = props.item.parent; - oldGroup.remove(props.item); - oldGroup.order(); - group.add(props.item); - group.order(); + 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; - props.item.data.group = group.groupId; - } - } - }); + this.setWindow(start, end); + } + }; - // TODO: implement onMoving handler + /** + * Set groups + * @param {vis.DataSet | Array | google.visualization.DataTable} groups + */ + Graph2d.prototype.setGroups = function(groups) { + // convert to type DataSet when needed + var newDataSet; + if (!groups) { + newDataSet = null; + } + else if (groups instanceof DataSet || groups instanceof DataView) { + newDataSet = groups; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(groups); + } - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change'); + this.groupsData = newDataSet; + this.linegraph.setGroups(newDataSet); + }; - event.stopPropagation(); - } -}; + /** + * Clear the Graph2d. By Default, items, groups and options are cleared. + * Example usage: + * + * timeline.clear(); // clear items, groups, and options + * timeline.clear({options: true}); // clear options only + * + * @param {Object} [what] Optionally specify what to clear. By default: + * {items: true, groups: true, options: true} + */ + Graph2d.prototype.clear = function(what) { + // clear items + if (!what || what.items) { + this.setItems(null); + } -/** - * End of dragging selected items - * @param {Event} event - * @private - */ -ItemSet.prototype._onDragEnd = function (event) { - if (this.touchParams.itemProps) { - // prepare a change set for the changed items - var changes = [], - me = this, - dataset = this.itemsData.getDataSet(); + // clear groups + if (!what || what.groups) { + this.setGroups(null); + } - this.touchParams.itemProps.forEach(function (props) { - var id = props.item.id, - itemData = me.itemsData.get(id, me.itemOptions); + // clear options of timeline and of each of the components + if (!what || what.options) { + this.components.forEach(function (component) { + component.setOptions(component.defaultOptions); + }); - var changed = false; - if ('start' in props.item.data) { - changed = (props.start != props.item.data.start.valueOf()); - itemData.start = util.convert(props.item.data.start, - dataset._options.type && dataset._options.type.start || 'Date'); - } - if ('end' in props.item.data) { - changed = changed || (props.end != props.item.data.end.valueOf()); - itemData.end = util.convert(props.item.data.end, - dataset._options.type && dataset._options.type.end || 'Date'); - } - if ('group' in props.item.data) { - changed = changed || (props.group != props.item.data.group); - itemData.group = props.item.data.group; - } + this.setOptions(this.defaultOptions); // this will also do a redraw + } + }; - // only apply changes when start or end is actually changed - if (changed) { - me.options.onMove(itemData, function (itemData) { - if (itemData) { - // apply changes - itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined) - changes.push(itemData); - } - else { - // restore original values - if ('start' in props) props.item.data.start = props.start; - if ('end' in props) props.item.data.end = props.end; + /** + * Set Graph2d window such that it fits all items + */ + Graph2d.prototype.fit = function() { + // apply the data range as range + var dataRange = this.getItemRange(); - me.stackDirty = true; // force re-stacking of all items next redraw - me.body.emitter.emit('change'); - } - }); + // add 5% space on both sides + var start = dataRange.min; + var end = dataRange.max; + if (start != null && end != null) { + var interval = (end.valueOf() - start.valueOf()); + if (interval <= 0) { + // prevent an empty interval + interval = 24 * 60 * 60 * 1000; // 1 day } - }); - this.touchParams.itemProps = null; - - // apply the changes to the data (if there are changes) - if (changes.length) { - dataset.update(changes); + start = new Date(start.valueOf() - interval * 0.05); + end = new Date(end.valueOf() + interval * 0.05); } - event.stopPropagation(); - } -}; - -/** - * 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(); -}; + // skip range set if there is no start and end date + if (start === null && end === null) { + return; + } -/** - * 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; + this.range.setRange(start, end); + }; - var me = this, - snap = this.body.util.snap || null, - item = ItemSet.itemFromTarget(event); + /** + * Get the data range of the item set. + * @returns {{min: Date, max: Date}} range A range with a start and end Date. + * When no minimum is found, min==null + * When no maximum is found, max==null + */ + Graph2d.prototype.getItemRange = function() { + // calculate min from start filed + var itemsData = this.itemsData, + min = null, + max = null; - if (item) { - // update item + if (itemsData) { + // calculate the minimum value of the field 'start' + var minItem = itemsData.min('start'); + min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; + // Note: we convert first to Date and then to number because else + // a conversion from ISODate to Number will fail - // 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); + // calculate maximum value of fields 'start' and 'end' + var maxStartItem = itemsData.max('start'); + if (maxStartItem) { + max = util.convert(maxStartItem.start, 'Date').valueOf(); + } + var maxEndItem = itemsData.max('end'); + if (maxEndItem) { + if (max == null) { + max = util.convert(maxEndItem.end, 'Date').valueOf(); + } + else { + max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); + } } - }); - } - 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') { - var end = this.body.util.toTime(x + this.props.width / 5); - newItem.end = snap ? snap(end) : end; } - newItem[this.itemsData.fieldId] = util.randomUUID(); + return { + min: (min != null) ? new Date(min) : null, + max: (max != null) ? new Date(max) : null + }; + }; - var group = ItemSet.groupFromTarget(event); - if (group) { - newItem.group = group.groupId; + /** + * Set the visible window. Both parameters are optional, you can change only + * start or only end. Syntax: + * + * TimeLine.setWindow(start, end) + * TimeLine.setWindow(range) + * + * Where start and end can be a Date, number, or string, and range is an + * object with properties start and end. + * + * @param {Date | Number | String | Object} [start] Start date of visible window + * @param {Date | Number | String} [end] End date of visible window + */ + Graph2d.prototype.setWindow = function(start, end) { + if (arguments.length == 1) { + var range = arguments[0]; + this.range.setRange(range.start, range.end); } + else { + this.range.setRange(start, end); + } + }; - // 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; + /** + * Get the visible window + * @return {{start: Date, end: Date}} Visible range + */ + Graph2d.prototype.getWindow = function() { + var range = this.range.getRange(); + return { + start: new Date(range.start), + end: new Date(range.end) + }; + }; - var selection, - item = ItemSet.itemFromTarget(event); + /** + * Force a redraw of the Graph2d. Can be useful to manually redraw when + * option autoResize=false + */ + Graph2d.prototype.redraw = function() { + var resized = false, + options = this.options, + props = this.props, + dom = this.dom; - 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); + 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'); - this.body.emitter.emit('select', { - items: this.getSelection() + // 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.backgroundHorizontalContainer.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.backgroundHorizontalContainer.style.width = props.background.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.backgroundHorizontalContainer.style.left = '0'; + dom.backgroundHorizontalContainer.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 Graph2d or of the contents of the center changed + this._updateScrollTop(); + + // reposition the scrollable contents + var offset = this.props.scrollTop; + if (options.orientation == 'bottom') { + offset += Math.max(this.props.centerContainer.height - this.props.center.height - + this.props.border.top - this.props.border.bottom, 0); + } + dom.center.style.left = '0'; + dom.center.style.top = offset + 'px'; + dom.backgroundHorizontal.style.left = '0'; + dom.backgroundHorizontal.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 redrawing until all sizes are settled + this.redraw(); + } + }; - event.stopPropagation(); - } -}; + /** + * Convert a position on screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x + * @private + */ + // TODO: move this function to Range + Graph2d.prototype._toTime = function(x) { + var conversion = this.range.conversion(this.props.center.width); + return new Date(x / conversion.scale + conversion.offset); + }; -/** - * Find an item from an event target: - * searches for the attribute 'timeline-item' in the event target's element tree - * @param {Event} event - * @return {Item | null} item - */ -ItemSet.itemFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-item')) { - return target['timeline-item']; - } - target = target.parentNode; - } + /** + * Convert a datetime (Date object) into a position on the root + * This is used to get the pixel density estimate for the screen, not the center panel + * @param {Date} time A date + * @return {int} x The position on root in pixels which corresponds + * with the given date. + * @private + */ + // TODO: move this function to Range + Graph2d.prototype._toGlobalTime = function(x) { + var conversion = this.range.conversion(this.props.root.width); + return new Date(x / conversion.scale + conversion.offset); + }; - return null; -}; + /** + * 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 + Graph2d.prototype._toScreen = function(time) { + var conversion = this.range.conversion(this.props.center.width); + return (time.valueOf() - conversion.offset) * conversion.scale; + }; -/** - * Find the Group from an event target: - * searches for the attribute 'timeline-group' in the event target's element tree - * @param {Event} event - * @return {Group | null} group - */ -ItemSet.groupFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-group')) { - return target['timeline-group']; - } - target = target.parentNode; - } - return null; -}; + /** + * Convert a datetime (Date object) into a position on the root + * This is used to get the pixel density estimate for the screen, not the center panel + * @param {Date} time A date + * @return {int} x The position on root in pixels which corresponds + * with the given date. + * @private + */ + // TODO: move this function to Range + Graph2d.prototype._toGlobalScreen = function(time) { + var conversion = this.range.conversion(this.props.root.width); + return (time.valueOf() - conversion.offset) * conversion.scale; + }; -/** - * Find the ItemSet from an event target: - * searches for the attribute 'timeline-itemset' in the event target's element tree - * @param {Event} event - * @return {ItemSet | null} item - */ -ItemSet.itemSetFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-itemset')) { - return target['timeline-itemset']; + /** + * Initialize watching when option autoResize is true + * @private + */ + Graph2d.prototype._initAutoResize = function () { + if (this.options.autoResize == true) { + this._startAutoResize(); } - target = target.parentNode; - } + else { + this._stopAutoResize(); + } + }; - return null; -}; + /** + * Watch for changes in the size of the container. On resize, the Panel will + * automatically redraw itself. + * @private + */ + Graph2d.prototype._startAutoResize = function () { + var me = this; + this._stopAutoResize(); -/** - * @constructor Item - * @param {Object} data Object containing (optional) parameters type, - * start, end, content, group, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} options Configuration options - * // TODO: describe available options - */ -function Item (data, conversion, options) { - this.id = null; - this.parent = null; - this.data = data; - this.dom = null; - this.conversion = conversion || {}; - this.options = options || {}; - - this.selected = false; - this.displayed = false; - this.dirty = true; - - this.top = null; - this.left = null; - this.width = null; - this.height = null; -} + this._onResize = function() { + if (me.options.autoResize != true) { + // stop watching when the option autoResize is changed to false + me._stopAutoResize(); + return; + } -/** - * Select current item - */ -Item.prototype.select = function() { - this.selected = true; - if (this.displayed) this.redraw(); -}; + 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; -/** - * Unselect current item - */ -Item.prototype.unselect = function() { - this.selected = false; - if (this.displayed) this.redraw(); -}; + me.emit('change'); + } + } + }; -/** - * Set a parent for the item - * @param {ItemSet | Group} parent - */ -Item.prototype.setParent = function(parent) { - if (this.displayed) { - this.hide(); - this.parent = parent; - if (this.parent) { - this.show(); - } - } - else { - this.parent = parent; - } -}; + // add event listener to window resize + util.addEventListener(window, 'resize', this._onResize); -/** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible - */ -Item.prototype.isVisible = function(range) { - // Should be implemented by Item implementations - return false; -}; + this.watchTimer = setInterval(this._onResize, 1000); + }; -/** - * Show the Item in the DOM (when not already visible) - * @return {Boolean} changed - */ -Item.prototype.show = function() { - return false; -}; + /** + * Stop watching for a resize of the frame. + * @private + */ + Graph2d.prototype._stopAutoResize = function () { + if (this.watchTimer) { + clearInterval(this.watchTimer); + this.watchTimer = undefined; + } -/** - * Hide the Item from the DOM (when visible) - * @return {Boolean} changed - */ -Item.prototype.hide = function() { - return false; -}; + // remove event listener on window.resize + util.removeEventListener(window, 'resize', this._onResize); + this._onResize = null; + }; -/** - * Repaint the item - */ -Item.prototype.redraw = function() { - // should be implemented by the item -}; + /** + * Start moving the timeline vertically + * @param {Event} event + * @private + */ + Graph2d.prototype._onTouch = function (event) { + this.touch.allowDragging = true; + }; -/** - * Reposition the Item horizontally - */ -Item.prototype.repositionX = function() { - // should be implemented by the item -}; + /** + * Start moving the timeline vertically + * @param {Event} event + * @private + */ + Graph2d.prototype._onPinch = function (event) { + this.touch.allowDragging = false; + }; -/** - * Reposition the Item vertically - */ -Item.prototype.repositionY = function() { - // should be implemented by the item -}; + /** + * Start moving the timeline vertically + * @param {Event} event + * @private + */ + Graph2d.prototype._onDragStart = function (event) { + this.touch.initialScrollTop = this.props.scrollTop; + }; -/** - * Repaint a delete button on the top right of the item when the item is selected - * @param {HTMLElement} anchor - * @protected - */ -Item.prototype._repaintDeleteButton = function (anchor) { - if (this.selected && this.options.editable.remove && !this.dom.deleteButton) { - // create and show button - var me = this; + /** + * Move the timeline vertically + * @param {Event} event + * @private + */ + Graph2d.prototype._onDrag = function (event) { + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.touch.allowDragging) return; - var deleteButton = document.createElement('div'); - deleteButton.className = 'delete'; - deleteButton.title = 'Delete this item'; + var delta = event.gesture.deltaY; - Hammer(deleteButton, { - preventDefault: true - }).on('tap', function (event) { - me.parent.removeFromDataSet(me); - event.stopPropagation(); - }); + var oldScrollTop = this._getScrollTop(); + var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); - anchor.appendChild(deleteButton); - this.dom.deleteButton = deleteButton; - } - else if (!this.selected && this.dom.deleteButton) { - // remove button - if (this.dom.deleteButton.parentNode) { - this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); + if (newScrollTop != oldScrollTop) { + this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already } - this.dom.deleteButton = null; - } -}; + }; -/** - * @constructor ItemBox - * @extends Item - * @param {Object} data Object containing parameters start - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe available options - */ -function ItemBox (data, conversion, options) { - this.props = { - dot: { - width: 0, - height: 0 - }, - line: { - width: 0, - height: 0 - } + /** + * Apply a scrollTop + * @param {Number} scrollTop + * @returns {Number} scrollTop Returns the applied scrollTop + * @private + */ + Graph2d.prototype._setScrollTop = function (scrollTop) { + this.props.scrollTop = scrollTop; + this._updateScrollTop(); + return this.props.scrollTop; }; - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data); + /** + * Update the current scrollTop when the height of the containers has been changed + * @returns {Number} scrollTop Returns the applied scrollTop + * @private + */ + Graph2d.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; } - } - Item.call(this, data, conversion, options); -} + // 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; -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(range) { - // determine visibility - // TODO: account for the real width of the item. Right now we just add 1/4 to the window - var interval = (range.end - range.start) / 4; - return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); -}; + return this.props.scrollTop; + }; -/** - * Repaint the item - */ -ItemBox.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + /** + * Get the current scrollTop + * @returns {number} scrollTop + * @private + */ + Graph2d.prototype._getScrollTop = function () { + return this.props.scrollTop; + }; - // create main box - dom.box = document.createElement('DIV'); + module.exports = Graph2d; - // contents box (inside the background box). used for making margins - dom.content = document.createElement('DIV'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); - // line to axis - dom.line = document.createElement('DIV'); - dom.line.className = 'line'; +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { - // dot on axis - dom.dot = document.createElement('DIV'); - dom.dot.className = 'dot'; + /** + * @constructor DataStep + * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an + * end data point. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. + * + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + */ + function DataStep(start, end, minimumStep, containerHeight, forcedStepSize) { + // variables + this.current = 0; - // attach this item as attribute - dom.box['timeline-item'] = this; - } + this.autoScale = true; + this.stepIndex = 0; + this.step = 1; + this.scale = 1; - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) throw new Error('Cannot redraw time axis: parent has no foreground container element'); - foreground.appendChild(dom.box); - } - if (!dom.line.parentNode) { - var background = this.parent.dom.background; - if (!background) throw new Error('Cannot redraw time axis: parent has no background container element'); - background.appendChild(dom.line); - } - if (!dom.dot.parentNode) { - var axis = this.parent.dom.axis; - if (!background) throw new Error('Cannot redraw time axis: parent has no axis container element'); - axis.appendChild(dom.dot); - } - this.displayed = true; + this.marginStart; + this.marginEnd; - // update contents - if (this.data.content != this.content) { - this.content = this.data.content; - if (this.content instanceof Element) { - dom.content.innerHTML = ''; - dom.content.appendChild(this.content); - } - else if (this.data.content != undefined) { - dom.content.innerHTML = this.content; - } - else { - throw new Error('Property "content" missing in item ' + this.data.id); - } + this.majorSteps = [1, 2, 5, 10]; + this.minorSteps = [0.25, 0.5, 1, 2]; - this.dirty = true; + this.setRange(start, end, minimumStep, containerHeight, forcedStepSize); } - // update title - if (this.data.title != this.title) { - dom.box.title = this.data.title; - this.title = this.data.title; - } - // update class - var className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' selected' : ''); - if (this.className != className) { - this.className = className; - dom.box.className = 'item box' + className; - dom.line.className = 'item line' + className; - dom.dot.className = 'item dot' + className; - this.dirty = true; - } + /** + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Number} [start] The start date and time. + * @param {Number} [end] The end date and time. + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + */ + DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, forcedStepSize) { + this._start = start; + this._end = end; - // recalculate size - if (this.dirty) { - this.props.dot.height = dom.dot.offsetHeight; - this.props.dot.width = dom.dot.offsetWidth; - this.props.line.width = dom.line.offsetWidth; - this.width = dom.box.offsetWidth; - this.height = dom.box.offsetHeight; + if (this.autoScale) { + this.setMinimumStep(minimumStep, containerHeight, forcedStepSize); + } + this.setFirst(); + }; - this.dirty = false; - } + /** + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds + */ + DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { + // round to floor + var size = this._end - this._start; + var safeSize = size * 1.1; + var minimumStepValue = minimumStep * (safeSize / containerHeight); + var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); + + var minorStepIdx = -1; + var magnitudefactor = Math.pow(10,orderOfMagnitude); + + var start = 0; + if (orderOfMagnitude < 0) { + start = orderOfMagnitude; + } + + var solutionFound = false; + for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { + magnitudefactor = Math.pow(10,i); + for (var j = 0; j < this.minorSteps.length; j++) { + var stepSize = magnitudefactor * this.minorSteps[j]; + if (stepSize >= minimumStepValue) { + solutionFound = true; + minorStepIdx = j; + break; + } + } + if (solutionFound == true) { + break; + } + } + this.stepIndex = minorStepIdx; + this.scale = magnitudefactor; + this.step = magnitudefactor * this.minorSteps[minorStepIdx]; + }; - this._repaintDeleteButton(dom.box); -}; -/** - * Show the item in the DOM (when not already displayed). The items DOM will - * be created when needed. - */ -ItemBox.prototype.show = function() { - if (!this.displayed) { - this.redraw(); - } -}; + /** + * Set the range iterator to the start date. + */ + DataStep.prototype.first = function() { + this.setFirst(); + }; -/** - * Hide the item from the DOM (when visible) - */ -ItemBox.prototype.hide = function() { - if (this.displayed) { - var dom = this.dom; + /** + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date + */ + DataStep.prototype.setFirst = function() { + var niceStart = this._start - (this.scale * this.minorSteps[this.stepIndex]); + var niceEnd = this._end + (this.scale * this.minorSteps[this.stepIndex]); - if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); - if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); - if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); + this.marginEnd = this.roundToMinor(niceEnd); + this.marginStart = this.roundToMinor(niceStart); + this.marginRange = this.marginEnd - this.marginStart; - this.top = null; - this.left = null; + this.current = this.marginEnd; - this.displayed = false; - } -}; + }; -/** - * Reposition the item horizontally - * @Override - */ -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, - dot = this.dom.dot; - - // calculate left position of the box - if (align == 'right') { - this.left = start - this.width; - } - else if (align == 'left') { - this.left = start; - } - else { - // default or 'center' - this.left = start - this.width / 2; + DataStep.prototype.roundToMinor = function(value) { + var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); + if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { + return rounded + (this.scale * this.minorSteps[this.stepIndex]); + } + else { + return rounded; + } } - // reposition box - box.style.left = this.left + 'px'; - // reposition line - line.style.left = (start - this.props.line.width / 2) + 'px'; + /** + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date + */ + DataStep.prototype.hasNext = function () { + return (this.current >= this.marginStart); + }; - // reposition dot - dot.style.left = (start - this.props.dot.width / 2) + 'px'; -}; + /** + * Do the next step + */ + DataStep.prototype.next = function() { + var prev = this.current; + this.current -= this.step; -/** - * Reposition the item vertically - * @Override - */ -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'; - - line.style.top = '0'; - line.style.height = (this.parent.top + this.top + 1) + 'px'; - line.style.bottom = ''; - } - else { // orientation 'bottom' - var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty - var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; + // safety mechanism: if current time is still unchanged, move to the end + if (this.current == prev) { + this.current = this._end; + } + }; - box.style.top = (this.parent.height - this.top - this.height || 0) + 'px'; - line.style.top = (itemSetHeight - lineHeight) + 'px'; - line.style.bottom = '0'; - } + /** + * Do the next step + */ + DataStep.prototype.previous = function() { + this.current += this.step; + this.marginEnd += this.step; + this.marginRange = this.marginEnd - this.marginStart; + }; - dot.style.top = (-this.props.dot.height / 2) + 'px'; -}; -/** - * @constructor ItemPoint - * @extends Item - * @param {Object} data Object containing parameters start - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe available options - */ -function ItemPoint (data, conversion, options) { - this.props = { - dot: { - top: 0, - width: 0, - height: 0 - }, - content: { - height: 0, - marginLeft: 0 + + /** + * Get the current datetime + * @return {String} current The current date + */ + DataStep.prototype.getCurrent = function() { + var toPrecision = '' + Number(this.current).toPrecision(5); + for (var i = toPrecision.length-1; i > 0; i--) { + if (toPrecision[i] == "0") { + toPrecision = toPrecision.slice(0,i); + } + else if (toPrecision[i] == "." || toPrecision[i] == ",") { + toPrecision = toPrecision.slice(0,i); + break; + } + else{ + break; + } } + + return toPrecision; }; - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data); - } - } - Item.call(this, data, conversion, options); -} -ItemPoint.prototype = new Item (null, null, null); + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + DataStep.prototype.snap = function(date) { -/** - * 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(range) { - // determine visibility - // TODO: account for the real width of the item. Right now we just add 1/4 to the window - var interval = (range.end - range.start) / 4; - return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); -}; + }; -/** - * Repaint the item - */ -ItemPoint.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + /** + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. + */ + DataStep.prototype.isMajor = function() { + return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); + }; - // background box - dom.point = document.createElement('div'); - // className is updated in redraw() + module.exports = DataStep; - // contents box, right from the dot - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.point.appendChild(dom.content); - // dot at start - dom.dot = document.createElement('div'); - dom.point.appendChild(dom.dot); +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { - // attach this item as attribute - dom.point['timeline-item'] = this; - } + var util = __webpack_require__(1); + var moment = __webpack_require__(39); + var Component = __webpack_require__(12); - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.point.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) { - throw new Error('Cannot redraw time axis: parent has no foreground container element'); - } - foreground.appendChild(dom.point); - } - this.displayed = true; - - // update contents - if (this.data.content != this.content) { - this.content = this.data.content; - if (this.content instanceof Element) { - dom.content.innerHTML = ''; - dom.content.appendChild(this.content); - } - else if (this.data.content != undefined) { - dom.content.innerHTML = this.content; - } - else { - throw new Error('Property "content" missing in item ' + this.data.id); - } - - this.dirty = true; - } - - // update title - if (this.data.title != this.title) { - dom.point.title = this.data.title; - this.title = this.data.title; - } + /** + * @constructor Range + * A Range controls a numeric range with a start and end value. + * The Range adjusts the range based on mouse events or programmatic changes, + * and triggers events when the range is changing or has been changed. + * @param {{dom: Object, domProps: Object, emitter: Emitter}} body + * @param {Object} [options] See description at Range.setOptions + */ + function Range(body, options) { + var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); + this.start = now.clone().add('days', -3).valueOf(); // Number + this.end = now.clone().add('days', 4).valueOf(); // Number + + 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); - // update class - var className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' selected' : ''); - if (this.className != className) { - this.className = className; - dom.point.className = 'item point' + className; - dom.dot.className = 'item dot' + className; + this.props = { + touch: {} + }; - this.dirty = true; - } + // drag listeners for dragging + this.body.emitter.on('dragstart', this._onDragStart.bind(this)); + this.body.emitter.on('drag', this._onDrag.bind(this)); + this.body.emitter.on('dragend', this._onDragEnd.bind(this)); - // recalculate size - if (this.dirty) { - this.width = dom.point.offsetWidth; - this.height = dom.point.offsetHeight; - this.props.dot.width = dom.dot.offsetWidth; - this.props.dot.height = dom.dot.offsetHeight; - this.props.content.height = dom.content.offsetHeight; + // ignore dragging when holding + this.body.emitter.on('hold', this._onHold.bind(this)); - // resize contents - dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; - //dom.content.style.marginRight = ... + 'px'; // TODO: margin right + // mouse wheel for zooming + this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); + this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF - dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; - dom.dot.style.left = (this.props.dot.width / 2) + 'px'; + // pinch to zoom + this.body.emitter.on('touch', this._onTouch.bind(this)); + this.body.emitter.on('pinch', this._onPinch.bind(this)); - this.dirty = false; + this.setOptions(options); } - this._repaintDeleteButton(dom.point); -}; + Range.prototype = new Component(); -/** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. - */ -ItemPoint.prototype.show = function() { - if (!this.displayed) { - this.redraw(); - } -}; + /** + * Set options for the range controller + * @param {Object} options Available options: + * {Number | Date | String} start Start date for the range + * {Number | Date | String} end End date for the range + * {Number} min Minimum value for start + * {Number} max Maximum value for end + * {Number} zoomMin Set a minimum value for + * (end - start). + * {Number} zoomMax Set a maximum value for + * (end - start). + * {Boolean} moveable Enable moving of the range + * by dragging. True by default + * {Boolean} zoomable Enable zooming of the range + * by pinching/scrolling. True by default + */ + Range.prototype.setOptions = function (options) { + if (options) { + // copy the options that we know + var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable']; + util.selectiveExtend(fields, this.options, options); -/** - * Hide the item from the DOM (when visible) - */ -ItemPoint.prototype.hide = function() { - if (this.displayed) { - if (this.dom.point.parentNode) { - this.dom.point.parentNode.removeChild(this.dom.point); + if ('start' in options || 'end' in options) { + // apply a new range. both start and end are optional + this.setRange(options.start, options.end); + } } + }; - this.top = null; - this.left = null; - - this.displayed = false; + /** + * Test whether direction has a valid value + * @param {String} direction 'horizontal' or 'vertical' + */ + function validateDirection (direction) { + if (direction != 'horizontal' && direction != 'vertical') { + throw new TypeError('Unknown direction "' + direction + '". ' + + 'Choose "horizontal" or "vertical".'); + } } -}; - -/** - * Reposition the item horizontally - * @Override - */ -ItemPoint.prototype.repositionX = function() { - var start = this.conversion.toScreen(this.data.start); - - this.left = start - this.props.dot.width; - - // reposition point - this.dom.point.style.left = this.left + 'px'; -}; -/** - * Reposition the item vertically - * @Override - */ -ItemPoint.prototype.repositionY = function() { - var orientation = this.options.orientation, - point = this.dom.point; + /** + * Set a new start and end range + * @param {Number} [start] + * @param {Number} [end] + */ + Range.prototype.setRange = function(start, end) { + var changed = this._applyRange(start, end); + if (changed) { + var params = { + start: new Date(this.start), + end: new Date(this.end) + }; + this.body.emitter.emit('rangechange', params); + this.body.emitter.emit('rangechanged', params); + } + }; - if (orientation == 'top') { - point.style.top = this.top + 'px'; - } - else { - point.style.top = (this.parent.height - this.top - this.height) + 'px'; - } -}; + /** + * Set a new start and end range. This method is the same as setRange, but + * does not trigger a range change and range changed event, and it returns + * true when the range is changed + * @param {Number} [start] + * @param {Number} [end] + * @return {Boolean} changed + * @private + */ + Range.prototype._applyRange = function(start, end) { + var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start, + newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end, + max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null, + min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null, + diff; -/** - * @constructor ItemRange - * @extends Item - * @param {Object} data Object containing parameters start, end - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe options - */ -function ItemRange (data, conversion, options) { - this.props = { - content: { - width: 0 + // check for valid number + if (isNaN(newStart) || newStart === null) { + throw new Error('Invalid start "' + start + '"'); + } + if (isNaN(newEnd) || newEnd === null) { + throw new Error('Invalid end "' + end + '"'); } - }; - this.overflow = false; // if contents can overflow (css styling), this flag is set to true - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data.id); + // prevent start < end + if (newEnd < newStart) { + newEnd = newStart; } - if (data.end == undefined) { - throw new Error('Property "end" missing in item ' + data.id); + + // prevent start < min + if (min !== null) { + if (newStart < min) { + diff = (min - newStart); + newStart += diff; + newEnd += diff; + + // prevent end > max + if (max != null) { + if (newEnd > max) { + newEnd = max; + } + } + } } - } - Item.call(this, data, conversion, options); -} + // prevent end > max + if (max !== null) { + if (newEnd > max) { + diff = (newEnd - max); + newStart -= diff; + newEnd -= diff; -ItemRange.prototype = new Item (null, null, null); + // prevent start < min + if (min != null) { + if (newStart < min) { + newStart = min; + } + } + } + } -ItemRange.prototype.baseClassName = 'item range'; + // prevent (end-start) < zoomMin + if (this.options.zoomMin !== null) { + var zoomMin = parseFloat(this.options.zoomMin); + if (zoomMin < 0) { + zoomMin = 0; + } + if ((newEnd - newStart) < zoomMin) { + if ((this.end - this.start) === zoomMin) { + // ignore this action, we are already zoomed to the minimum + newStart = this.start; + newEnd = this.end; + } + else { + // zoom to the minimum + diff = (zoomMin - (newEnd - newStart)); + newStart -= diff / 2; + newEnd += diff / 2; + } + } + } -/** - * 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 - */ -ItemRange.prototype.isVisible = function(range) { - // determine visibility - return (this.data.start < range.end) && (this.data.end > range.start); -}; + // prevent (end-start) > zoomMax + if (this.options.zoomMax !== null) { + var zoomMax = parseFloat(this.options.zoomMax); + if (zoomMax < 0) { + zoomMax = 0; + } + if ((newEnd - newStart) > zoomMax) { + if ((this.end - this.start) === zoomMax) { + // ignore this action, we are already zoomed to the maximum + newStart = this.start; + newEnd = this.end; + } + else { + // zoom to the maximum + diff = ((newEnd - newStart) - zoomMax); + newStart += diff / 2; + newEnd -= diff / 2; + } + } + } -/** - * Repaint the item - */ -ItemRange.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + var changed = (this.start != newStart || this.end != newEnd); - // background box - dom.box = document.createElement('div'); - // className is updated in redraw() + this.start = newStart; + this.end = newEnd; - // contents box - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); + return changed; + }; - // attach this item as attribute - dom.box['timeline-item'] = this; - } + /** + * Retrieve the current range. + * @return {Object} An object with start and end properties + */ + Range.prototype.getRange = function() { + return { + start: this.start, + end: this.end + }; + }; - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) { - throw new Error('Cannot redraw time axis: parent has no foreground container element'); - } - foreground.appendChild(dom.box); - } - this.displayed = true; + /** + * Calculate the conversion offset and scale for current range, based on + * the provided width + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion + */ + Range.prototype.conversion = function (width) { + return Range.conversion(this.start, this.end, width); + }; - // update contents - if (this.data.content != this.content) { - this.content = this.data.content; - if (this.content instanceof Element) { - dom.content.innerHTML = ''; - dom.content.appendChild(this.content); - } - else if (this.data.content != undefined) { - dom.content.innerHTML = this.content; + /** + * Static method to calculate the conversion offset and scale for a range, + * based on the provided start, end, and width + * @param {Number} start + * @param {Number} end + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion + */ + Range.conversion = function (start, end, width) { + if (width != 0 && (end - start != 0)) { + return { + offset: start, + scale: width / (end - start) + } } else { - throw new Error('Property "content" missing in item ' + this.data.id); + return { + offset: 0, + scale: 1 + }; } + }; - this.dirty = true; - } - - // update title - if (this.data.title != this.title) { - dom.box.title = this.data.title; - this.title = this.data.title; - } - - // update class - var className = (this.data.className ? (' ' + this.data.className) : '') + - (this.selected ? ' selected' : ''); - if (this.className != className) { - this.className = className; - dom.box.className = this.baseClassName + className; - - this.dirty = true; - } + /** + * 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; - // recalculate size - if (this.dirty) { - // determine from css whether this box has overflow - this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - this.props.content.width = this.dom.content.offsetWidth; - this.height = this.dom.box.offsetHeight; + this.props.touch.start = this.start; + this.props.touch.end = this.end; - this.dirty = false; - } + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'move'; + } + }; - this._repaintDeleteButton(dom.box); - this._repaintDragLeft(); - this._repaintDragRight(); -}; + /** + * 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); + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; + var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY, + 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(this.props.touch.start + diffRange, this.props.touch.end + diffRange); + this.body.emitter.emit('rangechange', { + start: new Date(this.start), + end: new Date(this.end) + }); + }; -/** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. - */ -ItemRange.prototype.show = function() { - if (!this.displayed) { - this.redraw(); - } -}; + /** + * Stop dragging operation + * @param {event} event + * @private + */ + Range.prototype._onDragEnd = function (event) { + // only allow dragging when configured as movable + if (!this.options.moveable) return; -/** - * Hide the item from the DOM (when visible) - * @return {Boolean} changed - */ -ItemRange.prototype.hide = function() { - if (this.displayed) { - var box = this.dom.box; + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - if (box.parentNode) { - box.parentNode.removeChild(box); + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'auto'; } - this.top = null; - this.left = null; - - this.displayed = false; - } -}; + // fire a rangechanged event + this.body.emitter.emit('rangechanged', { + start: new Date(this.start), + end: new Date(this.end) + }); + }; -/** - * Reposition the item horizontally - * @Override - */ -// TODO: delete the old function -ItemRange.prototype.repositionX = function() { - var props = this.props, - parentWidth = this.parent.width, - 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 - if (start < -parentWidth) { - start = -parentWidth; - } - if (end > 2 * parentWidth) { - end = 2 * parentWidth; - } - var boxWidth = Math.max(end - start, 1); + /** + * Event handler for mouse wheel event, used to zoom + * Code from http://adomas.org/javascript-mouse-wheel/ + * @param {Event} event + * @private + */ + Range.prototype._onMouseWheel = function(event) { + // only allow zooming when configured as zoomable and moveable + if (!(this.options.zoomable && this.options.moveable)) return; + + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta / 120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail / 3; + } + + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + // perform the zoom action. Delta is normally 1 or -1 + + // adjust a negative delta such that zooming in with delta 0.1 + // equals zooming out with a delta -0.1 + var scale; + if (delta < 0) { + scale = 1 - (delta / 5); + } + else { + scale = 1 / (1 + (delta / 5)) ; + } - if (this.overflow) { - // when range exceeds left of the window, position the contents at the left of the visible area - contentLeft = Math.max(-start, 0); + // calculate center, the date to zoom around + var gesture = util.fakeGesture(this, event), + pointer = getPointer(gesture.center, this.body.dom.center), + pointerDate = this._pointerToDate(pointer); - this.left = start; - this.width = boxWidth + this.props.content.width; - // Note: The calculation of width is an optimistic calculation, giving - // a width which will not change when moving the Timeline - // So no restacking needed, which is nicer for the eye; - } - else { // no overflow - // when range exceeds left of the window, position the contents at the left of the visible area - if (start < 0) { - contentLeft = Math.min(-start, - (end - start - props.content.width - 2 * padding)); - // TODO: remove the need for options.padding. it's terrible. - } - else { - contentLeft = 0; + this.zoom(scale, pointerDate); } - this.left = start; - this.width = boxWidth; - } + // Prevent default actions caused by mouse wheel + // (else the page and timeline both zoom and scroll) + event.preventDefault(); + }; + + /** + * Start of a touch gesture + * @private + */ + Range.prototype._onTouch = function (event) { + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.allowDragging = true; + this.props.touch.center = null; + }; - this.dom.box.style.left = this.left + 'px'; - this.dom.box.style.width = boxWidth + 'px'; - this.dom.content.style.left = contentLeft + 'px'; -}; + /** + * On start of a hold gesture + * @private + */ + Range.prototype._onHold = function () { + this.props.touch.allowDragging = false; + }; -/** - * Reposition the item vertically - * @Override - */ -ItemRange.prototype.repositionY = function() { - var orientation = this.options.orientation, - box = this.dom.box; + /** + * Handle pinch event + * @param {Event} event + * @private + */ + Range.prototype._onPinch = function (event) { + // only allow zooming when configured as zoomable and moveable + if (!(this.options.zoomable && this.options.moveable)) return; - if (orientation == 'top') { - box.style.top = this.top + 'px'; - } - else { - box.style.top = (this.parent.height - this.top - this.height) + 'px'; - } -}; + this.props.touch.allowDragging = false; -/** - * Repaint a drag area on the left side of the range when the range is selected - * @protected - */ -ItemRange.prototype._repaintDragLeft = function () { - if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) { - // create and show drag area - var dragLeft = document.createElement('div'); - dragLeft.className = 'drag-left'; - dragLeft.dragLeftItem = this; - - // TODO: this should be redundant? - Hammer(dragLeft, { - preventDefault: true - }).on('drag', function () { - //console.log('drag left') - }); + if (event.gesture.touches.length > 1) { + if (!this.props.touch.center) { + this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center); + } - this.dom.box.appendChild(dragLeft); - this.dom.dragLeft = dragLeft; - } - else if (!this.selected && this.dom.dragLeft) { - // delete drag area - if (this.dom.dragLeft.parentNode) { - this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); - } - this.dom.dragLeft = null; - } -}; + var scale = 1 / event.gesture.scale, + initDate = this._pointerToDate(this.props.touch.center); -/** - * Repaint a drag area on the right side of the range when the range is selected - * @protected - */ -ItemRange.prototype._repaintDragRight = function () { - if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { - // create and show drag area - var dragRight = document.createElement('div'); - dragRight.className = 'drag-right'; - dragRight.dragRightItem = this; - - // TODO: this should be redundant? - Hammer(dragRight, { - preventDefault: true - }).on('drag', function () { - //console.log('drag right') - }); + // calculate new start and end + var newStart = parseInt(initDate + (this.props.touch.start - initDate) * scale); + var newEnd = parseInt(initDate + (this.props.touch.end - initDate) * scale); - this.dom.box.appendChild(dragRight); - this.dom.dragRight = dragRight; - } - else if (!this.selected && this.dom.dragRight) { - // delete drag area - if (this.dom.dragRight.parentNode) { - this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); + // apply new range + this.setRange(newStart, newEnd); } - this.dom.dragRight = null; - } -}; + }; -/** - * @constructor Group - * @param {Number | String} groupId - * @param {Object} data - * @param {ItemSet} itemSet - */ -function Group (groupId, data, itemSet) { - this.groupId = groupId; + /** + * Helper function to calculate the center date for zooming + * @param {{x: Number, y: Number}} pointer + * @return {number} date + * @private + */ + Range.prototype._pointerToDate = function (pointer) { + var conversion; + var direction = this.options.direction; - this.itemSet = itemSet; + validateDirection(direction); - this.dom = {}; - this.props = { - label: { - width: 0, - height: 0 + if (direction == 'horizontal') { + var width = this.body.domProps.center.width; + conversion = this.conversion(width); + return pointer.x / conversion.scale + conversion.offset; + } + else { + var height = this.body.domProps.center.height; + conversion = this.conversion(height); + return pointer.y / conversion.scale + conversion.offset; } }; - this.className = null; - this.items = {}; // items filtered by groupId of this group - this.visibleItems = []; // items currently visible in window - this.orderedItems = { // items sorted by start and by end - byStart: [], - byEnd: [] - }; + /** + * Get the pointer location relative to the location of the dom element + * @param {{pageX: Number, pageY: Number}} touch + * @param {Element} element HTML DOM element + * @return {{x: Number, y: Number}} pointer + * @private + */ + function getPointer (touch, element) { + return { + x: touch.pageX - vis.util.getAbsoluteLeft(element), + y: touch.pageY - vis.util.getAbsoluteTop(element) + }; + } - this._create(); + /** + * Zoom the range the given scale in or out. Start and end date will + * be adjusted, and the timeline will be redrawn. You can optionally give a + * date around which to zoom. + * For example, try scale = 0.9 or 1.1 + * @param {Number} scale Scaling factor. Values above 1 will zoom out, + * values below 1 will zoom in. + * @param {Number} [center] Value representing a date around which will + * be zoomed. + */ + Range.prototype.zoom = function(scale, center) { + // if centerDate is not provided, take it half between start Date and end Date + if (center == null) { + center = (this.start + this.end) / 2; + } - this.setData(data); -} + // calculate new start and end + var newStart = center + (this.start - center) * scale; + var newEnd = center + (this.end - center) * scale; -/** - * Create DOM elements for the group - * @private - */ -Group.prototype._create = function() { - var label = document.createElement('div'); - label.className = 'vlabel'; - this.dom.label = label; - - var inner = document.createElement('div'); - inner.className = 'inner'; - label.appendChild(inner); - this.dom.inner = inner; - - var foreground = document.createElement('div'); - foreground.className = 'group'; - foreground['timeline-group'] = this; - this.dom.foreground = foreground; - - this.dom.background = document.createElement('div'); - this.dom.background.className = 'group'; - - this.dom.axis = document.createElement('div'); - this.dom.axis.className = 'group'; - - // create a hidden marker to detect when the Timelines container is attached - // to the DOM, or the style of a parent of the Timeline is changed from - // display:none is changed to visible. - this.dom.marker = document.createElement('div'); - this.dom.marker.style.visibility = 'hidden'; - this.dom.marker.innerHTML = '?'; - this.dom.background.appendChild(this.dom.marker); -}; + this.setRange(newStart, newEnd); + }; -/** - * Set the group data for this group - * @param {Object} data Group data, can contain properties content and className - */ -Group.prototype.setData = function(data) { - // update contents - var content = data && data.content; - if (content instanceof Element) { - this.dom.inner.appendChild(content); - } - else if (content != undefined) { - this.dom.inner.innerHTML = content; - } - else { - this.dom.inner.innerHTML = this.groupId; - } + /** + * Move the range with a given delta to the left or right. Start and end + * value will be adjusted. For example, try delta = 0.1 or -0.1 + * @param {Number} delta Moving amount. Positive value will move right, + * negative value will move left + */ + Range.prototype.move = function(delta) { + // zoom start Date and end Date relative to the centerDate + var diff = (this.end - this.start); - // update title - this.dom.label.title = data && data.title || ''; + // apply new values + var newStart = this.start + diff * delta; + var newEnd = this.end + diff * delta; - if (!this.dom.inner.firstChild) { - util.addClassName(this.dom.inner, 'hidden'); - } - else { - util.removeClassName(this.dom.inner, 'hidden'); - } + // TODO: reckon with min and max range - // update 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); - } -}; + this.start = newStart; + this.end = newEnd; + }; -/** - * Get the width of the group label - * @return {number} width - */ -Group.prototype.getLabelWidth = function() { - return this.props.label.width; -}; + /** + * Move the range to a new center point + * @param {Number} moveTo New center point of the range + */ + Range.prototype.moveTo = function(moveTo) { + var center = (this.start + this.end) / 2; + var diff = center - moveTo; -/** - * Repaint this group - * @param {{start: number, end: number}} range - * @param {{item: number, axis: number}} margin - * @param {boolean} [restack=false] Force restacking of all items - * @return {boolean} Returns true if the group is resized - */ -Group.prototype.redraw = function(range, margin, restack) { - var resized = false; + // calculate new start and end + var newStart = this.start - diff; + var newEnd = this.end - diff; - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + this.setRange(newStart, newEnd); + }; - // force recalculation of the height of the items when the marker height changed - // (due to the Timeline being attached to the DOM or changed from display:none to visible) - var markerHeight = this.dom.marker.clientHeight; - if (markerHeight != this.lastMarkerHeight) { - this.lastMarkerHeight = markerHeight; + module.exports = Range; - util.forEach(this.items, function (item) { - item.dirty = true; - if (item.displayed) item.redraw(); - }); - restack = true; - } +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { - // reposition visible items vertically - if (this.itemSet.options.stack) { // TODO: ugly way to access options... - stack.stack(this.visibleItems, margin, restack); - } - else { // no stacking - stack.nostack(this.visibleItems, margin); - } + // Utility functions for ordering and stacking of items - // recalculate the height of the group - var height; - var visibleItems = this.visibleItems; - if (visibleItems.length) { - var min = visibleItems[0].top; - var max = visibleItems[0].top + visibleItems[0].height; - util.forEach(visibleItems, function (item) { - min = Math.min(min, item.top); - max = Math.max(max, (item.top + item.height)); + /** + * Order items by their start data + * @param {Item[]} items + */ + exports.orderByStart = function(items) { + items.sort(function (a, b) { + return a.data.start - b.data.start; }); - if (min > margin.axis) { - // there is an empty gap between the lowest item and the axis - var offset = min - margin.axis; - max -= offset; - util.forEach(visibleItems, function (item) { - item.top -= offset; - }); - } - height = max + margin.item / 2; - } - else { - height = margin.axis + margin.item; - } - height = Math.max(height, this.props.label.height); - - // calculate actual size and position - var foreground = this.dom.foreground; - this.top = foreground.offsetTop; - this.left = foreground.offsetLeft; - this.width = foreground.offsetWidth; - resized = util.updateProperty(this, 'height', height) || resized; - - // recalculate size of label - resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; - resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; - - // apply new height - this.dom.background.style.height = height + 'px'; - this.dom.foreground.style.height = height + 'px'; - this.dom.label.style.height = height + 'px'; - - // update vertical position of items after they are re-stacked and the height of the group is calculated - for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { - var item = this.visibleItems[i]; - item.repositionY(); - } + }; - return resized; -}; + /** + * Order items by their end date. If they have no end date, their start date + * is used. + * @param {Item[]} items + */ + exports.orderByEnd = function(items) { + items.sort(function (a, b) { + var aTime = ('end' in a.data) ? a.data.end : a.data.start, + bTime = ('end' in b.data) ? b.data.end : b.data.start; -/** - * Show this group: attach to the DOM - */ -Group.prototype.show = function() { - if (!this.dom.label.parentNode) { - this.itemSet.dom.labelSet.appendChild(this.dom.label); - } + return aTime - bTime; + }); + }; - if (!this.dom.foreground.parentNode) { - this.itemSet.dom.foreground.appendChild(this.dom.foreground); - } + /** + * Adjust vertical positions of the items such that they don't overlap each + * other. + * @param {Item[]} items + * All visible items + * @param {{item: number, axis: number}} margin + * Margins between items and between items and the axis. + * @param {boolean} [force=false] + * If true, all items will be repositioned. If false (default), only + * items having a top===null will be re-stacked + */ + exports.stack = function(items, margin, force) { + var i, iMax; - if (!this.dom.background.parentNode) { - this.itemSet.dom.background.appendChild(this.dom.background); - } + if (force) { + // reset top position of all items + for (i = 0, iMax = items.length; i < iMax; i++) { + items[i].top = null; + } + } - if (!this.dom.axis.parentNode) { - this.itemSet.dom.axis.appendChild(this.dom.axis); - } -}; + // calculate new, non-overlapping positions + for (i = 0, iMax = items.length; i < iMax; i++) { + var item = items[i]; + if (item.top === null) { + // initialize top position + item.top = margin.axis; + + do { + // TODO: optimize checking for overlap. when there is a gap without items, + // you only need to check for items from the next item on, not from zero + var collidingItem = null; + for (var j = 0, jj = items.length; j < jj; j++) { + var other = items[j]; + if (other.top !== null && other !== item && exports.collision(item, other, margin.item)) { + collidingItem = other; + break; + } + } -/** - * Hide this group: remove from the DOM - */ -Group.prototype.hide = function() { - var label = this.dom.label; - if (label.parentNode) { - label.parentNode.removeChild(label); - } + if (collidingItem != null) { + // There is a collision. Reposition the items above the colliding element + item.top = collidingItem.top + collidingItem.height + margin.item; + } + } while (collidingItem); + } + } + }; - var foreground = this.dom.foreground; - if (foreground.parentNode) { - foreground.parentNode.removeChild(foreground); - } + /** + * Adjust vertical positions of the items without stacking them + * @param {Item[]} items + * All visible items + * @param {{item: number, axis: number}} margin + * Margins between items and between items and the axis. + */ + exports.nostack = function(items, margin) { + var i, iMax; - var background = this.dom.background; - if (background.parentNode) { - background.parentNode.removeChild(background); - } + // reset top position of all items + for (i = 0, iMax = items.length; i < iMax; i++) { + items[i].top = margin.axis; + } + }; - var axis = this.dom.axis; - if (axis.parentNode) { - axis.parentNode.removeChild(axis); - } -}; + /** + * Test if the two provided items collide + * The items must have parameters left, width, top, and height. + * @param {Item} a The first item + * @param {Item} b The second item + * @param {Number} margin A minimum required margin. + * If margin is provided, the two items will be + * marked colliding when they overlap or + * when the margin between the two is smaller than + * the requested margin. + * @return {boolean} true if a and b collide, else false + */ + exports.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) && + (a.top + a.height + margin) > b.top); + }; -/** - * Add an item to the group - * @param {Item} 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.body.range; // TODO: not nice accessing the range like this - this._checkIfVisible(item, this.visibleItems, range); - } -}; +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { -/** - * Remove an item from the group - * @param {Item} item - */ -Group.prototype.remove = function(item) { - delete this.items[item.id]; - item.setParent(this.itemSet); + var moment = __webpack_require__(39); - // remove from visible items - var index = this.visibleItems.indexOf(item); - if (index != -1) this.visibleItems.splice(index, 1); + /** + * @constructor TimeStep + * The class TimeStep is an iterator for dates. You provide a start date and an + * end date. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. + * + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + */ + function TimeStep(start, end, minimumStep) { + // variables + this.current = new Date(); + this._start = new Date(); + this._end = new Date(); + + this.autoScale = true; + this.scale = TimeStep.SCALE.DAY; + this.step = 1; + + // initialize the range + this.setRange(start, end, minimumStep); + } + + /// enum scale + TimeStep.SCALE = { + MILLISECOND: 1, + SECOND: 2, + MINUTE: 3, + HOUR: 4, + DAY: 5, + WEEKDAY: 6, + MONTH: 7, + YEAR: 8 + }; - // TODO: also remove from ordered items? -}; -/** - * Remove an item from the corresponding DataSet - * @param {Item} item - */ -Group.prototype.removeFromDataSet = function(item) { - this.itemSet.removeItem(item.id); -}; + /** + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Date} [start] The start date and time. + * @param {Date} [end] The end date and time. + * @param {int} [minimumStep] Optional. Minimum step size in milliseconds + */ + TimeStep.prototype.setRange = function(start, end, minimumStep) { + if (!(start instanceof Date) || !(end instanceof Date)) { + throw "No legal start or end date in method setRange"; + } -/** - * Reorder the items - */ -Group.prototype.order = function() { - var array = util.toArray(this.items); - this.orderedItems.byStart = array; - this.orderedItems.byEnd = this._constructByEndArray(array); + this._start = (start != undefined) ? new Date(start.valueOf()) : new Date(); + this._end = (end != undefined) ? new Date(end.valueOf()) : new Date(); - stack.orderByStart(this.orderedItems.byStart); - stack.orderByEnd(this.orderedItems.byEnd); -}; + if (this.autoScale) { + this.setMinimumStep(minimumStep); + } + }; -/** - * Create an array containing all items being a range (having an end date) - * @param {Item[]} array - * @returns {ItemRange[]} - * @private - */ -Group.prototype._constructByEndArray = function(array) { - var endArray = []; + /** + * Set the range iterator to the start date. + */ + TimeStep.prototype.first = function() { + this.current = new Date(this._start.valueOf()); + this.roundToMinor(); + }; - for (var i = 0; i < array.length; i++) { - if (array[i] instanceof ItemRange) { - endArray.push(array[i]); + /** + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date + */ + TimeStep.prototype.roundToMinor = function() { + // round to floor + // IMPORTANT: we have no breaks in this switch! (this is no bug) + //noinspection FallthroughInSwitchStatementJS + switch (this.scale) { + case 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: // intentional fall through + 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); + //case TimeStep.SCALE.MILLISECOND: // nothing to do for milliseconds + } + + if (this.step != 1) { + // round down to the first minor value that is a multiple of the current step size + switch (this.scale) { + case 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: // intentional fall through + 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); break; + default: break; + } } - } - return endArray; -}; + }; -/** - * Update the visible items - * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date - * @param {Item[]} visibleItems The previously visible items. - * @param {{start: number, end: number}} range Visible range - * @return {Item[]} visibleItems The new visible items. - * @private - */ -Group.prototype._updateVisibleItems = function(orderedItems, visibleItems, range) { - var initialPosByStart, - newVisibleItems = [], - i; + /** + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date + */ + TimeStep.prototype.hasNext = function () { + return (this.current.valueOf() <= this._end.valueOf()); + }; - // first check if the items that were in view previously are still in view. - // this handles the case for the ItemRange that is both before and after the current one. - if (visibleItems.length > 0) { - for (i = 0; i < visibleItems.length; i++) { - this._checkIfVisible(visibleItems[i], newVisibleItems, range); + /** + * Do the next step + */ + TimeStep.prototype.next = function() { + var prev = this.current.valueOf(); + + // Two cases, needed to prevent issues with switching daylight savings + // (end of March and end of October) + if (this.current.getMonth() < 6) { + switch (this.scale) { + case TimeStep.SCALE.MILLISECOND: + + this.current = new Date(this.current.valueOf() + this.step); break; + case TimeStep.SCALE.SECOND: this.current = new Date(this.current.valueOf() + this.step * 1000); break; + case TimeStep.SCALE.MINUTE: this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break; + case TimeStep.SCALE.HOUR: + this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60); + // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...) + var h = this.current.getHours(); + this.current.setHours(h - (h % this.step)); + break; + case TimeStep.SCALE.WEEKDAY: // intentional fall through + 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); break; + default: break; + } } - } + 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: // intentional fall through + 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); break; + default: break; + } + } + + if (this.step != 1) { + // round down to the correct major value + switch (this.scale) { + case TimeStep.SCALE.MILLISECOND: if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break; + case TimeStep.SCALE.SECOND: if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break; + case TimeStep.SCALE.MINUTE: if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break; + case TimeStep.SCALE.HOUR: if(this.current.getHours() < this.step) this.current.setHours(0); break; + case TimeStep.SCALE.WEEKDAY: // intentional fall through + case TimeStep.SCALE.DAY: if(this.current.getDate() < this.step+1) this.current.setDate(1); break; + case TimeStep.SCALE.MONTH: if(this.current.getMonth() < this.step) this.current.setMonth(0); break; + case TimeStep.SCALE.YEAR: break; // nothing to do for year + default: break; + } + } + + // safety mechanism: if current time is still unchanged, move to the end + if (this.current.valueOf() == prev) { + this.current = new Date(this._end.valueOf()); + } + }; - // If there were no visible items previously, use binarySearch to find a visible ItemPoint or ItemRange (based on startTime) - if (newVisibleItems.length == 0) { - initialPosByStart = util.binarySearch(orderedItems.byStart, range, 'data','start'); - } - else { - initialPosByStart = orderedItems.byStart.indexOf(newVisibleItems[0]); - } - // use visible search to find a visible ItemRange (only based on endTime) - var initialPosByEnd = util.binarySearch(orderedItems.byEnd, range, 'data','end'); + /** + * Get the current datetime + * @return {Date} current The current date + */ + TimeStep.prototype.getCurrent = function() { + return this.current; + }; - // if we found a initial ID to use, trace it up and down until we meet an invisible item. - if (initialPosByStart != -1) { - for (i = initialPosByStart; i >= 0; i--) { - if (this._checkIfInvisible(orderedItems.byStart[i], newVisibleItems, range)) {break;} - } - for (i = initialPosByStart + 1; i < orderedItems.byStart.length; i++) { - if (this._checkIfInvisible(orderedItems.byStart[i], newVisibleItems, range)) {break;} - } - } + /** + * Set a custom scale. Autoscaling will be disabled. + * For example setScale(SCALE.MINUTES, 5) will result + * in minor steps of 5 minutes, and major steps of an hour. + * + * @param {TimeStep.SCALE} newScale + * A scale. Choose from SCALE.MILLISECOND, + * SCALE.SECOND, SCALE.MINUTE, SCALE.HOUR, + * SCALE.WEEKDAY, SCALE.DAY, SCALE.MONTH, + * SCALE.YEAR. + * @param {Number} newStep A step size, by default 1. Choose for + * example 1, 2, 5, or 10. + */ + TimeStep.prototype.setScale = function(newScale, newStep) { + this.scale = newScale; - // if we found a initial ID to use, trace it up and down until we meet an invisible item. - if (initialPosByEnd != -1) { - for (i = initialPosByEnd; i >= 0; i--) { - if (this._checkIfInvisible(orderedItems.byEnd[i], newVisibleItems, range)) {break;} - } - for (i = initialPosByEnd + 1; i < orderedItems.byEnd.length; i++) { - if (this._checkIfInvisible(orderedItems.byEnd[i], newVisibleItems, range)) {break;} + if (newStep > 0) { + this.step = newStep; } - } - return newVisibleItems; -}; + this.autoScale = false; + }; + /** + * Enable or disable autoscaling + * @param {boolean} enable If true, autoascaling is set true + */ + TimeStep.prototype.setAutoScale = function (enable) { + this.autoScale = enable; + }; -/** - * this function checks if an item is invisible. If it is NOT we make it visible - * and add it to the global visible items. If it is, return true. - * - * @param {Item} item - * @param {Item[]} visibleItems - * @param {{start:number, end:number}} range - * @returns {boolean} - * @private - */ -Group.prototype._checkIfInvisible = function(item, visibleItems, range) { - if (item.isVisible(range)) { - if (!item.displayed) item.show(); - item.repositionX(); - if (visibleItems.indexOf(item) == -1) { - visibleItems.push(item); + /** + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds + */ + TimeStep.prototype.setMinimumStep = function(minimumStep) { + if (minimumStep == undefined) { + return; } - return false; - } - else { - return true; - } -}; -/** - * this function is very similar to the _checkIfInvisible() but it does not - * return booleans, hides the item if it should not be seen and always adds to - * the visibleItems. - * this one is for brute forcing and hiding. - * - * @param {Item} item - * @param {Array} visibleItems - * @param {{start:number, end:number}} range - * @private - */ -Group.prototype._checkIfVisible = function(item, visibleItems, range) { - if (item.isVisible(range)) { - if (!item.displayed) item.show(); - // reposition item horizontally - item.repositionX(); - visibleItems.push(item); - } - else { - if (item.displayed) item.hide(); - } -}; + var stepYear = (1000 * 60 * 60 * 24 * 30 * 12); + var stepMonth = (1000 * 60 * 60 * 24 * 30); + var stepDay = (1000 * 60 * 60 * 24); + var stepHour = (1000 * 60 * 60); + var stepMinute = (1000 * 60); + var stepSecond = (1000); + var stepMillisecond= (1); + + // find the smallest step that is larger than the provided minimumStep + if (stepYear*1000 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1000;} + if (stepYear*500 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 500;} + if (stepYear*100 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 100;} + if (stepYear*50 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 50;} + if (stepYear*10 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 10;} + if (stepYear*5 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 5;} + if (stepYear > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1;} + if (stepMonth*3 > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 3;} + if (stepMonth > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 1;} + if (stepDay*5 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 5;} + if (stepDay*2 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 2;} + if (stepDay > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 1;} + if (stepDay/2 > minimumStep) {this.scale = TimeStep.SCALE.WEEKDAY; this.step = 1;} + if (stepHour*4 > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 4;} + if (stepHour > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 1;} + if (stepMinute*15 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 15;} + if (stepMinute*10 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 10;} + if (stepMinute*5 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 5;} + if (stepMinute > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 1;} + if (stepSecond*15 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 15;} + if (stepSecond*10 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 10;} + if (stepSecond*5 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 5;} + if (stepSecond > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 1;} + if (stepMillisecond*200 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 200;} + if (stepMillisecond*100 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 100;} + if (stepMillisecond*50 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 50;} + if (stepMillisecond*10 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 10;} + if (stepMillisecond*5 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 5;} + if (stepMillisecond > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 1;} + }; -/** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {Object} [options] See Timeline.setOptions for the available options. - * @constructor - */ -function Timeline (container, items, options) { - if (!(this instanceof Timeline)) { - throw new SyntaxError('Constructor must be called with the new operator'); - } + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + TimeStep.prototype.snap = function(date) { + var clone = new Date(date.valueOf()); + + if (this.scale == TimeStep.SCALE.YEAR) { + var year = clone.getFullYear() + Math.round(clone.getMonth() / 12); + clone.setFullYear(Math.round(year / this.step) * this.step); + clone.setMonth(0); + clone.setDate(0); + clone.setHours(0); + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == TimeStep.SCALE.MONTH) { + if (clone.getDate() > 15) { + clone.setDate(1); + clone.setMonth(clone.getMonth() + 1); + // important: first set Date to 1, after that change the month. + } + else { + clone.setDate(1); + } + + clone.setHours(0); + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == TimeStep.SCALE.DAY) { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 5: + case 2: + clone.setHours(Math.round(clone.getHours() / 24) * 24); break; + default: + clone.setHours(Math.round(clone.getHours() / 12) * 12); break; + } + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == TimeStep.SCALE.WEEKDAY) { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 5: + case 2: + clone.setHours(Math.round(clone.getHours() / 12) * 12); break; + default: + clone.setHours(Math.round(clone.getHours() / 6) * 6); break; + } + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == TimeStep.SCALE.HOUR) { + switch (this.step) { + case 4: + clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break; + default: + clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break; + } + clone.setSeconds(0); + clone.setMilliseconds(0); + } else if (this.scale == TimeStep.SCALE.MINUTE) { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 15: + case 10: + clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5); + clone.setSeconds(0); + break; + case 5: + clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break; + default: + clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break; + } + clone.setMilliseconds(0); + } + else if (this.scale == TimeStep.SCALE.SECOND) { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 15: + case 10: + clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5); + clone.setMilliseconds(0); + break; + case 5: + clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break; + default: + clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break; + } + } + else if (this.scale == TimeStep.SCALE.MILLISECOND) { + var step = this.step > 5 ? this.step / 2 : 1; + clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step); + } + + return clone; + }; + + /** + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. + */ + TimeStep.prototype.isMajor = function() { + switch (this.scale) { + case TimeStep.SCALE.MILLISECOND: + return (this.current.getMilliseconds() == 0); + case TimeStep.SCALE.SECOND: + return (this.current.getSeconds() == 0); + case TimeStep.SCALE.MINUTE: + return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); + // Note: this is no bug. Major label is equal for both minute and hour scale + case TimeStep.SCALE.HOUR: + return (this.current.getHours() == 0); + case TimeStep.SCALE.WEEKDAY: // intentional fall through + case TimeStep.SCALE.DAY: + return (this.current.getDate() == 1); + case TimeStep.SCALE.MONTH: + return (this.current.getMonth() == 0); + case TimeStep.SCALE.YEAR: + return false; + default: + return false; + } + }; - var me = this; - this.defaultOptions = { - start: null, - end: null, - autoResize: true, + /** + * Returns formatted text for the minor axislabel, depending on the current + * date and the scale. For example when scale is MINUTE, the current time is + * formatted as "hh:mm". + * @param {Date} [date] custom date. if not provided, current date is taken + */ + TimeStep.prototype.getLabelMinor = function(date) { + if (date == undefined) { + date = this.current; + } - orientation: 'bottom', - width: null, - height: null, - maxHeight: null, - minHeight: null + switch (this.scale) { + case TimeStep.SCALE.MILLISECOND: return moment(date).format('SSS'); + case TimeStep.SCALE.SECOND: return moment(date).format('s'); + case TimeStep.SCALE.MINUTE: return moment(date).format('HH:mm'); + case TimeStep.SCALE.HOUR: return moment(date).format('HH:mm'); + case TimeStep.SCALE.WEEKDAY: return moment(date).format('ddd D'); + case TimeStep.SCALE.DAY: return moment(date).format('D'); + case TimeStep.SCALE.MONTH: return moment(date).format('MMM'); + case TimeStep.SCALE.YEAR: return moment(date).format('YYYY'); + default: return ''; + } }; - this.options = util.deepExtend({}, this.defaultOptions); - // Create the DOM, props, and emitter - this._create(container); - // all components listed here will be repainted automatically - this.components = []; + /** + * Returns formatted text for the major axis label, depending on the current + * date and the scale. For example when scale is MINUTE, the major scale is + * hours, and the hour will be formatted as "hh". + * @param {Date} [date] custom date. if not provided, current date is taken + */ + TimeStep.prototype.getLabelMajor = function(date) { + if (date == undefined) { + date = this.current; + } - this.body = { - dom: this.dom, - domProps: this.props, - emitter: { - on: this.on.bind(this), - off: this.off.bind(this), - emit: this.emit.bind(this) - }, - util: { - snap: null, // will be specified after TimeAxis is created - toScreen: me._toScreen.bind(me), - toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width - toTime: me._toTime.bind(me), - toGlobalTime : me._toGlobalTime.bind(me) + //noinspection FallthroughInSwitchStatementJS + switch (this.scale) { + case TimeStep.SCALE.MILLISECOND:return moment(date).format('HH:mm:ss'); + case TimeStep.SCALE.SECOND: return moment(date).format('D MMMM HH:mm'); + case TimeStep.SCALE.MINUTE: + case TimeStep.SCALE.HOUR: return moment(date).format('ddd D MMMM'); + case TimeStep.SCALE.WEEKDAY: + case TimeStep.SCALE.DAY: return moment(date).format('MMMM YYYY'); + case TimeStep.SCALE.MONTH: return moment(date).format('YYYY'); + case TimeStep.SCALE.YEAR: return ''; + default: return ''; } }; - // range - this.range = new Range(this.body); - this.components.push(this.range); - this.body.range = this.range; + module.exports = TimeStep; - // time axis - this.timeAxis = new TimeAxis(this.body); - this.components.push(this.timeAxis); - this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - // current time bar - this.currentTime = new CurrentTime(this.body); - this.components.push(this.currentTime); +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { - // custom time bar - // Note: time bar will be attached in this.setOptions when selected - this.customTime = new CustomTime(this.body); - this.components.push(this.customTime); + /** + * Prototype for visual components + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] + * @param {Object} [options] + */ + function Component (body, options) { + this.options = null; + this.props = null; + } - // item set - this.itemSet = new ItemSet(this.body); - this.components.push(this.itemSet); + /** + * Set options for the component. The new options will be merged into the + * current options. + * @param {Object} options + */ + Component.prototype.setOptions = function(options) { + if (options) { + util.extend(this.options, options); + } + }; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + Component.prototype.redraw = function() { + // should be implemented by the component + return false; + }; - // apply options - if (options) { - this.setOptions(options); - } + /** + * Destroy the component. Cleanup DOM and event listeners + */ + Component.prototype.destroy = function() { + // should be implemented by the component + }; - // create itemset - if (items) { - this.setItems(items); - } - else { - this.redraw(); - } -} + /** + * Test whether the component is resized since the last time _isResized() was + * called. + * @return {Boolean} Returns true if the component is resized + * @protected + */ + Component.prototype._isResized = function() { + var resized = (this.props._previousWidth !== this.props.width || + this.props._previousHeight !== this.props.height); -// turn Timeline into an event emitter -Emitter(Timeline.prototype); + this.props._previousWidth = this.props.width; + this.props._previousHeight = this.props.height; -/** - * 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._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)); - - // 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 = {}; - - 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 - - // attach the root panel to the provided container - if (!container) throw new Error('No container provided'); - container.appendChild(this.dom.root); -}; + return resized; + }; -/** - * Destroy the Timeline, clean up all DOM elements and event listeners. - */ -Timeline.prototype.destroy = function () { - // unbind datasets - this.clear(); + module.exports = Component; - // remove all event listeners - this.off(); - // stop checking for changed size - this._stopAutoResize(); +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { - // remove from DOM - if (this.dom.root.parentNode) { - this.dom.root.parentNode.removeChild(this.dom.root); - } - this.dom = null; + var util = __webpack_require__(1); + var Component = __webpack_require__(12); - // cleanup hammer touch events - for (var event in this.listeners) { - if (this.listeners.hasOwnProperty(event)) { - delete this.listeners[event]; - } - } - this.listeners = null; - this.hammer = null; + /** + * A current time bar + * @param {{range: Range, dom: Object, domProps: Object}} body + * @param {Object} [options] Available parameters: + * {Boolean} [showCurrentTime] + * @constructor CurrentTime + * @extends Component + */ + function CurrentTime (body, options) { + this.body = body; - // give all components the opportunity to cleanup - this.components.forEach(function (component) { - component.destroy(); - }); + // default options + this.defaultOptions = { + showCurrentTime: true + }; + this.options = util.extend({}, this.defaultOptions); - this.body = null; -}; + this._create(); -/** - * 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(); + this.setOptions(options); } - // propagate options to all components - this.components.forEach(function (component) { - component.setOptions(options); - }); + CurrentTime.prototype = new Component(); - // 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.'); - } + /** + * Create the HTML DOM for the current time bar + * @private + */ + CurrentTime.prototype._create = function() { + var bar = document.createElement('div'); + bar.className = 'currenttime'; + bar.style.position = 'absolute'; + bar.style.top = '0px'; + bar.style.height = '100%'; + + this.bar = bar; + }; - // redraw everything - this.redraw(); -}; + /** + * Destroy the CurrentTime bar + */ + CurrentTime.prototype.destroy = function () { + this.options.showCurrentTime = false; + this.redraw(); // will remove the bar from the DOM and stop refreshing -/** - * Set a custom time bar - * @param {Date} time - */ -Timeline.prototype.setCustomTime = function (time) { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); - } + this.body = null; + }; - this.customTime.setCustomTime(time); -}; + /** + * Set options for the component. Options will be merged in current options. + * @param {Object} options Available parameters: + * {boolean} [showCurrentTime] + */ + CurrentTime.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend(['showCurrentTime'], this.options, options); + } + }; -/** - * Retrieve the current custom time. - * @return {Date} customTime - */ -Timeline.prototype.getCustomTime = function() { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); - } + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + CurrentTime.prototype.redraw = function() { + if (this.options.showCurrentTime) { + var parent = this.body.dom.backgroundVertical; + if (this.bar.parentNode != parent) { + // attach to the dom + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + parent.appendChild(this.bar); - return this.customTime.getCustomTime(); -}; + this.start(); + } -/** - * Set items - * @param {vis.DataSet | Array | google.visualization.DataTable | null} items - */ -Timeline.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); + var now = new Date(); + var x = this.body.util.toScreen(now); - // convert to type DataSet when needed - var newDataSet; - if (!items) { - newDataSet = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - newDataSet = items; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(items, { - type: { - start: 'Date', - end: 'Date' + this.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(); + } - // set items - this.itemsData = newDataSet; - this.itemSet && this.itemSet.setItems(newDataSet); + return false; + }; - if (initialLoad && ('start' in this.options || 'end' in this.options)) { - this.fit(); + /** + * Start auto refreshing the current time bar + */ + CurrentTime.prototype.start = function() { + var me = this; - 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; + function update () { + me.stop(); - this.setWindow(start, end); - } -}; + // determine interval to refresh + var scale = me.body.range.conversion(me.body.domProps.center.width).scale; + var interval = 1 / scale / 10; + if (interval < 30) interval = 30; + if (interval > 1000) interval = 1000; -/** - * Set groups - * @param {vis.DataSet | Array | google.visualization.DataTable} groups - */ -Timeline.prototype.setGroups = function(groups) { - // convert to type DataSet when needed - var newDataSet; - if (!groups) { - newDataSet = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - newDataSet = groups; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(groups); - } + me.redraw(); - this.groupsData = newDataSet; - this.itemSet.setGroups(newDataSet); -}; + // start a timer to adjust for the new time + me.currentTimeTimer = setTimeout(update, interval); + } -/** - * Clear the Timeline. By Default, items, groups and options are cleared. - * Example usage: - * - * timeline.clear(); // clear items, groups, and options - * timeline.clear({options: true}); // clear options only - * - * @param {Object} [what] Optionally specify what to clear. By default: - * {items: true, groups: true, options: true} - */ -Timeline.prototype.clear = function(what) { - // clear items - if (!what || what.items) { - this.setItems(null); - } + update(); + }; - // clear groups - if (!what || what.groups) { - this.setGroups(null); - } + /** + * Stop auto refreshing the current time bar + */ + CurrentTime.prototype.stop = function() { + if (this.currentTimeTimer !== undefined) { + clearTimeout(this.currentTimeTimer); + delete this.currentTimeTimer; + } + }; - // clear options of timeline and of each of the components - if (!what || what.options) { - this.components.forEach(function (component) { - component.setOptions(component.defaultOptions); - }); + module.exports = CurrentTime; - this.setOptions(this.defaultOptions); // this will also do a redraw - } -}; -/** - * Set Timeline window such that it fits all items - */ -Timeline.prototype.fit = function() { - // apply the data range as range - var dataRange = this.getItemRange(); - - // add 5% space on both sides - var start = dataRange.min; - var end = dataRange.max; - if (start != null && end != null) { - var interval = (end.valueOf() - start.valueOf()); - if (interval <= 0) { - // prevent an empty interval - interval = 24 * 60 * 60 * 1000; // 1 day - } - start = new Date(start.valueOf() - interval * 0.05); - end = new Date(end.valueOf() + interval * 0.05); - } +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { - // skip range set if there is no start and end date - if (start === null && end === null) { - return; - } + var Hammer = __webpack_require__(49); + var util = __webpack_require__(1); + var Component = __webpack_require__(12); - this.range.setRange(start, end); -}; + /** + * A custom time bar + * @param {{range: Range, dom: Object}} body + * @param {Object} [options] Available parameters: + * {Boolean} [showCustomTime] + * @constructor CustomTime + * @extends Component + */ -/** - * Get the data range of the item set. - * @returns {{min: Date, max: Date}} range A range with a start and end Date. - * When no minimum is found, min==null - * When no maximum is found, max==null - */ -Timeline.prototype.getItemRange = function() { - // calculate min from start filed - var dataset = this.itemsData.getDataSet(), - min = null, - max = null; + function CustomTime (body, options) { + this.body = body; - if (dataset) { - // calculate the minimum value of the field 'start' - var minItem = dataset.min('start'); - min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; - // Note: we convert first to Date and then to number because else - // a conversion from ISODate to Number will fail + // default options + this.defaultOptions = { + showCustomTime: false + }; + this.options = util.extend({}, this.defaultOptions); - // calculate maximum value of fields 'start' and 'end' - var maxStartItem = dataset.max('start'); - if (maxStartItem) { - max = util.convert(maxStartItem.start, 'Date').valueOf(); - } - var maxEndItem = dataset.max('end'); - if (maxEndItem) { - if (max == null) { - max = util.convert(maxEndItem.end, 'Date').valueOf(); - } - else { - max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); - } - } + this.customTime = new Date(); + this.eventParams = {}; // stores state parameters while dragging the bar + + // create the DOM + this._create(); + + this.setOptions(options); } - return { - min: (min != null) ? new Date(min) : null, - max: (max != null) ? new Date(max) : null + CustomTime.prototype = new Component(); + + /** + * 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); + } }; -}; -/** - * Set selected items by their id. Replaces the current selection - * Unknown id's are silently ignored. - * @param {Array} [ids] An array with zero or more id's of the items to be - * selected. If ids is an empty array, all items will be - * unselected. - */ -Timeline.prototype.setSelection = function(ids) { - this.itemSet && this.itemSet.setSelection(ids); -}; + /** + * Create the DOM for the custom time + * @private + */ + CustomTime.prototype._create = function() { + var bar = document.createElement('div'); + bar.className = 'customtime'; + bar.style.position = 'absolute'; + bar.style.top = '0px'; + bar.style.height = '100%'; + this.bar = bar; + + var drag = document.createElement('div'); + drag.style.position = 'relative'; + drag.style.top = '0px'; + drag.style.left = '-10px'; + drag.style.height = '100%'; + drag.style.width = '20px'; + bar.appendChild(drag); + + // attach event listeners + this.hammer = Hammer(bar, { + prevent_default: true + }); + this.hammer.on('dragstart', this._onDragStart.bind(this)); + this.hammer.on('drag', this._onDrag.bind(this)); + this.hammer.on('dragend', this._onDragEnd.bind(this)); + }; -/** - * Get the selected items by their id - * @return {Array} ids The ids of the selected items - */ -Timeline.prototype.getSelection = function() { - return this.itemSet && this.itemSet.getSelection() || []; -}; + /** + * Destroy the CustomTime bar + */ + CustomTime.prototype.destroy = function () { + this.options.showCustomTime = false; + this.redraw(); // will remove the bar from the DOM -/** - * Set the visible window. Both parameters are optional, you can change only - * start or only end. Syntax: - * - * TimeLine.setWindow(start, end) - * TimeLine.setWindow(range) - * - * Where start and end can be a Date, number, or string, and range is an - * object with properties start and end. - * - * @param {Date | Number | String | Object} [start] Start date of visible window - * @param {Date | Number | String} [end] End date of visible window - */ -Timeline.prototype.setWindow = function(start, end) { - if (arguments.length == 1) { - var range = arguments[0]; - this.range.setRange(range.start, range.end); - } - else { - this.range.setRange(start, end); - } -}; + this.hammer.enable(false); + this.hammer = null; -/** - * Get the visible window - * @return {{start: Date, end: Date}} Visible range - */ -Timeline.prototype.getWindow = function() { - var range = this.range.getRange(); - return { - start: new Date(range.start), - end: new Date(range.end) + this.body = null; }; -}; -/** - * Force a redraw of the Timeline. Can be useful to manually redraw when - * option autoResize=false - */ -Timeline.prototype.redraw = function() { - var resized = false, - options = this.options, - props = this.props, - dom = this.dom; + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + CustomTime.prototype.redraw = function () { + if (this.options.showCustomTime) { + var parent = this.body.dom.backgroundVertical; + if (this.bar.parentNode != parent) { + // attach to the dom + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + parent.appendChild(this.bar); + } - if (!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'); + var x = this.body.util.toScreen(this.customTime); - // 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 - - this.props.border.top - this.props.border.bottom, 0); - } - dom.center.style.left = '0'; - dom.center.style.top = offset + 'px'; - dom.left.style.left = '0'; - dom.left.style.top = offset + 'px'; - dom.right.style.left = '0'; - dom.right.style.top = offset + 'px'; - - // show shadows when vertical scrolling is available - var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; - var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; - dom.shadowTop.style.visibility = visibilityTop; - dom.shadowBottom.style.visibility = visibilityBottom; - dom.shadowTopLeft.style.visibility = visibilityTop; - dom.shadowBottomLeft.style.visibility = visibilityBottom; - dom.shadowTopRight.style.visibility = visibilityTop; - dom.shadowBottomRight.style.visibility = visibilityBottom; - - // redraw all components - this.components.forEach(function (component) { - resized = component.redraw() || resized; - }); - if (resized) { - // keep repainting until all sizes are settled + 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; + }; + + /** + * Set custom time. + * @param {Date} time + */ + CustomTime.prototype.setCustomTime = function(time) { + this.customTime = new Date(time.valueOf()); this.redraw(); - } -}; + }; -// TODO: deprecated since version 1.1.0, remove some day -Timeline.prototype.repaint = function () { - throw new Error('Function repaint is deprecated. Use redraw instead.'); -}; + /** + * Retrieve the current custom time. + * @return {Date} customTime + */ + CustomTime.prototype.getCustomTime = function() { + return new Date(this.customTime.valueOf()); + }; -/** - * Convert a position on screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x - * @private - */ -// TODO: move this function to Range -Timeline.prototype._toTime = function(x) { - var conversion = this.range.conversion(this.props.center.width); - return new Date(x / conversion.scale + conversion.offset); -}; + /** + * Start moving horizontally + * @param {Event} event + * @private + */ + CustomTime.prototype._onDragStart = function(event) { + this.eventParams.dragging = true; + this.eventParams.customTime = this.customTime; + event.stopPropagation(); + event.preventDefault(); + }; -/** - * Convert a position on the global screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x - * @private - */ -// TODO: move this function to Range -Timeline.prototype._toGlobalTime = function(x) { - var conversion = this.range.conversion(this.props.root.width); - return new Date(x / conversion.scale + conversion.offset); -}; + /** + * Perform moving operating. + * @param {Event} event + * @private + */ + CustomTime.prototype._onDrag = function (event) { + if (!this.eventParams.dragging) return; -/** - * 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; -}; + var deltaX = event.gesture.deltaX, + x = this.body.util.toScreen(this.eventParams.customTime) + deltaX, + time = this.body.util.toTime(x); + this.setCustomTime(time); -/** - * Convert a datetime (Date object) into a position on the root - * This is used to get the pixel density estimate for the screen, not the center panel - * @param {Date} time A date - * @return {int} x The position on root in pixels which corresponds - * with the given date. - * @private - */ -// TODO: move this function to Range -Timeline.prototype._toGlobalScreen = function(time) { - var conversion = this.range.conversion(this.props.root.width); - return (time.valueOf() - conversion.offset) * conversion.scale; -}; + // fire a timechange event + this.body.emitter.emit('timechange', { + time: new Date(this.customTime.valueOf()) + }); + event.stopPropagation(); + event.preventDefault(); + }; -/** - * Initialize watching when option autoResize is true - * @private - */ -Timeline.prototype._initAutoResize = function () { - if (this.options.autoResize == true) { - this._startAutoResize(); - } - else { - this._stopAutoResize(); - } -}; + /** + * Stop moving operating. + * @param {event} event + * @private + */ + CustomTime.prototype._onDragEnd = function (event) { + if (!this.eventParams.dragging) return; -/** - * Watch for changes in the size of the container. On resize, the Panel will - * automatically redraw itself. - * @private - */ -Timeline.prototype._startAutoResize = function () { - var me = this; + // fire a timechanged event + this.body.emitter.emit('timechanged', { + time: new Date(this.customTime.valueOf()) + }); - this._stopAutoResize(); + event.stopPropagation(); + event.preventDefault(); + }; - this._onResize = function() { - if (me.options.autoResize != true) { - // stop watching when the option autoResize is changed to false - me._stopAutoResize(); - return; - } + module.exports = CustomTime; - 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; - me.emit('change'); - } - } - }; +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { - // add event listener to window resize - util.addEventListener(window, 'resize', this._onResize); + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var Component = __webpack_require__(12); + var DataStep = __webpack_require__(8); - this.watchTimer = setInterval(this._onResize, 1000); -}; + /** + * A horizontal time axis + * @param {Object} [options] See DataAxis.setOptions for the available + * options. + * @constructor DataAxis + * @extends Component + * @param body + */ + function DataAxis (body, options, svg) { + this.id = util.randomUUID(); + this.body = body; -/** - * Stop watching for a resize of the frame. - * @private - */ -Timeline.prototype._stopAutoResize = function () { - if (this.watchTimer) { - clearInterval(this.watchTimer); - this.watchTimer = undefined; - } + this.defaultOptions = { + orientation: 'left', // supported: 'left', 'right' + showMinorLabels: true, + showMajorLabels: true, + icons: true, + majorLinesOffset: 7, + minorLinesOffset: 4, + labelOffsetX: 10, + labelOffsetY: 2, + iconWidth: 20, + width: '40px', + visible: true + }; - // remove event listener on window.resize - util.removeEventListener(window, 'resize', this._onResize); - this._onResize = null; -}; + this.linegraphSVG = svg; + this.props = {}; + this.DOMelements = { // dynamic elements + lines: {}, + labels: {} + }; -/** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ -Timeline.prototype._onTouch = function (event) { - this.touch.allowDragging = true; -}; + this.dom = {}; -/** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ -Timeline.prototype._onPinch = function (event) { - this.touch.allowDragging = false; -}; + this.range = {start:0, end:0}; -/** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ -Timeline.prototype._onDragStart = function (event) { - this.touch.initialScrollTop = this.props.scrollTop; -}; + this.options = util.extend({}, this.defaultOptions); + this.conversionFactor = 1; -/** - * 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; + this.setOptions(options); + this.width = Number(('' + this.options.width).replace("px","")); + this.minWidth = this.width; + this.height = this.linegraphSVG.offsetHeight; - var delta = event.gesture.deltaY; + this.stepPixels = 25; + this.stepPixelsForced = 25; + this.lineOffset = 0; + this.master = true; + this.svgElements = {}; - var oldScrollTop = this._getScrollTop(); - var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); - if (newScrollTop != oldScrollTop) { - this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already + this.groups = {}; + this.amountOfGroups = 0; + + // create the HTML DOM + this._create(); } -}; -/** - * Apply a scrollTop - * @param {Number} scrollTop - * @returns {Number} scrollTop Returns the applied scrollTop - * @private - */ -Timeline.prototype._setScrollTop = function (scrollTop) { - this.props.scrollTop = scrollTop; - this._updateScrollTop(); - return this.props.scrollTop; -}; + DataAxis.prototype = new Component(); -/** - * Update the current scrollTop when the height of the containers has been changed - * @returns {Number} scrollTop Returns the applied scrollTop - * @private - */ -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; -}; + DataAxis.prototype.addGroup = function(label, graphOptions) { + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; + } + this.amountOfGroups += 1; + }; -/** - * Get the current scrollTop - * @returns {number} scrollTop - * @private - */ -Timeline.prototype._getScrollTop = function () { - return this.props.scrollTop; -}; + DataAxis.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; + }; -/** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {Object} [options] See Graph2d.setOptions for the available options. - * @constructor - */ -function Graph2d (container, items, options, groups) { - var me = this; - this.defaultOptions = { - start: null, - end: null, - - autoResize: true, - - orientation: 'bottom', - width: null, - height: null, - maxHeight: null, - minHeight: null - }; - this.options = util.deepExtend({}, this.defaultOptions); - - // Create the DOM, props, and emitter - this._create(container); - - // all components listed here will be repainted automatically - this.components = []; - - this.body = { - dom: this.dom, - domProps: this.props, - emitter: { - on: this.on.bind(this), - off: this.off.bind(this), - emit: this.emit.bind(this) - }, - util: { - snap: null, // will be specified after TimeAxis is created - toScreen: me._toScreen.bind(me), - toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width - toTime: me._toTime.bind(me), - toGlobalTime : me._toGlobalTime.bind(me) - } - }; - - // range - this.range = new Range(this.body); - this.components.push(this.range); - this.body.range = this.range; - - // time axis - this.timeAxis = new TimeAxis(this.body); - this.components.push(this.timeAxis); - this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - - // current time bar - this.currentTime = new CurrentTime(this.body); - this.components.push(this.currentTime); - - // custom time bar - // Note: time bar will be attached in this.setOptions when selected - this.customTime = new CustomTime(this.body); - this.components.push(this.customTime); - - // item set - this.linegraph = new LineGraph(this.body); - this.components.push(this.linegraph); - - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet - - // apply options - if (options) { - this.setOptions(options); - } + DataAxis.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; + } + }; - // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! - if (groups) { - this.setGroups(groups); - } - // create itemset - if (items) { - this.setItems(items); - } - else { - this.redraw(); - } -} + DataAxis.prototype.setOptions = function (options) { + if (options) { + var redraw = false; + if (this.options.orientation != options.orientation && options.orientation !== undefined) { + redraw = true; + } + var fields = [ + 'orientation', + 'showMinorLabels', + 'showMajorLabels', + 'icons', + 'majorLinesOffset', + 'minorLinesOffset', + 'labelOffsetX', + 'labelOffsetY', + 'iconWidth', + 'width', + 'visible']; + util.selectiveExtend(fields, this.options, options); -// turn Graph2d into an event emitter -Emitter(Graph2d.prototype); + this.minWidth = Number(('' + this.options.width).replace("px","")); -/** - * Create the main DOM for the Graph2d: a root panel containing left, right, - * top, bottom, content, and background panel. - * @param {Element} container The container element where the Graph2d will - * be attached. - * @private - */ -Graph2d.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.backgroundHorizontalContainer = document.createElement('div'); - this.dom.centerContainer = document.createElement('div'); - this.dom.leftContainer = document.createElement('div'); - this.dom.rightContainer = document.createElement('div'); - this.dom.backgroundHorizontal = 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.backgroundHorizontalContainer.className = 'vispanel background horizontal'; - 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.backgroundHorizontalContainer); - 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.backgroundHorizontalContainer.appendChild(this.dom.backgroundHorizontal); - 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)); - - // 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 = {}; - - 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 - - // attach the root panel to the provided container - if (!container) throw new Error('No container provided'); - container.appendChild(this.dom.root); -}; + if (redraw == true && this.dom.frame) { + this.hide(); + this.show(); + } + } + }; -/** - * Destroy the Graph2d, clean up all DOM elements and event listeners. - */ -Graph2d.prototype.destroy = function () { - // unbind datasets - this.clear(); - // remove all event listeners - this.off(); + /** + * Create the HTML DOM for the DataAxis + */ + DataAxis.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.style.width = this.options.width; + this.dom.frame.style.height = this.height; + + this.dom.lineContainer = document.createElement('div'); + this.dom.lineContainer.style.width = '100%'; + this.dom.lineContainer.style.height = this.height; + + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = "absolute"; + this.svg.style.top = '0px'; + this.svg.style.height = '100%'; + this.svg.style.width = '100%'; + this.svg.style.display = "block"; + this.dom.frame.appendChild(this.svg); + }; - // stop checking for changed size - this._stopAutoResize(); + DataAxis.prototype._redrawGroupIcons = function () { + DOMutil.prepareElements(this.svgElements); - // remove from DOM - if (this.dom.root.parentNode) { - this.dom.root.parentNode.removeChild(this.dom.root); - } - this.dom = null; + var x; + var iconWidth = this.options.iconWidth; + var iconHeight = 15; + var iconOffset = 4; + var y = iconOffset + 0.5 * iconHeight; - // cleanup hammer touch events - for (var event in this.listeners) { - if (this.listeners.hasOwnProperty(event)) { - delete this.listeners[event]; + if (this.options.orientation == 'left') { + x = iconOffset; + } + else { + x = this.width - iconWidth - iconOffset; } - } - this.listeners = null; - this.hammer = null; - // give all components the opportunity to cleanup - this.components.forEach(function (component) { - component.destroy(); - }); + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + iconOffset; + } + } - this.body = null; -}; + DOMutil.cleanupElements(this.svgElements); + }; -/** - * Set options. Options will be passed to all components loaded in the Graph2d. - * @param {Object} [options] - * {String} orientation - * Vertical orientation for the Graph2d, - * 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 Graph2d, a number in pixels or - * a css string like '400px' or '75%'. If undefined, - * The Graph2d will automatically size such that - * its contents fit. - * {String | Number} minHeight - * Minimum height for the Graph2d, a number in pixels or - * a css string like '400px' or '75%'. - * {String | Number} maxHeight - * Maximum height for the Graph2d, 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 - */ -Graph2d.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(); - } + /** + * Create the HTML DOM for the DataAxis + */ + DataAxis.prototype.show = function() { + if (!this.dom.frame.parentNode) { + if (this.options.orientation == 'left') { + this.body.dom.left.appendChild(this.dom.frame); + } + else { + this.body.dom.right.appendChild(this.dom.frame); + } + } - // propagate options to all components - this.components.forEach(function (component) { - component.setOptions(options); - }); + if (!this.dom.lineContainer.parentNode) { + this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); + } + }; - // 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.'); - } + /** + * Create the HTML DOM for the DataAxis + */ + DataAxis.prototype.hide = function() { + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } - // redraw everything - this.redraw(); -}; + if (this.dom.lineContainer.parentNode) { + this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); + } + }; -/** - * Set a custom time bar - * @param {Date} time - */ -Graph2d.prototype.setCustomTime = function (time) { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); - } + /** + * Set a range (start and end) + * @param end + * @param start + * @param end + */ + DataAxis.prototype.setRange = function (start, end) { + this.range.start = start; + this.range.end = end; + }; - this.customTime.setCustomTime(time); -}; + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + DataAxis.prototype.redraw = function () { + var changeCalled = false; + if (this.amountOfGroups == 0) { + this.hide(); + } + else { + this.show(); + this.height = Number(this.linegraphSVG.style.height.replace("px","")); + // svg offsetheight did not work in firefox and explorer... -/** - * Retrieve the current custom time. - * @return {Date} customTime - */ -Graph2d.prototype.getCustomTime = function() { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); - } + this.dom.lineContainer.style.height = this.height + 'px'; + this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; - return this.customTime.getCustomTime(); -}; + var props = this.props; + var frame = this.dom.frame; -/** - * Set items - * @param {vis.DataSet | Array | google.visualization.DataTable | null} items - */ -Graph2d.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); + // update classname + frame.className = 'dataaxis'; - // convert to type DataSet when needed - var newDataSet; - if (!items) { - newDataSet = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - newDataSet = items; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(items, { - type: { - start: 'Date', - end: 'Date' - } - }); - } + // calculate character width and height + this._calculateCharSize(); - // set items - this.itemsData = newDataSet; - this.linegraph && this.linegraph.setItems(newDataSet); + var orientation = this.options.orientation; + var showMinorLabels = this.options.showMinorLabels; + var showMajorLabels = this.options.showMajorLabels; - if (initialLoad && ('start' in this.options || 'end' in this.options)) { - this.fit(); + // determine the width and height of the elemens for the axis + props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; + props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - 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; + props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; + props.minorLineHeight = 1; + props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; + props.majorLineHeight = 1; - this.setWindow(start, end); - } -}; + // take frame offline while updating (is almost twice as fast) + if (orientation == 'left') { + frame.style.top = '0'; + frame.style.left = '0'; + frame.style.bottom = ''; + frame.style.width = this.width + 'px'; + frame.style.height = this.height + "px"; + } + else { // right + frame.style.top = ''; + frame.style.bottom = '0'; + frame.style.left = '0'; + frame.style.width = this.width + 'px'; + frame.style.height = this.height + "px"; + } + changeCalled = this._redrawLabels(); + if (this.options.icons == true) { + this._redrawGroupIcons(); + } + } + return changeCalled; + }; -/** - * Set groups - * @param {vis.DataSet | Array | google.visualization.DataTable} groups - */ -Graph2d.prototype.setGroups = function(groups) { - // convert to type DataSet when needed - var newDataSet; - if (!groups) { - newDataSet = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - newDataSet = groups; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(groups); - } + /** + * Repaint major and minor text labels and vertical grid lines + * @private + */ + DataAxis.prototype._redrawLabels = function () { + DOMutil.prepareElements(this.DOMelements); - this.groupsData = newDataSet; - this.linegraph.setGroups(newDataSet); -}; + var orientation = this.options['orientation']; -/** - * Clear the Graph2d. By Default, items, groups and options are cleared. - * Example usage: - * - * timeline.clear(); // clear items, groups, and options - * timeline.clear({options: true}); // clear options only - * - * @param {Object} [what] Optionally specify what to clear. By default: - * {items: true, groups: true, options: true} - */ -Graph2d.prototype.clear = function(what) { - // clear items - if (!what || what.items) { - this.setItems(null); - } + // calculate range and step (step such that we have space for 7 characters per label) + var minimumStep = this.master ? this.props.majorCharHeight || 10 : this.stepPixelsForced; + var step = new DataStep(this.range.start, this.range.end, minimumStep, this.dom.frame.offsetHeight); + this.step = step; + step.first(); - // clear groups - if (!what || what.groups) { - this.setGroups(null); - } + // get the distance in pixels for a step + var stepPixels = this.dom.frame.offsetHeight / ((step.marginRange / step.step) + 1); + this.stepPixels = stepPixels; - // clear options of timeline and of each of the components - if (!what || what.options) { - this.components.forEach(function (component) { - component.setOptions(component.defaultOptions); - }); + var amountOfSteps = this.height / stepPixels; + var stepDifference = 0; - this.setOptions(this.defaultOptions); // this will also do a redraw - } -}; + if (this.master == false) { + stepPixels = this.stepPixelsForced; + stepDifference = Math.round((this.height / stepPixels) - amountOfSteps); + for (var i = 0; i < 0.5 * stepDifference; i++) { + step.previous(); + } + amountOfSteps = this.height / stepPixels; + } -/** - * Set Graph2d window such that it fits all items - */ -Graph2d.prototype.fit = function() { - // apply the data range as range - var dataRange = this.getItemRange(); - - // add 5% space on both sides - var start = dataRange.min; - var end = dataRange.max; - if (start != null && end != null) { - var interval = (end.valueOf() - start.valueOf()); - if (interval <= 0) { - // prevent an empty interval - interval = 24 * 60 * 60 * 1000; // 1 day - } - start = new Date(start.valueOf() - interval * 0.05); - end = new Date(end.valueOf() + interval * 0.05); - } - // skip range set if there is no start and end date - if (start === null && end === null) { - return; - } + this.valueAtZero = step.marginEnd; + var marginStartPos = 0; + + // do not draw the first label + var max = 1; + step.next(); - this.range.setRange(start, end); -}; + this.maxLabelSize = 0; + var y = 0; + while (max < Math.round(amountOfSteps)) { -/** - * Get the data range of the item set. - * @returns {{min: Date, max: Date}} range A range with a start and end Date. - * When no minimum is found, min==null - * When no maximum is found, max==null - */ -Graph2d.prototype.getItemRange = function() { - // calculate min from start filed - var itemsData = this.itemsData, - min = null, - max = null; - - if (itemsData) { - // calculate the minimum value of the field 'start' - var minItem = itemsData.min('start'); - min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; - // Note: we convert first to Date and then to number because else - // a conversion from ISODate to Number will fail - - // calculate maximum value of fields 'start' and 'end' - var maxStartItem = itemsData.max('start'); - if (maxStartItem) { - max = util.convert(maxStartItem.start, 'Date').valueOf(); - } - var maxEndItem = itemsData.max('end'); - if (maxEndItem) { - if (max == null) { - max = util.convert(maxEndItem.end, 'Date').valueOf(); + y = Math.round(max * stepPixels); + marginStartPos = max * stepPixels; + var isMajor = step.isMajor(); + + if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) { + this._redrawLabel(y - 2, step.getCurrent(), orientation, 'yAxis minor', this.props.minorCharHeight); + } + + if (isMajor && this.options['showMajorLabels'] && this.master == true || + this.options['showMinorLabels'] == false && this.master == false && isMajor == true) { + + if (y >= 0) { + this._redrawLabel(y - 2, step.getCurrent(), orientation, 'yAxis major', this.props.majorCharHeight); + } + this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); } else { - max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); + this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); } + + step.next(); + max++; } - } - return { - min: (min != null) ? new Date(min) : null, - max: (max != null) ? new Date(max) : null + this.conversionFactor = marginStartPos/((amountOfSteps-1) * step.step); + + var offset = this.options.icons == true ? this.options.iconWidth + this.options.labelOffsetX + 15 : this.options.labelOffsetX + 15; + // this will resize the yAxis to accomodate the labels. + if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { + this.width = this.maxLabelSize + offset; + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements); + this.redraw(); + return true; + } + // this will resize the yAxis if it is too big for the labels. + else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { + this.width = Math.max(this.minWidth,this.maxLabelSize + offset); + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements); + this.redraw(); + return true; + } + else { + DOMutil.cleanupElements(this.DOMelements); + return false; + } }; -}; -/** - * Set the visible window. Both parameters are optional, you can change only - * start or only end. Syntax: - * - * TimeLine.setWindow(start, end) - * TimeLine.setWindow(range) - * - * Where start and end can be a Date, number, or string, and range is an - * object with properties start and end. - * - * @param {Date | Number | String | Object} [start] Start date of visible window - * @param {Date | Number | String} [end] End date of visible window - */ -Graph2d.prototype.setWindow = function(start, end) { - if (arguments.length == 1) { - var range = arguments[0]; - this.range.setRange(range.start, range.end); - } - else { - this.range.setRange(start, end); - } -}; + /** + * Create a label for the axis at position x + * @private + * @param y + * @param text + * @param orientation + * @param className + * @param characterHeight + */ + DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { + // reuse redundant label + var label = DOMutil.getDOMElement('div',this.DOMelements, this.dom.frame); //this.dom.redundant.labels.shift(); + label.className = className; + label.innerHTML = text; -/** - * Get the visible window - * @return {{start: Date, end: Date}} Visible range - */ -Graph2d.prototype.getWindow = function() { - var range = this.range.getRange(); - return { - start: new Date(range.start), - end: new Date(range.end) + if (orientation == 'left') { + label.style.left = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "right"; + } + else { + label.style.right = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "left"; + } + + label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; + + text += ''; + + var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); + if (this.maxLabelSize < text.length * largestWidth) { + this.maxLabelSize = text.length * largestWidth; + } }; -}; -/** - * Force a redraw of the Graph2d. Can be useful to manually redraw when - * option autoResize=false - */ -Graph2d.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.backgroundHorizontalContainer.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.backgroundHorizontalContainer.style.width = props.background.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.backgroundHorizontalContainer.style.left = '0'; - dom.backgroundHorizontalContainer.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 Graph2d or of the contents of the center changed - this._updateScrollTop(); - - // reposition the scrollable contents - var offset = this.props.scrollTop; - if (options.orientation == 'bottom') { - offset += Math.max(this.props.centerContainer.height - this.props.center.height - - this.props.border.top - this.props.border.bottom, 0); - } - dom.center.style.left = '0'; - dom.center.style.top = offset + 'px'; - dom.backgroundHorizontal.style.left = '0'; - dom.backgroundHorizontal.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 redrawing until all sizes are settled - this.redraw(); - } -}; + /** + * Create a minor line for the axis at position y + * @param y + * @param orientation + * @param className + * @param offset + * @param width + */ + DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { + if (this.master == true) { + var line = DOMutil.getDOMElement('div',this.DOMelements, this.dom.lineContainer);//this.dom.redundant.lines.shift(); + line.className = className; + line.innerHTML = ''; -/** - * Convert a position on screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x - * @private - */ -// TODO: move this function to Range -Graph2d.prototype._toTime = function(x) { - var conversion = this.range.conversion(this.props.center.width); - return new Date(x / conversion.scale + conversion.offset); -}; + if (orientation == 'left') { + line.style.left = (this.width - offset) + 'px'; + } + else { + line.style.right = (this.width - offset) + 'px'; + } -/** - * Convert a datetime (Date object) into a position on the root - * This is used to get the pixel density estimate for the screen, not the center panel - * @param {Date} time A date - * @return {int} x The position on root in pixels which corresponds - * with the given date. - * @private - */ -// TODO: move this function to Range -Graph2d.prototype._toGlobalTime = function(x) { - var conversion = this.range.conversion(this.props.root.width); - return new Date(x / conversion.scale + conversion.offset); -}; + line.style.width = width + 'px'; + line.style.top = y + 'px'; + } + }; -/** - * 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 -Graph2d.prototype._toScreen = function(time) { - var conversion = this.range.conversion(this.props.center.width); - return (time.valueOf() - conversion.offset) * conversion.scale; -}; + DataAxis.prototype.convertValue = function (value) { + var invertedValue = this.valueAtZero - value; + var convertedValue = invertedValue * this.conversionFactor; + return convertedValue; // the -2 is to compensate for the borders + }; -/** - * Convert a datetime (Date object) into a position on the root - * This is used to get the pixel density estimate for the screen, not the center panel - * @param {Date} time A date - * @return {int} x The position on root in pixels which corresponds - * with the given date. - * @private - */ -// TODO: move this function to Range -Graph2d.prototype._toGlobalScreen = function(time) { - var conversion = this.range.conversion(this.props.root.width); - return (time.valueOf() - conversion.offset) * conversion.scale; -}; -/** - * Initialize watching when option autoResize is true - * @private - */ -Graph2d.prototype._initAutoResize = function () { - if (this.options.autoResize == true) { - this._startAutoResize(); - } - else { - this._stopAutoResize(); - } -}; + /** + * Determine the size of text on the axis (both major and minor axis). + * The size is calculated only once and then cached in this.props. + * @private + */ + DataAxis.prototype._calculateCharSize = function () { + // determine the char width and height on the minor axis + if (!('minorCharHeight' in this.props)) { -/** - * Watch for changes in the size of the container. On resize, the Panel will - * automatically redraw itself. - * @private - */ -Graph2d.prototype._startAutoResize = function () { - var me = this; + var textMinor = document.createTextNode('0'); + var measureCharMinor = document.createElement('DIV'); + measureCharMinor.className = 'yAxis minor measure'; + measureCharMinor.appendChild(textMinor); + this.dom.frame.appendChild(measureCharMinor); - this._stopAutoResize(); + this.props.minorCharHeight = measureCharMinor.clientHeight; + this.props.minorCharWidth = measureCharMinor.clientWidth; - this._onResize = function() { - if (me.options.autoResize != true) { - // stop watching when the option autoResize is changed to false - me._stopAutoResize(); - return; + this.dom.frame.removeChild(measureCharMinor); } - 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; + if (!('majorCharHeight' in this.props)) { + var textMajor = document.createTextNode('0'); + var measureCharMajor = document.createElement('DIV'); + measureCharMajor.className = 'yAxis major measure'; + measureCharMajor.appendChild(textMajor); + this.dom.frame.appendChild(measureCharMajor); - me.emit('change'); - } + this.props.majorCharHeight = measureCharMajor.clientHeight; + this.props.majorCharWidth = measureCharMajor.clientWidth; + + this.dom.frame.removeChild(measureCharMajor); } }; - // add event listener to window resize - util.addEventListener(window, 'resize', this._onResize); + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + DataAxis.prototype.snap = function(date) { + return this.step.snap(date); + }; + + module.exports = DataAxis; - this.watchTimer = setInterval(this._onResize, 1000); -}; -/** - * Stop watching for a resize of the frame. - * @private - */ -Graph2d.prototype._stopAutoResize = function () { - if (this.watchTimer) { - clearInterval(this.watchTimer); - this.watchTimer = undefined; +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + + /** + * @constructor Group + * @param {Number | String} groupId + * @param {Object} data + * @param {ItemSet} itemSet + */ + function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { + this.id = groupId; + var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] + this.options = util.selectiveBridgeObject(fields,options); + this.usingDefaultStyle = group.className === undefined; + this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; + this.zeroPosition = 0; + this.update(group); + if (this.usingDefaultStyle == true) { + this.groupsUsingDefaultStyles[0] += 1; + } + this.itemsData = []; } - // remove event listener on window.resize - util.removeEventListener(window, 'resize', this._onResize); - this._onResize = null; -}; + GraphGroup.prototype.setItems = function(items) { + if (items != null) { + this.itemsData = items; + if (this.options.sort == true) { + this.itemsData.sort(function (a,b) {return a.x - b.x;}) + } + } + else { + this.itemsData = []; + } + }; -/** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ -Graph2d.prototype._onTouch = function (event) { - this.touch.allowDragging = true; -}; + GraphGroup.prototype.setZeroPosition = function(pos) { + this.zeroPosition = pos; + }; -/** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ -Graph2d.prototype._onPinch = function (event) { - this.touch.allowDragging = false; -}; + GraphGroup.prototype.setOptions = function(options) { + if (options !== undefined) { + var fields = ['sampling','style','sort','yAxisOrientation','barChart']; + util.selectiveDeepExtend(fields, this.options, options); -/** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ -Graph2d.prototype._onDragStart = function (event) { - this.touch.initialScrollTop = this.props.scrollTop; -}; + util.mergeOptions(this.options, options,'catmullRom'); + util.mergeOptions(this.options, options,'drawPoints'); + util.mergeOptions(this.options, options,'shaded'); -/** - * Move the timeline vertically - * @param {Event} event - * @private - */ -Graph2d.prototype._onDrag = function (event) { - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.touch.allowDragging) return; + if (options.catmullRom) { + if (typeof options.catmullRom == 'object') { + if (options.catmullRom.parametrization) { + if (options.catmullRom.parametrization == 'uniform') { + this.options.catmullRom.alpha = 0; + } + else if (options.catmullRom.parametrization == 'chordal') { + this.options.catmullRom.alpha = 1.0; + } + else { + this.options.catmullRom.parametrization = 'centripetal'; + this.options.catmullRom.alpha = 0.5; + } + } + } + } + } + }; - var delta = event.gesture.deltaY; + GraphGroup.prototype.update = function(group) { + this.group = group; + this.content = group.content || 'graph'; + this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10; + this.setOptions(group.options); + }; + + GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { + var fillHeight = iconHeight * 0.5; + var path, fillPath; + + var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); + outline.setAttributeNS(null, "x", x); + outline.setAttributeNS(null, "y", y - fillHeight); + outline.setAttributeNS(null, "width", iconWidth); + outline.setAttributeNS(null, "height", 2*fillHeight); + outline.setAttributeNS(null, "class", "outline"); + + if (this.options.style == 'line') { + path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + path.setAttributeNS(null, "class", this.className); + path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); + if (this.options.shaded.enabled == true) { + fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + if (this.options.shaded.orientation == 'top') { + fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + + "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); + } + else { + fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + + "L"+x+"," + (y + fillHeight) + " " + + "L"+ (x + iconWidth) + "," + (y + fillHeight) + + "L"+ (x + iconWidth) + ","+y); + } + fillPath.setAttributeNS(null, "class", this.className + " iconFill"); + } - var oldScrollTop = this._getScrollTop(); - var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); + if (this.options.drawPoints.enabled == true) { + DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); + } + } + else { + var barWidth = Math.round(0.3 * iconWidth); + var bar1Height = Math.round(0.4 * iconHeight); + var bar2Height = Math.round(0.75 * iconHeight); - if (newScrollTop != oldScrollTop) { - this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already - } -}; + var offset = Math.round((iconWidth - (2 * barWidth))/3); -/** - * Apply a scrollTop - * @param {Number} scrollTop - * @returns {Number} scrollTop Returns the applied scrollTop - * @private - */ -Graph2d.prototype._setScrollTop = function (scrollTop) { - this.props.scrollTop = scrollTop; - this._updateScrollTop(); - return this.props.scrollTop; -}; + DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); + DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); + } + }; -/** - * Update the current scrollTop when the height of the containers has been changed - * @returns {Number} scrollTop Returns the applied scrollTop - * @private - */ -Graph2d.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; - } + module.exports = GraphGroup; - // 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; -}; +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { -/** - * Get the current scrollTop - * @returns {number} scrollTop - * @private - */ -Graph2d.prototype._getScrollTop = function () { - return this.props.scrollTop; -}; + var util = __webpack_require__(1); + var stack = __webpack_require__(10); + var ItemRange = __webpack_require__(25); -(function(exports) { /** - * Parse a text source containing data in DOT language into a JSON object. - * The object contains two lists: one with nodes and one with edges. - * - * DOT language reference: http://www.graphviz.org/doc/info/lang.html - * - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graph An object containing two parameters: - * {Object[]} nodes - * {Object[]} edges + * @constructor Group + * @param {Number | String} groupId + * @param {Object} data + * @param {ItemSet} itemSet */ - function parseDOT (data) { - dot = data; - return parseGraph(); - } + function Group (groupId, data, itemSet) { + this.groupId = groupId; - // token types enumeration - var TOKENTYPE = { - NULL : 0, - DELIMITER : 1, - IDENTIFIER: 2, - UNKNOWN : 3 - }; + this.itemSet = itemSet; - // map with all delimiters - var DELIMITERS = { - '{': true, - '}': true, - '[': true, - ']': true, - ';': true, - '=': true, - ',': true, + this.dom = {}; + this.props = { + label: { + width: 0, + height: 0 + } + }; + this.className = null; - '->': true, - '--': true - }; + this.items = {}; // items filtered by groupId of this group + this.visibleItems = []; // items currently visible in window + this.orderedItems = { // items sorted by start and by end + byStart: [], + byEnd: [] + }; - var dot = ''; // current dot file - var index = 0; // current index in dot file - var c = ''; // current token character in expr - var token = ''; // current token - var tokenType = TOKENTYPE.NULL; // type of the token + this._create(); - /** - * Get the first character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function first() { - index = 0; - c = dot.charAt(0); + this.setData(data); } /** - * Get the next character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. + * Create DOM elements for the group + * @private */ - function next() { - index++; - c = dot.charAt(index); - } + Group.prototype._create = function() { + var label = document.createElement('div'); + label.className = 'vlabel'; + this.dom.label = label; + + var inner = document.createElement('div'); + inner.className = 'inner'; + label.appendChild(inner); + this.dom.inner = inner; + + var foreground = document.createElement('div'); + foreground.className = 'group'; + foreground['timeline-group'] = this; + this.dom.foreground = foreground; + + this.dom.background = document.createElement('div'); + this.dom.background.className = 'group'; + + this.dom.axis = document.createElement('div'); + this.dom.axis.className = 'group'; + + // create a hidden marker to detect when the Timelines container is attached + // to the DOM, or the style of a parent of the Timeline is changed from + // display:none is changed to visible. + this.dom.marker = document.createElement('div'); + this.dom.marker.style.visibility = 'hidden'; + this.dom.marker.innerHTML = '?'; + this.dom.background.appendChild(this.dom.marker); + }; /** - * Preview the next character from the dot file. - * @return {String} cNext + * Set the group data for this group + * @param {Object} data Group data, can contain properties content and className */ - function nextPreview() { - return dot.charAt(index + 1); - } + Group.prototype.setData = function(data) { + // update contents + var content = data && data.content; + if (content instanceof Element) { + this.dom.inner.appendChild(content); + } + else if (content != undefined) { + this.dom.inner.innerHTML = content; + } + else { + this.dom.inner.innerHTML = this.groupId; + } + + // update title + this.dom.label.title = data && data.title || ''; + + if (!this.dom.inner.firstChild) { + util.addClassName(this.dom.inner, 'hidden'); + } + else { + util.removeClassName(this.dom.inner, 'hidden'); + } + + // update className + var className = data && data.className || null; + if (className != this.className) { + if (this.className) { + util.removeClassName(this.dom.label, 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); + } + }; /** - * Test whether given character is alphabetic or numeric - * @param {String} c - * @return {Boolean} isAlphaNumeric + * Get the width of the group label + * @return {number} width */ - var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; - function isAlphaNumeric(c) { - return regexAlphaNumeric.test(c); - } + Group.prototype.getLabelWidth = function() { + return this.props.label.width; + }; + /** - * Merge all properties of object b into object b - * @param {Object} a - * @param {Object} b - * @return {Object} a + * Repaint this group + * @param {{start: number, end: number}} range + * @param {{item: number, axis: number}} margin + * @param {boolean} [restack=false] Force restacking of all items + * @return {boolean} Returns true if the group is resized */ - function merge (a, b) { - if (!a) { - a = {}; + Group.prototype.redraw = function(range, margin, restack) { + var resized = false; + + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + + // force recalculation of the height of the items when the marker height changed + // (due to the Timeline being attached to the DOM or changed from display:none to visible) + var markerHeight = this.dom.marker.clientHeight; + if (markerHeight != this.lastMarkerHeight) { + this.lastMarkerHeight = markerHeight; + + util.forEach(this.items, function (item) { + item.dirty = true; + if (item.displayed) item.redraw(); + }); + + restack = true; } - if (b) { - for (var name in b) { - if (b.hasOwnProperty(name)) { - a[name] = b[name]; - } + // reposition visible items vertically + if (this.itemSet.options.stack) { // TODO: ugly way to access options... + stack.stack(this.visibleItems, margin, restack); + } + else { // no stacking + stack.nostack(this.visibleItems, margin); + } + + // recalculate the height of the group + var height; + var visibleItems = this.visibleItems; + if (visibleItems.length) { + var min = visibleItems[0].top; + var max = visibleItems[0].top + visibleItems[0].height; + util.forEach(visibleItems, function (item) { + min = Math.min(min, item.top); + max = Math.max(max, (item.top + item.height)); + }); + if (min > margin.axis) { + // there is an empty gap between the lowest item and the axis + var offset = min - margin.axis; + max -= offset; + util.forEach(visibleItems, function (item) { + item.top -= offset; + }); } + height = max + margin.item / 2; } - return a; - } + else { + height = margin.axis + margin.item; + } + height = Math.max(height, this.props.label.height); + + // calculate actual size and position + var foreground = this.dom.foreground; + this.top = foreground.offsetTop; + this.left = foreground.offsetLeft; + this.width = foreground.offsetWidth; + resized = util.updateProperty(this, 'height', height) || resized; + + // recalculate size of label + resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; + resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; + + // apply new height + this.dom.background.style.height = height + 'px'; + this.dom.foreground.style.height = height + 'px'; + this.dom.label.style.height = height + 'px'; + + // update vertical position of items after they are re-stacked and the height of the group is calculated + for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { + var item = this.visibleItems[i]; + item.repositionY(); + } + + return resized; + }; /** - * Set a value in an object, where the provided parameter name can be a - * path with nested parameters. For example: - * - * var obj = {a: 2}; - * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} - * - * @param {Object} obj - * @param {String} path A parameter name or dot-separated parameter path, - * like "color.highlight.border". - * @param {*} value + * Show this group: attach to the DOM */ - function setValue(obj, path, value) { - var keys = path.split('.'); - var o = obj; - while (keys.length) { - var key = keys.shift(); - if (keys.length) { - // this isn't the end point - if (!o[key]) { - o[key] = {}; - } - o = o[key]; - } - else { - // this is the end point - o[key] = value; - } + Group.prototype.show = function() { + if (!this.dom.label.parentNode) { + this.itemSet.dom.labelSet.appendChild(this.dom.label); } - } - /** - * Add a node to a graph object. If there is already a node with - * the same id, their attributes will be merged. - * @param {Object} graph - * @param {Object} node - */ - function addNode(graph, node) { - var i, len; - var current = null; + if (!this.dom.foreground.parentNode) { + this.itemSet.dom.foreground.appendChild(this.dom.foreground); + } - // find root graph (in case of subgraph) - var graphs = [graph]; // list with all graphs from current graph to root graph - var root = graph; - while (root.parent) { - graphs.push(root.parent); - root = root.parent; + if (!this.dom.background.parentNode) { + this.itemSet.dom.background.appendChild(this.dom.background); } - // find existing node (at root level) by its id - if (root.nodes) { - for (i = 0, len = root.nodes.length; i < len; i++) { - if (node.id === root.nodes[i].id) { - current = root.nodes[i]; - break; - } - } + if (!this.dom.axis.parentNode) { + this.itemSet.dom.axis.appendChild(this.dom.axis); } + }; - if (!current) { - // this is a new node - current = { - id: node.id - }; - if (graph.node) { - // clone default attributes - current.attr = merge(current.attr, graph.node); - } + /** + * Hide this group: remove from the DOM + */ + Group.prototype.hide = function() { + var label = this.dom.label; + if (label.parentNode) { + label.parentNode.removeChild(label); } - // add node to this (sub)graph and all its parent graphs - for (i = graphs.length - 1; i >= 0; i--) { - var g = graphs[i]; + var foreground = this.dom.foreground; + if (foreground.parentNode) { + foreground.parentNode.removeChild(foreground); + } - if (!g.nodes) { - g.nodes = []; - } - if (g.nodes.indexOf(current) == -1) { - g.nodes.push(current); - } + var background = this.dom.background; + if (background.parentNode) { + background.parentNode.removeChild(background); } - // merge attributes - if (node.attr) { - current.attr = merge(current.attr, node.attr); + var axis = this.dom.axis; + if (axis.parentNode) { + axis.parentNode.removeChild(axis); } - } + }; /** - * Add an edge to a graph object - * @param {Object} graph - * @param {Object} edge + * Add an item to the group + * @param {Item} item */ - function addEdge(graph, edge) { - if (!graph.edges) { - graph.edges = []; - } - graph.edges.push(edge); - if (graph.edge) { - var attr = merge({}, graph.edge); // clone default attributes - edge.attr = merge(attr, edge.attr); // merge attributes + 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.body.range; // TODO: not nice accessing the range like this + this._checkIfVisible(item, this.visibleItems, range); } - } + }; /** - * Create an edge to a graph object - * @param {Object} graph - * @param {String | Number | Object} from - * @param {String | Number | Object} to - * @param {String} type - * @param {Object | null} attr - * @return {Object} edge + * Remove an item from the group + * @param {Item} item */ - function createEdge(graph, from, to, type, attr) { - var edge = { - from: from, - to: to, - type: type - }; + Group.prototype.remove = function(item) { + delete this.items[item.id]; + item.setParent(this.itemSet); - if (graph.edge) { - edge.attr = merge({}, graph.edge); // clone default attributes - } - edge.attr = merge(edge.attr || {}, attr); // merge attributes + // remove from visible items + var index = this.visibleItems.indexOf(item); + if (index != -1) this.visibleItems.splice(index, 1); - return edge; - } + // TODO: also remove from ordered items? + }; /** - * Get next token in the current dot file. - * The token and token type are available as token and tokenType + * Remove an item from the corresponding DataSet + * @param {Item} item */ - function getToken() { - tokenType = TOKENTYPE.NULL; - token = ''; + Group.prototype.removeFromDataSet = function(item) { + this.itemSet.removeItem(item.id); + }; - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } + /** + * Reorder the items + */ + Group.prototype.order = function() { + var array = util.toArray(this.items); + this.orderedItems.byStart = array; + this.orderedItems.byEnd = this._constructByEndArray(array); - do { - var isComment = false; + stack.orderByStart(this.orderedItems.byStart); + stack.orderByEnd(this.orderedItems.byEnd); + }; - // skip comment - if (c == '#') { - // find the previous non-space character - var i = index - 1; - while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { - i--; - } - if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { - // the # is at the start of a line, this is indeed a line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } - } - if (c == '/' && nextPreview() == '/') { - // skip line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } - if (c == '/' && nextPreview() == '*') { - // skip block comment - while (c != '') { - if (c == '*' && nextPreview() == '/') { - // end of block comment found. skip these last two characters - next(); - next(); - break; - } - else { - next(); - } - } - isComment = true; - } + /** + * Create an array containing all items being a range (having an end date) + * @param {Item[]} array + * @returns {ItemRange[]} + * @private + */ + Group.prototype._constructByEndArray = function(array) { + var endArray = []; - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); + for (var i = 0; i < array.length; i++) { + if (array[i] instanceof ItemRange) { + endArray.push(array[i]); } } - while (isComment); + return endArray; + }; - // check for end of dot file - if (c == '') { - // token is still empty - tokenType = TOKENTYPE.DELIMITER; - return; - } + /** + * Update the visible items + * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date + * @param {Item[]} visibleItems The previously visible items. + * @param {{start: number, end: number}} range Visible range + * @return {Item[]} visibleItems The new visible items. + * @private + */ + Group.prototype._updateVisibleItems = function(orderedItems, visibleItems, range) { + var initialPosByStart, + newVisibleItems = [], + i; - // check for delimiters consisting of 2 characters - var c2 = c + nextPreview(); - if (DELIMITERS[c2]) { - tokenType = TOKENTYPE.DELIMITER; - token = c2; - next(); - next(); - return; + // first check if the items that were in view previously are still in view. + // this handles the case for the ItemRange that is both before and after the current one. + if (visibleItems.length > 0) { + for (i = 0; i < visibleItems.length; i++) { + this._checkIfVisible(visibleItems[i], newVisibleItems, range); + } } - // check for delimiters consisting of 1 character - if (DELIMITERS[c]) { - tokenType = TOKENTYPE.DELIMITER; - token = c; - next(); - return; + // If there were no visible items previously, use binarySearch to find a visible ItemPoint or ItemRange (based on startTime) + if (newVisibleItems.length == 0) { + initialPosByStart = util.binarySearch(orderedItems.byStart, range, 'data','start'); + } + else { + initialPosByStart = orderedItems.byStart.indexOf(newVisibleItems[0]); } - // check for an identifier (number or string) - // TODO: more precise parsing of numbers/strings (and the port separator ':') - if (isAlphaNumeric(c) || c == '-') { - token += c; - next(); + // use visible search to find a visible ItemRange (only based on endTime) + var initialPosByEnd = util.binarySearch(orderedItems.byEnd, range, 'data','end'); - while (isAlphaNumeric(c)) { - token += c; - next(); - } - if (token == 'false') { - token = false; // convert to boolean + // if we found a initial ID to use, trace it up and down until we meet an invisible item. + if (initialPosByStart != -1) { + for (i = initialPosByStart; i >= 0; i--) { + if (this._checkIfInvisible(orderedItems.byStart[i], newVisibleItems, range)) {break;} } - else if (token == 'true') { - token = true; // convert to boolean - } - else if (!isNaN(Number(token))) { - token = Number(token); // convert to number + for (i = initialPosByStart + 1; i < orderedItems.byStart.length; i++) { + if (this._checkIfInvisible(orderedItems.byStart[i], newVisibleItems, range)) {break;} } - tokenType = TOKENTYPE.IDENTIFIER; - return; } - // check for a string enclosed by double quotes - if (c == '"') { - next(); - while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { - token += c; - if (c == '"') { // skip the escape character - next(); - } - next(); + // if we found a initial ID to use, trace it up and down until we meet an invisible item. + if (initialPosByEnd != -1) { + for (i = initialPosByEnd; i >= 0; i--) { + if (this._checkIfInvisible(orderedItems.byEnd[i], newVisibleItems, range)) {break;} } - if (c != '"') { - throw newSyntaxError('End of string " expected'); + for (i = initialPosByEnd + 1; i < orderedItems.byEnd.length; i++) { + if (this._checkIfInvisible(orderedItems.byEnd[i], newVisibleItems, range)) {break;} } - next(); - tokenType = TOKENTYPE.IDENTIFIER; - return; } - // something unknown is found, wrong characters, a syntax error - tokenType = TOKENTYPE.UNKNOWN; - while (c != '') { - token += c; - next(); + return newVisibleItems; + }; + + + + /** + * this function checks if an item is invisible. If it is NOT we make it visible + * and add it to the global visible items. If it is, return true. + * + * @param {Item} item + * @param {Item[]} visibleItems + * @param {{start:number, end:number}} range + * @returns {boolean} + * @private + */ + Group.prototype._checkIfInvisible = function(item, visibleItems, range) { + if (item.isVisible(range)) { + if (!item.displayed) item.show(); + item.repositionX(); + if (visibleItems.indexOf(item) == -1) { + visibleItems.push(item); + } + return false; } - throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); - } + else { + return true; + } + }; /** - * Parse a graph. - * @returns {Object} graph + * this function is very similar to the _checkIfInvisible() but it does not + * return booleans, hides the item if it should not be seen and always adds to + * the visibleItems. + * this one is for brute forcing and hiding. + * + * @param {Item} item + * @param {Array} visibleItems + * @param {{start:number, end:number}} range + * @private */ - function parseGraph() { - var graph = {}; + Group.prototype._checkIfVisible = function(item, visibleItems, range) { + if (item.isVisible(range)) { + if (!item.displayed) item.show(); + // reposition item horizontally + item.repositionX(); + visibleItems.push(item); + } + else { + if (item.displayed) item.hide(); + } + }; - first(); - getToken(); + module.exports = Group; - // optional strict keyword - if (token == 'strict') { - graph.strict = true; - getToken(); - } - // graph or digraph keyword - if (token == 'graph' || token == 'digraph') { - graph.type = token; - getToken(); - } +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - graph.id = token; - getToken(); - } + var Hammer = __webpack_require__(49); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Component = __webpack_require__(12); + var Group = __webpack_require__(17); + var ItemBox = __webpack_require__(23); + var ItemPoint = __webpack_require__(24); + var ItemRange = __webpack_require__(25); - // open angle bracket - if (token != '{') { - throw newSyntaxError('Angle bracket { expected'); - } - getToken(); - // statements - parseStatements(graph); + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); - } - getToken(); + /** + * An ItemSet holds a set of items and ranges which can be displayed in a + * range. The width is determined by the parent of the ItemSet, and the height + * is determined by the size of the items. + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body + * @param {Object} [options] See ItemSet.setOptions for the available options. + * @constructor ItemSet + * @extends Component + */ + function ItemSet(body, options) { + this.body = body; + + this.defaultOptions = { + type: null, // 'box', 'point', 'range' + 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 + }, - // end of file - if (token !== '') { - throw newSyntaxError('End of file expected'); - } - getToken(); + onAdd: function (item, callback) { + callback(item); + }, + onUpdate: function (item, callback) { + callback(item); + }, + onMove: function (item, callback) { + callback(item); + }, + onRemove: function (item, callback) { + callback(item); + }, - // remove temporary default properties - delete graph.node; - delete graph.edge; - delete graph.graph; + margin: { + item: 10, + axis: 20 + }, + padding: 5 + }; - return graph; - } + // options is shared by this ItemSet and all its items + this.options = util.extend({}, this.defaultOptions); - /** - * Parse a list with statements. - * @param {Object} graph - */ - function parseStatements (graph) { - while (token !== '' && token != '}') { - parseStatement(graph); - if (token == ';') { - getToken(); + // options for getting items from the DataSet with the correct type + this.itemOptions = { + type: {start: 'Date', end: 'Date'} + }; + + this.conversion = { + toScreen: body.util.toScreen, + toTime: body.util.toTime + }; + this.dom = {}; + this.props = {}; + this.hammer = null; + + var me = this; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet + + // listeners for the DataSet of the items + this.itemListeners = { + 'add': function (event, params, senderId) { + me._onAdd(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdate(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemove(params.items); } - } - } + }; - /** - * Parse a single statement. Can be a an attribute statement, node - * statement, a series of node statements and edge statements, or a - * parameter. - * @param {Object} graph - */ - function parseStatement(graph) { - // parse subgraph - var subgraph = parseSubgraph(graph); - if (subgraph) { - // edge statements - parseEdge(graph, subgraph); + // listeners for the DataSet of the groups + this.groupListeners = { + 'add': function (event, params, senderId) { + me._onAddGroups(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdateGroups(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemoveGroups(params.items); + } + }; - return; - } + this.items = {}; // object with an Item for every data item + this.groups = {}; // Group object for every group + this.groupIds = []; - // parse an attribute statement - var attr = parseAttributeStatement(graph); - if (attr) { - return; - } + this.selection = []; // list with the ids of all selected nodes + this.stackDirty = true; // if true, all items will be restacked on next redraw - // parse node - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - var id = token; // id can be a string or a number - getToken(); + this.touchParams = {}; // stores properties while dragging + // create the HTML DOM - if (token == '=') { - // id statement - getToken(); - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - graph[id] = token; - getToken(); - // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " - } - else { - parseNodeStatement(graph, id); - } + this._create(); + + this.setOptions(options); } + ItemSet.prototype = new Component(); + + // available item types will be registered here + ItemSet.types = { + box: ItemBox, + range: ItemRange, + point: ItemPoint + }; + /** - * Parse a subgraph - * @param {Object} graph parent graph object - * @return {Object | null} subgraph + * Create the HTML DOM for the ItemSet */ - function parseSubgraph (graph) { - var subgraph = null; + ItemSet.prototype._create = function(){ + var frame = document.createElement('div'); + frame.className = 'itemset'; + frame['timeline-itemset'] = this; + this.dom.frame = frame; + + // create background panel + var background = document.createElement('div'); + background.className = 'background'; + frame.appendChild(background); + this.dom.background = background; + + // create foreground panel + var foreground = document.createElement('div'); + foreground.className = 'foreground'; + frame.appendChild(foreground); + this.dom.foreground = foreground; + + // create axis panel + var axis = document.createElement('div'); + axis.className = 'axis'; + this.dom.axis = axis; + + // create labelset + var labelSet = document.createElement('div'); + labelSet.className = 'labelset'; + this.dom.labelSet = labelSet; + + // create ungrouped Group + this._updateUngrouped(); - // optional subgraph keyword - if (token == 'subgraph') { - subgraph = {}; - subgraph.type = 'subgraph'; - getToken(); + // attach event listeners + // Note: we bind to the centerContainer for the case where the height + // of the center container is larger than of the ItemSet, so we + // can click in the empty area to create a new item or deselect an item. + this.hammer = Hammer(this.body.dom.centerContainer, { + prevent_default: true + }); - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - subgraph.id = token; - getToken(); - } - } + // 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)); - // open angle bracket - if (token == '{') { - getToken(); + // single select (or unselect) when tapping an item + this.hammer.on('tap', this._onSelectItem.bind(this)); - if (!subgraph) { - subgraph = {}; - } - subgraph.parent = graph; - subgraph.node = graph.node; - subgraph.edge = graph.edge; - subgraph.graph = graph.graph; + // multi select when holding mouse/touch, or on ctrl+click + this.hammer.on('hold', this._onMultiSelectItem.bind(this)); - // statements - parseStatements(subgraph); + // add item on doubletap + this.hammer.on('doubletap', this._onAddItem.bind(this)); - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); - } - getToken(); + // attach to the DOM + this.show(); + }; - // remove temporary default properties - delete subgraph.node; - delete subgraph.edge; - delete subgraph.graph; - delete subgraph.parent; + /** + * Set options for the ItemSet. Existing options will be extended/overwritten. + * @param {Object} [options] The following options are available: + * {String} type + * Default type for the items. Choose from 'box' + * (default), 'point', or 'range'. The default + * Style can be overwritten by individual items. + * {String} align + * Alignment for the items, only applicable for + * ItemBox. Choose 'center' (default), 'left', or + * 'right'. + * {String} orientation + * Orientation of the item set. Choose 'top' or + * 'bottom' (default). + * {Function} groupOrder + * A sorting function for ordering groups + * {Boolean} stack + * If true (deafult), items will be stacked on + * top of each other. + * {Number} margin.axis + * Margin between the axis and the items in pixels. + * Default is 20. + * {Number} margin.item + * 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. + * {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); - // register at the parent graph - if (!graph.subgraphs) { - graph.subgraphs = []; + 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); + } } - graph.subgraphs.push(subgraph); + + 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(); } + }; - return subgraph; - } + /** + * Mark the ItemSet dirty so it will refresh everything with next redraw + */ + ItemSet.prototype.markDirty = function() { + this.groupIds = []; + this.stackDirty = true; + }; /** - * parse an attribute statement like "node [shape=circle fontSize=16]". - * Available keywords are 'node', 'edge', 'graph'. - * The previous list with default attributes will be replaced - * @param {Object} graph - * @returns {String | null} keyword Returns the name of the parsed attribute - * (node, edge, graph), or null if nothing - * is parsed. + * Destroy the ItemSet */ - function parseAttributeStatement (graph) { - // attribute statements - if (token == 'node') { - getToken(); + ItemSet.prototype.destroy = function() { + this.hide(); + this.setItems(null); + this.setGroups(null); - // node attributes - graph.node = parseAttributeList(); - return 'node'; - } - else if (token == 'edge') { - getToken(); + this.hammer = null; - // edge attributes - graph.edge = parseAttributeList(); - return 'edge'; + this.body = null; + this.conversion = null; + }; + + /** + * Hide the component from the DOM + */ + ItemSet.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); } - else if (token == 'graph') { - getToken(); - // graph attributes - graph.graph = parseAttributeList(); - return 'graph'; + // remove the axis with dots + if (this.dom.axis.parentNode) { + this.dom.axis.parentNode.removeChild(this.dom.axis); } - return null; - } + // remove the labelset containing all group labels + if (this.dom.labelSet.parentNode) { + this.dom.labelSet.parentNode.removeChild(this.dom.labelSet); + } + }; /** - * parse a node statement - * @param {Object} graph - * @param {String | Number} id + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed */ - function parseNodeStatement(graph, id) { - // node statement - var node = { - id: id - }; - var attr = parseAttributeList(); - if (attr) { - node.attr = attr; + ItemSet.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); } - addNode(graph, node); - // edge statements - parseEdge(graph, id); - } + // 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.body.dom.left.appendChild(this.dom.labelSet); + } + }; /** - * Parse an edge or a series of edges - * @param {Object} graph - * @param {String | Number} from Id of the from node + * Set selected items by their id. Replaces the current selection + * Unknown id's are silently ignored. + * @param {Array} [ids] An array with zero or more id's of the items to be + * selected. If ids is an empty array, all items will be + * unselected. */ - function parseEdge(graph, from) { - while (token == '->' || token == '--') { - var to; - var type = token; - getToken(); + ItemSet.prototype.setSelection = function(ids) { + var i, ii, id, item; - var subgraph = parseSubgraph(graph); - if (subgraph) { - to = subgraph; + if (ids) { + if (!Array.isArray(ids)) { + throw new TypeError('Array expected'); } - else { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier or subgraph expected'); - } - to = token; - addNode(graph, { - id: to - }); - getToken(); + + // unselect currently selected items + for (i = 0, ii = this.selection.length; i < ii; i++) { + id = this.selection[i]; + item = this.items[id]; + if (item) item.unselect(); } - // parse edge attributes - var attr = parseAttributeList(); + // select items + this.selection = []; + for (i = 0, ii = ids.length; i < ii; i++) { + id = ids[i]; + item = this.items[id]; + if (item) { + this.selection.push(id); + item.select(); + } + } + } + }; - // create edge - var edge = createEdge(graph, from, to, type, attr); - addEdge(graph, edge); + /** + * Get the selected items by their id + * @return {Array} ids The ids of the selected items + */ + ItemSet.prototype.getSelection = function() { + return this.selection.concat([]); + }; - from = to; + /** + * Deselect a selected item + * @param {String | Number} id + * @private + */ + ItemSet.prototype._deselect = function(id) { + var selection = this.selection; + for (var i = 0, ii = selection.length; i < ii; i++) { + if (selection[i] == id) { // non-strict comparison! + selection.splice(i, 1); + break; + } } - } + }; /** - * Parse a set with attributes, - * for example [label="1.000", shape=solid] - * @return {Object | null} attr + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - function parseAttributeList() { - var attr = null; - - while (token == '[') { - getToken(); - attr = {}; - while (token !== '' && token != ']') { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute name expected'); - } - var name = token; + ItemSet.prototype.redraw = function() { + var margin = this.options.margin, + range = this.body.range, + asSize = util.option.asSize, + options = this.options, + orientation = options.orientation, + resized = false, + frame = this.dom.frame, + editable = options.editable.updateTime || options.editable.updateGroup; + + // 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 = range.end - range.start; + var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth); + if (zoomed) this.stackDirty = true; + this.lastVisibleInterval = visibleInterval; + this.props.lastWidth = this.props.width; + + // redraw all groups + var restack = this.stackDirty, + firstGroup = this._firstGroup(), + firstMargin = { + item: margin.item, + axis: margin.axis + }, + nonFirstMargin = { + item: margin.item, + axis: margin.item / 2 + }, + height = 0, + minHeight = margin.axis + margin.item; + util.forEach(this.groups, function (group) { + var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin; + var groupResized = group.redraw(range, groupMargin, restack); + resized = groupResized || resized; + height += group.height; + }); + height = Math.max(height, minHeight); + this.stackDirty = false; - getToken(); - if (token != '=') { - throw newSyntaxError('Equal sign = expected'); - } - getToken(); + // update frame height + frame.style.height = asSize(height); - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute value expected'); - } - var value = token; - setValue(attr, name, value); // name can be a path + // calculate actual size and position + this.props.top = frame.offsetTop; + this.props.left = frame.offsetLeft; + this.props.width = frame.offsetWidth; + this.props.height = height; - getToken(); - if (token ==',') { - getToken(); - } - } + // reposition axis + this.dom.axis.style.top = asSize((orientation == 'top') ? + (this.body.domProps.top.height + this.body.domProps.border.top) : + (this.body.domProps.top.height + this.body.domProps.centerContainer.height)); + this.dom.axis.style.left = this.body.domProps.border.left + 'px'; - if (token != ']') { - throw newSyntaxError('Bracket ] expected'); - } - getToken(); - } + // check if this component is resized + resized = this._isResized() || resized; - return attr; - } + return resized; + }; /** - * Create a syntax error with extra information on current token and index. - * @param {String} message - * @returns {SyntaxError} err + * Get the first group, aligned with the axis + * @return {Group | null} firstGroup + * @private */ - function newSyntaxError(message) { - return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); - } + 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]; - /** - * Chop off text after a maximum length - * @param {String} text - * @param {Number} maxLength - * @returns {String} - */ - function chop (text, maxLength) { - return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); - } + return firstGroup || null; + }; /** - * Execute a function fn for each pair of elements in two arrays - * @param {Array | *} array1 - * @param {Array | *} array2 - * @param {function} fn + * Create or delete the group holding all ungrouped items. This group is used when + * there are no groups specified. + * @protected */ - function forEach2(array1, array2, fn) { - if (array1 instanceof Array) { - array1.forEach(function (elem1) { - if (array2 instanceof Array) { - array2.forEach(function (elem2) { - fn(elem1, elem2); - }); - } - else { - fn(elem1, array2); - } - }); + ItemSet.prototype._updateUngrouped = function() { + var ungrouped = this.groups[UNGROUPED]; + + if (this.groupsData) { + // remove the group holding all ungrouped items + if (ungrouped) { + ungrouped.hide(); + delete this.groups[UNGROUPED]; + } } else { - if (array2 instanceof Array) { - array2.forEach(function (elem2) { - fn(array1, elem2); - }); - } - else { - fn(array1, array2); + // create a group holding all (unfiltered) items + if (!ungrouped) { + var id = null; + var data = null; + ungrouped = new Group(id, data, this); + this.groups[UNGROUPED] = ungrouped; + + for (var itemId in this.items) { + if (this.items.hasOwnProperty(itemId)) { + ungrouped.add(this.items[itemId]); + } + } + + ungrouped.show(); } } - } + }; /** - * Convert a string containing a graph in DOT language into a map containing - * with nodes and edges in the format of graph. - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graphData + * Get the element for the labelset + * @return {HTMLElement} labelSet */ - function DOTToGraph (data) { - // parse the DOT file - var dotData = parseDOT(data); - var graphData = { - nodes: [], - edges: [], - options: {} - }; - - // copy the nodes - if (dotData.nodes) { - dotData.nodes.forEach(function (dotNode) { - var graphNode = { - id: dotNode.id, - label: String(dotNode.label || dotNode.id) - }; - merge(graphNode, dotNode.attr); - if (graphNode.image) { - graphNode.shape = 'image'; - } - graphData.nodes.push(graphNode); - }); - } - - // copy the edges - if (dotData.edges) { - /** - * Convert an edge in DOT format to an edge with VisGraph format - * @param {Object} dotEdge - * @returns {Object} graphEdge - */ - function convertEdge(dotEdge) { - var graphEdge = { - from: dotEdge.from, - to: dotEdge.to - }; - merge(graphEdge, dotEdge.attr); - graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; - return graphEdge; - } - - dotData.edges.forEach(function (dotEdge) { - var from, to; - if (dotEdge.from instanceof Object) { - from = dotEdge.from.nodes; - } - else { - from = { - id: dotEdge.from - } - } - - if (dotEdge.to instanceof Object) { - to = dotEdge.to.nodes; - } - else { - to = { - id: dotEdge.to - } - } - - if (dotEdge.from instanceof Object && dotEdge.from.edges) { - dotEdge.from.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } + ItemSet.prototype.getLabelSet = function() { + return this.dom.labelSet; + }; - forEach2(from, to, function (from, to) { - var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); + /** + * Set items + * @param {vis.DataSet | null} items + */ + ItemSet.prototype.setItems = function(items) { + var me = this, + ids, + oldItemsData = this.itemsData; - if (dotEdge.to instanceof Object && dotEdge.to.edges) { - dotEdge.to.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } - }); + // replace the dataset + if (!items) { + this.itemsData = null; } - - // copy the options - if (dotData.attr) { - graphData.options = dotData.attr; + else if (items instanceof DataSet || items instanceof DataView) { + this.itemsData = items; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); } - return graphData; - } + if (oldItemsData) { + // unsubscribe from old dataset + util.forEach(this.itemListeners, function (callback, event) { + oldItemsData.off(event, callback); + }); - // exports - exports.parseDOT = parseDOT; - exports.DOTToGraph = DOTToGraph; + // remove all drawn items + ids = oldItemsData.getIds(); + this._onRemove(ids); + } -})(typeof util !== 'undefined' ? util : exports); + if (this.itemsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.itemListeners, function (callback, event) { + me.itemsData.on(event, callback, id); + }); -/** - * Canvas shapes used by Network - */ -if (typeof CanvasRenderingContext2D !== 'undefined') { + // add all new items + ids = this.itemsData.getIds(); + this._onAdd(ids); - /** - * Draw a circle shape - */ - CanvasRenderingContext2D.prototype.circle = function(x, y, r) { - this.beginPath(); - this.arc(x, y, r, 0, 2*Math.PI, false); + // update the group holding all ungrouped items + this._updateUngrouped(); + } }; /** - * Draw a square shape - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r size, width and height of the square + * Get the current items + * @returns {vis.DataSet | null} */ - CanvasRenderingContext2D.prototype.square = function(x, y, r) { - this.beginPath(); - this.rect(x - r, y - r, r * 2, r * 2); + ItemSet.prototype.getItems = function() { + return this.itemsData; }; /** - * Draw a triangle shape - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius, half the length of the sides of the triangle + * Set groups + * @param {vis.DataSet} groups */ - CanvasRenderingContext2D.prototype.triangle = function(x, y, r) { - // http://en.wikipedia.org/wiki/Equilateral_triangle - this.beginPath(); + ItemSet.prototype.setGroups = function(groups) { + var me = this, + ids; - var s = r * 2; - var s2 = s / 2; - var ir = Math.sqrt(3) / 6 * s; // radius of inner circle - var h = Math.sqrt(s * s - s2 * s2); // height + // unsubscribe from current dataset + if (this.groupsData) { + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.unsubscribe(event, callback); + }); - this.moveTo(x, y - (h - ir)); - this.lineTo(x + s2, y + ir); - this.lineTo(x - s2, y + ir); - this.lineTo(x, y - (h - ir)); - this.closePath(); - }; + // remove all drawn groups + ids = this.groupsData.getIds(); + this.groupsData = null; + this._onRemoveGroups(ids); // note: this will cause a redraw + } - /** - * Draw a triangle shape in downward orientation - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius - */ - CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) { - // http://en.wikipedia.org/wiki/Equilateral_triangle - this.beginPath(); + // replace the dataset + if (!groups) { + this.groupsData = null; + } + else if (groups instanceof DataSet || groups instanceof DataView) { + this.groupsData = groups; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); + } - var s = r * 2; - var s2 = s / 2; - var ir = Math.sqrt(3) / 6 * s; // radius of inner circle - var h = Math.sqrt(s * s - s2 * s2); // height + if (this.groupsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.on(event, callback, id); + }); - this.moveTo(x, y + (h - ir)); - this.lineTo(x + s2, y - ir); - this.lineTo(x - s2, y - ir); - this.lineTo(x, y + (h - ir)); - this.closePath(); - }; + // draw all ms + ids = this.groupsData.getIds(); + this._onAddGroups(ids); + } - /** - * Draw a star shape, a star with 5 points - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius, half the length of the sides of the triangle - */ - CanvasRenderingContext2D.prototype.star = function(x, y, r) { - // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ - this.beginPath(); + // update the group holding all ungrouped items + this._updateUngrouped(); - for (var n = 0; n < 10; n++) { - var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5; - this.lineTo( - x + radius * Math.sin(n * 2 * Math.PI / 10), - y - radius * Math.cos(n * 2 * Math.PI / 10) - ); - } + // update the order of all items in each group + this._order(); - this.closePath(); + this.body.emitter.emit('change'); }; /** - * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas + * Get the current groups + * @returns {vis.DataSet | null} groups */ - CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { - var r2d = Math.PI/180; - if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x - if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y - this.beginPath(); - this.moveTo(x+r,y); - this.lineTo(x+w-r,y); - this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false); - this.lineTo(x+w,y+h-r); - this.arc(x+w-r,y+h-r,r,0,r2d*90,false); - this.lineTo(x+r,y+h); - this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false); - this.lineTo(x,y+r); - this.arc(x+r,y+r,r,r2d*180,r2d*270,false); + ItemSet.prototype.getGroups = function() { + return this.groupsData; }; /** - * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + * Remove an item by its id + * @param {String | Number} id */ - CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) { - var kappa = .5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle + ItemSet.prototype.removeItem = function(id) { + var item = this.itemsData.get(id), + dataset = this.itemsData.getDataSet(); - this.beginPath(); - this.moveTo(x, ym); - this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + if (item) { + // confirm deletion + this.options.onRemove(item, function (item) { + if (item) { + // remove by id here, it is possible that an item has no id defined + // itself, so better not delete by the item itself + dataset.remove(id); + } + }); + } }; - - /** - * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + * Handle updated items + * @param {Number[]} ids + * @protected */ - CanvasRenderingContext2D.prototype.database = function(x, y, w, h) { - var f = 1/3; - var wEllipse = w; - var hEllipse = h * f; - - var kappa = .5522848, - ox = (wEllipse / 2) * kappa, // control point offset horizontal - oy = (hEllipse / 2) * kappa, // control point offset vertical - xe = x + wEllipse, // x-end - ye = y + hEllipse, // y-end - xm = x + wEllipse / 2, // x-middle - ym = y + hEllipse / 2, // y-middle - ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse - yeb = y + h; // y-end, bottom ellipse - - this.beginPath(); - this.moveTo(xe, ym); + ItemSet.prototype._onUpdate = function(ids) { + var me = this; - this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + ids.forEach(function (id) { + var itemData = me.itemsData.get(id, me.itemOptions), + item = me.items[id], + type = itemData.type || me.options.type || (itemData.end ? 'range' : 'box'); - this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + var constructor = ItemSet.types[type]; - this.lineTo(xe, ymb); + if (item) { + // update item + if (!constructor || !(item instanceof constructor)) { + // item type has changed, delete the item and recreate it + me._removeItem(item); + item = null; + } + else { + me._updateItem(item, itemData); + } + } - this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); - this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); - - this.lineTo(x, ym); - }; + if (!item) { + // create item + if (constructor) { + item = new constructor(itemData, me.conversion, me.options); + item.id = id; // TODO: not so nice setting id afterwards + me._addItem(item); + } + else if (type == 'rangeoverflow') { + // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day + throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + + '.vis.timeline .item.range .content {overflow: visible;}'); + } + else { + throw new TypeError('Unknown item type "' + type + '"'); + } + } + }); + this._order(); + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change'); + }; /** - * Draw an arrow point (no line) + * Handle added items + * @param {Number[]} ids + * @protected */ - CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) { - // tail - var xt = x - length * Math.cos(angle); - var yt = y - length * Math.sin(angle); + ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate; - // inner tail - // TODO: allow to customize different shapes - var xi = x - length * 0.9 * Math.cos(angle); - var yi = y - length * 0.9 * Math.sin(angle); + /** + * Handle removed items + * @param {Number[]} ids + * @protected + */ + ItemSet.prototype._onRemove = function(ids) { + var count = 0; + var me = this; + ids.forEach(function (id) { + var item = me.items[id]; + if (item) { + count++; + me._removeItem(item); + } + }); - // left - var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); - var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); + if (count) { + // update order + this._order(); + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change'); + } + }; - // right - var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); - var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); + /** + * Update the order of item in all groups + * @private + */ + ItemSet.prototype._order = function() { + // reorder the items in all groups + // TODO: optimization: only reorder groups affected by the changed items + util.forEach(this.groups, function (group) { + group.order(); + }); + }; - this.beginPath(); - this.moveTo(x, y); - this.lineTo(xl, yl); - this.lineTo(xi, yi); - this.lineTo(xr, yr); - this.closePath(); + /** + * Handle updated groups + * @param {Number[]} ids + * @private + */ + ItemSet.prototype._onUpdateGroups = function(ids) { + this._onAddGroups(ids); }; /** - * Sets up the dashedLine functionality for drawing - * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas - * @author David Jordan - * @date 2012-08-08 + * Handle changed groups + * @param {Number[]} ids + * @private */ - CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ - if (!dashArray) dashArray=[10,5]; - if (dashLength==0) dashLength = 0.001; // Hack for Safari - var dashCount = dashArray.length; - this.moveTo(x, y); - var dx = (x2-x), dy = (y2-y); - var slope = dy/dx; - var distRemaining = Math.sqrt( dx*dx + dy*dy ); - var dashIndex=0, draw=true; - while (distRemaining>=0.1){ - var dashLength = dashArray[dashIndex++%dashCount]; - if (dashLength > distRemaining) dashLength = distRemaining; - var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); - if (dx<0) xStep = -xStep; - x += xStep; - y += slope*xStep; - this[draw ? 'lineTo' : 'moveTo'](x,y); - distRemaining -= dashLength; - draw = !draw; - } - }; - - // TODO: add diamond shape -} + ItemSet.prototype._onAddGroups = function(ids) { + var me = this; -/** - * @class Node - * A node. A node can be connected to other nodes via one or multiple edges. - * @param {object} properties An object containing properties for the node. All - * properties are optional, except for the id. - * {number} id Id of the node. Required - * {string} label Text label for the node - * {number} x Horizontal position of the node - * {number} y Vertical position of the node - * {string} shape Node shape, available: - * "database", "circle", "ellipse", - * "box", "image", "text", "dot", - * "star", "triangle", "triangleDown", - * "square" - * {string} image An image url - * {string} title An title text, can be HTML - * {anytype} group A group name or number - * @param {Network.Images} imagelist A list with images. Only needed - * when the node has an image - * @param {Network.Groups} grouplist A list with groups. Needed for - * retrieving group properties - * @param {Object} constants An object with default values for - * example for the color - * - */ -function Node(properties, imagelist, grouplist, constants) { - this.selected = false; - this.hover = false; - - this.edges = []; // all edges connected to this node - this.dynamicEdges = []; - this.reroutedEdges = {}; - - this.group = constants.nodes.group; - this.fontSize = Number(constants.nodes.fontSize); - this.fontFace = constants.nodes.fontFace; - this.fontColor = constants.nodes.fontColor; - this.fontDrawThreshold = 3; - - this.color = constants.nodes.color; - - // set defaults for the properties - this.id = undefined; - this.shape = constants.nodes.shape; - this.image = constants.nodes.image; - this.x = null; - this.y = null; - this.xFixed = false; - this.yFixed = false; - this.horizontalAlignLeft = true; // these are for the navigation controls - this.verticalAlignTop = true; // these are for the navigation controls - this.radius = constants.nodes.radius; - this.baseRadiusValue = constants.nodes.radius; - this.radiusFixed = false; - this.radiusMin = constants.nodes.radiusMin; - this.radiusMax = constants.nodes.radiusMax; - this.level = -1; - this.preassignedLevel = false; - - - this.imagelist = imagelist; - this.grouplist = grouplist; - - // physics properties - this.fx = 0.0; // external force x - this.fy = 0.0; // external force y - this.vx = 0.0; // velocity x - this.vy = 0.0; // velocity y - this.minForce = constants.minForce; - this.damping = constants.physics.damping; - this.mass = 1; // kg - this.fixedData = {x:null,y:null}; - - this.setProperties(properties, constants); - - // creating the variables for clustering - this.resetCluster(); - this.dynamicEdgesLength = 0; - this.clusterSession = 0; - this.clusterSizeWidthFactor = constants.clustering.nodeScaling.width; - this.clusterSizeHeightFactor = constants.clustering.nodeScaling.height; - this.clusterSizeRadiusFactor = constants.clustering.nodeScaling.radius; - this.maxNodeSizeIncrements = constants.clustering.maxNodeSizeIncrements; - this.growthIndicator = 0; - - // variables to tell the node about the network. - this.networkScaleInv = 1; - this.networkScale = 1; - this.canvasTopLeft = {"x": -300, "y": -300}; - this.canvasBottomRight = {"x": 300, "y": 300}; - this.parentEdgeId = null; -} + ids.forEach(function (id) { + var groupData = me.groupsData.get(id); + var group = me.groups[id]; -/** - * (re)setting the clustering variables and objects - */ -Node.prototype.resetCluster = function() { - // clustering variables - this.formationScale = undefined; // this is used to determine when to open the cluster - this.clusterSize = 1; // this signifies the total amount of nodes in this cluster - this.containedNodes = {}; - this.containedEdges = {}; - this.clusterSessions = []; -}; + if (!group) { + // check for reserved ids + if (id == UNGROUPED) { + throw new Error('Illegal group id. ' + id + ' is a reserved id.'); + } -/** - * Attach a edge to the node - * @param {Edge} edge - */ -Node.prototype.attachEdge = function(edge) { - if (this.edges.indexOf(edge) == -1) { - this.edges.push(edge); - } - if (this.dynamicEdges.indexOf(edge) == -1) { - this.dynamicEdges.push(edge); - } - this.dynamicEdgesLength = this.dynamicEdges.length; -}; + var groupOptions = Object.create(me.options); + util.extend(groupOptions, { + height: null + }); -/** - * Detach a edge from the node - * @param {Edge} edge - */ -Node.prototype.detachEdge = function(edge) { - var index = this.edges.indexOf(edge); - if (index != -1) { - this.edges.splice(index, 1); - this.dynamicEdges.splice(index, 1); - } - this.dynamicEdgesLength = this.dynamicEdges.length; -}; + group = new Group(id, groupData, me); + me.groups[id] = group; + // add items with this groupId to the new group + for (var itemId in me.items) { + if (me.items.hasOwnProperty(itemId)) { + var item = me.items[itemId]; + if (item.data.group == id) { + group.add(item); + } + } + } -/** - * Set or overwrite properties for the node - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties - */ -Node.prototype.setProperties = function(properties, constants) { - if (!properties) { - return; - } - this.originalLabel = undefined; - // basic properties - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.label !== undefined) {this.label = properties.label; this.originalLabel = properties.label;} - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.group !== undefined) {this.group = properties.group;} - if (properties.x !== undefined) {this.x = properties.x;} - if (properties.y !== undefined) {this.y = properties.y;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.level !== undefined) {this.level = properties.level; this.preassignedLevel = true;} - - - // physics - if (properties.mass !== undefined) {this.mass = properties.mass;} - - // navigation controls properties - if (properties.horizontalAlignLeft !== undefined) {this.horizontalAlignLeft = properties.horizontalAlignLeft;} - if (properties.verticalAlignTop !== undefined) {this.verticalAlignTop = properties.verticalAlignTop;} - if (properties.triggerFunction !== undefined) {this.triggerFunction = properties.triggerFunction;} - - if (this.id === undefined) { - throw "Node must have an id"; - } + group.order(); + group.show(); + } + else { + // update group + group.setData(groupData); + } + }); + + this.body.emitter.emit('change'); + }; + + /** + * Handle removed groups + * @param {Number[]} ids + * @private + */ + ItemSet.prototype._onRemoveGroups = function(ids) { + var groups = this.groups; + ids.forEach(function (id) { + var group = groups[id]; - // copy group properties - if (this.group) { - var groupObj = this.grouplist.get(this.group); - for (var prop in groupObj) { - if (groupObj.hasOwnProperty(prop)) { - this[prop] = groupObj[prop]; + if (group) { + group.hide(); + delete groups[id]; } - } - } + }); + + this.markDirty(); + + this.body.emitter.emit('change'); + }; + + /** + * Reorder the groups if needed + * @return {boolean} changed + * @private + */ + ItemSet.prototype._orderGroups = function () { + if (this.groupsData) { + // reorder the groups + var groupIds = this.groupsData.getIds({ + order: this.options.groupOrder + }); + + var changed = !util.equalArray(groupIds, this.groupIds); + if (changed) { + // hide all groups, removes them from the DOM + var groups = this.groups; + groupIds.forEach(function (groupId) { + groups[groupId].hide(); + }); - // individual shape properties - if (properties.shape !== undefined) {this.shape = properties.shape;} - if (properties.image !== undefined) {this.image = properties.image;} - if (properties.radius !== undefined) {this.radius = properties.radius;} - if (properties.color !== undefined) {this.color = util.parseColor(properties.color);} + // show the groups again, attach them to the DOM in correct order + groupIds.forEach(function (groupId) { + groups[groupId].show(); + }); - if (properties.fontColor !== undefined) {this.fontColor = properties.fontColor;} - if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;} - if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;} + this.groupIds = groupIds; + } - if (this.image !== undefined && this.image != "") { - if (this.imagelist) { - this.imageObj = this.imagelist.load(this.image); + return changed; } else { - throw "No imagelist provided"; + return false; } - } + }; - this.xFixed = this.xFixed || (properties.x !== undefined && !properties.allowedToMoveX); - this.yFixed = this.yFixed || (properties.y !== undefined && !properties.allowedToMoveY); - this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); + /** + * Add a new item + * @param {Item} item + * @private + */ + ItemSet.prototype._addItem = function(item) { + this.items[item.id] = item; - if (this.shape == 'image') { - this.radiusMin = constants.nodes.widthMin; - this.radiusMax = constants.nodes.widthMax; - } + // add to group + var groupId = this.groupsData ? item.data.group : UNGROUPED; + var group = this.groups[groupId]; + if (group) group.add(item); + }; - // choose draw method depending on the shape - switch (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; - // TODO: add diamond shape - 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; break; - } - // reset the size of the node, this can be changed - this._reset(); -}; + /** + * Update an existing item + * @param {Item} item + * @param {Object} itemData + * @private + */ + ItemSet.prototype._updateItem = function(item, itemData) { + var oldGroupId = item.data.group; -/** - * select this node - */ -Node.prototype.select = function() { - this.selected = true; - this._reset(); -}; + item.data = itemData; + if (item.displayed) { + item.redraw(); + } -/** - * unselect this node - */ -Node.prototype.unselect = function() { - this.selected = false; - this._reset(); -}; + // update group + if (oldGroupId != item.data.group) { + var oldGroup = this.groups[oldGroupId]; + if (oldGroup) oldGroup.remove(item); + var groupId = this.groupsData ? item.data.group : UNGROUPED; + var group = this.groups[groupId]; + if (group) group.add(item); + } + }; -/** - * Reset the calculated size of the node, forces it to recalculate its size - */ -Node.prototype.clearSizeCache = function() { - this._reset(); -}; + /** + * Delete an item from the ItemSet: remove it from the DOM, from the map + * with items, and from the map with visible items, and from the selection + * @param {Item} item + * @private + */ + ItemSet.prototype._removeItem = function(item) { + // remove from DOM + item.hide(); -/** - * Reset the calculated size of the node, forces it to recalculate its size - * @private - */ -Node.prototype._reset = function() { - this.width = undefined; - this.height = undefined; -}; + // remove from items + delete this.items[item.id]; -/** - * get the title of this node. - * @return {string} title The title of the node, or undefined when no title - * has been set. - */ -Node.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; -}; + // remove from selection + var index = this.selection.indexOf(item.id); + if (index != -1) this.selection.splice(index, 1); -/** - * Calculate the distance to the border of the Node - * @param {CanvasRenderingContext2D} ctx - * @param {Number} angle Angle in radians - * @returns {number} distance Distance to the border in pixels - */ -Node.prototype.distanceToBorder = function (ctx, angle) { - var borderWidth = 1; + // remove from group + var groupId = this.groupsData ? item.data.group : UNGROUPED; + var group = this.groups[groupId]; + if (group) group.remove(item); + }; - if (!this.width) { - this.resize(ctx); - } + /** + * Create an array containing all items being a range (having an end date) + * @param array + * @returns {Array} + * @private + */ + ItemSet.prototype._constructByEndArray = function(array) { + var endArray = []; - switch (this.shape) { - case 'circle': - case 'dot': - return this.radius + borderWidth; - - case 'ellipse': - var a = this.width / 2; - var b = this.height / 2; - var w = (Math.sin(angle) * a); - var h = (Math.cos(angle) * b); - return a * b / Math.sqrt(w * w + h * h); - - // TODO: implement distanceToBorder for database - // TODO: implement distanceToBorder for triangle - // TODO: implement distanceToBorder for triangleDown - - case 'box': - case 'image': - case 'text': - default: - if (this.width) { - return Math.min( - Math.abs(this.width / 2 / Math.cos(angle)), - Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; - // TODO: reckon with border radius too in case of box - } - else { - return 0; + for (var i = 0; i < array.length; i++) { + if (array[i] instanceof ItemRange) { + endArray.push(array[i]); } + } + return endArray; + }; - } - // TODO: implement calculation of distance to border for all shapes -}; + /** + * Register the clicked item on touch, before dragStart is initiated. + * + * dragStart is initiated from a mousemove event, which can have left the item + * already resulting in an item == null + * + * @param {Event} event + * @private + */ + ItemSet.prototype._onTouch = function (event) { + // store the touched item, used in _onDragStart + this.touchParams.item = ItemSet.itemFromTarget(event); + }; -/** - * Set forces acting on the node - * @param {number} fx Force in horizontal direction - * @param {number} fy Force in vertical direction - */ -Node.prototype._setForce = function(fx, fy) { - this.fx = fx; - this.fy = fy; -}; + /** + * Start dragging the selected events + * @param {Event} event + * @private + */ + ItemSet.prototype._onDragStart = function (event) { + if (!this.options.editable.updateTime && !this.options.editable.updateGroup) { + return; + } -/** - * Add forces acting on the node - * @param {number} fx Force in horizontal direction - * @param {number} fy Force in vertical direction - * @private - */ -Node.prototype._addForce = function(fx, fy) { - this.fx += fx; - this.fy += fy; -}; + var item = this.touchParams.item || null, + me = this, + props; -/** - * Perform one discrete step for the node - * @param {number} interval Time interval in seconds - */ -Node.prototype.discreteStep = function(interval) { - if (!this.xFixed) { - var dx = this.damping * this.vx; // damping force - var ax = (this.fx - dx) / this.mass; // acceleration - this.vx += ax * interval; // velocity - this.x += this.vx * interval; // position - } + if (item && item.selected) { + var dragLeftItem = event.target.dragLeftItem; + var dragRightItem = event.target.dragRightItem; - if (!this.yFixed) { - var dy = this.damping * this.vy; // damping force - var ay = (this.fy - dy) / this.mass; // acceleration - this.vy += ay * interval; // velocity - this.y += this.vy * interval; // position - } -}; + if (dragLeftItem) { + props = { + item: dragLeftItem + }; + if (me.options.editable.updateTime) { + props.start = item.data.start.valueOf(); + } + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; + } + this.touchParams.itemProps = [props]; + } + else if (dragRightItem) { + props = { + item: dragRightItem + }; -/** - * Perform one discrete step for the node - * @param {number} interval Time interval in seconds - * @param {number} maxVelocity The speed limit imposed on the velocity - */ -Node.prototype.discreteStepLimited = function(interval, maxVelocity) { - if (!this.xFixed) { - var dx = this.damping * this.vx; // damping force - var ax = (this.fx - dx) / this.mass; // acceleration - this.vx += ax * interval; // velocity - this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx; - this.x += this.vx * interval; // position - } - else { - this.fx = 0; - } + if (me.options.editable.updateTime) { + props.end = item.data.end.valueOf(); + } + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; + } - if (!this.yFixed) { - var dy = this.damping * this.vy; // damping force - var ay = (this.fy - dy) / this.mass; // acceleration - this.vy += ay * interval; // velocity - this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy; - this.y += this.vy * interval; // position - } - else { - this.fy = 0; - } -}; + this.touchParams.itemProps = [props]; + } + else { + this.touchParams.itemProps = this.getSelection().map(function (id) { + var item = me.items[id]; + var props = { + item: item + }; -/** - * Check if this node has a fixed x and y position - * @return {boolean} true if fixed, false if not - */ -Node.prototype.isFixed = function() { - return (this.xFixed && this.yFixed); -}; + if (me.options.editable.updateTime) { + if ('start' in item.data) props.start = item.data.start.valueOf(); + if ('end' in item.data) props.end = item.data.end.valueOf(); + } + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; + } -/** - * Check if this node is moving - * @param {number} vmin the minimum velocity considered as "moving" - * @return {boolean} true if moving, false if it has no velocity - */ -// TODO: replace this method with calculating the kinetic energy -Node.prototype.isMoving = function(vmin) { - return (Math.abs(this.vx) > vmin || Math.abs(this.vy) > vmin); -}; + return props; + }); + } -/** - * check if this node is selecte - * @return {boolean} selected True if node is selected, else false - */ -Node.prototype.isSelected = function() { - return this.selected; -}; + event.stopPropagation(); + } + }; -/** - * Retrieve the value of the node. Can be undefined - * @return {Number} value - */ -Node.prototype.getValue = function() { - return this.value; -}; + /** + * Drag selected items + * @param {Event} event + * @private + */ + ItemSet.prototype._onDrag = function (event) { + if (this.touchParams.itemProps) { + var range = this.body.range, + snap = this.body.util.snap || null, + deltaX = event.gesture.deltaX, + scale = (this.props.width / (range.end - range.start)), + offset = deltaX / scale; + + // move + this.touchParams.itemProps.forEach(function (props) { + if ('start' in props) { + var start = new Date(props.start + offset); + props.item.data.start = snap ? snap(start) : start; + } + + if ('end' in props) { + var end = new Date(props.end + offset); + props.item.data.end = snap ? snap(end) : end; + } + + if ('group' in props) { + // drag from one group to another + var group = ItemSet.groupFromTarget(event); + if (group && group.groupId != props.item.data.group) { + var oldGroup = props.item.parent; + oldGroup.remove(props.item); + oldGroup.order(); + group.add(props.item); + group.order(); + + props.item.data.group = group.groupId; + } + } + }); -/** - * Calculate the distance from the nodes location to the given location (x,y) - * @param {Number} x - * @param {Number} y - * @return {Number} value - */ -Node.prototype.getDistance = function(x, y) { - var dx = this.x - x, - dy = this.y - y; - return Math.sqrt(dx * dx + dy * dy); -}; + // TODO: implement onMoving handler + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change'); -/** - * Adjust the value range of the node. The node will adjust it's radius - * based on its value. - * @param {Number} min - * @param {Number} max - */ -Node.prototype.setValueRange = function(min, max) { - if (!this.radiusFixed && this.value !== undefined) { - if (max == min) { - this.radius = (this.radiusMin + this.radiusMax) / 2; - } - else { - var scale = (this.radiusMax - this.radiusMin) / (max - min); - this.radius = (this.value - min) * scale + this.radiusMin; + event.stopPropagation(); } - } - this.baseRadiusValue = this.radius; -}; + }; -/** - * Draw this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - */ -Node.prototype.draw = function(ctx) { - throw "Draw method not initialized for node"; -}; + /** + * End of dragging selected items + * @param {Event} event + * @private + */ + ItemSet.prototype._onDragEnd = function (event) { + if (this.touchParams.itemProps) { + // prepare a change set for the changed items + var changes = [], + me = this, + dataset = this.itemsData.getDataSet(); + + this.touchParams.itemProps.forEach(function (props) { + var id = props.item.id, + itemData = me.itemsData.get(id, me.itemOptions); + + var changed = false; + if ('start' in props.item.data) { + changed = (props.start != props.item.data.start.valueOf()); + itemData.start = util.convert(props.item.data.start, + dataset._options.type && dataset._options.type.start || 'Date'); + } + if ('end' in props.item.data) { + changed = changed || (props.end != props.item.data.end.valueOf()); + itemData.end = util.convert(props.item.data.end, + dataset._options.type && dataset._options.type.end || 'Date'); + } + if ('group' in props.item.data) { + changed = changed || (props.group != props.item.data.group); + itemData.group = props.item.data.group; + } + + // only apply changes when start or end is actually changed + if (changed) { + me.options.onMove(itemData, function (itemData) { + if (itemData) { + // apply changes + itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined) + changes.push(itemData); + } + else { + // restore original values + if ('start' in props) props.item.data.start = props.start; + if ('end' in props) props.item.data.end = props.end; -/** - * Recalculate the size of this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - */ -Node.prototype.resize = function(ctx) { - throw "Resize method not initialized for node"; -}; + me.stackDirty = true; // force re-stacking of all items next redraw + me.body.emitter.emit('change'); + } + }); + } + }); + this.touchParams.itemProps = null; -/** - * Check if this object is overlapping with the provided object - * @param {Object} obj an object with parameters left, top, right, bottom - * @return {boolean} True if location is located on node - */ -Node.prototype.isOverlappingWith = function(obj) { - return (this.left < obj.right && - this.left + this.width > obj.left && - this.top < obj.bottom && - this.top + this.height > obj.top); -}; - -Node.prototype._resizeImage = function (ctx) { - // TODO: pre calculate the image size - - if (!this.width || !this.height) { // undefined or 0 - var width, height; - if (this.value) { - this.radius = this.baseRadiusValue; - var scale = this.imageObj.height / this.imageObj.width; - if (scale !== undefined) { - width = this.radius || this.imageObj.width; - height = this.radius * scale || this.imageObj.height; - } - else { - width = 0; - height = 0; + // apply the changes to the data (if there are changes) + if (changes.length) { + dataset.update(changes); } - } - else { - width = this.imageObj.width; - height = this.imageObj.height; - } - this.width = width; - this.height = height; - this.growthIndicator = 0; - if (this.width > 0 && this.height > 0) { - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - width; + event.stopPropagation(); } - } - -}; - -Node.prototype._drawImage = function (ctx) { - this._resizeImage(ctx); - - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + }; - var yLabel; - if (this.imageObj.width != 0 ) { - // draw the shade - if (this.clusterSize > 1) { - var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0); - lineWidth *= this.networkScaleInv; - lineWidth = Math.min(0.2 * this.width,lineWidth); + /** + * Handle selecting/deselecting an item when tapping it + * @param {Event} event + * @private + */ + ItemSet.prototype._onSelectItem = function (event) { + if (!this.options.selectable) return; - ctx.globalAlpha = 0.5; - ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth); + 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; } - // draw the image - ctx.globalAlpha = 1.0; - ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); - yLabel = this.y + this.height / 2; - } - else { - // image still loading... just draw the label for now - yLabel = this.y; - } - - this._label(ctx, this.label, this.x, yLabel, undefined, "top"); -}; - + var oldSelection = this.getSelection(); -Node.prototype._resizeBox = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; + var item = ItemSet.itemFromTarget(event); + var selection = item ? [item.id] : []; + this.setSelection(selection); - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.growthIndicator = this.width - (textSize.width + 2 * margin); -// this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + 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() + }); + } -Node.prototype._drawBox = function (ctx) { - this._resizeBox(ctx); + event.stopPropagation(); + }; - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + /** + * 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 clusterLineWidth = 2.5; - var selectionLineWidth = 2; + var me = this, + snap = this.body.util.snap || null, + item = ItemSet.itemFromTarget(event); - ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + if (item) { + // update item - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); - - ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.radius); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + // 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' + }; - ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background; + // when default type is a range, add a default end date to the new item + if (this.options.type === 'range') { + var end = this.body.util.toTime(x + this.props.width / 5); + newItem.end = snap ? snap(end) : end; + } - ctx.roundRect(this.left, this.top, this.width, this.height, this.radius); - ctx.fill(); - ctx.stroke(); + newItem[this.itemsData.fieldId] = util.randomUUID(); - this._label(ctx, this.label, this.x, this.y); -}; + 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? + } + }); + } + }; -Node.prototype._resizeDatabase = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - var size = textSize.width + 2 * margin; - this.width = size; - this.height = size; + /** + * Handle selecting/deselecting multiple items when holding an item + * @param {Event} event + * @private + */ + ItemSet.prototype._onMultiSelectItem = function (event) { + if (!this.options.selectable) return; - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - size; - } -}; + var selection, + item = ItemSet.itemFromTarget(event); -Node.prototype._drawDatabase = function (ctx) { - this._resizeDatabase(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + 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); - var clusterLineWidth = 2.5; - var selectionLineWidth = 2; + this.body.emitter.emit('select', { + items: this.getSelection() + }); - ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + event.stopPropagation(); + } + }; - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + /** + * Find an item from an event target: + * searches for the attribute 'timeline-item' in the event target's element tree + * @param {Event} event + * @return {Item | null} item + */ + ItemSet.itemFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-item')) { + return target['timeline-item']; + } + target = target.parentNode; + } - ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - 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.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height); - ctx.fill(); - ctx.stroke(); - - this._label(ctx, this.label, this.x, this.y); -}; - - -Node.prototype._resizeCircle = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; - this.radius = diameter / 2; - - this.width = diameter; - this.height = diameter; - - // scaling used for clustering -// this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; -// this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.radius - 0.5*diameter; - } -}; + return null; + }; -Node.prototype._drawCircle = function (ctx) { - this._resizeCircle(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + /** + * Find the Group from an event target: + * searches for the attribute 'timeline-group' in the event target's element tree + * @param {Event} event + * @return {Group | null} group + */ + ItemSet.groupFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-group')) { + return target['timeline-group']; + } + target = target.parentNode; + } - var clusterLineWidth = 2.5; - var selectionLineWidth = 2; + return null; + }; - ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + /** + * Find the ItemSet from an event target: + * searches for the attribute 'timeline-itemset' in the event target's element tree + * @param {Event} event + * @return {ItemSet | null} item + */ + ItemSet.itemSetFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-itemset')) { + return target['timeline-itemset']; + } + target = target.parentNode; + } - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + return null; + }; - ctx.circle(this.x, this.y, this.radius+2*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + module.exports = ItemSet; - ctx.fillStyle = this.selected ? this.color.highlight.background : this.hover ? this.color.hover.background : this.color.background; - ctx.circle(this.x, this.y, this.radius); - ctx.fill(); - ctx.stroke(); - this._label(ctx, this.label, this.x, this.y); -}; +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { -Node.prototype._resizeEllipse = function (ctx) { - if (!this.width) { - var textSize = this.getTextSize(ctx); + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var Component = __webpack_require__(12); - this.width = textSize.width * 1.5; - this.height = textSize.height * 2; - if (this.width < this.height) { - this.width = this.height; + /** + * Legend for Graph2d + */ + function Legend(body, options, side) { + this.body = body; + this.defaultOptions = { + enabled: true, + icons: true, + iconSize: 20, + iconSpacing: 6, + left: { + visible: true, + position: 'top-left' // top/bottom - left,center,right + }, + right: { + visible: true, + position: 'top-left' // top/bottom - left,center,right + } } - var defaultSize = this.width; - - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - defaultSize; - } -}; + this.side = side; + this.options = util.extend({},this.defaultOptions); -Node.prototype._drawEllipse = function (ctx) { - this._resizeEllipse(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + this.svgElements = {}; + this.dom = {}; + this.groups = {}; + this.amountOfGroups = 0; + this._create(); - var clusterLineWidth = 2.5; - var selectionLineWidth = 2; + this.setOptions(options); + } - ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + Legend.prototype = new Component(); - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); - ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - 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.ellipse(this.left, this.top, this.width, this.height); - ctx.fill(); - ctx.stroke(); - this._label(ctx, this.label, this.x, this.y); -}; - -Node.prototype._drawDot = function (ctx) { - this._drawShape(ctx, 'circle'); -}; - -Node.prototype._drawTriangle = function (ctx) { - this._drawShape(ctx, 'triangle'); -}; - -Node.prototype._drawTriangleDown = function (ctx) { - this._drawShape(ctx, 'triangleDown'); -}; - -Node.prototype._drawSquare = function (ctx) { - this._drawShape(ctx, 'square'); -}; - -Node.prototype._drawStar = function (ctx) { - this._drawShape(ctx, 'star'); -}; - -Node.prototype._resizeShape = function (ctx) { - if (!this.width) { - this.radius = this.baseRadiusValue; - var size = 2 * this.radius; - this.width = size; - this.height = size; - - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - size; - } -}; + Legend.prototype.addGroup = function(label, graphOptions) { + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; + } + this.amountOfGroups += 1; + }; -Node.prototype._drawShape = function (ctx, shape) { - this._resizeShape(ctx); + Legend.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; + }; - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + Legend.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; + } + }; - var clusterLineWidth = 2.5; - var selectionLineWidth = 2; - var radiusMultiplier = 2; + Legend.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.className = 'legend'; + this.dom.frame.style.position = "absolute"; + this.dom.frame.style.top = "10px"; + this.dom.frame.style.display = "block"; + + this.dom.textArea = document.createElement('div'); + this.dom.textArea.className = 'legendText'; + this.dom.textArea.style.position = "relative"; + this.dom.textArea.style.top = "0px"; + + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = 'absolute'; + this.svg.style.top = 0 +'px'; + this.svg.style.width = this.options.iconSize + 5 + 'px'; + + this.dom.frame.appendChild(this.svg); + this.dom.frame.appendChild(this.dom.textArea); + }; - // choose draw method depending on the shape - switch (shape) { - case 'dot': radiusMultiplier = 2; break; - case 'square': radiusMultiplier = 2; break; - case 'triangle': radiusMultiplier = 3; break; - case 'triangleDown': radiusMultiplier = 3; break; - case 'star': radiusMultiplier = 4; break; - } + /** + * Hide the component from the DOM + */ + Legend.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } + }; - ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + /** + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed + */ + Legend.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } + }; - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + Legend.prototype.setOptions = function(options) { + var fields = ['enabled','orientation','icons','left','right']; + util.selectiveDeepExtend(fields, this.options, options); + }; - ctx[shape](this.x, this.y, this.radius + radiusMultiplier * ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + Legend.prototype.redraw = function() { + if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false) { + this.hide(); + } + else { + this.show(); + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { + this.dom.frame.style.left = '4px'; + this.dom.frame.style.textAlign = "left"; + this.dom.textArea.style.textAlign = "left"; + this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.right = ''; + this.svg.style.left = 0 +'px'; + this.svg.style.right = ''; + } + else { + this.dom.frame.style.right = '4px'; + this.dom.frame.style.textAlign = "right"; + this.dom.textArea.style.textAlign = "right"; + this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.left = ''; + this.svg.style.right = 0 +'px'; + this.svg.style.left = ''; + } - 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(); + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { + this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.bottom = ''; + } + else { + this.dom.frame.style.bottom = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.top = ''; + } - if (this.label) { - this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top',true); - } -}; - -Node.prototype._resizeText = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; - - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - (textSize.width + 2 * margin); - } -}; + if (this.options.icons == false) { + this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; + this.dom.textArea.style.right = ''; + this.dom.textArea.style.left = ''; + this.svg.style.width = '0px'; + } + else { + this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' + this.drawLegendIcons(); + } -Node.prototype._drawText = function (ctx) { - this._resizeText(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + var content = ''; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + content += this.groups[groupId].content + '
'; + } + } + this.dom.textArea.innerHTML = content; + this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; + } + }; - this._label(ctx, this.label, this.x, this.y); -}; + Legend.prototype.drawLegendIcons = function() { + if (this.dom.frame.parentNode) { + DOMutil.prepareElements(this.svgElements); + var padding = window.getComputedStyle(this.dom.frame).paddingTop; + var iconOffset = Number(padding.replace('px','')); + var x = iconOffset; + var iconWidth = this.options.iconSize; + var iconHeight = 0.75 * this.options.iconSize; + var y = iconOffset + 0.5 * iconHeight + 3; + this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; -Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNode) { - if (text && this.fontSize * this.networkScale > this.fontDrawThreshold) { - ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace; - ctx.fillStyle = this.fontColor || "black"; - ctx.textAlign = align || "center"; - ctx.textBaseline = baseline || "middle"; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + this.options.iconSpacing; + } + } - var lines = text.split('\n'); - var lineCount = lines.length; - var fontSize = (this.fontSize + 4); - var yLine = y + (1 - lineCount) / 2 * fontSize; - if (labelUnderNode == true) { - yLine = y + (1 - lineCount) / (2 * fontSize); + DOMutil.cleanupElements(this.svgElements); } + }; - for (var i = 0; i < lineCount; i++) { - ctx.fillText(lines[i], x, yLine); - yLine += fontSize; - } - } -}; + module.exports = Legend; -Node.prototype.getTextSize = function(ctx) { - if (this.label !== undefined) { - ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace; +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { - var lines = this.label.split('\n'), - height = (this.fontSize + 4) * lines.length, - width = 0; + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Component = __webpack_require__(12); + var DataAxis = __webpack_require__(15); + var GraphGroup = __webpack_require__(16); + var Legend = __webpack_require__(19); - for (var i = 0, iMax = lines.length; i < iMax; i++) { - width = Math.max(width, ctx.measureText(lines[i]).width); - } + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items - return {"width": width, "height": height}; - } - else { - return {"width": 0, "height": 0}; - } -}; + /** + * This is the constructor of the LineGraph. It requires a Timeline body and options. + * + * @param body + * @param options + * @constructor + */ + function LineGraph(body, options) { + this.id = util.randomUUID(); + this.body = body; + + this.defaultOptions = { + yAxisOrientation: 'left', + defaultGroup: 'default', + sort: true, + sampling: true, + graphHeight: '400px', + shaded: { + enabled: false, + orientation: 'bottom' // top, bottom + }, + style: 'line', // line, bar + barChart: { + width: 50, + align: 'center' // left, center, right + }, + catmullRom: { + enabled: true, + parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) + alpha: 0.5 + }, + drawPoints: { + enabled: true, + size: 6, + style: 'square' // square, circle + }, + dataAxis: { + showMinorLabels: true, + showMajorLabels: true, + icons: false, + width: '40px', + visible: true + }, + legend: { + enabled: false, + icons: true, + left: { + visible: true, + position: 'top-left' // top/bottom - left,right + }, + right: { + visible: true, + position: 'top-right' // top/bottom - left,right + } + } + }; -/** - * this is used to determine if a node is visible at all. this is used to determine when it needs to be drawn. - * there is a safety margin of 0.3 * width; - * - * @returns {boolean} - */ -Node.prototype.inArea = function() { - if (this.width !== undefined) { - return (this.x + this.width *this.networkScaleInv >= this.canvasTopLeft.x && - this.x - this.width *this.networkScaleInv < this.canvasBottomRight.x && - this.y + this.height*this.networkScaleInv >= this.canvasTopLeft.y && - this.y - this.height*this.networkScaleInv < this.canvasBottomRight.y); - } - else { - return true; - } -}; + // options is shared by this ItemSet and all its items + this.options = util.extend({}, this.defaultOptions); + this.dom = {}; + this.props = {}; + this.hammer = null; + this.groups = {}; -/** - * checks if the core of the node is in the display area, this is used for opening clusters around zoom - * @returns {boolean} - */ -Node.prototype.inView = function() { - return (this.x >= this.canvasTopLeft.x && - this.x < this.canvasBottomRight.x && - this.y >= this.canvasTopLeft.y && - this.y < this.canvasBottomRight.y); -}; + var me = this; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet -/** - * This allows the zoom level of the network to influence the rendering - * We store the inverted scale and the coordinates of the top left, and bottom right points of the canvas - * - * @param scale - * @param canvasTopLeft - * @param canvasBottomRight - */ -Node.prototype.setScaleAndPos = function(scale,canvasTopLeft,canvasBottomRight) { - this.networkScaleInv = 1.0/scale; - this.networkScale = scale; - this.canvasTopLeft = canvasTopLeft; - this.canvasBottomRight = canvasBottomRight; -}; + // listeners for the DataSet of the items + this.itemListeners = { + 'add': function (event, params, senderId) { + me._onAdd(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdate(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemove(params.items); + } + }; + // listeners for the DataSet of the groups + this.groupListeners = { + 'add': function (event, params, senderId) { + me._onAddGroups(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdateGroups(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemoveGroups(params.items); + } + }; -/** - * This allows the zoom level of the network to influence the rendering - * - * @param scale - */ -Node.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; - this.networkScale = scale; -}; + this.items = {}; // object with an Item for every data item + this.selection = []; // list with the ids of all selected nodes + this.lastStart = this.body.range.start; + this.touchParams = {}; // stores properties while dragging + this.svgElements = {}; + this.setOptions(options); + this.groupsUsingDefaultStyles = [0]; + + this.body.emitter.on("rangechange",function() { + if (me.lastStart != 0) { + var offset = me.body.range.start - me.lastStart; + var range = me.body.range.end - me.body.range.start; + if (me.width != 0) { + var rangePerPixelInv = me.width/range; + var xOffset = offset * rangePerPixelInv; + me.svg.style.left = (-me.width - xOffset) + "px"; + } + } + }); + this.body.emitter.on("rangechanged", function() { + me.lastStart = me.body.range.start; + me.svg.style.left = util.option.asSize(-me.width); + me._updateGraph.apply(me); + }); + // create the HTML DOM + this._create(); + this.body.emitter.emit("change"); + } -/** - * set the velocity at 0. Is called when this node is contained in another during clustering - */ -Node.prototype.clearVelocity = function() { - this.vx = 0; - this.vy = 0; -}; + LineGraph.prototype = new Component(); + /** + * Create the HTML DOM for the ItemSet + */ + LineGraph.prototype._create = function(){ + var frame = document.createElement('div'); + frame.className = 'LineGraph'; + this.dom.frame = frame; + + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = "relative"; + this.svg.style.height = ('' + this.options.graphHeight).replace("px",'') + 'px'; + this.svg.style.display = "block"; + frame.appendChild(this.svg); + + // data axis + this.options.dataAxis.orientation = 'left'; + this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg); + + this.options.dataAxis.orientation = 'right'; + this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg); + delete this.options.dataAxis.orientation; + + // legends + this.legendLeft = new Legend(this.body, this.options.legend, 'left'); + this.legendRight = new Legend(this.body, this.options.legend, 'right'); -/** - * Basic preservation of (kinectic) energy - * - * @param massBeforeClustering - */ -Node.prototype.updateVelocity = function(massBeforeClustering) { - var energyBefore = this.vx * this.vx * massBeforeClustering; - //this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.mass) : Math.sqrt(energyBefore/this.mass); - this.vx = Math.sqrt(energyBefore/this.mass); - energyBefore = this.vy * this.vy * massBeforeClustering; - //this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.mass) : Math.sqrt(energyBefore/this.mass); - this.vy = Math.sqrt(energyBefore/this.mass); -}; + this.show(); + }; + /** + * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. + * @param options + */ + LineGraph.prototype.setOptions = function(options) { + if (options) { + var fields = ['sampling','defaultGroup','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort']; + util.selectiveDeepExtend(fields, this.options, options); + util.mergeOptions(this.options, options,'catmullRom'); + util.mergeOptions(this.options, options,'drawPoints'); + util.mergeOptions(this.options, options,'shaded'); + util.mergeOptions(this.options, options,'legend'); + + if (options.catmullRom) { + if (typeof options.catmullRom == 'object') { + if (options.catmullRom.parametrization) { + if (options.catmullRom.parametrization == 'uniform') { + this.options.catmullRom.alpha = 0; + } + else if (options.catmullRom.parametrization == 'chordal') { + this.options.catmullRom.alpha = 1.0; + } + else { + this.options.catmullRom.parametrization = 'centripetal'; + this.options.catmullRom.alpha = 0.5; + } + } + } + } -/** - * @class Edge - * - * A edge connects two nodes - * @param {Object} properties Object with properties. Must contain - * At least properties from and to. - * Available properties: from (number), - * to (number), label (string, color (string), - * width (number), style (string), - * length (number), title (string) - * @param {Network} network A Network object, used to find and edge to - * nodes. - * @param {Object} constants An object with default values for - * example for the color - */ -function Edge (properties, network, constants) { - if (!network) { - throw "No network provided"; - } - this.network = network; - - // initialize constants - this.widthMin = constants.edges.widthMin; - this.widthMax = constants.edges.widthMax; - - // initialize variables - this.id = undefined; - this.fromId = undefined; - this.toId = undefined; - this.style = constants.edges.style; - this.title = undefined; - this.width = constants.edges.width; - this.widthSelectionMultiplier = constants.edges.widthSelectionMultiplier; - this.widthSelected = this.width * this.widthSelectionMultiplier; - this.hoverWidth = constants.edges.hoverWidth; - this.value = undefined; - this.length = constants.physics.springLength; - this.customLength = false; - this.selected = false; - this.hover = false; - this.smooth = constants.smoothCurves; - this.arrowScaleFactor = constants.edges.arrowScaleFactor; - - this.from = null; // a node - this.to = null; // a node - this.via = null; // a temp node - - // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster - // by storing the original information we can revert to the original connection when the cluser is opened. - this.originalFromId = []; - this.originalToId = []; - - this.connected = false; - - // Added to support dashed lines - // David Jordan - // 2012-08-08 - this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength - - this.color = {color:constants.edges.color.color, - highlight:constants.edges.color.highlight, - hover:constants.edges.color.hover}; - this.widthFixed = false; - this.lengthFixed = false; - - this.setProperties(properties, constants); - - this.controlNodesEnabled = false; - this.controlNodes = {from:null, to:null, positions:{}}; - this.connectedNode = null; -} + if (this.yAxisLeft) { + if (options.dataAxis !== undefined) { + this.yAxisLeft.setOptions(this.options.dataAxis); + this.yAxisRight.setOptions(this.options.dataAxis); + } + } -/** - * Set or overwrite properties for the edge - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties - */ -Edge.prototype.setProperties = function(properties, constants) { - if (!properties) { - return; - } + if (this.legendLeft) { + if (options.legend !== undefined) { + this.legendLeft.setOptions(this.options.legend); + this.legendRight.setOptions(this.options.legend); + } + } - if (properties.from !== undefined) {this.fromId = properties.from;} - if (properties.to !== undefined) {this.toId = properties.to;} + if (this.groups.hasOwnProperty(UNGROUPED)) { + this.groups[UNGROUPED].setOptions(options); + } + } + if (this.dom.frame) { + this._updateGraph(); + } + }; - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.style !== undefined) {this.style = properties.style;} - if (properties.label !== undefined) {this.label = properties.label;} + /** + * Hide the component from the DOM + */ + LineGraph.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } + }; - if (this.label) { - this.fontSize = constants.edges.fontSize; - this.fontFace = constants.edges.fontFace; - this.fontColor = constants.edges.fontColor; - this.fontFill = constants.edges.fontFill; + /** + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed + */ + LineGraph.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } + }; - if (properties.fontColor !== undefined) {this.fontColor = properties.fontColor;} - if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;} - if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;} - if (properties.fontFill !== undefined) {this.fontFill = properties.fontFill;} - } - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.width !== undefined) {this.width = properties.width;} - if (properties.widthSelectionMultiplier !== undefined) - {this.widthSelectionMultiplier = properties.widthSelectionMultiplier;} - if (properties.hoverWidth !== undefined) {this.hoverWidth = properties.hoverWidth;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.length !== undefined) {this.length = properties.length; - this.customLength = true;} - - // scale the arrow - if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor;} - - // Added to support dashed lines - // David Jordan - // 2012-08-08 - if (properties.dash) { - if (properties.dash.length !== undefined) {this.dash.length = properties.dash.length;} - if (properties.dash.gap !== undefined) {this.dash.gap = properties.dash.gap;} - if (properties.dash.altLength !== undefined) {this.dash.altLength = properties.dash.altLength;} - } + /** + * Set items + * @param {vis.DataSet | null} items + */ + LineGraph.prototype.setItems = function(items) { + var me = this, + ids, + oldItemsData = this.itemsData; - if (properties.color !== undefined) { - if (util.isString(properties.color)) { - this.color.color = properties.color; - this.color.highlight = properties.color; + // replace the dataset + if (!items) { + this.itemsData = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + this.itemsData = items; } else { - if (properties.color.color !== undefined) {this.color.color = properties.color.color;} - if (properties.color.highlight !== undefined) {this.color.highlight = properties.color.highlight;} - if (properties.color.hover !== undefined) {this.color.hover = properties.color.hover;} + throw new TypeError('Data must be an instance of DataSet or DataView'); } - } - // A node is connected when it has a from and to node. - this.connect(); + if (oldItemsData) { + // unsubscribe from old dataset + util.forEach(this.itemListeners, function (callback, event) { + oldItemsData.off(event, callback); + }); - this.widthFixed = this.widthFixed || (properties.width !== undefined); - this.lengthFixed = this.lengthFixed || (properties.length !== undefined); + // remove all drawn items + ids = oldItemsData.getIds(); + this._onRemove(ids); + } - this.widthSelected = this.width * this.widthSelectionMultiplier; + if (this.itemsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.itemListeners, function (callback, event) { + me.itemsData.on(event, callback, id); + }); - // set draw method based on style - switch (this.style) { - case 'line': this.draw = this._drawLine; break; - case 'arrow': this.draw = this._drawArrow; break; - case 'arrow-center': this.draw = this._drawArrowCenter; break; - case 'dash-line': this.draw = this._drawDashLine; break; - default: this.draw = this._drawLine; break; - } -}; + // add all new items + ids = this.itemsData.getIds(); + this._onAdd(ids); + } + this._updateUngrouped(); + this._updateGraph(); + this.redraw(); + }; -/** - * Connect an edge to its nodes - */ -Edge.prototype.connect = function () { - this.disconnect(); + /** + * Set groups + * @param {vis.DataSet} groups + */ + LineGraph.prototype.setGroups = function(groups) { + var me = this, + ids; - this.from = this.network.nodes[this.fromId] || null; - this.to = this.network.nodes[this.toId] || null; - this.connected = (this.from && this.to); + // unsubscribe from current dataset + if (this.groupsData) { + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.unsubscribe(event, callback); + }); - if (this.connected) { - this.from.attachEdge(this); - this.to.attachEdge(this); - } - else { - if (this.from) { - this.from.detachEdge(this); + // remove all drawn groups + ids = this.groupsData.getIds(); + this.groupsData = null; + this._onRemoveGroups(ids); // note: this will cause a redraw } - if (this.to) { - this.to.detachEdge(this); + + // replace the dataset + if (!groups) { + this.groupsData = null; + } + else if (groups instanceof DataSet || groups instanceof DataView) { + this.groupsData = groups; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); } - } -}; -/** - * Disconnect an edge from its nodes - */ -Edge.prototype.disconnect = function () { - if (this.from) { - this.from.detachEdge(this); - this.from = null; - } - if (this.to) { - this.to.detachEdge(this); - this.to = null; - } - - this.connected = false; -}; - -/** - * get the title of this edge. - * @return {string} title The title of the edge, or undefined when no title - * has been set. - */ -Edge.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; -}; - - -/** - * Retrieve the value of the edge. Can be undefined - * @return {Number} value - */ -Edge.prototype.getValue = function() { - return this.value; -}; + if (this.groupsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.on(event, callback, id); + }); -/** - * Adjust the value range of the edge. The edge will adjust it's width - * based on its value. - * @param {Number} min - * @param {Number} max - */ -Edge.prototype.setValueRange = function(min, max) { - if (!this.widthFixed && this.value !== undefined) { - var scale = (this.widthMax - this.widthMin) / (max - min); - this.width = (this.value - min) * scale + this.widthMin; - } -}; + // draw all ms + ids = this.groupsData.getIds(); + this._onAddGroups(ids); + } + this._onUpdate(); + }; -/** - * Redraw a edge - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - */ -Edge.prototype.draw = function(ctx) { - throw "Method draw not initialized in edge"; -}; -/** - * Check if this object is overlapping with the provided object - * @param {Object} obj an object with parameters left, top - * @return {boolean} True if location is located on the edge - */ -Edge.prototype.isOverlappingWith = function(obj) { - if (this.connected) { - var distMax = 10; - var xFrom = this.from.x; - var yFrom = this.from.y; - var xTo = this.to.x; - var yTo = this.to.y; - var xObj = obj.left; - var yObj = obj.top; - - var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); - - return (dist < distMax); - } - else { - return false - } -}; + LineGraph.prototype._onUpdate = function(ids) { + this._updateUngrouped(); + this._updateAllGroupData(); + this._updateGraph(); + this.redraw(); + }; + LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; + LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; + LineGraph.prototype._onUpdateGroups = function (groupIds) { + for (var i = 0; i < groupIds.length; i++) { + var group = this.groupsData.get(groupIds[i]); + this._updateGroup(group, groupIds[i]); + } -/** - * Redraw a edge as a line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ -Edge.prototype._drawLine = function(ctx) { - // set style - if (this.selected == true) {ctx.strokeStyle = this.color.highlight;} - else if (this.hover == true) {ctx.strokeStyle = this.color.hover;} - else {ctx.strokeStyle = this.color.color;} - ctx.lineWidth = this._getLineWidth(); + this._updateGraph(); + this.redraw(); + }; + LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; - if (this.from != this.to) { - // draw line - this._line(ctx); + LineGraph.prototype._onRemoveGroups = function (groupIds) { + for (var i = 0; i < groupIds.length; i++) { + if (!this.groups.hasOwnProperty(groupIds[i])) { + if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { + this.yAxisRight.removeGroup(groupIds[i]); + this.legendRight.removeGroup(groupIds[i]); + this.legendRight.redraw(); + } + else { + this.yAxisLeft.removeGroup(groupIds[i]); + this.legendLeft.removeGroup(groupIds[i]); + this.legendLeft.redraw(); + } + delete this.groups[groupIds[i]]; + } + } + this._updateUngrouped(); + this._updateGraph(); + this.redraw(); + }; - // draw label - var point; - if (this.label) { - if (this.smooth == true) { - var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x)); - var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y)); - point = {x:midpointX, y:midpointY}; + /** + * update a group object + * + * @param group + * @param groupId + * @private + */ + LineGraph.prototype._updateGroup = function (group, groupId) { + if (!this.groups.hasOwnProperty(groupId)) { + this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.addGroup(groupId, this.groups[groupId]); + this.legendRight.addGroup(groupId, this.groups[groupId]); } else { - point = this._pointOnLine(0.5); + this.yAxisLeft.addGroup(groupId, this.groups[groupId]); + this.legendLeft.addGroup(groupId, this.groups[groupId]); } - this._label(ctx, this.label, point.x, point.y); - } - } - else { - var x, y; - 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 { - x = node.x + radius; - y = node.y - node.height / 2; + this.groups[groupId].update(group); + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.updateGroup(groupId, this.groups[groupId]); + this.legendRight.updateGroup(groupId, this.groups[groupId]); + } + else { + this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); + this.legendLeft.updateGroup(groupId, this.groups[groupId]); + } } - this._circle(ctx, x, y, radius); - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } -}; + this.legendLeft.redraw(); + this.legendRight.redraw(); + }; -/** - * Get the line width of the edge. Depends on width and whether one of the - * connected nodes is selected. - * @return {Number} width - * @private - */ -Edge.prototype._getLineWidth = function() { - if (this.selected == true) { - return Math.min(this.widthSelected, this.widthMax)*this.networkScaleInv; - } - else { - if (this.hover == true) { - return Math.min(this.hoverWidth, this.widthMax)*this.networkScaleInv; + LineGraph.prototype._updateAllGroupData = function () { + if (this.itemsData != null) { + // ~450 ms @ 500k + + var groupsContent = {}; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + groupsContent[groupId] = []; + } + } + for (var itemId in this.itemsData._data) { + if (this.itemsData._data.hasOwnProperty(itemId)) { + var item = this.itemsData._data[itemId]; + item.x = util.convert(item.x,"Date"); + groupsContent[item.group].push(item); + } + } + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + this.groups[groupId].setItems(groupsContent[groupId]); + } + } + // // ~4500ms @ 500k + // for (var groupId in this.groups) { + // if (this.groups.hasOwnProperty(groupId)) { + // this.groups[groupId].setItems(this.itemsData.get({filter: + // function (item) { + // return (item.group == groupId); + // }, type:{x:"Date"}} + // )); + // } + // } + } + }; + + /** + * Create or delete the group holding all ungrouped items. This group is used when + * there are no groups specified. This anonymous group is called 'graph'. + * @protected + */ + LineGraph.prototype._updateUngrouped = function() { + if (this.itemsData != null) { + // var t0 = new Date(); + var group = {id: UNGROUPED, content: this.options.defaultGroup}; + this._updateGroup(group, UNGROUPED); + var ungroupedCounter = 0; + if (this.itemsData) { + for (var itemId in this.itemsData._data) { + if (this.itemsData._data.hasOwnProperty(itemId)) { + var item = this.itemsData._data[itemId]; + if (item != undefined) { + if (item.hasOwnProperty('group')) { + if (item.group === undefined) { + item.group = UNGROUPED; + } + } + else { + item.group = UNGROUPED; + } + ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; + } + } + } + } + + // much much slower + // var datapoints = this.itemsData.get({ + // filter: function (item) {return item.group === undefined;}, + // showInternalIds:true + // }); + // if (datapoints.length > 0) { + // var updateQuery = []; + // for (var i = 0; i < datapoints.length; i++) { + // updateQuery.push({id:datapoints[i].id, group: UNGROUPED}); + // } + // this.itemsData.update(updateQuery, true); + // } + // var t1 = new Date(); + // var pointInUNGROUPED = this.itemsData.get({filter: function (item) {return item.group == UNGROUPED;}}); + if (ungroupedCounter == 0) { + delete this.groups[UNGROUPED]; + this.legendLeft.removeGroup(UNGROUPED); + this.legendRight.removeGroup(UNGROUPED); + this.yAxisLeft.removeGroup(UNGROUPED); + this.yAxisRight.removeGroup(UNGROUPED); + } + // console.log("getting amount ungrouped",new Date() - t1); + // console.log("putting in ungrouped",new Date() - t0); } else { - return this.width*this.networkScaleInv; + delete this.groups[UNGROUPED]; + this.legendLeft.removeGroup(UNGROUPED); + this.legendRight.removeGroup(UNGROUPED); + this.yAxisLeft.removeGroup(UNGROUPED); + this.yAxisRight.removeGroup(UNGROUPED); } - } -}; - -/** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ -Edge.prototype._line = function (ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - if (this.smooth == true) { - ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); - } - else { - ctx.lineTo(this.to.x, this.to.y); - } - ctx.stroke(); -}; -/** - * Draw a line from a node to itself, a circle - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @private - */ -Edge.prototype._circle = function (ctx, x, y, radius) { - // draw a circle - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); -}; + this.legendLeft.redraw(); + this.legendRight.redraw(); + }; -/** - * Draw label with white background and with the middle at (x, y) - * @param {CanvasRenderingContext2D} ctx - * @param {String} text - * @param {Number} x - * @param {Number} y - * @private - */ -Edge.prototype._label = function (ctx, text, x, y) { - if (text) { - // TODO: cache the calculated size - ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + - this.fontSize + "px " + this.fontFace; - ctx.fillStyle = this.fontFill; - var width = ctx.measureText(text).width; - var height = this.fontSize; - var left = x - width / 2; - var top = y - height / 2; - - ctx.fillRect(left, top, width, height); - - // draw text - ctx.fillStyle = this.fontColor || "black"; - ctx.textAlign = "left"; - ctx.textBaseline = "top"; - ctx.fillText(text, left, top); - } -}; -/** - * Redraw a edge as a dashed line - * Draw this edge in the given canvas - * @author David Jordan - * @date 2012-08-08 - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ -Edge.prototype._drawDashLine = function(ctx) { - // set style - if (this.selected == true) {ctx.strokeStyle = this.color.highlight;} - else if (this.hover == true) {ctx.strokeStyle = this.color.hover;} - else {ctx.strokeStyle = this.color.color;} + /** + * Redraw the component, mandatory function + * @return {boolean} Returns true if the component is resized + */ + LineGraph.prototype.redraw = function() { + var resized = false; - ctx.lineWidth = this._getLineWidth(); + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + if (this.lastWidth === undefined && this.width || this.lastWidth != this.width) { + resized = true; + } + // check if this component is resized + resized = this._isResized() || resized; + // check whether zoomed (in that case we need to re-stack everything) + var visibleInterval = this.body.range.end - this.body.range.start; + var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth); + this.lastVisibleInterval = visibleInterval; + this.lastWidth = this.width; - // only firefox and chrome support this method, else we use the legacy one. - if (ctx.mozDash !== undefined || ctx.setLineDash !== undefined) { - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); + // calculate actual size and position + this.width = this.dom.frame.offsetWidth; - // configure the dash pattern - var pattern = [0]; - if (this.dash.length !== undefined && this.dash.gap !== undefined) { - pattern = [this.dash.length,this.dash.gap]; + // the svg element is three times as big as the width, this allows for fully dragging left and right + // without reloading the graph. the controls for this are bound to events in the constructor + if (resized == true) { + this.svg.style.width = util.option.asSize(3*this.width); + this.svg.style.left = util.option.asSize(-this.width); } - else { - pattern = [5,5]; + if (zoomed == true) { + this._updateGraph(); } - // set dash settings for chrome or firefox - if (typeof ctx.setLineDash !== 'undefined') { //Chrome - ctx.setLineDash(pattern); - ctx.lineDashOffset = 0; + this.legendLeft.redraw(); + this.legendRight.redraw(); - } else { //Firefox - ctx.mozDash = pattern; - ctx.mozDashOffset = 0; - } + return resized; + }; - // draw the line - if (this.smooth == true) { - ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); - } - else { - ctx.lineTo(this.to.x, this.to.y); - } - ctx.stroke(); + /** + * Update and redraw the graph. + * + */ + LineGraph.prototype._updateGraph = function () { + // reset the svg elements + DOMutil.prepareElements(this.svgElements); + // // very slow... + // groupData = group.itemsData.get({filter: + // function (item) { + // return (item.x > minDate && item.x < maxDate); + // }} + // ); + + + if (this.width != 0 && this.itemsData != null) { + var group, groupData, preprocessedGroup, i; + var preprocessedGroupData = []; + var processedGroupData = []; + var groupRanges = []; + var changeCalled = false; + + // getting group Ids + var groupIds = []; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + groupIds.push(groupId); + } + } + + // this is the range of the SVG canvas + var minDate = this.body.util.toGlobalTime(- this.body.domProps.root.width); + var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); + + // first select and preprocess the data from the datasets. + // the groups have their preselection of data, we now loop over this data to see + // what data we need to draw. Sorted data is much faster. + // more optimization is possible by doing the sampling before and using the binary search + // to find the end date to determine the increment. + if (groupIds.length > 0) { + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + groupData = []; + // optimization for sorted data + if (group.options.sort == true) { + var guess = Math.max(0,util.binarySearchGeneric(group.itemsData, minDate, 'x', 'before')); + + for (var j = guess; j < group.itemsData.length; j++) { + var item = group.itemsData[j]; + if (item !== undefined) { + if (item.x > maxDate) { + groupData.push(item); + break; + } + else { + groupData.push(item); + } + } + } + } + else { + for (var j = 0; j < group.itemsData.length; j++) { + var item = group.itemsData[j]; + if (item !== undefined) { + if (item.x > minDate && item.x < maxDate) { + groupData.push(item); + } + } + } + } + // preprocess, split into ranges and data + preprocessedGroup = this._preprocessData(groupData, group); + groupRanges.push({min: preprocessedGroup.min, max: preprocessedGroup.max}); + preprocessedGroupData.push(preprocessedGroup.data); + } + + // update the Y axis first, we use this data to draw at the correct Y points + // changeCalled is required to clean the SVG on a change emit. + changeCalled = this._updateYAxis(groupIds, groupRanges); + if (changeCalled == true) { + DOMutil.cleanupElements(this.svgElements); + this.body.emitter.emit("change"); + return; + } - // restore the dash settings. - if (typeof ctx.setLineDash !== 'undefined') { //Chrome - ctx.setLineDash([0]); - ctx.lineDashOffset = 0; + // with the yAxis scaled correctly, use this to get the Y values of the points. + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + processedGroupData.push(this._convertYvalues(preprocessedGroupData[i],group)) + } - } else { //Firefox - ctx.mozDash = [0]; - ctx.mozDashOffset = 0; - } - } - else { // unsupporting smooth lines - // draw dashed line - ctx.beginPath(); - ctx.lineCap = 'round'; - if (this.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]); - } - else if (this.dash.length !== undefined && this.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.dash.length,this.dash.gap]); - } - else //If all else fails draw a line - { - ctx.moveTo(this.from.x, this.from.y); - ctx.lineTo(this.to.x, this.to.y); + // draw the groups + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (group.options.style == 'line') { + this._drawLineGraph(processedGroupData[i], group); + } + else { + this._drawBarGraph (processedGroupData[i], group); + } + } + } } - ctx.stroke(); - } - // draw label - if (this.label) { - var point; - if (this.smooth == true) { - var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x)); - var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); - } -}; + // cleanup unused svg elements + DOMutil.cleanupElements(this.svgElements); + }; -/** - * Get a point on a line - * @param {Number} percentage. Value between 0 (line start) and 1 (line end) - * @return {Object} point - * @private - */ -Edge.prototype._pointOnLine = function (percentage) { - return { - x: (1 - percentage) * this.from.x + percentage * this.to.x, - y: (1 - percentage) * this.from.y + percentage * this.to.y - } -}; + /** + * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. + * @param {array} groupIds + * @private + */ + LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { + var changeCalled = false; + var yAxisLeftUsed = false; + var yAxisRightUsed = false; + var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; + var orientation = 'left'; -/** - * Get a point on a circle - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @param {Number} percentage. Value between 0 (line start) and 1 (line end) - * @return {Object} point - * @private - */ -Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { - var angle = (percentage - 3/8) * 2 * Math.PI; - return { - x: x + radius * Math.cos(angle), - y: y - radius * Math.sin(angle) - } -}; + // if groups are present + if (groupIds.length > 0) { + for (var i = 0; i < groupIds.length; i++) { + orientation = 'left'; + var group = this.groups[groupIds[i]]; + if (group.options.yAxisOrientation == 'right') { + orientation = 'right'; + } -/** - * Redraw a edge as a line with an arrow halfway the line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ -Edge.prototype._drawArrowCenter = function(ctx) { - var point; - // set style - if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;} - else if (this.hover == true) {ctx.strokeStyle = this.color.hover; ctx.fillStyle = this.color.hover;} - else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;} - ctx.lineWidth = this._getLineWidth(); - - if (this.from != this.to) { - // draw line - this._line(ctx); + minVal = groupRanges[i].min; + maxVal = groupRanges[i].max; - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var length = (10 + 5 * this.width) * this.arrowScaleFactor; - // draw an arrow halfway the line - if (this.smooth == true) { - var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x)); - var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); + if (orientation == 'left') { + yAxisLeftUsed = true; + minLeft = minLeft > minVal ? minVal : minLeft; + maxLeft = maxLeft < maxVal ? maxVal : maxLeft; + } + else { + yAxisRightUsed = true; + minRight = minRight > minVal ? minVal : minRight; + maxRight = maxRight < maxVal ? maxVal : maxRight; + } + } + if (yAxisLeftUsed == true) { + this.yAxisLeft.setRange(minLeft, maxLeft); + } + if (yAxisRightUsed == true) { + this.yAxisRight.setRange(minRight, maxRight); + } } - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); + changeCalled = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || changeCalled; + changeCalled = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || changeCalled; - // draw label - if (this.label) { - this._label(ctx, this.label, point.x, point.y); + if (yAxisRightUsed == true && yAxisLeftUsed == true) { + this.yAxisLeft.drawIcons = true; + this.yAxisRight.drawIcons = true; } - } - else { - // draw circle - var x, y; - var radius = 0.25 * Math.max(100,this.length); - var node = this.from; - if (!node.width) { - node.resize(ctx); + else { + this.yAxisLeft.drawIcons = false; + this.yAxisRight.drawIcons = false; } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; + + this.yAxisRight.master = !yAxisLeftUsed; + + if (this.yAxisRight.master == false) { + if (yAxisRightUsed == true) { + this.yAxisLeft.lineOffset = this.yAxisRight.width; + } + changeCalled = this.yAxisLeft.redraw() || changeCalled; + this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; + changeCalled = this.yAxisRight.redraw() || changeCalled; } else { - x = node.x + radius; - y = node.y - node.height * 0.5; + changeCalled = this.yAxisRight.redraw() || changeCalled; } - this._circle(ctx, x, y, radius); - - // draw all arrows - var angle = 0.2 * Math.PI; - var length = (10 + 5 * this.width) * this.arrowScaleFactor; - point = this._pointOnCircle(x, y, radius, 0.5); - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); + return changeCalled; + }; - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); + /** + * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function + * + * @param {boolean} axisUsed + * @returns {boolean} + * @private + * @param axis + */ + LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { + var changed = false; + if (axisUsed == false) { + if (axis.dom.frame.parentNode) { + axis.hide(); + changed = true; + } } - } -}; + else { + if (!axis.dom.frame.parentNode) { + axis.show(); + changed = true; + } + } + return changed; + }; + /** + * draw a bar graph + * @param datapoints + * @param group + */ + LineGraph.prototype._drawBarGraph = function (dataset, group) { + if (dataset != null) { + if (dataset.length > 0) { + var coreDistance; + var minWidth = 0.1 * group.options.barChart.width; + var offset = 0; + var width = group.options.barChart.width; -/** - * Redraw a edge as a line with an arrow - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ -Edge.prototype._drawArrow = function(ctx) { - // set style - if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;} - else if (this.hover == true) {ctx.strokeStyle = this.color.hover; ctx.fillStyle = this.color.hover;} - else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;} - - ctx.lineWidth = this._getLineWidth(); - - var angle, length; - //draw a line - if (this.from != this.to) { - 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); + if (group.options.barChart.align == 'left') {offset -= 0.5*width;} + else if (group.options.barChart.align == 'right') {offset += 0.5*width;} - 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; + for (var i = 0; i < dataset.length; i++) { + // dynammically downscale the width so there is no overlap up to 1/10th the original width + if (i+1 < dataset.length) {coreDistance = Math.abs(dataset[i+1].x - dataset[i].x);} + if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(dataset[i-1].x - dataset[i].x));} + if (coreDistance < width) {width = coreDistance < minWidth ? minWidth : coreDistance;} + DOMutil.drawBar(dataset[i].x + offset, dataset[i].y, width, group.zeroPosition - dataset[i].y, group.className + ' bar', this.svgElements, this.svg); + } - 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); + // draw points + if (group.options.drawPoints.enabled == true) { + this._drawPoints(dataset, group, this.svgElements, this.svg, offset); + } + } } - 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; - } - ctx.beginPath(); - ctx.moveTo(xFrom,yFrom); - if (this.smooth == true) { - ctx.quadraticCurveTo(this.via.x,this.via.y,xTo, yTo); - } - else { - ctx.lineTo(xTo, yTo); - } - ctx.stroke(); + /** + * draw a line graph + * + * @param datapoints + * @param group + */ + LineGraph.prototype._drawLineGraph = function (dataset, group) { + if (dataset != null) { + if (dataset.length > 0) { + var path, d; + var svgHeight = Number(this.svg.style.height.replace("px","")); + path = DOMutil.getSVGElement('path', this.svgElements, this.svg); + path.setAttributeNS(null, "class", group.className); + + // construct path from dataset + if (group.options.catmullRom.enabled == true) { + d = this._catmullRom(dataset, group); + } + else { + d = this._linear(dataset); + } - // draw arrow at the end of the line - length = (10 + 5 * this.width) * this.arrowScaleFactor; - ctx.arrow(xTo, yTo, angle, length); - ctx.fill(); - ctx.stroke(); + // append with points for fill and finalize the path + if (group.options.shaded.enabled == true) { + var fillPath = DOMutil.getSVGElement('path',this.svgElements, this.svg); + var dFill; + if (group.options.shaded.orientation == 'top') { + dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0; + } + else { + dFill = "M" + dataset[0].x + "," + svgHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + svgHeight; + } + fillPath.setAttributeNS(null, "class", group.className + " fill"); + fillPath.setAttributeNS(null, "d", dFill); + } + // copy properties to path for drawing. + path.setAttributeNS(null, "d", "M" + d); - // draw label - if (this.label) { - var point; - if (this.smooth == true) { - var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x)); - var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); + // draw points + if (group.options.drawPoints.enabled == true) { + this._drawPoints(dataset, group, this.svgElements, this.svg); + } } - this._label(ctx, this.label, point.x, point.y); - } - } - else { - // draw circle - var node = this.from; - var x, y, arrow; - var radius = 0.25 * Math.max(100,this.length); - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; - arrow = { - x: x, - y: node.y, - angle: 0.9 * Math.PI - }; } - else { - x = node.x + radius; - y = node.y - node.height * 0.5; - arrow = { - x: node.x, - y: y, - angle: 0.6 * Math.PI - }; + }; + + /** + * draw the data points + * + * @param dataset + * @param JSONcontainer + * @param svg + * @param group + */ + LineGraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg, offset) { + if (offset === undefined) {offset = 0;} + for (var i = 0; i < dataset.length; i++) { + DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, JSONcontainer, svg); } - ctx.beginPath(); - // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); + }; - // draw all arrows - var length = (10 + 5 * this.width) * this.arrowScaleFactor; - ctx.arrow(arrow.x, arrow.y, arrow.angle, length); - ctx.fill(); - ctx.stroke(); - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } - } -}; + /** + * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the + * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for + * the yAxis. + * + * @param datapoints + * @returns {Array} + * @private + */ + LineGraph.prototype._preprocessData = function (datapoints, group) { + var extractedData = []; + var xValue, yValue; + var toScreen = this.body.util.toScreen; + + var increment = 1; + var amountOfPoints = datapoints.length; + var yMin = datapoints[0].y; + var yMax = datapoints[0].y; -/** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * @param {number} x1 - * @param {number} y1 - * @param {number} x2 - * @param {number} y2 - * @param {number} x3 - * @param {number} y3 - * @private - */ -Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point - 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 + // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop + // of width changing of the yAxis. + if (group.options.sampling == true) { + var xDistance = this.body.util.toGlobalScreen(datapoints[datapoints.length-1].x) - this.body.util.toGlobalScreen(datapoints[0].x); + var pointsPerPixel = amountOfPoints/xDistance; + increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1,Math.round(pointsPerPixel))); } - 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; - } + for (var i = 0; i < amountOfPoints; i += increment) { + xValue = toScreen(datapoints[i].x) + this.width - 1; + yValue = datapoints[i].y; + extractedData.push({x: xValue, y: yValue}); + yMin = yMin > yValue ? yValue : yMin; + yMax = yMax < yValue ? yValue : yMax; + } - var x = x1 + u * px, - y = y1 + u * py, - dx = x - x3, - dy = y - y3; + // extractedData.sort(function (a,b) {return a.x - b.x;}); + return {min: yMin, max: yMax, data: extractedData}; + }; - //# 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 + /** + * This uses the DataAxis object to generate the correct Y coordinate on the SVG window. It uses the + * util function toScreen to get the x coordinate from the timestamp. + * + * @param datapoints + * @param options + * @returns {Array} + * @private + */ + LineGraph.prototype._convertYvalues = function (datapoints, group) { + var extractedData = []; + var xValue, yValue; + var axis = this.yAxisLeft; + var svgHeight = Number(this.svg.style.height.replace("px","")); - return Math.sqrt(dx*dx + dy*dy); + if (group.options.yAxisOrientation == 'right') { + axis = this.yAxisRight; } - } - else { - 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 { - x = node.x + radius; - y = node.y - node.height / 2; + + for (var i = 0; i < datapoints.length; i++) { + xValue = datapoints[i].x; + yValue = Math.round(axis.convertValue(datapoints[i].y)); + extractedData.push({x: xValue, y: yValue}); } - dx = x - x3; - dy = y - y3; - return Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); - } -}; + group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); + // extractedData.sort(function (a,b) {return a.x - b.x;}); + return extractedData; + }; -/** - * This allows the zoom level of the network to influence the rendering - * - * @param scale - */ -Edge.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; -}; + /** + * This uses an uniform parametrization of the CatmullRom algorithm: + * "On the Parameterization of Catmull-Rom Curves" by Cem Yuksel et al. + * @param data + * @returns {string} + * @private + */ + LineGraph.prototype._catmullRomUniform = function(data) { + // catmull rom + var p0, p1, p2, p3, bp1, bp2; + var d = Math.round(data[0].x) + "," + Math.round(data[0].y) + " "; + var normalization = 1/6; + var length = data.length; + for (var i = 0; i < length - 1; i++) { -Edge.prototype.select = function() { - this.selected = true; -}; + p0 = (i == 0) ? data[0] : data[i-1]; + p1 = data[i]; + p2 = data[i+1]; + p3 = (i + 2 < length) ? data[i+2] : p2; -Edge.prototype.unselect = function() { - this.selected = false; -}; -Edge.prototype.positionBezierNode = function() { - if (this.via !== null) { - this.via.x = 0.5 * (this.from.x + this.to.x); - this.via.y = 0.5 * (this.from.y + this.to.y); - } -}; + // Catmull-Rom to Cubic Bezier conversion matrix + // 0 1 0 0 + // -1/6 1 1/6 0 + // 0 1/6 1 -1/6 + // 0 0 1 0 -/** - * 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); + // bp0 = { x: p1.x, y: p1.y }; + bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; + bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; + // bp0 = { x: p2.x, y: p2.y }; + + d += "C" + + bp1.x + "," + + bp1.y + " " + + bp2.x + "," + + bp2.y + " " + + p2.x + "," + + p2.y + " "; } - 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; + return d; + }; + + /** + * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. + * By default, the centripetal parameterization is used because this gives the nicest results. + * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. + * + * One optimization can be used to reuse distances since this is a sliding window approach. + * @param data + * @returns {string} + * @private + */ + LineGraph.prototype._catmullRom = function(data, group) { + var alpha = group.options.catmullRom.alpha; + if (alpha == 0 || alpha === undefined) { + return this._catmullRomUniform(data); + } + else { + var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; + var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; + var d = Math.round(data[0].x) + "," + Math.round(data[0].y) + " "; + var length = data.length; + for (var i = 0; i < length - 1; i++) { + + p0 = (i == 0) ? data[0] : data[i-1]; + p1 = data[i]; + p2 = data[i+1]; + p3 = (i + 2 < length) ? data[i+2] : p2; + + d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); + d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); + d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); + + // Catmull-Rom to Cubic Bezier conversion matrix + // + // A = 2d1^2a + 3d1^a * d2^a + d3^2a + // B = 2d3^2a + 3d3^a * d2^a + d2^2a + // + // [ 0 1 0 0 ] + // [ -d2^2a/N A/N d1^2a/N 0 ] + // [ 0 d3^2a/M B/M -d2^2a/M ] + // [ 0 0 1 0 ] + + // [ 0 1 0 0 ] + // [ -d2pow2a/N A/N d1pow2a/N 0 ] + // [ 0 d3pow2a/M B/M -d2pow2a/M ] + // [ 0 0 1 0 ] + + d3powA = Math.pow(d3, alpha); + d3pow2A = Math.pow(d3,2*alpha); + d2powA = Math.pow(d2, alpha); + d2pow2A = Math.pow(d2,2*alpha); + d1powA = Math.pow(d1, alpha); + d1pow2A = Math.pow(d1,2*alpha); + + A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; + B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; + N = 3*d1powA * (d1powA + d2powA); + if (N > 0) {N = 1 / N;} + M = 3*d3powA * (d3powA + d2powA); + if (M > 0) {M = 1 / M;} + + bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), + y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; + + bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), + y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; + + if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} + if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} + d += "C" + + bp1.x + "," + + bp1.y + " " + + bp2.x + "," + + bp2.y + " " + + p2.x + "," + + p2.y + " "; + } + + return d; } + }; - this.controlNodes.from.draw(ctx); - this.controlNodes.to.draw(ctx); - } - else { - this.controlNodes = {from:null, to:null, positions:{}}; - } -} + /** + * this generates the SVG path for a linear drawing between datapoints. + * @param data + * @returns {string} + * @private + */ + LineGraph.prototype._linear = function(data) { + // linear + var d = ""; + for (var i = 0; i < data.length; i++) { + if (i == 0) { + d += data[i].x + "," + data[i].y; + } + else { + d += " " + data[i].x + "," + data[i].y; + } + } + return d; + }; -/** - * Enable control nodes. - * @private - */ -Edge.prototype._enableControlNodes = function() { - this.controlNodesEnabled = true; -} + module.exports = LineGraph; -/** - * 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; - } -} +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + var util = __webpack_require__(1); + var Component = __webpack_require__(12); + var TimeStep = __webpack_require__(11); -/** - * 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(); - } -} + /** + * 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 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 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; + this.defaultOptions = { + orientation: 'bottom', // supported: 'top', 'bottom' + // TODO: implement timeaxis orientations 'left' and 'right' + showMinorLabels: true, + showMajorLabels: true + }; + this.options = util.extend({}, this.defaultOptions); - 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; - } + this.body = body; - return {from:{x:xFrom,y:yFrom},to:{x:xTo,y:yTo}}; -} + // create the HTML DOM + this._create(); -/** - * Popup is a class to create a popup window with some text - * @param {Element} container The container object. - * @param {Number} [x] - * @param {Number} [y] - * @param {String} [text] - * @param {Object} [style] An object containing borderColor, - * backgroundColor, etc. - */ -function Popup(container, x, y, text, style) { - if (container) { - this.container = container; - } - else { - this.container = document.body; + this.setOptions(options); } - // x, y and text are optional, see if a style object was passed in their place - if (style === undefined) { - if (typeof x === "object") { - style = x; - x = undefined; - } else if (typeof text === "object") { - style = text; - text = undefined; - } else { - // for backwards compatibility, in case clients other than Network are creating Popup directly - style = { - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' - } - } + TimeAxis.prototype = new Component(); + + /** + * Set options for the TimeAxis. + * Parameters will be merged in current options. + * @param {Object} options Available options: + * {string} [orientation] + * {boolean} [showMinorLabels] + * {boolean} [showMajorLabels] + */ + TimeAxis.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels'], this.options, options); } - } + }; + + /** + * Create the HTML DOM for the TimeAxis + */ + TimeAxis.prototype._create = function() { + this.dom.foreground = document.createElement('div'); + this.dom.background = document.createElement('div'); - this.x = 0; - this.y = 0; - this.padding = 5; + this.dom.foreground.className = 'timeaxis foreground'; + this.dom.background.className = 'timeaxis background'; + }; - if (x !== undefined && y !== undefined ) { - this.setPosition(x, y); - } - if (text !== undefined) { - this.setText(text); - } + /** + * Destroy the TimeAxis + */ + TimeAxis.prototype.destroy = function() { + // remove from DOM + if (this.dom.foreground.parentNode) { + this.dom.foreground.parentNode.removeChild(this.dom.foreground); + } + if (this.dom.background.parentNode) { + this.dom.background.parentNode.removeChild(this.dom.background); + } - // create the frame - this.frame = document.createElement("div"); - var styleAttr = this.frame.style; - styleAttr.position = "absolute"; - styleAttr.visibility = "hidden"; - styleAttr.border = "1px solid " + style.color.border; - styleAttr.color = style.fontColor; - styleAttr.fontSize = style.fontSize + "px"; - styleAttr.fontFamily = style.fontFace; - styleAttr.padding = this.padding + "px"; - styleAttr.backgroundColor = style.color.background; - styleAttr.borderRadius = "3px"; - styleAttr.MozBorderRadius = "3px"; - styleAttr.WebkitBorderRadius = "3px"; - styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; - styleAttr.whiteSpace = "nowrap"; - this.container.appendChild(this.frame); -} + this.body = null; + }; -/** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window - */ -Popup.prototype.setPosition = function(x, y) { - this.x = parseInt(x); - this.y = parseInt(y); -}; + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + TimeAxis.prototype.redraw = function () { + var options = this.options, + props = this.props, + foreground = this.dom.foreground, + background = this.dom.background; -/** - * Set the text for the popup window. This can be HTML code - * @param {string} text - */ -Popup.prototype.setText = function(text) { - this.frame.innerHTML = text; -}; + // 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); -/** - * Show the popup window - * @param {boolean} show Optional. Show or hide the window - */ -Popup.prototype.show = function (show) { - if (show === undefined) { - show = true; - } + // calculate character width and height + this._calculateCharSize(); - if (show) { - var height = this.frame.clientHeight; - var width = this.frame.clientWidth; - var maxHeight = this.frame.parentNode.clientHeight; - var maxWidth = this.frame.parentNode.clientWidth; + // TODO: recalculate sizes only needed when parent is resized or options is changed + var orientation = this.options.orientation, + showMinorLabels = this.options.showMinorLabels, + showMajorLabels = this.options.showMajorLabels; - var top = (this.y - height); - if (top + height + this.padding > maxHeight) { - top = maxHeight - height - this.padding; + // determine the width and height of the elemens for the axis + props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; + props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; + props.height = props.minorLabelHeight + props.majorLabelHeight; + props.width = foreground.offsetWidth; + + props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - + (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); + props.minorLineWidth = 1; // TODO: really calculate width + props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; + props.majorLineWidth = 1; // TODO: really calculate width + + // take foreground and background offline while updating (is almost twice as fast) + var foregroundNextSibling = foreground.nextSibling; + var backgroundNextSibling = background.nextSibling; + foreground.parentNode && foreground.parentNode.removeChild(foreground); + background.parentNode && background.parentNode.removeChild(background); + + foreground.style.height = this.props.height + 'px'; + + this._repaintLabels(); + + // put DOM online again (at the same place) + if (foregroundNextSibling) { + parent.insertBefore(foreground, foregroundNextSibling); } - if (top < this.padding) { - top = this.padding; + else { + parent.appendChild(foreground) } - - var left = this.x; - if (left + width + this.padding > maxWidth) { - left = maxWidth - width - this.padding; + if (backgroundNextSibling) { + this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); } - if (left < this.padding) { - left = this.padding; + else { + this.body.dom.backgroundVertical.appendChild(background) } - this.frame.style.left = left + "px"; - this.frame.style.top = top + "px"; - this.frame.style.visibility = "visible"; - } - else { - this.hide(); - } -}; - -/** - * Hide the popup window - */ -Popup.prototype.hide = function () { - this.frame.style.visibility = "hidden"; -}; + return this._isResized() || parentChanged; + }; -/** - * @class Groups - * This class can store groups and properties specific for groups. - */ -function Groups() { - this.clear(); - this.defaultIndex = 0; -} + /** + * Repaint major and minor text labels and vertical grid lines + * @private + */ + TimeAxis.prototype._repaintLabels = function () { + var orientation = this.options.orientation; + // calculate range and step (step such that we have space for 7 characters per label) + var start = util.convert(this.body.range.start, 'Number'), + 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; + + // Move all DOM elements to a "redundant" list, where they + // can be picked for re-use, and clear the lists with lines and texts. + // At the end of the function _repaintLabels, left over elements will be cleaned up + var dom = this.dom; + dom.redundant.majorLines = dom.majorLines; + dom.redundant.majorTexts = dom.majorTexts; + dom.redundant.minorLines = dom.minorLines; + dom.redundant.minorTexts = dom.minorTexts; + dom.majorLines = []; + dom.majorTexts = []; + dom.minorLines = []; + dom.minorTexts = []; + + step.first(); + var xFirstMajorLabel = undefined; + var max = 0; + while (step.hasNext() && max < 1000) { + max++; + var cur = step.getCurrent(), + x = this.body.util.toScreen(cur), + isMajor = step.isMajor(); + + // TODO: lines must have a width, such that we can create css backgrounds + + if (this.options.showMinorLabels) { + this._repaintMinorText(x, step.getLabelMinor(), orientation); + } + + if (isMajor && this.options.showMajorLabels) { + if (x > 0) { + if (xFirstMajorLabel == undefined) { + xFirstMajorLabel = x; + } + this._repaintMajorText(x, step.getLabelMajor(), orientation); + } + this._repaintMajorLine(x, orientation); + } + else { + this._repaintMinorLine(x, orientation); + } -/** - * default constants for group colors - */ -Groups.DEFAULT = [ - {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue - {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}}, // yellow - {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}}, // red - {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}}, // green - {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}}, // magenta - {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}}, // purple - {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}}, // orange - {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue - {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}}, // pink - {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}} // mint -]; + step.next(); + } + // create a major label on the left when needed + if (this.options.showMajorLabels) { + var leftTime = this.body.util.toTime(0), + leftText = step.getLabelMajor(leftTime), + widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation -/** - * Clear all groups - */ -Groups.prototype.clear = function () { - this.groups = {}; - this.groups.length = function() - { - var i = 0; - for ( var p in this ) { - if (this.hasOwnProperty(p)) { - i++; + if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { + this._repaintMajorText(0, leftText, orientation); } } - return i; - } -}; + // Cleanup leftover DOM elements from the redundant list + util.forEach(this.dom.redundant, function (arr) { + while (arr.length) { + var elem = arr.pop(); + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); + } + } + }); + }; -/** - * get group properties of a groupname. If groupname is not found, a new group - * is added. - * @param {*} groupname Can be a number, string, Date, etc. - * @return {Object} group The created group, containing all group properties - */ -Groups.prototype.get = function (groupname) { - var group = this.groups[groupname]; - - if (group == undefined) { - // create new group - var index = this.defaultIndex % Groups.DEFAULT.length; - this.defaultIndex++; - group = {}; - group.color = Groups.DEFAULT[index]; - this.groups[groupname] = group; - } - - return group; -}; - -/** - * Add a custom group style - * @param {String} groupname - * @param {Object} style An object containing borderColor, - * backgroundColor, etc. - * @return {Object} group The created group object - */ -Groups.prototype.add = function (groupname, style) { - this.groups[groupname] = style; - if (style.color) { - style.color = util.parseColor(style.color); - } - return style; -}; - -/** - * @class Images - * This class loads images and keeps them stored. - */ -function Images() { - this.images = {}; + /** + * Create a minor label for the axis at position x + * @param {Number} x + * @param {String} text + * @param {String} orientation "top" or "bottom" (default) + * @private + */ + TimeAxis.prototype._repaintMinorText = function (x, text, orientation) { + // reuse redundant label + var label = this.dom.redundant.minorTexts.shift(); - this.callback = undefined; -} + if (!label) { + // create new label + var content = document.createTextNode(''); + label = document.createElement('div'); + label.appendChild(content); + label.className = 'text minor'; + this.dom.foreground.appendChild(label); + } + this.dom.minorTexts.push(label); -/** - * Set an onload callback function. This will be called each time an image - * is loaded - * @param {function} callback - */ -Images.prototype.setOnloadCallback = function(callback) { - this.callback = callback; -}; + label.childNodes[0].nodeValue = text; -/** - * - * @param {string} url Url of the image - * @return {Image} img The image object - */ -Images.prototype.load = function(url) { - var img = this.images[url]; - if (img == undefined) { - // create the image - var images = this; - img = new Image(); - this.images[url] = img; - img.onload = function() { - if (images.callback) { - images.callback(this); - } - }; - img.src = url; - } + label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; + label.style.left = x + 'px'; + //label.title = title; // TODO: this is a heavy operation + }; - return img; -}; + /** + * Create a Major label for the axis at position x + * @param {Number} x + * @param {String} text + * @param {String} orientation "top" or "bottom" (default) + * @private + */ + TimeAxis.prototype._repaintMajorText = function (x, text, orientation) { + // reuse redundant label + var label = this.dom.redundant.majorTexts.shift(); -/** - * Created by Alex on 2/6/14. - */ + if (!label) { + // create label + var content = document.createTextNode(text); + label = document.createElement('div'); + label.className = 'text major'; + label.appendChild(content); + this.dom.foreground.appendChild(label); + } + this.dom.majorTexts.push(label); + label.childNodes[0].nodeValue = text; + //label.title = title; // TODO: this is a heavy operation -var physicsMixin = { + label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); + label.style.left = x + 'px'; + }; /** - * Toggling barnes Hut calculation on and off. - * + * Create a minor line for the axis at position x + * @param {Number} x + * @param {String} orientation "top" or "bottom" (default) * @private */ - _toggleBarnesHut: function () { - this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; - this._loadSelectedForceSolver(); - this.moving = true; - this.start(); - }, + TimeAxis.prototype._repaintMinorLine = function (x, orientation) { + // reuse redundant line + var line = this.dom.redundant.minorLines.shift(); + if (!line) { + // create vertical line + line = document.createElement('div'); + line.className = 'grid vertical minor'; + this.dom.background.appendChild(line); + } + this.dom.minorLines.push(line); + + var props = this.props; + if (orientation == 'top') { + line.style.top = props.majorLabelHeight + 'px'; + } + else { + line.style.top = this.body.domProps.top.height + 'px'; + } + line.style.height = props.minorLineHeight + 'px'; + line.style.left = (x - props.minorLineWidth / 2) + 'px'; + }; /** - * This loads the node force solver based on the barnes hut or repulsion algorithm - * + * Create a Major line for the axis at position x + * @param {Number} x + * @param {String} orientation "top" or "bottom" (default) * @private */ - _loadSelectedForceSolver: function () { - // this overloads the this._calculateNodeForces - if (this.constants.physics.barnesHut.enabled == true) { - this._clearMixin(repulsionMixin); - this._clearMixin(hierarchalRepulsionMixin); - - this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; - this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; - this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; - this.constants.physics.damping = this.constants.physics.barnesHut.damping; + TimeAxis.prototype._repaintMajorLine = function (x, orientation) { + // reuse redundant line + var line = this.dom.redundant.majorLines.shift(); - this._loadMixin(barnesHutMixin); + if (!line) { + // create vertical line + line = document.createElement('DIV'); + line.className = 'grid vertical major'; + this.dom.background.appendChild(line); } - else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._clearMixin(barnesHutMixin); - this._clearMixin(repulsionMixin); - - this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; - this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; + this.dom.majorLines.push(line); - this._loadMixin(hierarchalRepulsionMixin); + var props = this.props; + if (orientation == 'top') { + line.style.top = '0'; } else { - this._clearMixin(barnesHutMixin); - this._clearMixin(hierarchalRepulsionMixin); - this.barnesHutTree = undefined; - - this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.repulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; - this.constants.physics.damping = this.constants.physics.repulsion.damping; - - this._loadMixin(repulsionMixin); + line.style.top = this.body.domProps.top.height + 'px'; } - }, + line.style.left = (x - props.majorLineWidth / 2) + 'px'; + line.style.height = props.majorLineHeight + 'px'; + }; /** - * Before calculating the forces, we check if we need to cluster to keep up performance and we check - * if there is more than one node. If it is just one node, we dont calculate anything. - * + * 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 */ - _initializeForceCalculation: function () { - // stop calculation if there is only one node - if (this.nodeIndices.length == 1) { - this.nodes[this.nodeIndices[0]]._setForce(0, 0); - } - else { - // if there are too many nodes on screen, we cluster without repositioning - if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { - this.clusterToFit(this.constants.clustering.reduceToNodes, false); - } + TimeAxis.prototype._calculateCharSize = function () { + // Note: We calculate char size with every redraw. Size may change, for + // example when any of the timelines parents had display:none for example. - // we now start the force calculation - this._calculateForces(); + // determine the char width and height on the minor axis + if (!this.dom.measureCharMinor) { + this.dom.measureCharMinor = document.createElement('DIV'); + this.dom.measureCharMinor.className = 'text minor measure'; + this.dom.measureCharMinor.style.position = 'absolute'; + + this.dom.measureCharMinor.appendChild(document.createTextNode('0')); + this.dom.foreground.appendChild(this.dom.measureCharMinor); } - }, + this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; + this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; + // determine the char width and height on the major axis + if (!this.dom.measureCharMajor) { + this.dom.measureCharMajor = document.createElement('DIV'); + this.dom.measureCharMajor.className = 'text minor measure'; + this.dom.measureCharMajor.style.position = 'absolute'; + + this.dom.measureCharMajor.appendChild(document.createTextNode('0')); + this.dom.foreground.appendChild(this.dom.measureCharMajor); + } + this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; + this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; + }; /** - * Calculate the external forces acting on the nodes - * Forces are caused by: edges, repulsing forces between nodes, gravity - * @private + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate */ - _calculateForces: function () { - // Gravity is required to keep separated groups from floating off - // the forces are reset to zero in this loop by using _setForce instead - // of _addForce + TimeAxis.prototype.snap = function(date) { + return this.step.snap(date); + }; - this._calculateGravitationalForces(); - this._calculateNodeForces(); + module.exports = TimeAxis; - if (this.constants.smoothCurves == true) { - this._calculateSpringForcesWithSupport(); - } - else { - if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._calculateHierarchicalSpringForces(); - } - else { - this._calculateSpringForces(); - } - } - }, +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(49); /** - * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also - * handled in the calculateForces function. We then use a quadratic curve with the center node as control. - * This function joins the datanodes and invisible (called support) nodes into one object. - * We do this so we do not contaminate this.nodes with the support nodes. - * - * @private + * @constructor Item + * @param {Object} data Object containing (optional) parameters type, + * start, end, content, group, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} options Configuration options + * // TODO: describe available options */ - _updateCalculationNodes: function () { - if (this.constants.smoothCurves == true) { - this.calculationNodes = {}; - this.calculationNodeIndices = []; + function Item (data, conversion, options) { + this.id = null; + this.parent = null; + this.data = data; + this.dom = null; + this.conversion = conversion || {}; + this.options = options || {}; + + this.selected = false; + this.displayed = false; + this.dirty = true; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId] = this.nodes[nodeId]; - } - } - var supportNodes = this.sectors['support']['nodes']; - for (var supportNodeId in supportNodes) { - if (supportNodes.hasOwnProperty(supportNodeId)) { - if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { - this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; - } - else { - supportNodes[supportNodeId]._setForce(0, 0); - } - } - } + this.top = null; + this.left = null; + this.width = null; + this.height = null; + } - for (var idx in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(idx)) { - this.calculationNodeIndices.push(idx); - } + /** + * Select current item + */ + Item.prototype.select = function() { + this.selected = true; + if (this.displayed) this.redraw(); + }; + + /** + * Unselect current item + */ + Item.prototype.unselect = function() { + this.selected = false; + if (this.displayed) this.redraw(); + }; + + /** + * Set a parent for the item + * @param {ItemSet | Group} parent + */ + Item.prototype.setParent = function(parent) { + if (this.displayed) { + this.hide(); + this.parent = parent; + if (this.parent) { + this.show(); } } else { - this.calculationNodes = this.nodes; - this.calculationNodeIndices = this.nodeIndices; + this.parent = parent; } - }, - + }; /** - * this function applies the central gravity effect to keep groups from floating off - * - * @private + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - _calculateGravitationalForces: function () { - var dx, dy, distance, node, i; - var nodes = this.calculationNodes; - var gravity = this.constants.physics.centralGravity; - var gravityForce = 0; + Item.prototype.isVisible = function(range) { + // Should be implemented by Item implementations + return false; + }; - for (i = 0; i < this.calculationNodeIndices.length; i++) { - node = nodes[this.calculationNodeIndices[i]]; - node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. - // gravity does not apply when we are in a pocket sector - if (this._sector() == "default" && gravity != 0) { - dx = -node.x; - dy = -node.y; - distance = Math.sqrt(dx * dx + dy * dy); + /** + * Show the Item in the DOM (when not already visible) + * @return {Boolean} changed + */ + Item.prototype.show = function() { + return false; + }; - gravityForce = (distance == 0) ? 0 : (gravity / distance); - node.fx = dx * gravityForce; - node.fy = dy * gravityForce; - } - else { - node.fx = 0; - node.fy = 0; - } - } - }, + /** + * Hide the Item from the DOM (when visible) + * @return {Boolean} changed + */ + Item.prototype.hide = function() { + return false; + }; + /** + * Repaint the item + */ + Item.prototype.redraw = function() { + // should be implemented by the item + }; + /** + * Reposition the Item horizontally + */ + Item.prototype.repositionX = function() { + // should be implemented by the item + }; + /** + * Reposition the Item vertically + */ + Item.prototype.repositionY = function() { + // should be implemented by the item + }; /** - * this function calculates the effects of the springs in the case of unsmooth curves. - * - * @private + * Repaint a delete button on the top right of the item when the item is selected + * @param {HTMLElement} anchor + * @protected */ - _calculateSpringForces: function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; + Item.prototype._repaintDeleteButton = function (anchor) { + if (this.selected && this.options.editable.remove && !this.dom.deleteButton) { + // create and show button + var me = this; - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + var deleteButton = document.createElement('div'); + deleteButton.className = 'delete'; + deleteButton.title = 'Delete this item'; - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); + Hammer(deleteButton, { + preventDefault: true + }).on('tap', function (event) { + me.parent.removeFromDataSet(me); + event.stopPropagation(); + }); - if (distance == 0) { - distance = 0.01; - } + anchor.appendChild(deleteButton); + this.dom.deleteButton = deleteButton; + } + else if (!this.selected && this.dom.deleteButton) { + // remove button + if (this.dom.deleteButton.parentNode) { + this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); + } + this.dom.deleteButton = null; + } + }; - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + module.exports = Item; - fx = dx * springForce; - fy = dy * springForce; - edge.from.fx += fx; - edge.from.fy += fy; - edge.to.fx -= fx; - edge.to.fy -= fy; - } - } +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + + var Item = __webpack_require__(22); + + /** + * @constructor ItemBox + * @extends Item + * @param {Object} data Object containing parameters start + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe available options + */ + function ItemBox (data, conversion, options) { + this.props = { + dot: { + width: 0, + height: 0 + }, + line: { + width: 0, + height: 0 + } + }; + + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data); } } - }, + Item.call(this, data, conversion, options); + } + 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(range) { + // determine visibility + // TODO: account for the real width of the item. Right now we just add 1/4 to the window + var interval = (range.end - range.start) / 4; + return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + }; /** - * This function calculates the springforces on the nodes, accounting for the support nodes. - * - * @private + * Repaint the item */ - _calculateSpringForcesWithSupport: function () { - var edgeLength, edge, edgeId, combinedClusterSize; - var edges = this.edges; + ItemBox.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - if (edge.via != null) { - var node1 = edge.to; - var node2 = edge.via; - var node3 = edge.from; + // create main box + dom.box = document.createElement('DIV'); - edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; + // contents box (inside the background box). used for making margins + dom.content = document.createElement('DIV'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + // line to axis + dom.line = document.createElement('DIV'); + dom.line.className = 'line'; - // this implies that the edges between big clusters are longer - edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; - this._calculateSpringForce(node1, node2, 0.5 * edgeLength); - this._calculateSpringForce(node2, node3, 0.5 * edgeLength); - } - } - } - } + // dot on axis + dom.dot = document.createElement('DIV'); + dom.dot.className = 'dot'; + + // attach this item as attribute + dom.box['timeline-item'] = this; } - }, + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) throw new Error('Cannot redraw time axis: parent has no foreground container element'); + foreground.appendChild(dom.box); + } + if (!dom.line.parentNode) { + var background = this.parent.dom.background; + if (!background) throw new Error('Cannot redraw time axis: parent has no background container element'); + background.appendChild(dom.line); + } + if (!dom.dot.parentNode) { + var axis = this.parent.dom.axis; + if (!background) throw new Error('Cannot redraw time axis: parent has no axis container element'); + axis.appendChild(dom.dot); + } + this.displayed = true; - /** - * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. - * - * @param node1 - * @param node2 - * @param edgeLength - * @private - */ - _calculateSpringForce: function (node1, node2, edgeLength) { - var dx, dy, fx, fy, springForce, distance; + // update contents + if (this.data.content != this.content) { + this.content = this.data.content; + if (this.content instanceof Element) { + dom.content.innerHTML = ''; + dom.content.appendChild(this.content); + } + else if (this.data.content != undefined) { + dom.content.innerHTML = this.content; + } + else { + throw new Error('Property "content" missing in item ' + this.data.id); + } - dx = (node1.x - node2.x); - dy = (node1.y - node2.y); - distance = Math.sqrt(dx * dx + dy * dy); + this.dirty = true; + } - if (distance == 0) { - distance = 0.01; + // update title + if (this.data.title != this.title) { + dom.box.title = this.data.title; + this.title = this.data.title; } - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + // update class + var className = (this.data.className? ' ' + this.data.className : '') + + (this.selected ? ' selected' : ''); + if (this.className != className) { + this.className = className; + dom.box.className = 'item box' + className; + dom.line.className = 'item line' + className; + dom.dot.className = 'item dot' + className; - fx = dx * springForce; - fy = dy * springForce; + this.dirty = true; + } - node1.fx += fx; - node1.fy += fy; - node2.fx -= fx; - node2.fy -= fy; - }, + // recalculate size + if (this.dirty) { + this.props.dot.height = dom.dot.offsetHeight; + this.props.dot.width = dom.dot.offsetWidth; + this.props.line.width = dom.line.offsetWidth; + this.width = dom.box.offsetWidth; + this.height = dom.box.offsetHeight; + + this.dirty = false; + } + this._repaintDeleteButton(dom.box); + }; /** - * Load the HTML for the physics config and bind it - * @private + * Show the item in the DOM (when not already displayed). The items DOM will + * be created when needed. */ - _loadPhysicsConfiguration: function () { - if (this.physicsConfiguration === undefined) { - this.backupConstants = {}; - util.deepExtend(this.backupConstants,this.constants); - - var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; - this.physicsConfiguration = document.createElement('div'); - this.physicsConfiguration.className = "PhysicsConfiguration"; - this.physicsConfiguration.innerHTML = '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Options:
' - this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); - this.optionsDiv = document.createElement("div"); - this.optionsDiv.style.fontSize = "14px"; - this.optionsDiv.style.fontFamily = "verdana"; - this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); + ItemBox.prototype.show = function() { + if (!this.displayed) { + this.redraw(); + } + }; - var rangeElement; - rangeElement = document.getElementById('graph_BH_gc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); - rangeElement = document.getElementById('graph_BH_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_BH_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_BH_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_BH_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); + /** + * Hide the item from the DOM (when visible) + */ + ItemBox.prototype.hide = function() { + if (this.displayed) { + var dom = this.dom; - rangeElement = document.getElementById('graph_R_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); - rangeElement = document.getElementById('graph_R_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_R_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_R_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_R_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); + if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); + if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); - rangeElement = document.getElementById('graph_H_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); - rangeElement = document.getElementById('graph_H_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_H_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_H_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_H_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); - rangeElement = document.getElementById('graph_H_direction'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); - rangeElement = document.getElementById('graph_H_levsep'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); - rangeElement = document.getElementById('graph_H_nspac'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + this.top = null; + this.left = null; - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - var radioButton3 = document.getElementById("graph_physicsMethod3"); - radioButton2.checked = true; - if (this.constants.physics.barnesHut.enabled) { - radioButton1.checked = true; - } - if (this.constants.hierarchicalLayout.enabled) { - radioButton3.checked = true; - } + this.displayed = false; + } + }; - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - var graph_repositionNodes = document.getElementById("graph_repositionNodes"); - var graph_generateOptions = document.getElementById("graph_generateOptions"); + /** + * Reposition the item horizontally + * @Override + */ + 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, + dot = this.dom.dot; - graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); - graph_repositionNodes.onclick = graphRepositionNodes.bind(this); - graph_generateOptions.onclick = graphGenerateOptions.bind(this); - if (this.constants.smoothCurves == true) { - graph_toggleSmooth.style.background = "#A4FF56"; - } - else { - graph_toggleSmooth.style.background = "#FF8532"; - } + // calculate left position of the box + if (align == 'right') { + this.left = start - this.width; + } + else if (align == 'left') { + this.left = start; + } + else { + // default or 'center' + this.left = start - this.width / 2; + } + // reposition box + box.style.left = this.left + 'px'; - switchConfigurations.apply(this); + // reposition line + line.style.left = (start - this.props.line.width / 2) + 'px'; - radioButton1.onchange = switchConfigurations.bind(this); - radioButton2.onchange = switchConfigurations.bind(this); - radioButton3.onchange = switchConfigurations.bind(this); - } - }, + // reposition dot + dot.style.left = (start - this.props.dot.width / 2) + 'px'; + }; /** - * This overwrites the this.constants. - * - * @param constantsVariableName - * @param value - * @private + * Reposition the item vertically + * @Override */ - _overWriteGraphConstants: function (constantsVariableName, value) { - var nameArray = constantsVariableName.split("_"); - if (nameArray.length == 1) { - this.constants[nameArray[0]] = value; - } - else if (nameArray.length == 2) { - this.constants[nameArray[0]][nameArray[1]] = value; + 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'; + + line.style.top = '0'; + line.style.height = (this.parent.top + this.top + 1) + 'px'; + line.style.bottom = ''; } - else if (nameArray.length == 3) { - this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; + else { // orientation 'bottom' + var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty + var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; + + box.style.top = (this.parent.height - this.top - this.height || 0) + 'px'; + line.style.top = (itemSetHeight - lineHeight) + 'px'; + line.style.bottom = '0'; } - } -}; -/** - * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. - */ -function graphToggleSmoothCurves () { - this.constants.smoothCurves = !this.constants.smoothCurves; - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} + dot.style.top = (-this.props.dot.height / 2) + 'px'; + }; - this._configureSmoothCurves(false); -}; + module.exports = ItemBox; -/** - * this function is used to scramble the nodes - * - */ -function graphRepositionNodes () { - for (var nodeId in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; - this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; - } - } - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); - } - else { - this.repositionNodes(); - } - this.moving = true; - this.start(); -}; -/** - * this is used to generate an options file from the playing with physics system. - */ -function graphGenerateOptions () { - var options = "No options are required, default values used."; - var optionsSpecific = []; - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - if (radioButton1.checked == true) { - if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options = "var options = {"; - options += "physics: {barnesHut: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + var Item = __webpack_require__(22); + + /** + * @constructor ItemPoint + * @extends Item + * @param {Object} data Object containing parameters start + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe available options + */ + function ItemPoint (data, conversion, options) { + this.props = { + dot: { + top: 0, + width: 0, + height: 0 + }, + content: { + height: 0, + marginLeft: 0 + } + }; + + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data); } - options += '}}' - } - if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { - if (optionsSpecific.length == 0) {options = "var options = {";} - else {options += ", "} - options += "smoothCurves: " + this.constants.smoothCurves; - } - if (options != "No options are required, default values used.") { - options += '};' - } - } - else if (radioButton2.checked == true) { - options = "var options = {"; - options += "physics: {barnesHut: {enabled: false}"; - if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += ", repulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}}' - } - if (optionsSpecific.length == 0) {options += "}"} - if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { - options += ", smoothCurves: " + this.constants.smoothCurves; - } - options += '};' - } - else { - options = "var options = {"; - if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += "physics: {hierarchicalRepulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", "; - } - } - options += '}},'; - } - options += 'hierarchicalLayout: {'; - optionsSpecific = []; - if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} - if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} - if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} - if (optionsSpecific.length != 0) { - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}' - } - else { - options += "enabled:true}"; } - options += '};' + + Item.call(this, data, conversion, options); } + 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(range) { + // determine visibility + // TODO: account for the real width of the item. Right now we just add 1/4 to the window + var interval = (range.end - range.start) / 4; + return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + }; + + /** + * Repaint the item + */ + ItemPoint.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - this.optionsDiv.innerHTML = options; + // background box + dom.point = document.createElement('div'); + // className is updated in redraw() -}; + // contents box, right from the dot + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.point.appendChild(dom.content); -/** - * this is used to switch between barnesHut, repulsion and hierarchical. - * - */ -function switchConfigurations () { - var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; - var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; - var tableId = "graph_" + radioButton + "_table"; - var table = document.getElementById(tableId); - table.style.display = "block"; - for (var i = 0; i < ids.length; i++) { - if (ids[i] != tableId) { - table = document.getElementById(ids[i]); - table.style.display = "none"; + // dot at start + dom.dot = document.createElement('div'); + dom.point.appendChild(dom.dot); + + // attach this item as attribute + dom.point['timeline-item'] = this; } - } - this._restoreNodes(); - if (radioButton == "R") { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = false; - } - else if (radioButton == "H") { - if (this.constants.hierarchicalLayout.enabled == false) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - this._setupHierarchicalLayout(); + + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); } - } - else { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = true; - } - this._loadSelectedForceSolver(); - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} - this.moving = true; - this.start(); + if (!dom.point.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) { + throw new Error('Cannot redraw time axis: parent has no foreground container element'); + } + foreground.appendChild(dom.point); + } + this.displayed = true; -} + // update contents + if (this.data.content != this.content) { + this.content = this.data.content; + if (this.content instanceof Element) { + dom.content.innerHTML = ''; + dom.content.appendChild(this.content); + } + else if (this.data.content != undefined) { + dom.content.innerHTML = this.content; + } + else { + throw new Error('Property "content" missing in item ' + this.data.id); + } + this.dirty = true; + } -/** - * this generates the ranges depending on the iniital values. - * - * @param id - * @param map - * @param constantsVariableName - */ -function showValueOfRange (id,map,constantsVariableName) { - var valueId = id + "_value"; - var rangeValue = document.getElementById(id).value; + // update title + if (this.data.title != this.title) { + dom.point.title = this.data.title; + this.title = this.data.title; + } - if (map instanceof Array) { - document.getElementById(valueId).value = map[parseInt(rangeValue)]; - this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); - } - else { - document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); - this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); - } + // update class + var className = (this.data.className? ' ' + this.data.className : '') + + (this.selected ? ' selected' : ''); + if (this.className != className) { + this.className = className; + dom.point.className = 'item point' + className; + dom.dot.className = 'item dot' + className; - if (constantsVariableName == "hierarchicalLayout_direction" || - constantsVariableName == "hierarchicalLayout_levelSeparation" || - constantsVariableName == "hierarchicalLayout_nodeSpacing") { - this._setupHierarchicalLayout(); - } - this.moving = true; - this.start(); -}; + this.dirty = true; + } + // recalculate size + if (this.dirty) { + this.width = dom.point.offsetWidth; + this.height = dom.point.offsetHeight; + this.props.dot.width = dom.dot.offsetWidth; + this.props.dot.height = dom.dot.offsetHeight; + this.props.content.height = dom.content.offsetHeight; + // resize contents + dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; + //dom.content.style.marginRight = ... + 'px'; // TODO: margin right -/** - * Created by Alex on 2/10/14. - */ + dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; + dom.dot.style.left = (this.props.dot.width / 2) + 'px'; -var hierarchalRepulsionMixin = { + this.dirty = false; + } + this._repaintDeleteButton(dom.point); + }; /** - * Calculate the forces the nodes apply on eachother based on a repulsion field. - * This field is linearly approximated. - * - * @private + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - _calculateNodeForces: function () { - var dx, dy, distance, fx, fy, combinedClusterSize, - repulsingForce, node1, node2, i, j; + ItemPoint.prototype.show = function() { + if (!this.displayed) { + this.redraw(); + } + }; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; + /** + * Hide the item from the DOM (when visible) + */ + ItemPoint.prototype.hide = function() { + if (this.displayed) { + if (this.dom.point.parentNode) { + this.dom.point.parentNode.removeChild(this.dom.point); + } - // approximation constants - var b = 5; - var a_base = 0.5 * -b; + this.top = null; + this.left = null; + this.displayed = false; + } + }; - // repulsing forces between nodes - var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; - var minimumDistance = nodeDistance; - var a = a_base / minimumDistance; + /** + * Reposition the item horizontally + * @Override + */ + ItemPoint.prototype.repositionX = function() { + var start = this.conversion.toScreen(this.data.start); - // we loop from i over all but the last entree in the array - // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j - for (i = 0; i < nodeIndices.length - 1; i++) { + this.left = start - this.props.dot.width; - node1 = nodes[nodeIndices[i]]; - for (j = i + 1; j < nodeIndices.length; j++) { - node2 = nodes[nodeIndices[j]]; - if (node1.level == node2.level) { + // reposition point + this.dom.point.style.left = this.left + 'px'; + }; - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); + /** + * Reposition the item vertically + * @Override + */ + ItemPoint.prototype.repositionY = function() { + var orientation = this.options.orientation, + point = this.dom.point; + if (orientation == 'top') { + point.style.top = this.top + 'px'; + } + else { + point.style.top = (this.parent.height - this.top - this.height) + 'px'; + } + }; - if (distance < 2 * minimumDistance) { - repulsingForce = a * distance + b; - var c = 0.05; - var d = 2 * minimumDistance * 2 * c; - repulsingForce = c * Math.pow(distance,2) - d * distance + d*d/(4*c); + module.exports = ItemPoint; - // normalize force with - if (distance == 0) { - distance = 0.01; - } - else { - repulsingForce = repulsingForce / distance; - } - fx = dx * repulsingForce; - fy = dy * repulsingForce; - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; - } - } +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(49); + var Item = __webpack_require__(22); + + /** + * @constructor ItemRange + * @extends Item + * @param {Object} data Object containing parameters start, end + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe options + */ + function ItemRange (data, conversion, options) { + this.props = { + content: { + width: 0 + } + }; + this.overflow = false; // if contents can overflow (css styling), this flag is set to true + + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data.id); + } + if (data.end == undefined) { + throw new Error('Property "end" missing in item ' + data.id); } } - }, + Item.call(this, data, conversion, options); + } + + ItemRange.prototype = new Item (null, null, null); + + ItemRange.prototype.baseClassName = 'item range'; /** - * this function calculates the effects of the springs in the case of unsmooth curves. - * - * @private + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - _calculateHierarchicalSpringForces: function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; + ItemRange.prototype.isVisible = function(range) { + // determine visibility + return (this.data.start < range.end) && (this.data.end > range.start); + }; - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + /** + * Repaint the item + */ + ItemRange.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); + // background box + dom.box = document.createElement('div'); + // className is updated in redraw() - if (distance == 0) { - distance = 0.01; - } + // contents box + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - distance = Math.max(0.8*edgeLength,Math.min(5*edgeLength, distance)); + // attach this item as attribute + dom.box['timeline-item'] = this; + } - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) { + throw new Error('Cannot redraw time axis: parent has no foreground container element'); + } + foreground.appendChild(dom.box); + } + this.displayed = true; - fx = dx * springForce; - fy = dy * springForce; + // update contents + if (this.data.content != this.content) { + this.content = this.data.content; + if (this.content instanceof Element) { + dom.content.innerHTML = ''; + dom.content.appendChild(this.content); + } + else if (this.data.content != undefined) { + dom.content.innerHTML = this.content; + } + else { + throw new Error('Property "content" missing in item ' + this.data.id); + } - edge.to.fx -= fx; - edge.to.fy -= fy; - edge.from.fx += fx; - edge.from.fy += fy; + this.dirty = true; + } + // update title + if (this.data.title != this.title) { + dom.box.title = this.data.title; + this.title = this.data.title; + } - var factor = 5; - if (distance > edgeLength) { - factor = 25; - } + // update class + var className = (this.data.className ? (' ' + this.data.className) : '') + + (this.selected ? ' selected' : ''); + if (this.className != className) { + this.className = className; + dom.box.className = this.baseClassName + className; - if (edge.from.level > edge.to.level) { - edge.to.fx -= factor*fx; - edge.to.fy -= factor*fy; - } - else if (edge.from.level < edge.to.level) { - edge.from.fx += factor*fx; - edge.from.fy += factor*fy; - } - } - } - } + this.dirty = true; } - } -}; -/** - * Created by Alex on 2/10/14. - */ -var barnesHutMixin = { + // recalculate size + if (this.dirty) { + // determine from css whether this box has overflow + this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; + + this.props.content.width = this.dom.content.offsetWidth; + this.height = this.dom.box.offsetHeight; + + this.dirty = false; + } + + this._repaintDeleteButton(dom.box); + this._repaintDragLeft(); + this._repaintDragRight(); + }; /** - * This function calculates the forces the nodes apply on eachother based on a gravitational model. - * The Barnes Hut method is used to speed up this N-body simulation. - * - * @private + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - _calculateNodeForces : function() { - if (this.constants.physics.barnesHut.gravitationalConstant != 0) { - var node; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - var nodeCount = nodeIndices.length; + ItemRange.prototype.show = function() { + if (!this.displayed) { + this.redraw(); + } + }; - this._formBarnesHutTree(nodes,nodeIndices); + /** + * Hide the item from the DOM (when visible) + * @return {Boolean} changed + */ + ItemRange.prototype.hide = function() { + if (this.displayed) { + var box = this.dom.box; - var barnesHutTree = this.barnesHutTree; + if (box.parentNode) { + box.parentNode.removeChild(box); + } - // place the nodes one by one recursively - for (var i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - // starting with root is irrelevant, it never passes the BarnesHut condition - this._getForceContribution(barnesHutTree.root.children.NW,node); - this._getForceContribution(barnesHutTree.root.children.NE,node); - this._getForceContribution(barnesHutTree.root.children.SW,node); - this._getForceContribution(barnesHutTree.root.children.SE,node); + this.top = null; + this.left = null; + + this.displayed = false; + } + }; + + /** + * Reposition the item horizontally + * @Override + */ + // TODO: delete the old function + ItemRange.prototype.repositionX = function() { + var props = this.props, + parentWidth = this.parent.width, + 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 + if (start < -parentWidth) { + start = -parentWidth; + } + if (end > 2 * parentWidth) { + end = 2 * parentWidth; + } + var boxWidth = Math.max(end - start, 1); + + if (this.overflow) { + // when range exceeds left of the window, position the contents at the left of the visible area + contentLeft = Math.max(-start, 0); + + this.left = start; + this.width = boxWidth + this.props.content.width; + // Note: The calculation of width is an optimistic calculation, giving + // a width which will not change when moving the Timeline + // So no restacking needed, which is nicer for the eye; + } + else { // no overflow + // when range exceeds left of the window, position the contents at the left of the visible area + if (start < 0) { + contentLeft = Math.min(-start, + (end - start - props.content.width - 2 * padding)); + // TODO: remove the need for options.padding. it's terrible. + } + else { + contentLeft = 0; } + + this.left = start; + this.width = boxWidth; } - }, + this.dom.box.style.left = this.left + 'px'; + this.dom.box.style.width = boxWidth + 'px'; + this.dom.content.style.left = contentLeft + 'px'; + }; /** - * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. - * If a region contains a single node, we check if it is not itself, then we apply the force. - * - * @param parentBranch - * @param node - * @private + * Reposition the item vertically + * @Override */ - _getForceContribution : function(parentBranch,node) { - // we get no force contribution from an empty region - if (parentBranch.childrenCount > 0) { - var dx,dy,distance; + ItemRange.prototype.repositionY = function() { + var orientation = this.options.orientation, + box = this.dom.box; - // get the distance from the center of mass to the node. - dx = parentBranch.centerOfMass.x - node.x; - dy = parentBranch.centerOfMass.y - node.y; - distance = Math.sqrt(dx * dx + dy * dy); + if (orientation == 'top') { + box.style.top = this.top + 'px'; + } + else { + box.style.top = (this.parent.height - this.top - this.height) + 'px'; + } + }; - // BarnesHut condition - // original condition : s/d < theta = passed === d/s > 1/theta = passed - // calcSize = 1/s --> d * 1/s > 1/theta = passed - if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) { - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.1*Math.random(); - dx = distance; - } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; + /** + * Repaint a drag area on the left side of the range when the range is selected + * @protected + */ + ItemRange.prototype._repaintDragLeft = function () { + if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) { + // create and show drag area + var dragLeft = document.createElement('div'); + dragLeft.className = 'drag-left'; + dragLeft.dragLeftItem = this; + + // TODO: this should be redundant? + Hammer(dragLeft, { + preventDefault: true + }).on('drag', function () { + //console.log('drag left') + }); + + this.dom.box.appendChild(dragLeft); + this.dom.dragLeft = dragLeft; + } + else if (!this.selected && this.dom.dragLeft) { + // delete drag area + if (this.dom.dragLeft.parentNode) { + this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); } - else { - // Did not pass the condition, go into children if available - if (parentBranch.childrenCount == 4) { - this._getForceContribution(parentBranch.children.NW,node); - this._getForceContribution(parentBranch.children.NE,node); - this._getForceContribution(parentBranch.children.SW,node); - this._getForceContribution(parentBranch.children.SE,node); - } - else { // parentBranch must have only one node, if it was empty we wouldnt be here - if (parentBranch.children.data.id != node.id) { // if it is not self - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.5*Math.random(); - dx = distance; - } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; - } - } + this.dom.dragLeft = null; + } + }; + + /** + * Repaint a drag area on the right side of the range when the range is selected + * @protected + */ + ItemRange.prototype._repaintDragRight = function () { + if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { + // create and show drag area + var dragRight = document.createElement('div'); + dragRight.className = 'drag-right'; + dragRight.dragRightItem = this; + + // TODO: this should be redundant? + Hammer(dragRight, { + preventDefault: true + }).on('drag', function () { + //console.log('drag right') + }); + + this.dom.box.appendChild(dragRight); + this.dom.dragRight = dragRight; + } + else if (!this.selected && this.dom.dragRight) { + // delete drag area + if (this.dom.dragRight.parentNode) { + this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); } + this.dom.dragRight = null; } - }, + }; + + module.exports = ItemRange; + + +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { + + var Emitter = __webpack_require__(41); + var Hammer = __webpack_require__(49); + var mousetrap = __webpack_require__(42); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var dotparser = __webpack_require__(32); + var Groups = __webpack_require__(28); + var Images = __webpack_require__(29); + var Node = __webpack_require__(30); + var Edge = __webpack_require__(27); + var Popup = __webpack_require__(31); + var MixinLoader = __webpack_require__(40); + + // Load custom shapes into CanvasRenderingContext2D + __webpack_require__(37); /** - * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * @constructor Network + * Create a network visualization, displaying nodes and edges. * - * @param nodes - * @param nodeIndices - * @private + * @param {Element} container The DOM element in which the Network will + * be created. Normally a div element. + * @param {Object} data An object containing parameters + * {Array} nodes + * {Array} edges + * @param {Object} options Options */ - _formBarnesHutTree : function(nodes,nodeIndices) { - var node; - var nodeCount = nodeIndices.length; + function Network (container, data, options) { + if (!(this instanceof Network)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this._initializeMixinLoaders(); + + // create variables and set default values + this.containerElement = container; + this.width = '100%'; + this.height = '100%'; + + // render and calculation settings + this.renderRefreshRate = 60; // hz (fps) + 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.50; // discrete stepsize of the simulation + + this.stabilize = true; // stabilize before displaying the network + this.selectable = true; + this.initializing = true; + + // these functions are triggered when the dataset is edited + this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; + + + // set constant values + this.constants = { + nodes: { + radiusMin: 5, + radiusMax: 20, + radius: 5, + shape: 'ellipse', + image: undefined, + widthMin: 16, // px + widthMax: 64, // px + fixed: false, + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + level: -1, + color: { + border: '#2B7CE9', + background: '#97C2FC', + highlight: { + border: '#2B7CE9', + background: '#D2E5FF' + }, + hover: { + border: '#2B7CE9', + background: '#D2E5FF' + } + }, + borderColor: '#2B7CE9', + backgroundColor: '#97C2FC', + highlightColor: '#D2E5FF', + group: undefined + }, + edges: { + widthMin: 1, + widthMax: 15, + width: 1, + widthSelectionMultiplier: 2, + hoverWidth: 1.5, + style: 'line', + color: { + color:'#848484', + highlight:'#848484', + hover: '#848484' + }, + fontColor: '#343434', + fontSize: 14, // px + fontFace: 'arial', + fontFill: 'white', + arrowScaleFactor: 1, + dash: { + length: 10, + gap: 5, + altLength: undefined + } + }, + configurePhysics:false, + physics: { + barnesHut: { + enabled: true, + theta: 1 / 0.6, // inverted to save time during calculation + gravitationalConstant: -2000, + centralGravity: 0.3, + springLength: 95, + springConstant: 0.04, + damping: 0.09 + }, + repulsion: { + centralGravity: 0.1, + springLength: 200, + springConstant: 0.05, + nodeDistance: 100, + damping: 0.09 + }, + hierarchicalRepulsion: { + enabled: false, + centralGravity: 0.5, + springLength: 150, + springConstant: 0.01, + nodeDistance: 60, + damping: 0.09 + }, + damping: null, + centralGravity: null, + springLength: null, + springConstant: null + }, + clustering: { // Per Node in Cluster = PNiC + enabled: false, // (Boolean) | global on/off switch for clustering. + initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. + clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes + reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this + chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). + clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. + sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. + screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node. + fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px). + maxFontSize: 1000, + forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). + distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). + edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. + nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster. + height: 1, // (px PNiC) | growth of the height per node in cluster. + radius: 1}, // (px PNiC) | growth of the radius per node in cluster. + maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. + activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. + clusterLevelDifference: 2 + }, + navigation: { + enabled: false + }, + keyboard: { + enabled: false, + speed: {x: 10, y: 10, zoom: 0.02} + }, + dataManipulation: { + enabled: false, + initiallyVisible: false + }, + hierarchicalLayout: { + enabled:false, + levelSeparation: 150, + nodeSpacing: 100, + direction: "UD" // UD, DU, LR, RL + }, + freezeForStabilization: false, + smoothCurves: true, + maxVelocity: 10, + minVelocity: 0.1, // px/s + stabilizationIterations: 1000, // maximum number of iteration to stabilize + labels:{ + add:"Add Node", + edit:"Edit", + 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).", + editBoundError:"No edit function has been bound to this button.", + deleteError:"The function for delete does not support two arguments (data, callback).", + deleteClusterError:"Clusters cannot be deleted." + }, + tooltip: { + delay: 300, + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' + } + }, + dragNetwork: true, + dragNodes: true, + zoomable: true, + hover: false + }; + this.hoverObj = {nodes:{},edges:{}}; - var minX = Number.MAX_VALUE, - minY = Number.MAX_VALUE, - maxX =-Number.MAX_VALUE, - maxY =-Number.MAX_VALUE; - // get the range of the nodes - for (var i = 0; i < nodeCount; i++) { - var x = nodes[nodeIndices[i]].x; - var y = nodes[nodeIndices[i]].y; - if (x < minX) { minX = x; } - if (x > maxX) { maxX = x; } - if (y < minY) { minY = y; } - if (y > maxY) { maxY = y; } - } - // make the range a square - var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y - if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize - else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize + // Node variables + var network = this; + this.groups = new Groups(); // object with groups + this.images = new Images(); // object with images + this.images.setOnloadCallback(function () { + network._redraw(); + }); + // keyboard navigation variables + this.xIncrement = 0; + this.yIncrement = 0; + this.zoomIncrement = 0; - var minimumTreeSize = 1e-5; - var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); - var halfRootSize = 0.5 * rootSize; - var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); + // loading all the mixins: + // load the force calculation functions, grouped under the physics system. + this._loadPhysicsSystem(); + // create a frame and canvas + this._create(); + // load the sector system. (mandatory, fully integrated with Network) + this._loadSectorSystem(); + // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) + this._loadClusterSystem(); + // load the selection system. (mandatory, required by Network) + this._loadSelectionSystem(); + // load the selection system. (mandatory, required by Network) + this._loadHierarchySystem(); + + // apply options + this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); + this._setScale(1); + this.setOptions(options); - // construct the barnesHutTree - var barnesHutTree = {root:{ - centerOfMass:{x:0,y:0}, // Center of Mass - mass:0, - range: {minX:centerX-halfRootSize,maxX:centerX+halfRootSize, - minY:centerY-halfRootSize,maxY:centerY+halfRootSize}, + // other vars + this.freezeSimulation = false;// freeze the simulation + this.cachedFunctions = {}; + + // containers for nodes and edges + this.calculationNodes = {}; + this.calculationNodeIndices = []; + this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation + this.nodes = {}; // object with Node objects + this.edges = {}; // object with Edge objects + + // position and scale variables and objects + this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw. + this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw + this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw + this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action + this.scale = 1; // defining the global scale variable in the constructor + this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out + + // datasets or dataviews + this.nodesData = null; // A DataSet or DataView + this.edgesData = null; // A DataSet or DataView + + // create event listeners used to subscribe on the DataSets of the nodes and edges + this.nodesListeners = { + 'add': function (event, params) { + network._addNodes(params.items); + network.start(); + }, + 'update': function (event, params) { + network._updateNodes(params.items); + network.start(); + }, + 'remove': function (event, params) { + network._removeNodes(params.items); + network.start(); + } + }; + this.edgesListeners = { + 'add': function (event, params) { + network._addEdges(params.items); + network.start(); + }, + 'update': function (event, params) { + network._updateEdges(params.items); + network.start(); + }, + 'remove': function (event, params) { + network._removeEdges(params.items); + network.start(); + } + }; - size: rootSize, - calcSize: 1 / rootSize, - children: {data:null}, - maxWidth: 0, - level: 0, - childrenCount: 4 - }}; - this._splitBranch(barnesHutTree.root); + // properties for the animation + this.moving = true; + this.timer = undefined; // Scheduling function. Is definded in this.start(); - // place the nodes one by one recursively - for (i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - this._placeInTree(barnesHutTree.root,node); + // load data (the disable start variable will be the same as the enabled clustering) + this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled); + + // hierarchical layout + this.initializing = false; + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + } + else { + // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here. + if (this.stabilize == false) { + this.zoomExtent(true,this.constants.clustering.enabled); + } } - // make global - this.barnesHutTree = barnesHutTree - }, + // if clustering is disabled, the simulation will have started in the setData function + if (this.constants.clustering.enabled) { + this.startWithClustering(); + } + } + // Extend Network with an Emitter mixin + Emitter(Network.prototype); /** - * this updates the mass of a branch. this is increased by adding a node. + * Get the script path where the vis.js library is located * - * @param parentBranch - * @param node + * @returns {string | null} path Path or null when not found. Path does not + * end with a slash. * @private */ - _updateBranchMass : function(parentBranch, node) { - var totalMass = parentBranch.mass + node.mass; - var totalMassInv = 1/totalMass; - - parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.mass; - parentBranch.centerOfMass.x *= totalMassInv; + Network.prototype._getScriptPath = function() { + var scripts = document.getElementsByTagName( 'script' ); - parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.mass; - parentBranch.centerOfMass.y *= totalMassInv; - - parentBranch.mass = totalMass; - var biggestSize = Math.max(Math.max(node.height,node.radius),node.width); - parentBranch.maxWidth = (parentBranch.maxWidth < biggestSize) ? biggestSize : parentBranch.maxWidth; + // find script named vis.js or vis.min.js + for (var i = 0; i < scripts.length; i++) { + var src = scripts[i].src; + var match = src && /\/?vis(.min)?\.js$/.exec(src); + if (match) { + // return path without the script name + return src.substring(0, src.length - match[0].length); + } + } - }, + return null; + }; /** - * determine in which branch the node will be placed. - * - * @param parentBranch - * @param node - * @param skipMassUpdate + * Find the center position of the network * @private */ - _placeInTree : function(parentBranch,node,skipMassUpdate) { - if (skipMassUpdate != true || skipMassUpdate === undefined) { - // update the mass of the branch. - this._updateBranchMass(parentBranch,node); - } - - if (parentBranch.children.NW.range.maxX > node.x) { // in NW or SW - if (parentBranch.children.NW.range.maxY > node.y) { // in NW - this._placeInRegion(parentBranch,node,"NW"); - } - else { // in SW - this._placeInRegion(parentBranch,node,"SW"); + Network.prototype._getRange = function() { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (minX > (node.x)) {minX = node.x;} + if (maxX < (node.x)) {maxX = node.x;} + if (minY > (node.y)) {minY = node.y;} + if (maxY < (node.y)) {maxY = node.y;} } } - else { // in NE or SE - if (parentBranch.children.NW.range.maxY > node.y) { // in NE - this._placeInRegion(parentBranch,node,"NE"); - } - else { // in SE - this._placeInRegion(parentBranch,node,"SE"); - } + if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { + minY = 0, maxY = 0, minX = 0, maxX = 0; } - }, + return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + }; /** - * actually place the node in a region (or branch) - * - * @param parentBranch - * @param node - * @param region + * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + * @returns {{x: number, y: number}} * @private */ - _placeInRegion : function(parentBranch,node,region) { - switch (parentBranch.children[region].childrenCount) { - case 0: // place node here - parentBranch.children[region].children.data = node; - parentBranch.children[region].childrenCount = 1; - this._updateBranchMass(parentBranch.children[region],node); - break; - case 1: // convert into children - // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) - // we move one node a pixel and we do not put it in the tree. - if (parentBranch.children[region].children.data.x == node.x && - parentBranch.children[region].children.data.y == node.y) { - node.x += Math.random(); - node.y += Math.random(); - } - else { - this._splitBranch(parentBranch.children[region]); - this._placeInTree(parentBranch.children[region],node); - } - break; - case 4: // place in branch - this._placeInTree(parentBranch.children[region],node); - break; - } - }, + Network.prototype._findCenter = function(range) { + return {x: (0.5 * (range.maxX + range.minX)), + y: (0.5 * (range.maxY + range.minY))}; + }; /** - * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch - * after the split is complete. + * center the network * - * @param parentBranch - * @private + * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; */ - _splitBranch : function(parentBranch) { - // if the branch is shaded with a node, replace the node in the new subset. - var containedNode = null; - if (parentBranch.childrenCount == 1) { - containedNode = parentBranch.children.data; - parentBranch.mass = 0; parentBranch.centerOfMass.x = 0; parentBranch.centerOfMass.y = 0; - } - parentBranch.childrenCount = 4; - parentBranch.children.data = null; - this._insertRegion(parentBranch,"NW"); - this._insertRegion(parentBranch,"NE"); - this._insertRegion(parentBranch,"SW"); - this._insertRegion(parentBranch,"SE"); + Network.prototype._centerNetwork = function(range) { + var center = this._findCenter(range); - if (containedNode != null) { - this._placeInTree(parentBranch,containedNode); - } - }, + center.x *= this.scale; + center.y *= this.scale; + center.x -= 0.5 * this.frame.canvas.clientWidth; + center.y -= 0.5 * this.frame.canvas.clientHeight; + + this._setTranslation(-center.x,-center.y); // set at 0,0 + }; /** - * This function subdivides the region into four new segments. - * Specifically, this inserts a single new segment. - * It fills the children section of the parentBranch + * This function zooms out to fit all data on screen based on amount of nodes * - * @param parentBranch - * @param region - * @param parentRange - * @private + * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; + * @param {Boolean} [disableStart] | If true, start is not called. */ - _insertRegion : function(parentBranch, region) { - var minX,maxX,minY,maxY; - var childSize = 0.5 * parentBranch.size; - switch (region) { - case "NW": - minX = parentBranch.range.minX; - maxX = parentBranch.range.minX + childSize; - minY = parentBranch.range.minY; - maxY = parentBranch.range.minY + childSize; - break; - case "NE": - minX = parentBranch.range.minX + childSize; - maxX = parentBranch.range.maxX; - minY = parentBranch.range.minY; - maxY = parentBranch.range.minY + childSize; - break; - case "SW": - minX = parentBranch.range.minX; - maxX = parentBranch.range.minX + childSize; - minY = parentBranch.range.minY + childSize; - maxY = parentBranch.range.maxY; - break; - case "SE": - minX = parentBranch.range.minX + childSize; - maxX = parentBranch.range.maxX; - minY = parentBranch.range.minY + childSize; - maxY = parentBranch.range.maxY; - break; + Network.prototype.zoomExtent = function(initialZoom, disableStart) { + if (initialZoom === undefined) { + initialZoom = false; + } + if (disableStart === undefined) { + disableStart = false; } + var range = this._getRange(); + var zoomLevel; - parentBranch.children[region] = { - centerOfMass:{x:0,y:0}, - mass:0, - range:{minX:minX,maxX:maxX,minY:minY,maxY:maxY}, - size: 0.5 * parentBranch.size, - calcSize: 2 * parentBranch.calcSize, - children: {data:null}, - maxWidth: 0, - level: parentBranch.level+1, - childrenCount: 0 - }; - }, + if (initialZoom == true) { + var numberOfNodes = this.nodeIndices.length; + if (this.constants.smoothCurves == true) { + if (this.constants.clustering.enabled == true && + numberOfNodes >= this.constants.clustering.initialMaxNodes) { + zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + else { + zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + } + else { + if (this.constants.clustering.enabled == true && + numberOfNodes >= this.constants.clustering.initialMaxNodes) { + zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + else { + zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + } + // correct for larger canvasses. + var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600); + zoomLevel *= factor; + } + else { + var xDistance = (Math.abs(range.minX) + Math.abs(range.maxX)) * 1.1; + var yDistance = (Math.abs(range.minY) + Math.abs(range.maxY)) * 1.1; - /** - * This function is for debugging purposed, it draws the tree. - * - * @param ctx - * @param color - * @private - */ - _drawTree : function(ctx,color) { - if (this.barnesHutTree !== undefined) { + var xZoomLevel = this.frame.canvas.clientWidth / xDistance; + var yZoomLevel = this.frame.canvas.clientHeight / yDistance; - ctx.lineWidth = 1; + zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; + } - this._drawBranch(this.barnesHutTree.root,ctx,color); + if (zoomLevel > 1.0) { + zoomLevel = 1.0; + } + + + this._setScale(zoomLevel); + this._centerNetwork(range); + if (disableStart == false) { + this.moving = true; + this.start(); } - }, + }; /** - * This function is for debugging purposes. It draws the branches recursively. - * - * @param branch - * @param ctx - * @param color + * Update the this.nodeIndices with the most recent node index list * @private */ - _drawBranch : function(branch,ctx,color) { - if (color === undefined) { - color = "#FF0000"; - } - - if (branch.childrenCount == 4) { - this._drawBranch(branch.children.NW,ctx); - this._drawBranch(branch.children.NE,ctx); - this._drawBranch(branch.children.SE,ctx); - this._drawBranch(branch.children.SW,ctx); + Network.prototype._updateNodeIndexList = function() { + this._clearNodeIndexList(); + for (var idx in this.nodes) { + if (this.nodes.hasOwnProperty(idx)) { + this.nodeIndices.push(idx); + } } - ctx.strokeStyle = color; - ctx.beginPath(); - ctx.moveTo(branch.range.minX,branch.range.minY); - ctx.lineTo(branch.range.maxX,branch.range.minY); - ctx.stroke(); + }; - ctx.beginPath(); - ctx.moveTo(branch.range.maxX,branch.range.minY); - ctx.lineTo(branch.range.maxX,branch.range.maxY); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(branch.range.maxX,branch.range.maxY); - ctx.lineTo(branch.range.minX,branch.range.maxY); - ctx.stroke(); + /** + * Set nodes and edges, and optionally options as well. + * + * @param {Object} data Object containing parameters: + * {Array | DataSet | DataView} [nodes] Array with nodes + * {Array | DataSet | DataView} [edges] Array with edges + * {String} [dot] String containing data in DOT format + * {Options} [options] Object with options + * @param {Boolean} [disableStart] | optional: disable the calling of the start function. + */ + Network.prototype.setData = function(data, disableStart) { + if (disableStart === undefined) { + disableStart = false; + } - ctx.beginPath(); - ctx.moveTo(branch.range.minX,branch.range.maxY); - ctx.lineTo(branch.range.minX,branch.range.minY); - ctx.stroke(); + if (data && data.dot && (data.nodes || data.edges)) { + throw new SyntaxError('Data must contain either parameter "dot" or ' + + ' parameter pair "nodes" and "edges", but not both.'); + } - /* - if (branch.mass > 0) { - ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); - ctx.stroke(); - } - */ - } + // set options + this.setOptions(data && data.options); -}; -/** - * Created by Alex on 2/10/14. - */ + // set all data + if (data && data.dot) { + // parse DOT file + if(data && data.dot) { + var dotData = dotparser.DOTToGraph(data.dot); + this.setData(dotData); + return; + } + } + else { + this._setNodes(data && data.nodes); + this._setEdges(data && data.edges); + } -var repulsionMixin = { + this._putDataInSector(); + if (!disableStart) { + // find a stable position or start animating to a stable position + if (this.stabilize) { + var me = this; + setTimeout(function() {me._stabilize(); me.start();},0) + } + else { + this.start(); + } + } + }; /** - * Calculate the forces the nodes apply on eachother based on a repulsion field. - * This field is linearly approximated. - * - * @private + * Set options + * @param {Object} options + * @param {Boolean} [initializeView] | set zoom and translation to default. */ - _calculateNodeForces: function () { - var dx, dy, angle, distance, fx, fy, combinedClusterSize, - repulsingForce, node1, node2, i, j; + Network.prototype.setOptions = function (options) { + if (options) { + var prop; + // retrieve parameter values + if (options.width !== undefined) {this.width = options.width;} + if (options.height !== undefined) {this.height = options.height;} + if (options.stabilize !== undefined) {this.stabilize = options.stabilize;} + if (options.selectable !== undefined) {this.selectable = options.selectable;} + if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;} + 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.dragNetwork !== undefined) {this.constants.dragNetwork = options.dragNetwork;} + 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;} + + // TODO: deprecated since version 3.0.0. Cleanup some day + if (options.dragGraph !== undefined) { + throw new Error('Option dragGraph is renamed to dragNetwork'); + } + + if (options.labels !== undefined) { + for (prop in options.labels) { + if (options.labels.hasOwnProperty(prop)) { + this.constants.labels[prop] = options.labels[prop]; + } + } + } - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; + if (options.onAdd) { + this.triggerFunctions.add = options.onAdd; + } - // approximation constants - var a_base = -2 / 3; - var b = 4 / 3; + if (options.onEdit) { + this.triggerFunctions.edit = options.onEdit; + } - // repulsing forces between nodes - var nodeDistance = this.constants.physics.repulsion.nodeDistance; - var minimumDistance = nodeDistance; + if (options.onEditEdge) { + this.triggerFunctions.editEdge = options.onEditEdge; + } - // we loop from i over all but the last entree in the array - // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j - for (i = 0; i < nodeIndices.length - 1; i++) { - node1 = nodes[nodeIndices[i]]; - for (j = i + 1; j < nodeIndices.length; j++) { - node2 = nodes[nodeIndices[j]]; - combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; + if (options.onConnect) { + this.triggerFunctions.connect = options.onConnect; + } - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); + if (options.onDelete) { + this.triggerFunctions.del = options.onDelete; + } - minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); - var a = a_base / minimumDistance; - if (distance < 2 * minimumDistance) { - if (distance < 0.5 * minimumDistance) { - repulsingForce = 1.0; - } - else { - repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) + if (options.physics) { + if (options.physics.barnesHut) { + this.constants.physics.barnesHut.enabled = true; + for (prop in options.physics.barnesHut) { + if (options.physics.barnesHut.hasOwnProperty(prop)) { + this.constants.physics.barnesHut[prop] = options.physics.barnesHut[prop]; + } } + } - // amplify the repulsion for clusters. - repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; - repulsingForce = repulsingForce / distance; + if (options.physics.repulsion) { + this.constants.physics.barnesHut.enabled = false; + for (prop in options.physics.repulsion) { + if (options.physics.repulsion.hasOwnProperty(prop)) { + this.constants.physics.repulsion[prop] = options.physics.repulsion[prop]; + } + } + } - fx = dx * repulsingForce; - fy = dy * repulsingForce; + if (options.physics.hierarchicalRepulsion) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + for (prop in options.physics.hierarchicalRepulsion) { + if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) { + this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop]; + } + } + } + } - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; + if (options.hierarchicalLayout) { + this.constants.hierarchicalLayout.enabled = true; + for (prop in options.hierarchicalLayout) { + if (options.hierarchicalLayout.hasOwnProperty(prop)) { + this.constants.hierarchicalLayout[prop] = options.hierarchicalLayout[prop]; + } } } - } - } -}; -var HierarchicalLayoutMixin = { + else if (options.hierarchicalLayout !== undefined) { + this.constants.hierarchicalLayout.enabled = false; + } + if (options.clustering) { + this.constants.clustering.enabled = true; + for (prop in options.clustering) { + if (options.clustering.hasOwnProperty(prop)) { + this.constants.clustering[prop] = options.clustering[prop]; + } + } + } + else if (options.clustering !== undefined) { + this.constants.clustering.enabled = false; + } + if (options.navigation) { + this.constants.navigation.enabled = true; + for (prop in options.navigation) { + if (options.navigation.hasOwnProperty(prop)) { + this.constants.navigation[prop] = options.navigation[prop]; + } + } + } + else if (options.navigation !== undefined) { + this.constants.navigation.enabled = false; + } - _resetLevels : function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.preassignedLevel == false) { - node.level = -1; + if (options.keyboard) { + this.constants.keyboard.enabled = true; + for (prop in options.keyboard) { + if (options.keyboard.hasOwnProperty(prop)) { + this.constants.keyboard[prop] = options.keyboard[prop]; + } } } - } - }, + else if (options.keyboard !== undefined) { + this.constants.keyboard.enabled = false; + } - /** - * This is the main function to layout the nodes in a hierarchical way. - * It checks if the node details are supplied correctly - * - * @private - */ - _setupHierarchicalLayout : function() { - if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { - if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "DU") { - this.constants.hierarchicalLayout.levelSeparation *= -1; + if (options.dataManipulation) { + this.constants.dataManipulation.enabled = true; + for (prop in options.dataManipulation) { + if (options.dataManipulation.hasOwnProperty(prop)) { + this.constants.dataManipulation[prop] = options.dataManipulation[prop]; + } + } + this.editMode = this.constants.dataManipulation.initiallyVisible; } - else { - this.constants.hierarchicalLayout.levelSeparation = Math.abs(this.constants.hierarchicalLayout.levelSeparation); + else if (options.dataManipulation !== undefined) { + this.constants.dataManipulation.enabled = false; } - // get the size of the largest hubs and check if the user has defined a level for a node. - var hubsize = 0; - var node, nodeId; - var definedLevel = false; - var undefinedLevel = false; - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level != -1) { - definedLevel = true; + // TODO: work out these options and document them + if (options.edges) { + for (prop in options.edges) { + if (options.edges.hasOwnProperty(prop)) { + if (typeof options.edges[prop] != "object") { + this.constants.edges[prop] = options.edges[prop]; + } + } + } + + + if (options.edges.color !== undefined) { + if (util.isString(options.edges.color)) { + this.constants.edges.color = {}; + this.constants.edges.color.color = options.edges.color; + this.constants.edges.color.highlight = options.edges.color; + this.constants.edges.color.hover = options.edges.color; } else { - undefinedLevel = true; + if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;} + if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} + if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} } - if (hubsize < node.edges.length) { - hubsize = node.edges.length; + } + + if (!options.edges.fontColor) { + if (options.edges.color !== undefined) { + if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;} + else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;} } } - } - // if the user defined some levels but not all, alert and run without hierarchical layout - if (undefinedLevel == true && definedLevel == true) { - alert("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); - this.zoomExtent(true,this.constants.clustering.enabled); - if (!this.constants.clustering.enabled) { - this.start(); + // Added to support dashed lines + // David Jordan + // 2012-08-08 + if (options.edges.dash) { + if (options.edges.dash.length !== undefined) { + this.constants.edges.dash.length = options.edges.dash.length; + } + if (options.edges.dash.gap !== undefined) { + this.constants.edges.dash.gap = options.edges.dash.gap; + } + if (options.edges.dash.altLength !== undefined) { + this.constants.edges.dash.altLength = options.edges.dash.altLength; + } } } - else { - // setup the system to use hierarchical method. - this._changeConstants(); - // define levels if undefined by the users. Based on hubsize - if (undefinedLevel == true) { - this._determineLevels(hubsize); + if (options.nodes) { + for (prop in options.nodes) { + if (options.nodes.hasOwnProperty(prop)) { + this.constants.nodes[prop] = options.nodes[prop]; + } } - // check the distribution of the nodes per level. - var distribution = this._getDistribution(); - // place the nodes on the canvas. This also stablilizes the system. - this._placeNodesByHierarchy(distribution); + if (options.nodes.color) { + this.constants.nodes.color = util.parseColor(options.nodes.color); + } - // start the simulation. - this.start(); + /* + if (options.nodes.widthMin) this.constants.nodes.radiusMin = options.nodes.widthMin; + if (options.nodes.widthMax) this.constants.nodes.radiusMax = options.nodes.widthMax; + */ } - } - }, - - - /** - * This function places the nodes on the canvas based on the hierarchial distribution. - * - * @param {Object} distribution | obtained by the function this._getDistribution() - * @private - */ - _placeNodesByHierarchy : function(distribution) { - var nodeId, node; - - // start placing all the level 0 nodes first. Then recursively position their branches. - for (nodeId in distribution[0].nodes) { - if (distribution[0].nodes.hasOwnProperty(nodeId)) { - node = distribution[0].nodes[nodeId]; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (node.xFixed) { - node.x = distribution[0].minPos; - node.xFixed = false; - - distribution[0].minPos += distribution[0].nodeSpacing; + if (options.groups) { + for (var groupname in options.groups) { + if (options.groups.hasOwnProperty(groupname)) { + var group = options.groups[groupname]; + this.groups.add(groupname, group); } } - else { - if (node.yFixed) { - node.y = distribution[0].minPos; - node.yFixed = false; + } - distribution[0].minPos += distribution[0].nodeSpacing; + if (options.tooltip) { + for (prop in options.tooltip) { + if (options.tooltip.hasOwnProperty(prop)) { + this.constants.tooltip[prop] = options.tooltip[prop]; } } - this._placeBranchNodes(node.edges,node.id,distribution,node.level); + if (options.tooltip.color) { + this.constants.tooltip.color = util.parseColor(options.tooltip.color); + } } } - // stabilize the system after positioning. This function calls zoomExtent. - this._stabilize(); - }, + // (Re)loading the mixins that can be enabled or disabled in the options. + // load the force calculation functions, grouped under the physics system. + this._loadPhysicsSystem(); + // load the navigation system. + this._loadNavigationControls(); + // load the data manipulation system + this._loadManipulationSystem(); + // configure the smooth curves + this._configureSmoothCurves(); + + + // bind keys. If disabled, this will not do anything; + this._createKeyBinds(); + this.setSize(this.width, this.height); + this.moving = true; + this.start(); + + }; /** - * This function get the distribution of levels based on hubsize - * - * @returns {Object} + * Create the main frame for the Network. + * This function is executed once when a Network object is created. The frame + * contains a canvas, and this canvas contains all objects like the axis and + * nodes. * @private */ - _getDistribution : function() { - var distribution = {}; - var nodeId, node, level; - - // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. - // the fix of X is removed after the x value has been set. - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.xFixed = true; - node.yFixed = true; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - else { - node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - if (!distribution.hasOwnProperty(node.level)) { - distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; - } - distribution[node.level].amount += 1; - distribution[node.level].nodes[node.id] = node; - } + Network.prototype._create = function () { + // remove all elements from the container element. + while (this.containerElement.hasChildNodes()) { + this.containerElement.removeChild(this.containerElement.firstChild); } - // determine the largest amount of nodes of all levels - var maxCount = 0; - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - if (maxCount < distribution[level].amount) { - maxCount = distribution[level].amount; - } - } - } + this.frame = document.createElement('div'); + this.frame.className = 'network-frame'; + this.frame.style.position = 'relative'; + this.frame.style.overflow = 'hidden'; - // set the initial position and spacing of each nodes accordingly - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; - distribution[level].nodeSpacing /= (distribution[level].amount + 1); - distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); - } + // create the network canvas (HTML canvas element) + this.frame.canvas = document.createElement( 'canvas' ); + this.frame.canvas.style.position = 'relative'; + this.frame.appendChild(this.frame.canvas); + if (!this.frame.canvas.getContext) { + var noCanvas = document.createElement( 'DIV' ); + noCanvas.style.color = 'red'; + noCanvas.style.fontWeight = 'bold' ; + noCanvas.style.padding = '10px'; + noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; + this.frame.canvas.appendChild(noCanvas); } - return distribution; - }, + var me = this; + this.drag = {}; + this.pinch = {}; + this.hammer = Hammer(this.frame.canvas, { + prevent_default: true + }); + this.hammer.on('tap', me._onTap.bind(me) ); + this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); + this.hammer.on('hold', me._onHold.bind(me) ); + this.hammer.on('pinch', me._onPinch.bind(me) ); + this.hammer.on('touch', me._onTouch.bind(me) ); + this.hammer.on('dragstart', me._onDragStart.bind(me) ); + this.hammer.on('drag', me._onDrag.bind(me) ); + this.hammer.on('dragend', me._onDragEnd.bind(me) ); + this.hammer.on('release', me._onRelease.bind(me) ); + this.hammer.on('mousewheel',me._onMouseWheel.bind(me) ); + this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF + this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); + + // add the frame to the container element + this.containerElement.appendChild(this.frame); + + }; /** - * this function allocates nodes in levels based on the recursive branching from the largest hubs. - * - * @param hubsize + * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin * @private */ - _determineLevels : function(hubsize) { - var nodeId, node; - - // determine hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.edges.length == hubsize) { - node.level = 0; - } - } + Network.prototype._createKeyBinds = function() { + var me = this; + this.mousetrap = mousetrap; + + this.mousetrap.reset(); + + if (this.constants.keyboard.enabled == true) { + this.mousetrap.bind("up", this._moveUp.bind(me) , "keydown"); + this.mousetrap.bind("up", this._yStopMoving.bind(me), "keyup"); + this.mousetrap.bind("down", this._moveDown.bind(me) , "keydown"); + this.mousetrap.bind("down", this._yStopMoving.bind(me), "keyup"); + this.mousetrap.bind("left", this._moveLeft.bind(me) , "keydown"); + this.mousetrap.bind("left", this._xStopMoving.bind(me), "keyup"); + this.mousetrap.bind("right",this._moveRight.bind(me), "keydown"); + this.mousetrap.bind("right",this._xStopMoving.bind(me), "keyup"); + this.mousetrap.bind("=", this._zoomIn.bind(me), "keydown"); + this.mousetrap.bind("=", this._stopZoom.bind(me), "keyup"); + this.mousetrap.bind("-", this._zoomOut.bind(me), "keydown"); + this.mousetrap.bind("-", this._stopZoom.bind(me), "keyup"); + this.mousetrap.bind("[", this._zoomIn.bind(me), "keydown"); + this.mousetrap.bind("[", this._stopZoom.bind(me), "keyup"); + this.mousetrap.bind("]", this._zoomOut.bind(me), "keydown"); + this.mousetrap.bind("]", this._stopZoom.bind(me), "keyup"); + this.mousetrap.bind("pageup",this._zoomIn.bind(me), "keydown"); + this.mousetrap.bind("pageup",this._stopZoom.bind(me), "keyup"); + this.mousetrap.bind("pagedown",this._zoomOut.bind(me),"keydown"); + this.mousetrap.bind("pagedown",this._stopZoom.bind(me), "keyup"); } - // branch from hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level == 0) { - this._setLevel(1,node.edges,node.id); - } - } + if (this.constants.dataManipulation.enabled == true) { + this.mousetrap.bind("escape",this._createManipulatorBar.bind(me)); + this.mousetrap.bind("del",this._deleteSelected.bind(me)); } - }, + }; + /** + * Get the pointer location from a touch location + * @param {{pageX: Number, pageY: Number}} touch + * @return {{x: Number, y: Number}} pointer + * @private + */ + Network.prototype._getPointer = function (touch) { + return { + x: touch.pageX - util.getAbsoluteLeft(this.frame.canvas), + y: touch.pageY - util.getAbsoluteTop(this.frame.canvas) + }; + }; /** - * Since hierarchical layout does not support: - * - smooth curves (based on the physics), - * - clustering (based on dynamic node counts) - * - * We disable both features so there will be no problems. - * + * On start of a touch gesture, store the pointer + * @param event * @private */ - _changeConstants : function() { - this.constants.clustering.enabled = false; - this.constants.physics.barnesHut.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this._loadSelectedForceSolver(); - this.constants.smoothCurves = false; - this._configureSmoothCurves(); - }, + Network.prototype._onTouch = function (event) { + this.drag.pointer = this._getPointer(event.gesture.center); + this.drag.pinched = false; + this.pinch.scale = this._getScale(); + this._handleTouch(this.drag.pointer); + }; /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. + * handle drag start event + * @private + */ + Network.prototype._onDragStart = function () { + this._handleDragStart(); + }; + + + /** + * This function is called by _onDragStart. + * It is separated out because we can then overload it for the datamanipulation system. * - * @param edges - * @param parentId - * @param distribution - * @param parentLevel * @private */ - _placeBranchNodes : function(edges, parentId, distribution, parentLevel) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } + Network.prototype._handleDragStart = function() { + var drag = this.drag; + var node = this._getNodeAt(drag.pointer); + // note: drag.pointer is set in _onTouch to get the initial touch location - // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. - var nodeMoved = false; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (childNode.xFixed && childNode.level > parentLevel) { - childNode.xFixed = false; - childNode.x = distribution[childNode.level].minPos; - nodeMoved = true; - } - } - else { - if (childNode.yFixed && childNode.level > parentLevel) { - childNode.yFixed = false; - childNode.y = distribution[childNode.level].minPos; - nodeMoved = true; - } - } + drag.dragging = true; + drag.selection = []; + drag.translation = this._getTranslation(); + drag.nodeId = null; - if (nodeMoved == true) { - distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; - if (childNode.edges.length > 1) { - this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); + if (node != null) { + drag.nodeId = node.id; + // select the clicked node if not yet selected + if (!node.isSelected()) { + this._selectObject(node,false); + } + + // create an array with the selected nodes and their original location and status + for (var objectId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(objectId)) { + var object = this.selectionObj.nodes[objectId]; + var s = { + id: object.id, + node: object, + + // store original x, y, xFixed and yFixed, make the node temporarily Fixed + x: object.x, + y: object.y, + xFixed: object.xFixed, + yFixed: object.yFixed + }; + + object.xFixed = true; + object.yFixed = true; + + drag.selection.push(s); } } } - }, + }; /** - * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. - * - * @param level - * @param edges - * @param parentId + * handle drag event * @private */ - _setLevel : function(level, edges, parentId) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1 || childNode.level > level) { - childNode.level = level; - if (edges.length > 1) { - this._setLevel(level+1, childNode.edges, childNode.id); - } - } - } - }, + Network.prototype._onDrag = function (event) { + this._handleOnDrag(event) + }; /** - * Unfix nodes + * This function is called by _onDrag. + * It is separated out because we can then overload it for the datamanipulation system. * * @private */ - _restoreNodes : function() { - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.nodes[nodeId].xFixed = false; - this.nodes[nodeId].yFixed = false; - } + Network.prototype._handleOnDrag = function(event) { + if (this.drag.pinched) { + return; } - } + var pointer = this._getPointer(event.gesture.center); + + var me = this, + drag = this.drag, + selection = drag.selection; + 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; -}; -/** - * Created by Alex on 2/4/14. - */ + // update position of all selected nodes + selection.forEach(function (s) { + var node = s.node; + + if (!s.xFixed) { + node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX); + } + + if (!s.yFixed) { + node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY); + } + }); -var manipulationMixin = { + // start _animationStep if not yet running + if (!this.moving) { + this.moving = true; + this.start(); + } + } + else { + if (this.constants.dragNetwork == true) { + // move the network + var diffX = pointer.x - this.drag.pointer.x; + var diffY = pointer.y - this.drag.pointer.y; + + this._setTranslation( + this.drag.translation.x + diffX, + this.drag.translation.y + diffY); + this._redraw(); + this.moving = true; + this.start(); + } + } + }; /** - * clears the toolbar div element of children - * + * handle drag start event * @private */ - _clearManipulatorBar : function() { - while (this.manipulationDiv.hasChildNodes()) { - this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + Network.prototype._onDragEnd = function () { + this.drag.dragging = false; + var selection = this.drag.selection; + if (selection) { + selection.forEach(function (s) { + // restore original xFixed and yFixed + s.node.xFixed = s.xFixed; + s.node.yFixed = s.yFixed; + }); } - }, + }; /** - * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore - * these functions to their original functionality, we saved them in this.cachedFunctions. - * This function restores these functions to their original function. - * + * handle tap/click event: select/unselect a node * @private */ - _restoreOverloadedFunctions : function() { - for (var functionName in this.cachedFunctions) { - if (this.cachedFunctions.hasOwnProperty(functionName)) { - this[functionName] = this.cachedFunctions[functionName]; - } - } - }, + Network.prototype._onTap = function (event) { + var pointer = this._getPointer(event.gesture.center); + this.pointerPosition = pointer; + this._handleTap(pointer); + + }; + /** - * Enable or disable edit-mode. - * + * handle doubletap event * @private */ - _toggleEditMode : function() { - this.editMode = !this.editMode; - var toolbar = document.getElementById("network-manipulationDiv"); - var closeDiv = document.getElementById("network-manipulation-closeDiv"); - var editModeDiv = document.getElementById("network-manipulation-editMode"); - if (this.editMode == true) { - toolbar.style.display="block"; - closeDiv.style.display="block"; - editModeDiv.style.display="none"; - closeDiv.onclick = this._toggleEditMode.bind(this); - } - else { - toolbar.style.display="none"; - closeDiv.style.display="none"; - editModeDiv.style.display="block"; - closeDiv.onclick = null; - } - this._createManipulatorBar() - }, + Network.prototype._onDoubleTap = function (event) { + var pointer = this._getPointer(event.gesture.center); + this._handleDoubleTap(pointer); + }; + /** - * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. + * handle long tap event: multi select nodes + * @private + */ + Network.prototype._onHold = function (event) { + var pointer = this._getPointer(event.gesture.center); + this.pointerPosition = pointer; + this._handleOnHold(pointer); + }; + + /** + * handle the release of the screen * * @private */ - _createManipulatorBar : function() { - // remove bound functions - if (this.boundFunction) { - this.off('select', this.boundFunction); - } - if (this.edgeBeingEdited !== undefined) { - this.edgeBeingEdited._disableControlNodes(); - this.edgeBeingEdited = undefined; - this.selectedControlNode = null; - } + Network.prototype._onRelease = function (event) { + var pointer = this._getPointer(event.gesture.center); + this._handleOnRelease(pointer); + }; - // restore overloaded functions - this._restoreOverloadedFunctions(); + /** + * Handle pinch event + * @param event + * @private + */ + Network.prototype._onPinch = function (event) { + var pointer = this._getPointer(event.gesture.center); - // resume calculation - this.freezeSimulation = false; + this.drag.pinched = true; + if (!('scale' in this.pinch)) { + this.pinch.scale = 1; + } - // reset global variables - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; + // TODO: enabled moving while pinching? + var scale = this.pinch.scale * event.gesture.scale; + this._zoom(scale, pointer) + }; - if (this.editMode == true) { - while (this.manipulationDiv.hasChildNodes()) { - this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); - } - // add the icons to the manipulator div - this.manipulationDiv.innerHTML = "" + - "" + - ""+this.constants.labels['add'] +"" + - "
" + - "" + - ""+this.constants.labels['link'] +""; - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - this.manipulationDiv.innerHTML += "" + - "
" + - "" + - ""+this.constants.labels['editNode'] +""; - } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - this.manipulationDiv.innerHTML += "" + - "
" + - "" + - ""+this.constants.labels['editEdge'] +""; + /** + * Zoom the network in or out + * @param {Number} scale a number around 1, and between 0.01 and 10 + * @param {{x: Number, y: Number}} pointer Position on screen + * @return {Number} appliedScale scale is limited within the boundaries + * @private + */ + Network.prototype._zoom = function(scale, pointer) { + if (this.constants.zoomable == true) { + var scaleOld = this._getScale(); + if (scale < 0.00001) { + scale = 0.00001; } - if (this._selectionIsEmpty() == false) { - this.manipulationDiv.innerHTML += "" + - "
" + - "" + - ""+this.constants.labels['del'] +""; + if (scale > 10) { + scale = 10; } + // + this.frame.canvas.clientHeight / 2 + var translation = this._getTranslation(); + var scaleFrac = scale / scaleOld; + var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; + var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; - // bind the icons - var addNodeButton = document.getElementById("network-manipulate-addNode"); - addNodeButton.onclick = this._createAddNodeToolbar.bind(this); - var addEdgeButton = document.getElementById("network-manipulate-connectNode"); - addEdgeButton.onclick = this._createAddEdgeToolbar.bind(this); - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - var editButton = document.getElementById("network-manipulate-editNode"); - editButton.onclick = this._editNode.bind(this); - } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - var editButton = document.getElementById("network-manipulate-editEdge"); - editButton.onclick = this._createEditEdgeToolbar.bind(this); + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; + + this._setScale(scale); + this._setTranslation(tx, ty); + this.updateClustersDefault(); + this._redraw(); + + if (scaleOld < scale) { + this.emit("zoom", {direction:"+"}); } - if (this._selectionIsEmpty() == false) { - var deleteButton = document.getElementById("network-manipulate-delete"); - deleteButton.onclick = this._deleteSelected.bind(this); + else { + this.emit("zoom", {direction:"-"}); } - var closeDiv = document.getElementById("network-manipulation-closeDiv"); - closeDiv.onclick = this._toggleEditMode.bind(this); - this.boundFunction = this._createManipulatorBar.bind(this); - this.on('select', this.boundFunction); + return scale; } - else { - this.editModeDiv.innerHTML = "" + - "" + - "" + this.constants.labels['edit'] + ""; - var editModeButton = document.getElementById("network-manipulate-editModeButton"); - editModeButton.onclick = this._toggleEditMode.bind(this); - } - }, - + }; /** - * Create the toolbar for adding Nodes - * + * Event handler for mouse wheel event, used to zoom the timeline + * See http://adomas.org/javascript-mouse-wheel/ + * https://github.com/EightMedia/hammer.js/issues/256 + * @param {MouseEvent} event * @private */ - _createAddNodeToolbar : function() { - // clear the toolbar - this._clearManipulatorBar(); - if (this.boundFunction) { - this.off('select', this.boundFunction); + Network.prototype._onMouseWheel = function(event) { + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta/120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail/3; } - // create the toolbar contents - this.manipulationDiv.innerHTML = "" + - "" + - "" + this.constants.labels['back'] + " " + - "
" + - "" + - "" + this.constants.labels['addDescription'] + ""; + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { - // bind the icon - var backButton = document.getElementById("network-manipulate-back"); - backButton.onclick = this._createManipulatorBar.bind(this); + // calculate the new scale + var scale = this._getScale(); + var zoom = delta / 10; + if (delta < 0) { + zoom = zoom / (1 - zoom); + } + scale *= (1 + zoom); - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - this.boundFunction = this._addNode.bind(this); - this.on('select', this.boundFunction); - }, + // calculate the pointer location + var gesture = util.fakeGesture(this, event); + var pointer = this._getPointer(gesture.center); + + // apply the new scale + this._zoom(scale, pointer); + } + + // Prevent default actions caused by mouse wheel. + event.preventDefault(); + }; /** - * create the toolbar to connect nodes - * + * Mouse move handler for checking whether the title moves over a node with a title. + * @param {Event} event * @private */ - _createAddEdgeToolbar : function() { - // clear the toolbar - this._clearManipulatorBar(); - this._unselectAll(true); - this.freezeSimulation = true; + Network.prototype._onMouseMoveTitle = function (event) { + var gesture = util.fakeGesture(this, event); + var pointer = this._getPointer(gesture.center); - if (this.boundFunction) { - this.off('select', this.boundFunction); + // check if the previously selected node is still selected + if (this.popupObj) { + this._checkHidePopup(pointer); } - this._unselectAll(); - this.forceAppendSelection = false; - this.blockConnectingEdgeSelection = true; - - this.manipulationDiv.innerHTML = "" + - "" + - "" + this.constants.labels['back'] + " " + - "
" + - "" + - "" + this.constants.labels['linkDescription'] + ""; + // start a timeout that will check if the mouse is positioned above + // an element + var me = this; + var checkShow = function() { + me._checkShowPopup(pointer); + }; + if (this.popupTimer) { + clearInterval(this.popupTimer); // stop any running calculationTimer + } + if (!this.drag.dragging) { + this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay); + } - // bind the icon - var backButton = document.getElementById("network-manipulate-back"); - backButton.onclick = this._createManipulatorBar.bind(this); - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - this.boundFunction = this._handleConnect.bind(this); - this.on('select', this.boundFunction); + /** + * Adding hover highlights + */ + if (this.constants.hover == true) { + // removing all hover highlights + for (var edgeId in this.hoverObj.edges) { + if (this.hoverObj.edges.hasOwnProperty(edgeId)) { + this.hoverObj.edges[edgeId].hover = false; + delete this.hoverObj.edges[edgeId]; + } + } - // temporarily overload functions - this.cachedFunctions["_handleTouch"] = this._handleTouch; - this.cachedFunctions["_handleOnRelease"] = this._handleOnRelease; - this._handleTouch = this._handleConnect; - this._handleOnRelease = this._finishConnect; + // adding hover highlights + var obj = this._getNodeAt(pointer); + if (obj == null) { + obj = this._getEdgeAt(pointer); + } + if (obj != null) { + this._hoverObject(obj); + } - // redraw to show the unselect - this._redraw(); - }, + // removing all node hover highlights except for the selected one. + for (var nodeId in this.hoverObj.nodes) { + if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { + if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) { + this._blurObject(this.hoverObj.nodes[nodeId]); + delete this.hoverObj.nodes[nodeId]; + } + } + } + this.redraw(); + } + }; /** - * create the toolbar to edit edges + * Check if there is an element on the given position in the network + * (a node or edge). If so, and if this element has a title, + * show a popup window with its title. * + * @param {{x:Number, y:Number}} pointer * @private */ - _createEditEdgeToolbar : function() { - // clear the toolbar - this._clearManipulatorBar(); + Network.prototype._checkShowPopup = function (pointer) { + var obj = { + left: this._XconvertDOMtoCanvas(pointer.x), + top: this._YconvertDOMtoCanvas(pointer.y), + right: this._XconvertDOMtoCanvas(pointer.x), + bottom: this._YconvertDOMtoCanvas(pointer.y) + }; - if (this.boundFunction) { - this.off('select', this.boundFunction); + var id; + var lastPopupNode = this.popupObj; + + if (this.popupObj == undefined) { + // search the nodes for overlap, select the top one in case of multiple nodes + var nodes = this.nodes; + for (id in nodes) { + if (nodes.hasOwnProperty(id)) { + var node = nodes[id]; + if (node.getTitle() !== undefined && node.isOverlappingWith(obj)) { + this.popupObj = node; + break; + } + } + } } - this.edgeBeingEdited = this._getSelectedEdge(); - this.edgeBeingEdited._enableControlNodes(); + if (this.popupObj === undefined) { + // search the edges for overlap + var edges = this.edges; + for (id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + if (edge.connected && (edge.getTitle() !== undefined) && + edge.isOverlappingWith(obj)) { + this.popupObj = edge; + break; + } + } + } + } - this.manipulationDiv.innerHTML = "" + - "" + - "" + this.constants.labels['back'] + " " + - "
" + - "" + - "" + this.constants.labels['editEdgeDescription'] + ""; + if (this.popupObj) { + // show popup message window + if (this.popupObj != lastPopupNode) { + var me = this; + if (!me.popup) { + me.popup = new Popup(me.frame, me.constants.tooltip); + } - // bind the icon - var backButton = document.getElementById("network-manipulate-back"); - backButton.onclick = this._createManipulatorBar.bind(this); + // adjust a small offset such that the mouse cursor is located in the + // bottom left location of the popup, and you can easily move over the + // popup area + me.popup.setPosition(pointer.x - 3, pointer.y - 3); + me.popup.setText(me.popupObj.getTitle()); + me.popup.show(); + } + } + else { + if (this.popup) { + this.popup.hide(); + } + } + }; - // 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(); - }, + /** + * Check if the popup must be hided, which is the case when the mouse is no + * longer hovering on the object + * @param {{x:Number, y:Number}} pointer + * @private + */ + Network.prototype._checkHidePopup = function (pointer) { + if (!this.popupObj || !this._getNodeAt(pointer) ) { + this.popupObj = undefined; + if (this.popup) { + this.popup.hide(); + } + } + }; + /** + * Set a new size for the network + * @param {string} width Width in pixels or percentage (for example '800px' + * or '50%') + * @param {string} height Height in pixels or percentage (for example '400px' + * or '30%') + */ + Network.prototype.setSize = function(width, height) { + this.frame.style.width = width; + this.frame.style.height = height; + this.frame.canvas.style.width = '100%'; + this.frame.canvas.style.height = '100%'; + this.frame.canvas.width = this.frame.canvas.clientWidth; + this.frame.canvas.height = this.frame.canvas.clientHeight; - /** - * 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; + if (this.manipulationDiv !== undefined) { + this.manipulationDiv.style.width = this.frame.canvas.clientWidth + "px"; } - this._redraw(); - }, + if (this.navigationDivs !== undefined) { + if (this.navigationDivs['wrapper'] !== undefined) { + 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}); + }; /** - * 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. - * + * Set a data set with nodes for the network + * @param {Array | DataSet | DataView} nodes The data containing the nodes. * @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); + Network.prototype._setNodes = function(nodes) { + var oldNodesData = this.nodesData; + + if (nodes instanceof DataSet || nodes instanceof DataView) { + this.nodesData = nodes; + } + else if (nodes instanceof Array) { + this.nodesData = new DataSet(); + this.nodesData.add(nodes); + } + else if (!nodes) { + this.nodesData = new DataSet(); + } + else { + throw new TypeError('Array or DataSet expected'); } - 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 (oldNodesData) { + // unsubscribe from old dataset + util.forEach(this.nodesListeners, function (callback, event) { + oldNodesData.off(event, callback); + }); + } + + // remove drawn nodes + this.nodes = {}; + + if (this.nodesData) { + // subscribe to new dataset + var me = this; + util.forEach(this.nodesListeners, function (callback, event) { + me.nodesData.on(event, callback); + }); + + // draw all new nodes + var ids = this.nodesData.getIds(); + this._addNodes(ids); + } + this._updateSelection(); + }; + + /** + * Add nodes + * @param {Number[] | String[]} ids + * @private + */ + Network.prototype._addNodes = function(ids) { + var id; + for (var i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + var data = this.nodesData.get(id); + var node = new Node(data, this.images, this.groups, this.constants); + this.nodes[id] = node; // note: this may replace an existing node + + if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) { + var radius = 10 * 0.1*ids.length; + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} } - if (this.edgeBeingEdited.controlNodes.to.selected == true) { - this._editEdge(this.edgeBeingEdited.from.id, newNode.id); - this.edgeBeingEdited.controlNodes.to.unselect(); + this.moving = true; + } + this._updateNodeIndexList(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + this._reconnectEdges(); + this._updateValueRange(this.nodes); + this.updateLabels(); + }; + + /** + * Update existing nodes, or create them when not yet existing + * @param {Number[] | String[]} ids + * @private + */ + Network.prototype._updateNodes = function(ids) { + var nodes = this.nodes, + nodesData = this.nodesData; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var node = nodes[id]; + var data = nodesData.get(id); + if (node) { + // update node + node.setProperties(data, this.constants); + } + else { + // create node + node = new Node(properties, this.images, this.groups, this.constants); + nodes[id] = node; } } - else { - this.edgeBeingEdited._restoreControlNodes(); + this.moving = true; + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); } - this.freezeSimulation = false; - this._redraw(); - }, + this._updateNodeIndexList(); + this._reconnectEdges(); + this._updateValueRange(nodes); + }; /** - * 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. - * + * Remove existing nodes. If nodes do not exist, the method will just ignore it. + * @param {Number[] | String[]} ids * @private */ - _handleConnect : function(pointer) { - if (this._getSelectedNodeCount() == 0) { - var node = this._getNodeAt(pointer); - if (node != null) { - if (node.clusterSize > 1) { - alert("Cannot create edges to a cluster.") - } - else { - this._selectObject(node,false); - // create a node the temporary line can look at - this.sectors['support']['nodes']['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); - this.sectors['support']['nodes']['targetNode'].x = node.x; - this.sectors['support']['nodes']['targetNode'].y = node.y; - this.sectors['support']['nodes']['targetViaNode'] = new Node({id:'targetViaNode'},{},{},this.constants); - this.sectors['support']['nodes']['targetViaNode'].x = node.x; - this.sectors['support']['nodes']['targetViaNode'].y = node.y; - this.sectors['support']['nodes']['targetViaNode'].parentEdgeId = "connectionEdge"; + Network.prototype._removeNodes = function(ids) { + var nodes = this.nodes; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + delete nodes[id]; + } + this._updateNodeIndexList(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + this._reconnectEdges(); + this._updateSelection(); + this._updateValueRange(nodes); + }; - // create a temporary edge - this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:this.sectors['support']['nodes']['targetNode'].id}, this, this.constants); - this.edges['connectionEdge'].from = node; - this.edges['connectionEdge'].connected = true; - this.edges['connectionEdge'].smooth = true; - this.edges['connectionEdge'].selected = true; - this.edges['connectionEdge'].to = this.sectors['support']['nodes']['targetNode']; - this.edges['connectionEdge'].via = this.sectors['support']['nodes']['targetViaNode']; + /** + * Load edges by reading the data table + * @param {Array | DataSet | DataView} edges The data containing the edges. + * @private + * @private + */ + Network.prototype._setEdges = function(edges) { + var oldEdgesData = this.edgesData; - this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; - this._handleOnDrag = function(event) { - var pointer = this._getPointer(event.gesture.center); - this.sectors['support']['nodes']['targetNode'].x = this._XconvertDOMtoCanvas(pointer.x); - this.sectors['support']['nodes']['targetNode'].y = this._YconvertDOMtoCanvas(pointer.y); - this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._XconvertDOMtoCanvas(pointer.x) + this.edges['connectionEdge'].from.x); - this.sectors['support']['nodes']['targetViaNode'].y = this._YconvertDOMtoCanvas(pointer.y); - }; + if (edges instanceof DataSet || edges instanceof DataView) { + this.edgesData = edges; + } + else if (edges instanceof Array) { + this.edgesData = new DataSet(); + this.edgesData.add(edges); + } + else if (!edges) { + this.edgesData = new DataSet(); + } + else { + throw new TypeError('Array or DataSet expected'); + } - this.moving = true; - this.start(); - } - } + if (oldEdgesData) { + // unsubscribe from old dataset + util.forEach(this.edgesListeners, function (callback, event) { + oldEdgesData.off(event, callback); + }); } - }, - _finishConnect : function(pointer) { - if (this._getSelectedNodeCount() == 1) { + // remove drawn edges + this.edges = {}; - // restore the drag function - this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; - delete this.cachedFunctions["_handleOnDrag"]; + if (this.edgesData) { + // subscribe to new dataset + var me = this; + util.forEach(this.edgesListeners, function (callback, event) { + me.edgesData.on(event, callback); + }); - // remember the edge id - var connectFromId = this.edges['connectionEdge'].fromId; + // draw all new nodes + var ids = this.edgesData.getIds(); + this._addEdges(ids); + } - // remove the temporary nodes and edge - delete this.edges['connectionEdge']; - delete this.sectors['support']['nodes']['targetNode']; - delete this.sectors['support']['nodes']['targetViaNode']; + this._reconnectEdges(); + }; - var node = this._getNodeAt(pointer); - if (node != null) { - if (node.clusterSize > 1) { - alert("Cannot create edges to a cluster.") - } - else { - this._createEdge(connectFromId,node.id); - this._createManipulatorBar(); - } + /** + * Add edges + * @param {Number[] | String[]} ids + * @private + */ + Network.prototype._addEdges = function (ids) { + var edges = this.edges, + edgesData = this.edgesData; + + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + + var oldEdge = edges[id]; + if (oldEdge) { + oldEdge.disconnect(); } - this._unselectAll(); + + var data = edgesData.get(id, {"showInternalIds" : true}); + edges[id] = new Edge(data, this, this.constants); } - }, + this.moving = true; + this._updateValueRange(edges); + this._createBezierNodes(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + }; /** - * Adds a node on the specified location + * Update existing edges, or create them when not yet existing + * @param {Number[] | String[]} ids + * @private */ - _addNode : function() { - if (this._selectionIsEmpty() && this.editMode == true) { - var positionObject = this._pointerToPositionObject(this.pointerPosition); - var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; - if (this.triggerFunctions.add) { - if (this.triggerFunctions.add.length == 2) { - var me = this; - this.triggerFunctions.add(defaultData, function(finalizedData) { - me.nodesData.add(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - alert(this.constants.labels['addError']); - this._createManipulatorBar(); - this.moving = true; - this.start(); - } + Network.prototype._updateEdges = function (ids) { + var edges = this.edges, + edgesData = this.edgesData; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + + var data = edgesData.get(id); + var edge = edges[id]; + if (edge) { + // update edge + edge.disconnect(); + edge.setProperties(data, this.constants); + edge.connect(); } else { - this.nodesData.add(defaultData); - this._createManipulatorBar(); - this.moving = true; - this.start(); + // create edge + edge = new Edge(data, this, this.constants); + this.edges[id] = edge; } } - }, + this._createBezierNodes(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this.moving = true; + this._updateValueRange(edges); + }; /** - * connect two nodes with a new edge. - * + * Remove existing edges. Non existing ids will be ignored + * @param {Number[] | String[]} ids * @private */ - _createEdge : function(sourceNodeId,targetNodeId) { - if (this.editMode == true) { - var defaultData = {from:sourceNodeId, to:targetNodeId}; - if (this.triggerFunctions.connect) { - if (this.triggerFunctions.connect.length == 2) { - var me = this; - this.triggerFunctions.connect(defaultData, function(finalizedData) { - me.edgesData.add(finalizedData); - me.moving = true; - me.start(); - }); - } - else { - alert(this.constants.labels["linkError"]); - this.moving = true; - this.start(); + Network.prototype._removeEdges = function (ids) { + var edges = this.edges; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var edge = edges[id]; + if (edge) { + if (edge.via != null) { + delete this.sectors['support']['nodes'][edge.via.id]; } + edge.disconnect(); + delete edges[id]; } - else { - this.edgesData.add(defaultData); - this.moving = true; - this.start(); + } + + this.moving = true; + this._updateValueRange(edges); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + }; + + /** + * Reconnect all edges + * @private + */ + Network.prototype._reconnectEdges = function() { + var id, + nodes = this.nodes, + edges = this.edges; + for (id in nodes) { + if (nodes.hasOwnProperty(id)) { + nodes[id].edges = []; + } + } + + for (id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + edge.from = null; + edge.to = null; + edge.connect(); } } - }, + }; /** - * connect two nodes with a new edge. - * + * Update the values of all object in the given array according to the current + * value range of the objects in the array. + * @param {Object} obj An object containing a set of Edges or Nodes + * The objects must have a method getValue() and + * setValueRange(min, max). * @private */ - _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(); + Network.prototype._updateValueRange = function(obj) { + var id; + + // determine the range of the objects + var valueMin = undefined; + var valueMax = undefined; + for (id in obj) { + if (obj.hasOwnProperty(id)) { + var value = obj[id].getValue(); + if (value !== undefined) { + valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin); + valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax); } } - else { - this.edgesData.update(defaultData); - this.moving = true; - this.start(); + } + + // adjust the range of all objects + if (valueMin !== undefined && valueMax !== undefined) { + for (id in obj) { + if (obj.hasOwnProperty(id)) { + obj[id].setValueRange(valueMin, valueMax); + } } } - }, + }; /** - * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color. - * + * Redraw the network with the current data + * chart will be resized too. + */ + Network.prototype.redraw = function() { + this.setSize(this.width, this.height); + this._redraw(); + }; + + /** + * Redraw the network with the current data * @private */ - _editNode : function() { - if (this.triggerFunctions.edit && this.editMode == true) { - var node = this._getSelectedNode(); - var data = {id:node.id, - label: node.label, - group: node.group, - shape: node.shape, - color: { - background:node.color.background, - border:node.color.border, - highlight: { - background:node.color.highlight.background, - border:node.color.highlight.border - } - }}; - if (this.triggerFunctions.edit.length == 2) { - var me = this; - this.triggerFunctions.edit(data, function (finalizedData) { - me.nodesData.update(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - alert(this.constants.labels["editError"]); - } - } - else { - alert(this.constants.labels["editBoundError"]); - } - }, + Network.prototype._redraw = function() { + var ctx = this.frame.canvas.getContext('2d'); + // clear the canvas + var w = this.frame.canvas.width; + var h = this.frame.canvas.height; + ctx.clearRect(0, 0, w, h); + + // set scaling and translation + ctx.save(); + ctx.translate(this.translation.x, this.translation.y); + ctx.scale(this.scale, this.scale); + + this.canvasTopLeft = { + "x": this._XconvertDOMtoCanvas(0), + "y": this._YconvertDOMtoCanvas(0) + }; + this.canvasBottomRight = { + "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth), + "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight) + }; + 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"); + // restore original scaling and translation + ctx.restore(); + }; /** - * delete everything in the selection - * + * Set the translation of the network + * @param {Number} offsetX Horizontal offset + * @param {Number} offsetY Vertical offset * @private */ - _deleteSelected : function() { - if (!this._selectionIsEmpty() && this.editMode == true) { - if (!this._clusterInSelection()) { - var selectedNodes = this.getSelectedNodes(); - var selectedEdges = this.getSelectedEdges(); - if (this.triggerFunctions.del) { - var me = this; - var data = {nodes: selectedNodes, edges: selectedEdges}; - if (this.triggerFunctions.del.length = 2) { - this.triggerFunctions.del(data, function (finalizedData) { - me.edgesData.remove(finalizedData.edges); - me.nodesData.remove(finalizedData.nodes); - me._unselectAll(); - me.moving = true; - me.start(); - }); - } - else { - alert(this.constants.labels["deleteError"]) - } - } - else { - this.edgesData.remove(selectedEdges); - this.nodesData.remove(selectedNodes); - this._unselectAll(); - this.moving = true; - this.start(); - } - } - else { - alert(this.constants.labels["deleteClusterError"]); - } + Network.prototype._setTranslation = function(offsetX, offsetY) { + if (this.translation === undefined) { + this.translation = { + x: 0, + y: 0 + }; } - } -}; -/** - * Creation of the SectorMixin var. - * - * This contains all the functions the Network object can use to employ the sector system. - * The sector system is always used by Network, though the benefits only apply to the use of clustering. - * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. - * - * Alex de Mulder - * 21-01-2013 - */ -var SectorMixin = { + + if (offsetX !== undefined) { + this.translation.x = offsetX; + } + if (offsetY !== undefined) { + this.translation.y = offsetY; + } + + this.emit('viewChanged'); + }; /** - * This function is only called by the setData function of the Network object. - * This loads the global references into the active sector. This initializes the sector. - * + * Get the translation of the network + * @return {Object} translation An object with parameters x and y, both a number * @private */ - _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; - }, - + Network.prototype._getTranslation = function() { + return { + x: this.translation.x, + y: this.translation.y + }; + }; /** - * /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied (active) sector. If a type is defined, do the specific type - * - * @param {String} sectorId - * @param {String} [sectorType] | "active" or "frozen" + * Scale the network + * @param {Number} scale Scaling factor 1.0 is unscaled * @private */ - _switchToSector : function(sectorId, sectorType) { - if (sectorType === undefined || sectorType == "active") { - this._switchToActiveSector(sectorId); - } - else { - this._switchToFrozenSector(sectorId); - } - }, - + Network.prototype._setScale = function(scale) { + this.scale = scale; + }; /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. - * - * @param sectorId + * Get the current scale of the network + * @return {Number} scale Scaling factor 1.0 is unscaled * @private */ - _switchToActiveSector : function(sectorId) { - this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["active"][sectorId]["nodes"]; - this.edges = this.sectors["active"][sectorId]["edges"]; - }, - + Network.prototype._getScale = function() { + return this.scale; + }; /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. - * - * @param sectorId + * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to + * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) + * @param {number} x + * @returns {number} * @private */ - _switchToSupportSector : function() { - this.nodeIndices = this.sectors["support"]["nodeIndices"]; - this.nodes = this.sectors["support"]["nodes"]; - this.edges = this.sectors["support"]["edges"]; - }, - + Network.prototype._XconvertDOMtoCanvas = function(x) { + return (x - this.translation.x) / this.scale; + }; /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied frozen sector. - * - * @param sectorId + * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to + * the X coordinate in DOM-space (coordinate point in browser relative to the container div) + * @param {number} x + * @returns {number} * @private */ - _switchToFrozenSector : function(sectorId) { - this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["frozen"][sectorId]["nodes"]; - this.edges = this.sectors["frozen"][sectorId]["edges"]; - }, + Network.prototype._XconvertCanvasToDOM = function(x) { + return x * this.scale + this.translation.x; + }; + /** + * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to + * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) + * @param {number} y + * @returns {number} + * @private + */ + Network.prototype._YconvertDOMtoCanvas = function(y) { + return (y - this.translation.y) / this.scale; + }; /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the currently active sector. - * + * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to + * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) + * @param {number} y + * @returns {number} * @private */ - _loadLatestSector : function() { - this._switchToSector(this._sector()); - }, + Network.prototype._YconvertCanvasToDOM = function(y) { + return y * this.scale + this.translation.y ; + }; /** - * This function returns the currently active sector Id * - * @returns {String} - * @private + * @param {object} pos = {x: number, y: number} + * @returns {{x: number, y: number}} + * @constructor */ - _sector : function() { - return this.activeSector[this.activeSector.length-1]; - }, - + Network.prototype.canvasToDOM = function(pos) { + return {x:this._XconvertCanvasToDOM(pos.x),y:this._YconvertCanvasToDOM(pos.y)}; + } /** - * This function returns the previously active sector Id * - * @returns {String} + * @param {object} pos = {x: number, y: number} + * @returns {{x: number, y: number}} + * @constructor + */ + Network.prototype.DOMtoCanvas = function(pos) { + return {x:this._XconvertDOMtoCanvas(pos.x),y:this._YconvertDOMtoCanvas(pos.y)}; + } + + /** + * Redraw all nodes + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @param {Boolean} [alwaysShow] * @private */ - _previousSector : function() { - if (this.activeSector.length > 1) { - return this.activeSector[this.activeSector.length-2]; + Network.prototype._drawNodes = function(ctx,alwaysShow) { + if (alwaysShow === undefined) { + alwaysShow = false; } - else { - throw new TypeError('there are not enough sectors in the this.activeSector array.'); + + // first draw the unselected nodes + var nodes = this.nodes; + var selected = []; + + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); + if (nodes[id].isSelected()) { + selected.push(id); + } + else { + if (nodes[id].inArea() || alwaysShow) { + nodes[id].draw(ctx); + } + } + } } - }, + // draw the selected nodes on top + for (var s = 0, sMax = selected.length; s < sMax; s++) { + if (nodes[selected[s]].inArea() || alwaysShow) { + nodes[selected[s]].draw(ctx); + } + } + }; /** - * We add the active sector at the end of the this.activeSector array - * This ensures it is the currently active sector returned by _sector() and it reaches the top - * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. - * - * @param newId + * Redraw all edges + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx * @private */ - _setActiveSector : function(newId) { - this.activeSector.push(newId); - }, - + Network.prototype._drawEdges = function(ctx) { + var edges = this.edges; + for (var id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + edge.setScale(this.scale); + if (edge.connected) { + edges[id].draw(ctx); + } + } + } + }; /** - * We remove the currently active sector id from the active sector stack. This happens when - * we reactivate the previously active sector - * + * Redraw all edges + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx * @private */ - _forgetLastSector : function() { - this.activeSector.pop(); - }, - + Network.prototype._drawControlNodes = function(ctx) { + var edges = this.edges; + for (var id in edges) { + if (edges.hasOwnProperty(id)) { + edges[id]._drawControlNodes(ctx); + } + } + }; /** - * This function creates a new active sector with the supplied newId. This newId - * is the expanding node id. - * - * @param {String} newId | Id of the new active sector + * Find a stable position for all nodes * @private */ - _createNewSector : function(newId) { - // create the new sector - this.sectors["active"][newId] = {"nodes":{}, - "edges":{}, - "nodeIndices":[], - "formationScale": this.scale, - "drawingNode": undefined}; - - // create the new sector render node. This gives visual feedback that you are in a new sector. - this.sectors["active"][newId]['drawingNode'] = new Node( - {id:newId, - color: { - background: "#eaefef", - border: "495c5e" - } - },{},{},this.constants); - this.sectors["active"][newId]['drawingNode'].clusterSize = 2; - }, + Network.prototype._stabilize = function() { + if (this.constants.freezeForStabilization == true) { + this._freezeDefinedNodes(); + } + // find stable position + var count = 0; + while (this.moving && count < this.constants.stabilizationIterations) { + this._physicsTick(); + count++; + } + this.zoomExtent(false,true); + if (this.constants.freezeForStabilization == true) { + this._restoreFrozenNodes(); + } + this.emit("stabilized",{iterations:count}); + }; /** - * This function removes the currently active sector. This is called when we create a new - * active sector. + * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization + * because only the supportnodes for the smoothCurves have to settle. * - * @param {String} sectorId | Id of the active sector that will be removed * @private */ - _deleteActiveSector : function(sectorId) { - delete this.sectors["active"][sectorId]; - }, - + Network.prototype._freezeDefinedNodes = function() { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + if (nodes[id].x != null && nodes[id].y != null) { + nodes[id].fixedData.x = nodes[id].xFixed; + nodes[id].fixedData.y = nodes[id].yFixed; + nodes[id].xFixed = true; + nodes[id].yFixed = true; + } + } + } + }; /** - * This function removes the currently active sector. This is called when we reactivate - * the previously active sector. + * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. * - * @param {String} sectorId | Id of the active sector that will be removed * @private */ - _deleteFrozenSector : function(sectorId) { - delete this.sectors["frozen"][sectorId]; - }, + Network.prototype._restoreFrozenNodes = function() { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + if (nodes[id].fixedData.x != null) { + nodes[id].xFixed = nodes[id].fixedData.x; + nodes[id].yFixed = nodes[id].fixedData.y; + } + } + } + }; /** - * Freezing an active sector means moving it from the "active" object to the "frozen" object. - * We copy the references, then delete the active entree. - * - * @param sectorId + * Check if any of the nodes is still moving + * @param {number} vmin the minimum velocity considered as 'moving' + * @return {boolean} true if moving, false if non of the nodes is moving * @private */ - _freezeSector : function(sectorId) { - // we move the set references from the active to the frozen stack. - this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; - - // we have moved the sector data into the frozen set, we now remove it from the active set - this._deleteActiveSector(sectorId); - }, + Network.prototype._isMoving = function(vmin) { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) { + return true; + } + } + return false; + }; /** - * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" - * object to the "active" object. + * /** + * Perform one discrete step for all nodes * - * @param sectorId * @private */ - _activateSector : function(sectorId) { - // we move the set references from the frozen to the active stack. - this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; - - // we have moved the sector data into the active set, we now remove it from the frozen stack - this._deleteFrozenSector(sectorId); - }, - + Network.prototype._discreteStepNodes = function() { + var interval = this.physicsDiscreteStepsize; + var nodes = this.nodes; + var nodeId; + var nodesPresent = false; - /** - * This function merges the data from the currently active sector with a frozen sector. This is used - * in the process of reverting back to the previously active sector. - * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it - * upon the creation of a new active sector. - * - * @param sectorId - * @private - */ - _mergeThisWithFrozen : function(sectorId) { - // copy all nodes - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; + if (this.constants.maxVelocity > 0) { + for (nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity); + nodesPresent = true; + } } } - - // copy all edges (if not fully clustered, else there are no edges) - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; + else { + for (nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].discreteStep(interval); + nodesPresent = true; + } } } - // merge the nodeIndices - for (var i = 0; i < this.nodeIndices.length; i++) { - this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); + if (nodesPresent == true) { + var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05); + if (vminCorrected > 0.5*this.constants.maxVelocity) { + this.moving = true; + } + else { + this.moving = this._isMoving(vminCorrected); + } } - }, - + }; /** - * This clusters the sector to one cluster. It was a single cluster before this process started so - * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. + * A single simulation step (or "tick") in the physics simulation * * @private */ - _collapseThisToSingleCluster : function() { - this.clusterToFit(1,false); - }, + Network.prototype._physicsTick = function() { + if (!this.freezeSimulation) { + if (this.moving) { + this._doInAllActiveSectors("_initializeForceCalculation"); + this._doInAllActiveSectors("_discreteStepNodes"); + if (this.constants.smoothCurves) { + this._doInSupportSector("_discreteStepNodes"); + } + this._findCenter(this._getRange()) + } + } + }; /** - * We create a new active sector from the node that we want to open. + * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick. + * It reschedules itself at the beginning of the function * - * @param node * @private */ - _addSector : function(node) { - // this is the currently active sector - var sector = this._sector(); - -// // this should allow me to select nodes from a frozen set. -// if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { -// console.log("the node is part of the active sector"); -// } -// else { -// console.log("I dont know what the fuck happened!!"); -// } - - // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. - delete this.nodes[node.id]; + Network.prototype._animationStep = function() { + // reset the timer so a new scheduled animation step can be set + this.timer = undefined; + // handle the keyboad movement + this._handleNavigation(); - var unqiueIdentifier = util.randomUUID(); + // this schedules a new animation step + this.start(); - // we fully freeze the currently active sector - this._freezeSector(sector); + // start the physics simulation + var calculationTime = Date.now(); + var maxSteps = 1; + this._physicsTick(); + var timeRequired = Date.now() - calculationTime; + while (timeRequired < 0.9*(this.renderTimestep - this.renderTime) && maxSteps < this.maxPhysicsTicksPerRender) { + this._physicsTick(); + timeRequired = Date.now() - calculationTime; + maxSteps++; + } + // start the rendering process + var renderTime = Date.now(); + this._redraw(); + this.renderTime = Date.now() - renderTime; + }; - // we create a new active sector. This sector has the Id of the node to ensure uniqueness - this._createNewSector(unqiueIdentifier); + if (typeof window !== 'undefined') { + window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; + } - // we add the active sector to the sectors array to be able to revert these steps later on - this._setActiveSector(unqiueIdentifier); + /** + * Schedule a animation step with the refreshrate interval. + */ + Network.prototype.start = function() { + if (this.moving || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0) { + if (!this.timer) { + var ua = navigator.userAgent.toLowerCase(); - // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier - this._switchToSector(this._sector()); + var requiresTimeout = false; + if (ua.indexOf('msie 9.0') != -1) { // IE 9 + requiresTimeout = true; + } + else if (ua.indexOf('safari') != -1) { // safari + if (ua.indexOf('chrome') <= -1) { + requiresTimeout = true; + } + } - // finally we add the node we removed from our previous active sector to the new active sector - this.nodes[node.id] = node; - }, + if (requiresTimeout == true) { + this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function + } + else{ + this.timer = window.requestAnimationFrame(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function + } + } + } + else { + this._redraw(); + } + }; /** - * We close the sector that is currently open and revert back to the one before. - * If the active sector is the "default" sector, nothing happens. + * Move the network according to the keyboard presses. * * @private */ - _collapseSector : function() { - // the currently active sector - var sector = this._sector(); - - // we cannot collapse the default sector - if (sector != "default") { - if ((this.nodeIndices.length == 1) || - (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - var previousSector = this._previousSector(); - - // we collapse the sector back to a single cluster - this._collapseThisToSingleCluster(); - - // we move the remaining nodes, edges and nodeIndices to the previous sector. - // This previous sector is the one we will reactivate - this._mergeThisWithFrozen(previousSector); - - // the previously active (frozen) sector now has all the data from the currently active sector. - // we can now delete the active sector. - this._deleteActiveSector(sector); - - // we activate the previously active (and currently frozen) sector. - this._activateSector(previousSector); - - // we load the references from the newly active sector into the global references - this._switchToSector(previousSector); - - // we forget the previously active sector because we reverted to the one before - this._forgetLastSector(); + Network.prototype._handleNavigation = function() { + if (this.xIncrement != 0 || this.yIncrement != 0) { + var translation = this._getTranslation(); + this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement); + } + if (this.zoomIncrement != 0) { + var center = { + x: this.frame.canvas.clientWidth / 2, + y: this.frame.canvas.clientHeight / 2 + }; + this._zoom(this.scale*(1 + this.zoomIncrement), center); + } + }; - // finally, we update the node index list. - this._updateNodeIndexList(); - // we refresh the list with calulation nodes and calculation node indices. - this._updateCalculationNodes(); - } + /** + * Freeze the _animationStep + */ + Network.prototype.toggleFreeze = function() { + if (this.freezeSimulation == false) { + this.freezeSimulation = true; + } + else { + this.freezeSimulation = false; + this.start(); } - }, + }; /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * This function cleans the support nodes if they are not needed and adds them when they are. * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @param {boolean} [disableStart] * @private */ - _doInAllActiveSectors : function(runFunction,argument) { - if (argument === undefined) { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - this[runFunction](); - } - } + Network.prototype._configureSmoothCurves = function(disableStart) { + if (disableStart === undefined) { + disableStart = true; + } + + if (this.constants.smoothCurves == true) { + this._createBezierNodes(); } else { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); - } + // delete the support nodes + this.sectors['support']['nodes'] = {}; + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.edges[edgeId].smooth = false; + this.edges[edgeId].via = null; } } } - // we revert the global references back to our active sector - this._loadLatestSector(); - }, + this._updateCalculationNodes(); + if (!disableStart) { + this.moving = true; + this.start(); + } + }; /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but + * are used for the force calculation. * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - _doInSupportSector : function(runFunction,argument) { - if (argument === undefined) { - this._switchToSupportSector(); - this[runFunction](); - } - else { - this._switchToSupportSector(); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); + Network.prototype._createBezierNodes = function() { + if (this.constants.smoothCurves == true) { + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.via == null) { + edge.smooth = true; + var nodeId = "edgeId:".concat(edge.id); + this.sectors['support']['nodes'][nodeId] = new Node( + {id:nodeId, + mass:1, + shape:'circle', + image:"", + internalMultiplier:1 + },{},{},this.constants); + edge.via = this.sectors['support']['nodes'][nodeId]; + edge.via.parentEdgeId = edge.id; + edge.positionBezierNode(); + } + } } } - // we revert the global references back to our active sector - this._loadLatestSector(); - }, - + }; /** - * This runs a function in all frozen sectors. This is used in the _redraw(). + * load the functions that load the mixins into the prototype. * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - _doInAllFrozenSectors : function(runFunction,argument) { - if (argument === undefined) { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - this[runFunction](); - } + Network.prototype._initializeMixinLoaders = function () { + for (var mixin in MixinLoader) { + if (MixinLoader.hasOwnProperty(mixin)) { + Network.prototype[mixin] = MixinLoader[mixin]; } } - else { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); - } + }; + + /** + * Load the XY positions of the nodes into the dataset. + */ + Network.prototype.storePosition = function() { + var dataArray = []; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + var allowedToMoveX = !this.nodes.xFixed; + var allowedToMoveY = !this.nodes.yFixed; + if (this.nodesData._data[nodeId].x != Math.round(node.x) || this.nodesData._data[nodeId].y != Math.round(node.y)) { + dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY}); } } } - this._loadLatestSector(); - }, + this.nodesData.update(dataArray); + }; /** - * This runs a function in all sectors. This is used in the _redraw(). + * Center a node in view. * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private + * @param {Number} nodeId + * @param {Number} [zoomLevel] */ - _doInAllSectors : function(runFunction,argument) { - var args = Array.prototype.splice.call(arguments, 1); - if (argument === undefined) { - this._doInAllActiveSectors(runFunction); - this._doInAllFrozenSectors(runFunction); + Network.prototype.focusOnNode = function (nodeId, zoomLevel) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (zoomLevel === undefined) { + zoomLevel = this._getScale(); + } + var nodePosition= {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y}; + + var requiredScale = zoomLevel; + this._setScale(requiredScale); + + var canvasCenter = this.DOMtoCanvas({x:0.5 * this.frame.canvas.width,y:0.5 * this.frame.canvas.height}); + var translation = this._getTranslation(); + + var distanceFromCenter = {x:canvasCenter.x - nodePosition.x, + y:canvasCenter.y - nodePosition.y}; + + this._setTranslation(translation.x + requiredScale * distanceFromCenter.x, + translation.y + requiredScale * distanceFromCenter.y); + this.redraw(); } else { - if (args.length > 1) { - this._doInAllActiveSectors(runFunction,args[0],args[1]); - this._doInAllFrozenSectors(runFunction,args[0],args[1]); - } - else { - this._doInAllActiveSectors(runFunction,argument); - this._doInAllFrozenSectors(runFunction,argument); - } + console.log("This nodeId cannot be found.") } - }, + }; + module.exports = Network; - /** - * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the - * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. - * - * @private - */ - _clearNodeIndexList : function() { - var sector = this._sector(); - this.sectors["active"][sector]["nodeIndices"] = []; - this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; - }, +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); /** - * Draw the encompassing sector node + * @class Edge * - * @param ctx - * @param sectorType - * @private + * A edge connects two nodes + * @param {Object} properties Object with properties. Must contain + * At least properties from and to. + * Available properties: from (number), + * to (number), label (string, color (string), + * width (number), style (string), + * length (number), title (string) + * @param {Network} network A Network object, used to find and edge to + * nodes. + * @param {Object} constants An object with default values for + * example for the color */ - _drawSectorNodes : function(ctx,sectorType) { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var sector in this.sectors[sectorType]) { - if (this.sectors[sectorType].hasOwnProperty(sector)) { - if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { - - this._switchToSector(sector,sectorType); - - minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.resize(ctx); - if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} - if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} - if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} - if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} - } - } - node = this.sectors[sectorType][sector]["drawingNode"]; - node.x = 0.5 * (maxX + minX); - node.y = 0.5 * (maxY + minY); - node.width = 2 * (node.x - minX); - node.height = 2 * (node.y - minY); - node.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); - node.setScale(this.scale); - node._drawCircle(ctx); - } - } - } - }, - - _drawAllSectorNodes : function(ctx) { - this._drawSectorNodes(ctx,"frozen"); - this._drawSectorNodes(ctx,"active"); - this._loadLatestSector(); + function Edge (properties, network, constants) { + if (!network) { + throw "No network provided"; + } + this.network = network; + + // initialize constants + this.widthMin = constants.edges.widthMin; + this.widthMax = constants.edges.widthMax; + + // initialize variables + this.id = undefined; + this.fromId = undefined; + this.toId = undefined; + this.style = constants.edges.style; + this.title = undefined; + this.width = constants.edges.width; + this.widthSelectionMultiplier = constants.edges.widthSelectionMultiplier; + this.widthSelected = this.width * this.widthSelectionMultiplier; + this.hoverWidth = constants.edges.hoverWidth; + this.value = undefined; + this.length = constants.physics.springLength; + this.customLength = false; + this.selected = false; + this.hover = false; + this.smooth = constants.smoothCurves; + this.arrowScaleFactor = constants.edges.arrowScaleFactor; + + this.from = null; // a node + this.to = null; // a node + this.via = null; // a temp node + + // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster + // by storing the original information we can revert to the original connection when the cluser is opened. + this.originalFromId = []; + this.originalToId = []; + + this.connected = false; + + // Added to support dashed lines + // David Jordan + // 2012-08-08 + this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength + + this.color = {color:constants.edges.color.color, + highlight:constants.edges.color.highlight, + hover:constants.edges.color.hover}; + this.widthFixed = false; + this.lengthFixed = false; + + this.setProperties(properties, constants); + + this.controlNodesEnabled = false; + this.controlNodes = {from:null, to:null, positions:{}}; + this.connectedNode = null; } -}; -/** - * Creation of the ClusterMixin var. - * - * This contains all the functions the Network object can use to employ clustering - * - * Alex de Mulder - * 21-01-2013 - */ -var ClusterMixin = { - - /** - * This is only called in the constructor of the network object - * - */ - startWithClustering : function() { - // cluster if the data set is big - this.clusterToFit(this.constants.clustering.initialMaxNodes, true); - - // updates the lables after clustering - this.updateLabels(); - - // this is called here because if clusterin is disabled, the start and stabilize are called in - // the setData function. - if (this.stabilize) { - this._stabilize(); - } - this.start(); - }, /** - * This function clusters until the initialMaxNodes has been reached - * - * @param {Number} maxNumberOfNodes - * @param {Boolean} reposition + * Set or overwrite properties for the edge + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - clusterToFit : function(maxNumberOfNodes, reposition) { - var numberOfNodes = this.nodeIndices.length; + Edge.prototype.setProperties = function(properties, constants) { + if (!properties) { + return; + } - var maxLevels = 50; - var level = 0; + if (properties.from !== undefined) {this.fromId = properties.from;} + if (properties.to !== undefined) {this.toId = properties.to;} - // we first cluster the hubs, then we pull in the outliers, repeat - while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { - if (level % 3 == 0) { - this.forceAggregateHubs(true); - this.normalizeClusterLevels(); + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.style !== undefined) {this.style = properties.style;} + if (properties.label !== undefined) {this.label = properties.label;} + + if (this.label) { + this.fontSize = constants.edges.fontSize; + this.fontFace = constants.edges.fontFace; + this.fontColor = constants.edges.fontColor; + this.fontFill = constants.edges.fontFill; + + if (properties.fontColor !== undefined) {this.fontColor = properties.fontColor;} + if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;} + if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;} + if (properties.fontFill !== undefined) {this.fontFill = properties.fontFill;} + } + + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.width !== undefined) {this.width = properties.width;} + if (properties.widthSelectionMultiplier !== undefined) + {this.widthSelectionMultiplier = properties.widthSelectionMultiplier;} + if (properties.hoverWidth !== undefined) {this.hoverWidth = properties.hoverWidth;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.length !== undefined) {this.length = properties.length; + this.customLength = true;} + + // scale the arrow + if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor;} + + // Added to support dashed lines + // David Jordan + // 2012-08-08 + if (properties.dash) { + if (properties.dash.length !== undefined) {this.dash.length = properties.dash.length;} + if (properties.dash.gap !== undefined) {this.dash.gap = properties.dash.gap;} + if (properties.dash.altLength !== undefined) {this.dash.altLength = properties.dash.altLength;} + } + + if (properties.color !== undefined) { + if (util.isString(properties.color)) { + this.color.color = properties.color; + this.color.highlight = properties.color; } else { - this.increaseClusterLevel(); // this also includes a cluster normalization + if (properties.color.color !== undefined) {this.color.color = properties.color.color;} + if (properties.color.highlight !== undefined) {this.color.highlight = properties.color.highlight;} + if (properties.color.hover !== undefined) {this.color.hover = properties.color.hover;} } - - numberOfNodes = this.nodeIndices.length; - level += 1; } - // after the clustering we reposition the nodes to reduce the initial chaos - if (level > 0 && reposition == true) { - this.repositionNodes(); + // A node is connected when it has a from and to node. + this.connect(); + + this.widthFixed = this.widthFixed || (properties.width !== undefined); + this.lengthFixed = this.lengthFixed || (properties.length !== undefined); + + this.widthSelected = this.width * this.widthSelectionMultiplier; + + // set draw method based on style + switch (this.style) { + case 'line': this.draw = this._drawLine; break; + case 'arrow': this.draw = this._drawArrow; break; + case 'arrow-center': this.draw = this._drawArrowCenter; break; + case 'dash-line': this.draw = this._drawDashLine; break; + default: this.draw = this._drawLine; break; } - this._updateCalculationNodes(); - }, + }; /** - * This function can be called to open up a specific cluster. It is only called by - * It will unpack the cluster back one level. - * - * @param node | Node object: cluster to open. + * Connect an edge to its nodes */ - openCluster : function(node) { - var isMovingBeforeClustering = this.moving; - if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && - !(this._sector() == "default" && this.nodeIndices.length == 1)) { - // this loads a new sector, loads the nodes and edges and nodeIndices of it. - this._addSector(node); - var level = 0; + Edge.prototype.connect = function () { + this.disconnect(); - // we decluster until we reach a decent number of nodes - while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { - this.decreaseClusterLevel(); - level += 1; - } + this.from = this.network.nodes[this.fromId] || null; + this.to = this.network.nodes[this.toId] || null; + this.connected = (this.from && this.to); + if (this.connected) { + this.from.attachEdge(this); + this.to.attachEdge(this); } else { - this._expandClusterNode(node,false,true); - - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this._updateCalculationNodes(); - this.updateLabels(); + if (this.from) { + this.from.detachEdge(this); + } + if (this.to) { + this.to.detachEdge(this); + } } + }; - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); + /** + * Disconnect an edge from its nodes + */ + Edge.prototype.disconnect = function () { + if (this.from) { + this.from.detachEdge(this); + this.from = null; + } + if (this.to) { + this.to.detachEdge(this); + this.to = null; } - }, + this.connected = false; + }; /** - * This calls the updateClustes with default arguments + * get the title of this edge. + * @return {string} title The title of the edge, or undefined when no title + * has been set. */ - updateClustersDefault : function() { - if (this.constants.clustering.enabled == true) { - this.updateClusters(0,false,false); - } - }, + Edge.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; + }; /** - * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will - * be clustered with their connected node. This can be repeated as many times as needed. - * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. + * Retrieve the value of the edge. Can be undefined + * @return {Number} value */ - increaseClusterLevel : function() { - this.updateClusters(-1,false,true); - }, - + Edge.prototype.getValue = function() { + return this.value; + }; /** - * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will - * be unpacked if they are a cluster. This can be repeated as many times as needed. - * This can be called externally (by a key-bind for instance) to look into clusters without zooming. + * Adjust the value range of the edge. The edge will adjust it's width + * based on its value. + * @param {Number} min + * @param {Number} max */ - decreaseClusterLevel : function() { - this.updateClusters(1,false,true); - }, + Edge.prototype.setValueRange = function(min, max) { + if (!this.widthFixed && this.value !== undefined) { + var scale = (this.widthMax - this.widthMin) / (max - min); + this.width = (this.value - min) * scale + this.widthMin; + } + }; + /** + * Redraw a edge + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + */ + Edge.prototype.draw = function(ctx) { + throw "Method draw not initialized in edge"; + }; /** - * This is the main clustering function. It clusters and declusters on zoom or forced - * This function clusters on zoom, it can be called with a predefined zoom direction - * If out, check if we can form clusters, if in, check if we can open clusters. - * This function is only called from _zoom() - * - * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn - * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} doNotStart | if true do not call start - * + * Check if this object is overlapping with the provided object + * @param {Object} obj an object with parameters left, top + * @return {boolean} True if location is located on the edge */ - updateClusters : function(zoomDirection,recursive,force,doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; + Edge.prototype.isOverlappingWith = function(obj) { + if (this.connected) { + var distMax = 10; + var xFrom = this.from.x; + var yFrom = this.from.y; + var xTo = this.to.x; + var yTo = this.to.y; + var xObj = obj.left; + var yObj = obj.top; - // on zoom out collapse the sector if the scale is at the level the sector was made - if (this.previousScale > this.scale && zoomDirection == 0) { - this._collapseSector(); + var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); + + return (dist < distMax); + } + else { + return false } + }; - // check if we zoom in or out - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - // forming clusters when forced pulls outliers in. When not forced, the edge length of the - // outer nodes determines if it is being clustered - this._formClusters(force); + + /** + * Redraw a edge as a line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._drawLine = function(ctx) { + // set style + if (this.selected == true) {ctx.strokeStyle = this.color.highlight;} + else if (this.hover == true) {ctx.strokeStyle = this.color.hover;} + else {ctx.strokeStyle = this.color.color;} + ctx.lineWidth = this._getLineWidth(); + + if (this.from != this.to) { + // draw line + this._line(ctx); + + // draw label + var point; + if (this.label) { + if (this.smooth == true) { + var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x)); + var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); + } } - else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in - if (force == true) { - // _openClusters checks for each node if the formationScale of the cluster is smaller than - // the current scale and if so, declusters. When forced, all clusters are reduced by one step - this._openClusters(recursive,force); + else { + var x, y; + 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 a cluster takes up a set percentage of the active window - this._openClustersBySize(); + x = node.x + radius; + y = node.y - node.height / 2; } + this._circle(ctx, x, y, radius); + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); } - this._updateNodeIndexList(); + }; - // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs - if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { - this._aggregateHubs(force); - this._updateNodeIndexList(); + /** + * Get the line width of the edge. Depends on width and whether one of the + * connected nodes is selected. + * @return {Number} width + * @private + */ + Edge.prototype._getLineWidth = function() { + if (this.selected == true) { + return Math.min(this.widthSelected, this.widthMax)*this.networkScaleInv; } - - // we now reduce chains. - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - this.handleChains(); - this._updateNodeIndexList(); + else { + if (this.hover == true) { + return Math.min(this.hoverWidth, this.widthMax)*this.networkScaleInv; + } + else { + return this.width*this.networkScaleInv; + } } + }; - this.previousScale = this.scale; - - // rest of the update the index list, dynamic edges and labels - this._updateDynamicEdges(); - this.updateLabels(); - - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place - this.clusterSession += 1; - // if clusters have been made, we normalize the cluster level - this.normalizeClusterLevels(); + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._line = function (ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + if (this.smooth == true) { + ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); } - - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); - } + else { + ctx.lineTo(this.to.x, this.to.y); } - - this._updateCalculationNodes(); - }, + ctx.stroke(); + }; /** - * This function handles the chains. It is called on every updateClusters(). + * Draw a line from a node to itself, a circle + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @private */ - handleChains : function() { - // after clustering we check how many chains there are - var chainPercentage = this._getChainFraction(); - if (chainPercentage > this.constants.clustering.chainThreshold) { - this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) + Edge.prototype._circle = function (ctx, x, y, radius) { + // draw a circle + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); + }; + /** + * Draw label with white background and with the middle at (x, y) + * @param {CanvasRenderingContext2D} ctx + * @param {String} text + * @param {Number} x + * @param {Number} y + * @private + */ + Edge.prototype._label = function (ctx, text, x, y) { + if (text) { + // TODO: cache the calculated size + ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + + this.fontSize + "px " + this.fontFace; + ctx.fillStyle = this.fontFill; + var width = ctx.measureText(text).width; + var height = this.fontSize; + var left = x - width / 2; + var top = y - height / 2; + + ctx.fillRect(left, top, width, height); + + // draw text + ctx.fillStyle = this.fontColor || "black"; + ctx.textAlign = "left"; + ctx.textBaseline = "top"; + ctx.fillText(text, left, top); } - }, + }; /** - * this functions starts clustering by hubs - * The minimum hub threshold is set globally - * + * Redraw a edge as a dashed line + * Draw this edge in the given canvas + * @author David Jordan + * @date 2012-08-08 + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx * @private */ - _aggregateHubs : function(force) { - this._getHubSize(); - this._formClustersByHub(force,false); - }, + Edge.prototype._drawDashLine = function(ctx) { + // set style + if (this.selected == true) {ctx.strokeStyle = this.color.highlight;} + else if (this.hover == true) {ctx.strokeStyle = this.color.hover;} + else {ctx.strokeStyle = this.color.color;} + ctx.lineWidth = this._getLineWidth(); - /** - * This function is fired by keypress. It forces hubs to form. - * - */ - forceAggregateHubs : function(doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; + // only firefox and chrome support this method, else we use the legacy one. + if (ctx.mozDash !== undefined || ctx.setLineDash !== undefined) { + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); - this._aggregateHubs(true); + // configure the dash pattern + var pattern = [0]; + if (this.dash.length !== undefined && this.dash.gap !== undefined) { + pattern = [this.dash.length,this.dash.gap]; + } + else { + pattern = [5,5]; + } - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this.updateLabels(); + // set dash settings for chrome or firefox + if (typeof ctx.setLineDash !== 'undefined') { //Chrome + ctx.setLineDash(pattern); + ctx.lineDashOffset = 0; - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; + } else { //Firefox + ctx.mozDash = pattern; + ctx.mozDashOffset = 0; + } + + // draw the line + if (this.smooth == true) { + ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); + } + else { + ctx.lineTo(this.to.x, this.to.y); + } + ctx.stroke(); + + // restore the dash settings. + if (typeof ctx.setLineDash !== 'undefined') { //Chrome + ctx.setLineDash([0]); + ctx.lineDashOffset = 0; + + } else { //Firefox + ctx.mozDash = [0]; + ctx.mozDashOffset = 0; + } + } + else { // unsupporting smooth lines + // draw dashed line + ctx.beginPath(); + ctx.lineCap = 'round'; + if (this.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]); + } + else if (this.dash.length !== undefined && this.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.dash.length,this.dash.gap]); + } + else //If all else fails draw a line + { + ctx.moveTo(this.from.x, this.from.y); + ctx.lineTo(this.to.x, this.to.y); + } + ctx.stroke(); } - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); + // draw label + if (this.label) { + var point; + if (this.smooth == true) { + var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x)); + var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y)); + point = {x:midpointX, y:midpointY}; } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); } - }, + }; /** - * If a cluster takes up more than a set percentage of the screen, open the cluster - * + * Get a point on a line + * @param {Number} percentage. Value between 0 (line start) and 1 (line end) + * @return {Object} point * @private */ - _openClustersBySize : function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.inView() == true) { - if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - this.openCluster(node); - } - } - } + Edge.prototype._pointOnLine = function (percentage) { + return { + x: (1 - percentage) * this.from.x + percentage * this.to.x, + y: (1 - percentage) * this.from.y + percentage * this.to.y } - }, - + }; /** - * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it - * has to be opened based on the current zoom level. - * + * Get a point on a circle + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @param {Number} percentage. Value between 0 (line start) and 1 (line end) + * @return {Object} point * @private */ - _openClusters : function(recursive,force) { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - this._expandClusterNode(node,recursive,force); - this._updateCalculationNodes(); + Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { + var angle = (percentage - 3/8) * 2 * Math.PI; + return { + x: x + radius * Math.cos(angle), + y: y - radius * Math.sin(angle) } - }, + }; /** - * This function checks if a node has to be opened. This is done by checking the zoom level. - * If the node contains child nodes, this function is recursively called on the child nodes as well. - * This recursive behaviour is optional and can be set by the recursive argument. - * - * @param {Node} parentNode | to check for cluster and expand - * @param {Boolean} recursive | enabled or disable recursive calling - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released + * Redraw a edge as a line with an arrow halfway the line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx * @private */ - _expandClusterNode : function(parentNode, recursive, force, openAll) { - // first check if node is a cluster - if (parentNode.clusterSize > 1) { - // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 - if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { - openAll = true; + Edge.prototype._drawArrowCenter = function(ctx) { + var point; + // set style + if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;} + else if (this.hover == true) {ctx.strokeStyle = this.color.hover; ctx.fillStyle = this.color.hover;} + else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;} + ctx.lineWidth = this._getLineWidth(); + + if (this.from != this.to) { + // draw line + this._line(ctx); + + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var length = (10 + 5 * this.width) * this.arrowScaleFactor; + // draw an arrow halfway the line + if (this.smooth == true) { + var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x)); + var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); } - recursive = openAll ? true : recursive; - // if the last child has been added on a smaller scale than current scale decluster - if (parentNode.formationScale < this.scale || force == true) { - // we will check if any of the contained child nodes should be removed from the cluster - for (var containedNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { - var childNode = parentNode.containedNodes[containedNodeId]; + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); - // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that - // the largest cluster is the one that comes from outside - if (force == true) { - if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] - || openAll) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } - else { - if (this._nodeInActiveArea(parentNode)) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } - } - } + // draw label + if (this.label) { + this._label(ctx, this.label, point.x, point.y); + } + } + else { + // draw circle + var x, y; + var radius = 0.25 * Math.max(100,this.length); + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + } + this._circle(ctx, x, y, radius); + + // draw all arrows + var angle = 0.2 * Math.PI; + var length = (10 + 5 * this.width) * this.arrowScaleFactor; + point = this._pointOnCircle(x, y, radius, 0.5); + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); } } - }, + }; + + /** - * ONLY CALLED FROM _expandClusterNode - * - * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove - * the child node from the parent contained_node object and put it back into the global nodes object. - * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. - * - * @param {Node} parentNode | the parent node - * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node - * @param {Boolean} recursive | This will also check if the child needs to be expanded. - * With force and recursive both true, the entire cluster is unpacked - * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent - * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released + * Redraw a edge as a line with an arrow + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx * @private */ - _expelChildFromParent : function(parentNode, containedNodeId, recursive, force, openAll) { - var childNode = parentNode.containedNodes[containedNodeId]; + Edge.prototype._drawArrow = function(ctx) { + // set style + if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;} + else if (this.hover == true) {ctx.strokeStyle = this.color.hover; ctx.fillStyle = this.color.hover;} + else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;} - // if child node has been added on smaller scale than current, kick out - if (childNode.formationScale < this.scale || force == true) { - // unselect all selected items - this._unselectAll(); + ctx.lineWidth = this._getLineWidth(); - // put the child node back in the global nodes object - this.nodes[containedNodeId] = childNode; + var angle, length; + //draw a line + if (this.from != this.to) { + 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); - // release the contained edges from this childNode back into the global edges - this._releaseContainedEdges(parentNode,childNode); + 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; - // reconnect rerouted edges to the childNode - this._connectEdgeBackToChild(parentNode,childNode); - // validate all edges in dynamicEdges - this._validateEdges(parentNode); + 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; - // undo the changes from the clustering operation on the parent node - parentNode.mass -= childNode.mass; - parentNode.clusterSize -= childNode.clusterSize; - parentNode.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); - parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; + 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; + } - // place the child node near the parent, not at the exact same location to avoid chaos in the system - childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); - childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); + ctx.beginPath(); + ctx.moveTo(xFrom,yFrom); + if (this.smooth == true) { + ctx.quadraticCurveTo(this.via.x,this.via.y,xTo, yTo); + } + else { + ctx.lineTo(xTo, yTo); + } + ctx.stroke(); - // remove node from the list - delete parentNode.containedNodes[containedNodeId]; + // draw arrow at the end of the line + length = (10 + 5 * this.width) * this.arrowScaleFactor; + ctx.arrow(xTo, yTo, angle, length); + ctx.fill(); + ctx.stroke(); - // check if there are other childs with this clusterSession in the parent. - var othersPresent = false; - for (var childNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { - if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { - othersPresent = true; - break; - } + // draw label + if (this.label) { + var point; + if (this.smooth == true) { + var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x)); + var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); } + this._label(ctx, this.label, point.x, point.y); } - // if there are no others, remove the cluster session from the list - if (othersPresent == false) { - parentNode.clusterSessions.pop(); + } + else { + // draw circle + var node = this.from; + var x, y, arrow; + var radius = 0.25 * Math.max(100,this.length); + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + arrow = { + x: x, + y: node.y, + angle: 0.9 * Math.PI + }; } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + arrow = { + x: node.x, + y: y, + angle: 0.6 * Math.PI + }; + } + ctx.beginPath(); + // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); - this._repositionBezierNodes(childNode); -// this._repositionBezierNodes(parentNode); - - // remove the clusterSession from the child node - childNode.clusterSession = 0; - - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); + // draw all arrows + var length = (10 + 5 * this.width) * this.arrowScaleFactor; + ctx.arrow(arrow.x, arrow.y, arrow.angle, length); + ctx.fill(); + ctx.stroke(); - // restart the simulation to reorganise all nodes - this.moving = true; + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } } + }; - // check if a further expansion step is possible if recursivity is enabled - if (recursive == true) { - this._expandClusterNode(childNode,recursive,force,openAll); - } - }, /** - * position the bezier nodes at the center of the edges - * - * @param node + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 * @private */ - _repositionBezierNodes : function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - node.dynamicEdges[i].positionBezierNode(); - } - }, + Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point + 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; + } - /** - * This function checks if any nodes at the end of their trees have edges below a threshold length - * This function is called only from updateClusters() - * forceLevelCollapse ignores the length of the edge and collapses one level - * This means that a node with only one edge will be clustered with its connected node - * - * @private - * @param {Boolean} force - */ - _formClusters : function(force) { - if (force == false) { - this._formClustersByZoom(); + 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 { - this._forceClustersByZoom(); + 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 { + x = node.x + radius; + y = node.y - node.height / 2; + } + dx = x - x3; + dy = y - y3; + return Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); } - }, + }; + /** - * This function handles the clustering by zooming out, this is based on a minimum edge distance + * This allows the zoom level of the network to influence the rendering * - * @private + * @param scale */ - _formClustersByZoom : function() { - var dx,dy,length, - minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + Edge.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + }; - // check if any edges are shorter than minLength and start the clustering - // the clustering favours the node with the larger mass - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); + Edge.prototype.select = function() { + this.selected = true; + }; - if (length < minLength) { - // first check which node is larger - var parentNode = edge.from; - var childNode = edge.to; - if (edge.to.mass > edge.from.mass) { - parentNode = edge.to; - childNode = edge.from; - } + Edge.prototype.unselect = function() { + this.selected = false; + }; - if (childNode.dynamicEdgesLength == 1) { - this._addToCluster(parentNode,childNode,false); - } - else if (parentNode.dynamicEdgesLength == 1) { - this._addToCluster(childNode,parentNode,false); - } - } - } - } - } + Edge.prototype.positionBezierNode = function() { + if (this.via !== null) { + this.via.x = 0.5 * (this.from.x + this.to.x); + this.via.y = 0.5 * (this.from.y + this.to.y); } - }, + }; /** - * This function forces the network to cluster all nodes with only one connecting edge to their - * connected node. - * - * @private + * This function draws the control nodes for the manipulator. In order to enable this, only set the this.controlNodesEnabled to true. + * @param ctx */ - _forceClustersByZoom : function() { - for (var nodeId in this.nodes) { - // another node could have absorbed this child. - if (this.nodes.hasOwnProperty(nodeId)) { - var childNode = this.nodes[nodeId]; - - // the edges can be swallowed by another decrease - if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { - var edge = childNode.dynamicEdges[0]; - var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; + 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); + } - // group to the largest node - if (childNode.id != parentNode.id) { - if (parentNode.mass > childNode.mass) { - this._addToCluster(parentNode,childNode,true); - } - else { - this._addToCluster(childNode,parentNode,true); - } - } - } + 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; + }; /** - * To keep the nodes of roughly equal size we normalize the cluster levels. - * This function clusters a node to its smallest connected neighbour. - * - * @param node + * disable control nodes * @private */ - _clusterToSmallestNeighbour : function(node) { - var smallestNeighbour = -1; - var smallestNeighbourNode = null; - for (var i = 0; i < node.dynamicEdges.length; i++) { - if (node.dynamicEdges[i] !== undefined) { - var neighbour = null; - if (node.dynamicEdges[i].fromId != node.id) { - neighbour = node.dynamicEdges[i].from; - } - else if (node.dynamicEdges[i].toId != node.id) { - neighbour = node.dynamicEdges[i].to; - } + 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 (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { - smallestNeighbour = neighbour.clusterSessions.length; - smallestNeighbourNode = neighbour; - } - } + if (fromDistance < 15) { + this.connectedNode = this.from; + this.from = this.controlNodes.from; + return this.controlNodes.from; } - - if (neighbour != null && this.nodes[neighbour.id] !== undefined) { - this._addToCluster(neighbour, node, true); + else if (toDistance < 15) { + this.connectedNode = this.to; + this.to = this.controlNodes.to; + return this.controlNodes.to; + } + else { + return null; } - }, + }; /** - * This function forms clusters from hubs, it loops over all nodes - * - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * this resets the control nodes to their original position. * @private */ - _formClustersByHub : function(force, onlyEqual) { - // we loop over all nodes in the list - for (var nodeId in this.nodes) { - // we check if it is still available since it can be used by the clustering in this loop - if (this.nodes.hasOwnProperty(nodeId)) { - this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); - } + 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 function forms a cluster from a specific preselected hub node + * this calculates the position of the control nodes on the edges of the parent nodes. * - * @param {Node} hubNode | the node we will cluster as a hub - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges - * @param {Number} [absorptionSizeOffset] | - * @private + * @param ctx + * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} */ - _formClusterFromHub : function(hubNode, force, onlyEqual, absorptionSizeOffset) { - if (absorptionSizeOffset === undefined) { - absorptionSizeOffset = 0; + 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); } - // we decide if the node is a hub - if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || - (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { - // initialize variables - var dx,dy,length; - var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - var allowCluster = false; + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - // we create a list of edges because the dynamicEdges change over the course of this loop - var edgesIdarray = []; - var amountOfInitialEdges = hubNode.dynamicEdges.length; - for (var j = 0; j < amountOfInitialEdges; j++) { - edgesIdarray.push(hubNode.dynamicEdges[j].id); - } + var 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; + } - // if the hub clustering is not forces, we check if one of the edges connected - // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold - if (force == false) { - allowCluster = false; - for (j = 0; j < amountOfInitialEdges; j++) { - var edge = this.edges[edgesIdarray[j]]; - if (edge !== undefined) { - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); + return {from:{x:xFrom,y:yFrom},to:{x:xTo,y:yTo}}; + }; - if (length < minLength) { - allowCluster = true; - break; - } - } - } - } - } - } + module.exports = Edge; - // start the clustering if allowed - if ((!force && allowCluster) || force) { - // we loop over all edges INITIALLY connected to this hub - for (j = 0; j < amountOfInitialEdges; j++) { - edge = this.edges[edgesIdarray[j]]; - // the edge can be clustered by this function in a previous loop - if (edge !== undefined) { - var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; - // we do not want hubs to merge with other hubs nor do we want to cluster itself. - if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && - (childNode.id != hubNode.id)) { - this._addToCluster(hubNode,childNode,force); - } - } - } - } - } - }, +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + /** + * @class Groups + * This class can store groups and properties specific for groups. + */ + function Groups() { + this.clear(); + this.defaultIndex = 0; + } /** - * This function adds the child node to the parent node, creating a cluster if it is not already. - * - * @param {Node} parentNode | this is the node that will house the child node - * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node - * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse - * @private + * default constants for group colors */ - _addToCluster : function(parentNode, childNode, force) { - // join child node in the parent node - parentNode.containedNodes[childNode.id] = childNode; + Groups.DEFAULT = [ + {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue + {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}}, // yellow + {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}}, // red + {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}}, // green + {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}}, // magenta + {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}}, // purple + {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}}, // orange + {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue + {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}}, // pink + {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}} // mint + ]; - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < childNode.dynamicEdges.length; i++) { - var edge = childNode.dynamicEdges[i]; - if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode - this._addToContainedEdges(parentNode,childNode,edge); - } - else { - this._connectEdgeToCluster(parentNode,childNode,edge); + + /** + * Clear all groups + */ + Groups.prototype.clear = function () { + this.groups = {}; + this.groups.length = function() + { + var i = 0; + for ( var p in this ) { + if (this.hasOwnProperty(p)) { + i++; + } } + return i; } - // a contained node has no dynamic edges. - childNode.dynamicEdges = []; + }; - // remove circular edges from clusters - this._containCircularEdgesFromNode(parentNode,childNode); + /** + * get group properties of a groupname. If groupname is not found, a new group + * is added. + * @param {*} groupname Can be a number, string, Date, etc. + * @return {Object} group The created group, containing all group properties + */ + Groups.prototype.get = function (groupname) { + var group = this.groups[groupname]; - // remove the childNode from the global nodes object - delete this.nodes[childNode.id]; + if (group == undefined) { + // create new group + var index = this.defaultIndex % Groups.DEFAULT.length; + this.defaultIndex++; + group = {}; + group.color = Groups.DEFAULT[index]; + this.groups[groupname] = group; + } - // update the properties of the child and parent - var massBefore = parentNode.mass; - childNode.clusterSession = this.clusterSession; - parentNode.mass += childNode.mass; - parentNode.clusterSize += childNode.clusterSize; - parentNode.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); + return group; + }; - // keep track of the clustersessions so we can open the cluster up as it has been formed. - if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { - parentNode.clusterSessions.push(this.clusterSession); + /** + * Add a custom group style + * @param {String} groupname + * @param {Object} style An object containing borderColor, + * backgroundColor, etc. + * @return {Object} group The created group object + */ + Groups.prototype.add = function (groupname, style) { + this.groups[groupname] = style; + if (style.color) { + style.color = util.parseColor(style.color); } + return style; + }; - // forced clusters only open from screen size and double tap - if (force == true) { - // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); - parentNode.formationScale = 0; - } - else { - parentNode.formationScale = this.scale; // The latest child has been added on this scale - } + module.exports = Groups; - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); - // set the pop-out scale for the childnode - parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { - // nullify the movement velocity of the child, this is to avoid hectic behaviour - childNode.clearVelocity(); - - // the mass has altered, preservation of energy dictates the velocity to be updated - parentNode.updateVelocity(massBefore); - - // restart the simulation to reorganise all nodes - this.moving = true; - }, + /** + * @class Images + * This class loads images and keeps them stored. + */ + function Images() { + this.images = {}; + this.callback = undefined; + } /** - * This function will apply the changes made to the remainingEdges during the formation of the clusters. - * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. - * It has to be called if a level is collapsed. It is called by _formClusters(). - * @private + * Set an onload callback function. This will be called each time an image + * is loaded + * @param {function} callback */ - _updateDynamicEdges : function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - node.dynamicEdgesLength = node.dynamicEdges.length; + Images.prototype.setOnloadCallback = function(callback) { + this.callback = callback; + }; - // this corrects for multiple edges pointing at the same other node - var correction = 0; - if (node.dynamicEdgesLength > 1) { - for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { - var edgeToId = node.dynamicEdges[j].toId; - var edgeFromId = node.dynamicEdges[j].fromId; - for (var k = j+1; k < node.dynamicEdgesLength; k++) { - if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || - (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { - correction += 1; - } - } + /** + * + * @param {string} url Url of the image + * @return {Image} img The image object + */ + Images.prototype.load = function(url) { + var img = this.images[url]; + if (img == undefined) { + // create the image + var images = this; + img = new Image(); + this.images[url] = img; + img.onload = function() { + if (images.callback) { + images.callback(this); } - } - node.dynamicEdgesLength -= correction; + }; + img.src = url; } - }, + + return img; + }; + + module.exports = Images; +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + /** - * This adds an edge from the childNode to the contained edges of the parent node + * @class Node + * A node. A node can be connected to other nodes via one or multiple edges. + * @param {object} properties An object containing properties for the node. All + * properties are optional, except for the id. + * {number} id Id of the node. Required + * {string} label Text label for the node + * {number} x Horizontal position of the node + * {number} y Vertical position of the node + * {string} shape Node shape, available: + * "database", "circle", "ellipse", + * "box", "image", "text", "dot", + * "star", "triangle", "triangleDown", + * "square" + * {string} image An image url + * {string} title An title text, can be HTML + * {anytype} group A group name or number + * @param {Network.Images} imagelist A list with images. Only needed + * when the node has an image + * @param {Network.Groups} grouplist A list with groups. Needed for + * retrieving group properties + * @param {Object} constants An object with default values for + * example for the color * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object - * @private */ - _addToContainedEdges : function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { - parentNode.containedEdges[childNode.id] = [] - } - // add this edge to the list - parentNode.containedEdges[childNode.id].push(edge); + function Node(properties, imagelist, grouplist, constants) { + this.selected = false; + this.hover = false; + + this.edges = []; // all edges connected to this node + this.dynamicEdges = []; + this.reroutedEdges = {}; + + this.group = constants.nodes.group; + this.fontSize = Number(constants.nodes.fontSize); + this.fontFace = constants.nodes.fontFace; + this.fontColor = constants.nodes.fontColor; + this.fontDrawThreshold = 3; + + this.color = constants.nodes.color; + + // set defaults for the properties + this.id = undefined; + this.shape = constants.nodes.shape; + this.image = constants.nodes.image; + this.x = null; + this.y = null; + this.xFixed = false; + this.yFixed = false; + this.horizontalAlignLeft = true; // these are for the navigation controls + this.verticalAlignTop = true; // these are for the navigation controls + this.radius = constants.nodes.radius; + this.baseRadiusValue = constants.nodes.radius; + this.radiusFixed = false; + this.radiusMin = constants.nodes.radiusMin; + this.radiusMax = constants.nodes.radiusMax; + this.level = -1; + this.preassignedLevel = false; + + + this.imagelist = imagelist; + this.grouplist = grouplist; + + // physics properties + this.fx = 0.0; // external force x + this.fy = 0.0; // external force y + this.vx = 0.0; // velocity x + this.vy = 0.0; // velocity y + this.minForce = constants.minForce; + this.damping = constants.physics.damping; + this.mass = 1; // kg + this.fixedData = {x:null,y:null}; + + this.setProperties(properties, constants); + + // creating the variables for clustering + this.resetCluster(); + this.dynamicEdgesLength = 0; + this.clusterSession = 0; + this.clusterSizeWidthFactor = constants.clustering.nodeScaling.width; + this.clusterSizeHeightFactor = constants.clustering.nodeScaling.height; + this.clusterSizeRadiusFactor = constants.clustering.nodeScaling.radius; + this.maxNodeSizeIncrements = constants.clustering.maxNodeSizeIncrements; + this.growthIndicator = 0; - // remove the edge from the global edges object - delete this.edges[edge.id]; + // variables to tell the node about the network. + this.networkScaleInv = 1; + this.networkScale = 1; + this.canvasTopLeft = {"x": -300, "y": -300}; + this.canvasBottomRight = {"x": 300, "y": 300}; + this.parentEdgeId = null; + } - // remove the edge from the parent object - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - if (parentNode.dynamicEdges[i].id == edge.id) { - parentNode.dynamicEdges.splice(i,1); - break; - } - } - }, + /** + * (re)setting the clustering variables and objects + */ + Node.prototype.resetCluster = function() { + // clustering variables + this.formationScale = undefined; // this is used to determine when to open the cluster + this.clusterSize = 1; // this signifies the total amount of nodes in this cluster + this.containedNodes = {}; + this.containedEdges = {}; + this.clusterSessions = []; + }; /** - * This function connects an edge that was connected to a child node to the parent node. - * It keeps track of which nodes it has been connected to with the originalId array. - * - * @param {Node} parentNode | Node object - * @param {Node} childNode | Node object - * @param {Edge} edge | Edge object - * @private + * Attach a edge to the node + * @param {Edge} edge */ - _connectEdgeToCluster : function(parentNode, childNode, edge) { - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); + Node.prototype.attachEdge = function(edge) { + if (this.edges.indexOf(edge) == -1) { + this.edges.push(edge); } - else { - if (edge.toId == childNode.id) { // edge connected to other node on the "to" side - edge.originalToId.push(childNode.id); - edge.to = parentNode; - edge.toId = parentNode.id; - } - else { // edge connected to other node with the "from" side - - edge.originalFromId.push(childNode.id); - edge.from = parentNode; - edge.fromId = parentNode.id; - } - - this._addToReroutedEdges(parentNode,childNode,edge); + if (this.dynamicEdges.indexOf(edge) == -1) { + this.dynamicEdges.push(edge); } - }, - + this.dynamicEdgesLength = this.dynamicEdges.length; + }; /** - * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain - * these edges inside of the cluster. - * - * @param parentNode - * @param childNode - * @private + * Detach a edge from the node + * @param {Edge} edge */ - _containCircularEdgesFromNode : function(parentNode, childNode) { - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); - } + Node.prototype.detachEdge = function(edge) { + var index = this.edges.indexOf(edge); + if (index != -1) { + this.edges.splice(index, 1); + this.dynamicEdges.splice(index, 1); } - }, + this.dynamicEdgesLength = this.dynamicEdges.length; + }; /** - * This adds an edge from the childNode to the rerouted edges of the parent node - * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object - * @private + * Set or overwrite properties for the node + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - _addToReroutedEdges : function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - // we store the edge in the rerouted edges so we can restore it when the cluster pops open - if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { - parentNode.reroutedEdges[childNode.id] = []; + Node.prototype.setProperties = function(properties, constants) { + if (!properties) { + return; } - parentNode.reroutedEdges[childNode.id].push(edge); + this.originalLabel = undefined; + // basic properties + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.label !== undefined) {this.label = properties.label; this.originalLabel = properties.label;} + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.group !== undefined) {this.group = properties.group;} + if (properties.x !== undefined) {this.x = properties.x;} + if (properties.y !== undefined) {this.y = properties.y;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.level !== undefined) {this.level = properties.level; this.preassignedLevel = true;} - // this edge becomes part of the dynamicEdges of the cluster node - parentNode.dynamicEdges.push(edge); - }, + // physics + if (properties.mass !== undefined) {this.mass = properties.mass;} + // navigation controls properties + if (properties.horizontalAlignLeft !== undefined) {this.horizontalAlignLeft = properties.horizontalAlignLeft;} + if (properties.verticalAlignTop !== undefined) {this.verticalAlignTop = properties.verticalAlignTop;} + if (properties.triggerFunction !== undefined) {this.triggerFunction = properties.triggerFunction;} - /** - * This function connects an edge that was connected to a cluster node back to the child node. - * - * @param parentNode | Node object - * @param childNode | Node object - * @private - */ - _connectEdgeBackToChild : function(parentNode, childNode) { - if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { - for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { - var edge = parentNode.reroutedEdges[childNode.id][i]; - if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { - edge.originalFromId.pop(); - edge.fromId = childNode.id; - edge.from = childNode; - } - else { - edge.originalToId.pop(); - edge.toId = childNode.id; - edge.to = childNode; + if (this.id === undefined) { + throw "Node must have an id"; + } + + // copy group properties + if (this.group) { + var groupObj = this.grouplist.get(this.group); + for (var prop in groupObj) { + if (groupObj.hasOwnProperty(prop)) { + this[prop] = groupObj[prop]; } + } + } - // append this edge to the list of edges connecting to the childnode - childNode.dynamicEdges.push(edge); + // individual shape properties + if (properties.shape !== undefined) {this.shape = properties.shape;} + if (properties.image !== undefined) {this.image = properties.image;} + if (properties.radius !== undefined) {this.radius = properties.radius;} + if (properties.color !== undefined) {this.color = util.parseColor(properties.color);} - // remove the edge from the parent object - for (var j = 0; j < parentNode.dynamicEdges.length; j++) { - if (parentNode.dynamicEdges[j].id == edge.id) { - parentNode.dynamicEdges.splice(j,1); - break; - } - } + if (properties.fontColor !== undefined) {this.fontColor = properties.fontColor;} + if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;} + if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;} + + if (this.image !== undefined && this.image != "") { + if (this.imagelist) { + this.imageObj = this.imagelist.load(this.image); } - // remove the entry from the rerouted edges - delete parentNode.reroutedEdges[childNode.id]; + else { + throw "No imagelist provided"; + } + } + + this.xFixed = this.xFixed || (properties.x !== undefined && !properties.allowedToMoveX); + this.yFixed = this.yFixed || (properties.y !== undefined && !properties.allowedToMoveY); + this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); + + if (this.shape == 'image') { + this.radiusMin = constants.nodes.widthMin; + this.radiusMax = constants.nodes.widthMax; } - }, + // choose draw method depending on the shape + switch (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; + // TODO: add diamond shape + 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; break; + } + // reset the size of the node, this can be changed + this._reset(); + }; /** - * When loops are clustered, an edge can be both in the rerouted array and the contained array. - * This function is called last to verify that all edges in dynamicEdges are in fact connected to the - * parentNode - * - * @param parentNode | Node object - * @private + * select this node */ - _validateEdges : function(parentNode) { - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { - parentNode.dynamicEdges.splice(i,1); - } - } - }, + Node.prototype.select = function() { + this.selected = true; + this._reset(); + }; + + /** + * unselect this node + */ + Node.prototype.unselect = function() { + this.selected = false; + this._reset(); + }; /** - * This function released the contained edges back into the global domain and puts them back into the - * dynamic edges of both parent and child. - * - * @param {Node} parentNode | - * @param {Node} childNode | + * Reset the calculated size of the node, forces it to recalculate its size + */ + Node.prototype.clearSizeCache = function() { + this._reset(); + }; + + /** + * Reset the calculated size of the node, forces it to recalculate its size * @private */ - _releaseContainedEdges : function(parentNode, childNode) { - for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { - var edge = parentNode.containedEdges[childNode.id][i]; + Node.prototype._reset = function() { + this.width = undefined; + this.height = undefined; + }; - // put the edge back in the global edges object - this.edges[edge.id] = edge; + /** + * get the title of this node. + * @return {string} title The title of the node, or undefined when no title + * has been set. + */ + Node.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; + }; - // put the edge back in the dynamic edges of the child and parent - childNode.dynamicEdges.push(edge); - parentNode.dynamicEdges.push(edge); + /** + * Calculate the distance to the border of the Node + * @param {CanvasRenderingContext2D} ctx + * @param {Number} angle Angle in radians + * @returns {number} distance Distance to the border in pixels + */ + Node.prototype.distanceToBorder = function (ctx, angle) { + var borderWidth = 1; + + if (!this.width) { + this.resize(ctx); } - // remove the entry from the contained edges - delete parentNode.containedEdges[childNode.id]; - }, + switch (this.shape) { + case 'circle': + case 'dot': + return this.radius + borderWidth; + case 'ellipse': + var a = this.width / 2; + var b = this.height / 2; + var w = (Math.sin(angle) * a); + var h = (Math.cos(angle) * b); + return a * b / Math.sqrt(w * w + h * h); + // TODO: implement distanceToBorder for database + // TODO: implement distanceToBorder for triangle + // TODO: implement distanceToBorder for triangleDown + case 'box': + case 'image': + case 'text': + default: + if (this.width) { + return Math.min( + Math.abs(this.width / 2 / Math.cos(angle)), + Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; + // TODO: reckon with border radius too in case of box + } + else { + return 0; + } - // ------------------- UTILITY FUNCTIONS ---------------------------- // + } + // TODO: implement calculation of distance to border for all shapes + }; + /** + * Set forces acting on the node + * @param {number} fx Force in horizontal direction + * @param {number} fy Force in vertical direction + */ + Node.prototype._setForce = function(fx, fy) { + this.fx = fx; + this.fy = fy; + }; /** - * This updates the node labels for all nodes (for debugging purposes) + * Add forces acting on the node + * @param {number} fx Force in horizontal direction + * @param {number} fy Force in vertical direction + * @private */ - updateLabels : function() { - var nodeId; - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.clusterSize > 1) { - node.label = "[".concat(String(node.clusterSize),"]"); - } - } - } + Node.prototype._addForce = function(fx, fy) { + this.fx += fx; + this.fy += fy; + }; - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.clusterSize == 1) { - if (node.originalLabel !== undefined) { - node.label = node.originalLabel; - } - else { - node.label = String(node.id); - } - } - } + /** + * Perform one discrete step for the node + * @param {number} interval Time interval in seconds + */ + Node.prototype.discreteStep = function(interval) { + if (!this.xFixed) { + var dx = this.damping * this.vx; // damping force + var ax = (this.fx - dx) / this.mass; // acceleration + this.vx += ax * interval; // velocity + this.x += this.vx * interval; // position } -// /* Debug Override */ -// for (nodeId in this.nodes) { -// if (this.nodes.hasOwnProperty(nodeId)) { -// node = this.nodes[nodeId]; -// node.label = String(node.level); -// } -// } + if (!this.yFixed) { + var dy = this.damping * this.vy; // damping force + var ay = (this.fy - dy) / this.mass; // acceleration + this.vy += ay * interval; // velocity + this.y += this.vy * interval; // position + } + }; - }, /** - * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes - * if the rest of the nodes are already a few cluster levels in. - * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not - * clustered enough to the clusterToSmallestNeighbours function. + * Perform one discrete step for the node + * @param {number} interval Time interval in seconds + * @param {number} maxVelocity The speed limit imposed on the velocity */ - normalizeClusterLevels : function() { - var maxLevel = 0; - var minLevel = 1e9; - var clusterLevel = 0; - var nodeId; - - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - clusterLevel = this.nodes[nodeId].clusterSessions.length; - if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} - if (minLevel > clusterLevel) {minLevel = clusterLevel;} - } + Node.prototype.discreteStepLimited = function(interval, maxVelocity) { + if (!this.xFixed) { + var dx = this.damping * this.vx; // damping force + var ax = (this.fx - dx) / this.mass; // acceleration + this.vx += ax * interval; // velocity + this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx; + this.x += this.vx * interval; // position } - - if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { - var amountOfNodes = this.nodeIndices.length; - var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].clusterSessions.length < targetLevel) { - this._clusterToSmallestNeighbour(this.nodes[nodeId]); - } - } - } - this._updateNodeIndexList(); - this._updateDynamicEdges(); - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; - } + else { + this.fx = 0; } - }, - + if (!this.yFixed) { + var dy = this.damping * this.vy; // damping force + var ay = (this.fy - dy) / this.mass; // acceleration + this.vy += ay * interval; // velocity + this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy; + this.y += this.vy * interval; // position + } + else { + this.fy = 0; + } + }; /** - * This function determines if the cluster we want to decluster is in the active area - * this means around the zoom center - * - * @param {Node} node - * @returns {boolean} - * @private + * Check if this node has a fixed x and y position + * @return {boolean} true if fixed, false if not */ - _nodeInActiveArea : function(node) { - return ( - Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale - && - Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale - ) - }, - + Node.prototype.isFixed = function() { + return (this.xFixed && this.yFixed); + }; /** - * This is an adaptation of the original repositioning function. This is called if the system is clustered initially - * It puts large clusters away from the center and randomizes the order. - * + * Check if this node is moving + * @param {number} vmin the minimum velocity considered as "moving" + * @return {boolean} true if moving, false if it has no velocity */ - repositionNodes : function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - if ((node.xFixed == false || node.yFixed == false)) { - var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.mass); - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - this._repositionBezierNodes(node); - } - } - }, - + // TODO: replace this method with calculating the kinetic energy + Node.prototype.isMoving = function(vmin) { + return (Math.abs(this.vx) > vmin || Math.abs(this.vy) > vmin); + }; /** - * We determine how many connections denote an important hub. - * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) - * - * @private + * check if this node is selecte + * @return {boolean} selected True if node is selected, else false */ - _getHubSize : function() { - var average = 0; - var averageSquared = 0; - var hubCounter = 0; - var largestHub = 0; - - for (var i = 0; i < this.nodeIndices.length; i++) { - - var node = this.nodes[this.nodeIndices[i]]; - if (node.dynamicEdgesLength > largestHub) { - largestHub = node.dynamicEdgesLength; - } - average += node.dynamicEdgesLength; - averageSquared += Math.pow(node.dynamicEdgesLength,2); - hubCounter += 1; - } - average = average / hubCounter; - averageSquared = averageSquared / hubCounter; - - var variance = averageSquared - Math.pow(average,2); - - var standardDeviation = Math.sqrt(variance); - - this.hubThreshold = Math.floor(average + 2*standardDeviation); - - // always have at least one to cluster - if (this.hubThreshold > largestHub) { - this.hubThreshold = largestHub; - } - - // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); - // console.log("hubThreshold:",this.hubThreshold); - }, - + Node.prototype.isSelected = function() { + return this.selected; + }; /** - * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. - * - * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce - * @private + * Retrieve the value of the node. Can be undefined + * @return {Number} value */ - _reduceAmountOfChains : function(fraction) { - this.hubThreshold = 2; - var reduceAmount = Math.floor(this.nodeIndices.length * fraction); - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - if (reduceAmount > 0) { - this._formClusterFromHub(this.nodes[nodeId],true,true,1); - reduceAmount -= 1; - } - } - } - } - }, + Node.prototype.getValue = function() { + return this.value; + }; /** - * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. - * - * @private + * Calculate the distance from the nodes location to the given location (x,y) + * @param {Number} x + * @param {Number} y + * @return {Number} value */ - _getChainFraction : function() { - var chains = 0; - var total = 0; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - chains += 1; - } - total += 1; - } - } - return chains/total; - } - -}; - + Node.prototype.getDistance = function(x, y) { + var dx = this.x - x, + dy = this.y - y; + return Math.sqrt(dx * dx + dy * dy); + }; -var SelectionMixin = { /** - * This function can be called from the _doInAllSectors function - * - * @param object - * @param overlappingNodes - * @private + * Adjust the value range of the node. The node will adjust it's radius + * based on its value. + * @param {Number} min + * @param {Number} max */ - _getNodesOverlappingWith : function(object, overlappingNodes) { - var nodes = this.nodes; - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].isOverlappingWith(object)) { - overlappingNodes.push(nodeId); - } + Node.prototype.setValueRange = function(min, max) { + if (!this.radiusFixed && this.value !== undefined) { + if (max == min) { + this.radius = (this.radiusMin + this.radiusMax) / 2; + } + else { + var scale = (this.radiusMax - this.radiusMin) / (max - min); + this.radius = (this.value - min) * scale + this.radiusMin; } } - }, + this.baseRadiusValue = this.radius; + }; /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private + * Draw this node in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx */ - _getAllNodesOverlappingWith : function (object) { - var overlappingNodes = []; - this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); - return overlappingNodes; - }, - + Node.prototype.draw = function(ctx) { + throw "Draw method not initialized for node"; + }; /** - * Return a position object in canvasspace from a single point in screenspace - * - * @param pointer - * @returns {{left: number, top: number, right: number, bottom: number}} - * @private + * Recalculate the size of this node in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx */ - _pointerToPositionObject : function(pointer) { - var x = this._XconvertDOMtoCanvas(pointer.x); - var y = this._YconvertDOMtoCanvas(pointer.y); - - return {left: x, - top: y, - right: x, - bottom: y}; - }, - + Node.prototype.resize = function(ctx) { + throw "Resize method not initialized for node"; + }; /** - * Get the top node at the a specific point (like a click) - * - * @param {{x: Number, y: Number}} pointer - * @return {Node | null} node - * @private + * Check if this object is overlapping with the provided object + * @param {Object} obj an object with parameters left, top, right, bottom + * @return {boolean} True if location is located on node */ - _getNodeAt : function (pointer) { - // we first check if this is an navigation controls element - var positionObject = this._pointerToPositionObject(pointer); - var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); - - // if there are overlapping nodes, select the last one, this is the - // one which is drawn on top of the others - if (overlappingNodes.length > 0) { - return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; - } - else { - return null; - } - }, + Node.prototype.isOverlappingWith = function(obj) { + return (this.left < obj.right && + this.left + this.width > obj.left && + this.top < obj.bottom && + this.top + this.height > obj.top); + }; + Node.prototype._resizeImage = function (ctx) { + // TODO: pre calculate the image size - /** - * retrieve all edges overlapping with given object, selector is around center - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - _getEdgesOverlappingWith : function (object, overlappingEdges) { - var edges = this.edges; - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].isOverlappingWith(object)) { - overlappingEdges.push(edgeId); + if (!this.width || !this.height) { // undefined or 0 + var width, height; + if (this.value) { + this.radius = this.baseRadiusValue; + var scale = this.imageObj.height / this.imageObj.width; + if (scale !== undefined) { + width = this.radius || this.imageObj.width; + height = this.radius * scale || this.imageObj.height; + } + else { + width = 0; + height = 0; } } + else { + width = this.imageObj.width; + height = this.imageObj.height; + } + this.width = width; + this.height = height; + + this.growthIndicator = 0; + if (this.width > 0 && this.height > 0) { + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - width; + } } - }, + }; - /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - _getAllEdgesOverlappingWith : function (object) { - var overlappingEdges = []; - this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); - return overlappingEdges; - }, + Node.prototype._drawImage = function (ctx) { + this._resizeImage(ctx); - /** - * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call - * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. - * - * @param pointer - * @returns {null} - * @private - */ - _getEdgeAt : function(pointer) { - var positionObject = this._pointerToPositionObject(pointer); - var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - if (overlappingEdges.length > 0) { - return this.edges[overlappingEdges[overlappingEdges.length - 1]]; - } - else { - return null; - } - }, + var yLabel; + if (this.imageObj.width != 0 ) { + // draw the shade + if (this.clusterSize > 1) { + var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0); + lineWidth *= this.networkScaleInv; + lineWidth = Math.min(0.2 * this.width,lineWidth); + ctx.globalAlpha = 0.5; + ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth); + } - /** - * Add object to the selection array. - * - * @param obj - * @private - */ - _addToSelection : function(obj) { - if (obj instanceof Node) { - this.selectionObj.nodes[obj.id] = obj; + // draw the image + ctx.globalAlpha = 1.0; + ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + yLabel = this.y + this.height / 2; } else { - this.selectionObj.edges[obj.id] = obj; + // image still loading... just draw the label for now + yLabel = this.y; } - }, - /** - * Add object to the selection array. - * - * @param obj - * @private - */ - _addToHover : function(obj) { - if (obj instanceof Node) { - this.hoverObj.nodes[obj.id] = obj; - } - else { - this.hoverObj.edges[obj.id] = obj; + this._label(ctx, this.label, this.x, yLabel, undefined, "top"); + }; + + + Node.prototype._resizeBox = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.growthIndicator = this.width - (textSize.width + 2 * margin); + // this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + } - }, + }; + Node.prototype._drawBox = function (ctx) { + this._resizeBox(ctx); - /** - * Remove a single option from selection. - * - * @param {Object} obj - * @private - */ - _removeFromSelection : function(obj) { - if (obj instanceof Node) { - delete this.selectionObj.nodes[obj.id]; + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var clusterLineWidth = 2.5; + var selectionLineWidth = 2; + + ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + + ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.radius); + ctx.stroke(); } - else { - delete this.selectionObj.edges[obj.id]; + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + + ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background; + + ctx.roundRect(this.left, this.top, this.width, this.height, this.radius); + ctx.fill(); + ctx.stroke(); + + this._label(ctx, this.label, this.x, this.y); + }; + + + Node.prototype._resizeDatabase = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + var size = textSize.width + 2 * margin; + this.width = size; + this.height = size; + + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - size; } - }, + }; - /** - * Unselect all. The selectionObj is useful for this. - * - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - _unselectAll : function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; + Node.prototype._drawDatabase = function (ctx) { + this._resizeDatabase(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var clusterLineWidth = 2.5; + var selectionLineWidth = 2; + + ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + + ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth); + ctx.stroke(); } - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - this.selectionObj.nodes[nodeId].unselect(); - } + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + 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.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height); + ctx.fill(); + ctx.stroke(); + + this._label(ctx, this.label, this.x, this.y); + }; + + + Node.prototype._resizeCircle = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; + this.radius = diameter / 2; + + this.width = diameter; + this.height = diameter; + + // scaling used for clustering + // this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + // this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.radius - 0.5*diameter; } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - this.selectionObj.edges[edgeId].unselect(); - } + }; + + Node.prototype._drawCircle = function (ctx) { + this._resizeCircle(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var clusterLineWidth = 2.5; + var selectionLineWidth = 2; + + ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + + ctx.circle(this.x, this.y, this.radius+2*ctx.lineWidth); + ctx.stroke(); } + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); - this.selectionObj = {nodes:{},edges:{}}; + ctx.fillStyle = this.selected ? this.color.highlight.background : this.hover ? this.color.hover.background : this.color.background; + ctx.circle(this.x, this.y, this.radius); + ctx.fill(); + ctx.stroke(); - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); + this._label(ctx, this.label, this.x, this.y); + }; + + Node.prototype._resizeEllipse = function (ctx) { + if (!this.width) { + var textSize = this.getTextSize(ctx); + + this.width = textSize.width * 1.5; + this.height = textSize.height * 2; + if (this.width < this.height) { + this.width = this.height; + } + var defaultSize = this.width; + + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - defaultSize; } - }, + }; - /** - * Unselect all clusters. The selectionObj is useful for this. - * - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - _unselectClusters : function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; + Node.prototype._drawEllipse = function (ctx) { + this._resizeEllipse(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var clusterLineWidth = 2.5; + var selectionLineWidth = 2; + + ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; + + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + + ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth); + ctx.stroke(); } + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - this.selectionObj.nodes[nodeId].unselect(); - this._removeFromSelection(this.selectionObj.nodes[nodeId]); - } - } + ctx.fillStyle = this.selected ? this.color.highlight.background : this.hover ? this.color.hover.background : this.color.background; + + ctx.ellipse(this.left, this.top, this.width, this.height); + ctx.fill(); + ctx.stroke(); + this._label(ctx, this.label, this.x, this.y); + }; + + Node.prototype._drawDot = function (ctx) { + this._drawShape(ctx, 'circle'); + }; + + Node.prototype._drawTriangle = function (ctx) { + this._drawShape(ctx, 'triangle'); + }; + + Node.prototype._drawTriangleDown = function (ctx) { + this._drawShape(ctx, 'triangleDown'); + }; + + Node.prototype._drawSquare = function (ctx) { + this._drawShape(ctx, 'square'); + }; + + Node.prototype._drawStar = function (ctx) { + this._drawShape(ctx, 'star'); + }; + + Node.prototype._resizeShape = function (ctx) { + if (!this.width) { + this.radius = this.baseRadiusValue; + var size = 2 * this.radius; + this.width = size; + this.height = size; + + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - size; } + }; - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); + Node.prototype._drawShape = function (ctx, shape) { + this._resizeShape(ctx); + + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var clusterLineWidth = 2.5; + var selectionLineWidth = 2; + var radiusMultiplier = 2; + + // choose draw method depending on the shape + switch (shape) { + case 'dot': radiusMultiplier = 2; break; + case 'square': radiusMultiplier = 2; break; + case 'triangle': radiusMultiplier = 3; break; + case 'triangleDown': radiusMultiplier = 3; break; + case 'star': radiusMultiplier = 4; break; } - }, + ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border; - /** - * return the number of selected nodes - * - * @returns {number} - * @private - */ - _getSelectedNodeCount : function() { - var count = 0; - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; - } + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); + + ctx[shape](this.x, this.y, this.radius + radiusMultiplier * ctx.lineWidth); + ctx.stroke(); } - return count; - }, + ctx.lineWidth = (this.selected ? selectionLineWidth : 1.0) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth); - /** - * return the selected node - * - * @returns {number} - * @private - */ - _getSelectedNode : function() { - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return this.selectionObj.nodes[nodeId]; - } + 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(); + + if (this.label) { + this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top',true); } - 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]; - } + Node.prototype._resizeText = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - (textSize.width + 2 * margin); } - return null; - }, + }; + Node.prototype._drawText = function (ctx) { + this._resizeText(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - /** - * return the number of selected edges - * - * @returns {number} - * @private - */ - _getSelectedEdgeCount : function() { - var count = 0; - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; + this._label(ctx, this.label, this.x, this.y); + }; + + + Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNode) { + if (text && this.fontSize * this.networkScale > this.fontDrawThreshold) { + ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace; + ctx.fillStyle = this.fontColor || "black"; + ctx.textAlign = align || "center"; + ctx.textBaseline = baseline || "middle"; + + var lines = text.split('\n'); + var lineCount = lines.length; + var fontSize = (this.fontSize + 4); + var yLine = y + (1 - lineCount) / 2 * fontSize; + if (labelUnderNode == true) { + yLine = y + (1 - lineCount) / (2 * fontSize); + } + + for (var i = 0; i < lineCount; i++) { + ctx.fillText(lines[i], x, yLine); + yLine += fontSize; } } - return count; - }, + }; - /** - * return the number of selected objects. - * - * @returns {number} - * @private - */ - _getSelectedObjectCount : function() { - var count = 0; - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; + Node.prototype.getTextSize = function(ctx) { + if (this.label !== undefined) { + ctx.font = (this.selected ? "bold " : "") + this.fontSize + "px " + this.fontFace; + + var lines = this.label.split('\n'), + height = (this.fontSize + 4) * lines.length, + width = 0; + + for (var i = 0, iMax = lines.length; i < iMax; i++) { + width = Math.max(width, ctx.measureText(lines[i]).width); } + + return {"width": width, "height": height}; } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; - } + else { + return {"width": 0, "height": 0}; } - return count; - }, + }; /** - * Check if anything is selected + * this is used to determine if a node is visible at all. this is used to determine when it needs to be drawn. + * there is a safety margin of 0.3 * width; * * @returns {boolean} - * @private */ - _selectionIsEmpty : function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return false; - } + Node.prototype.inArea = function() { + if (this.width !== undefined) { + return (this.x + this.width *this.networkScaleInv >= this.canvasTopLeft.x && + this.x - this.width *this.networkScaleInv < this.canvasBottomRight.x && + this.y + this.height*this.networkScaleInv >= this.canvasTopLeft.y && + this.y - this.height*this.networkScaleInv < this.canvasBottomRight.y); } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - return false; - } + else { + return true; } - return true; - }, - + }; /** - * check if one of the selected nodes is a cluster. - * + * checks if the core of the node is in the display area, this is used for opening clusters around zoom * @returns {boolean} - * @private */ - _clusterInSelection : function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - return true; - } - } - } - return false; - }, + Node.prototype.inView = function() { + return (this.x >= this.canvasTopLeft.x && + this.x < this.canvasBottomRight.x && + this.y >= this.canvasTopLeft.y && + this.y < this.canvasBottomRight.y); + }; /** - * select the edges connected to the node that is being selected + * This allows the zoom level of the network to influence the rendering + * We store the inverted scale and the coordinates of the top left, and bottom right points of the canvas * - * @param {Node} node - * @private + * @param scale + * @param canvasTopLeft + * @param canvasBottomRight */ - _selectConnectedEdges : function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.select(); - this._addToSelection(edge); - } - }, + Node.prototype.setScaleAndPos = function(scale,canvasTopLeft,canvasBottomRight) { + this.networkScaleInv = 1.0/scale; + this.networkScale = scale; + this.canvasTopLeft = canvasTopLeft; + this.canvasBottomRight = canvasBottomRight; + }; + /** - * select the edges connected to the node that is being selected + * This allows the zoom level of the network to influence the rendering * - * @param {Node} node - * @private + * @param scale */ - _hoverConnectedEdges : function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.hover = true; - this._addToHover(edge); - } - }, + Node.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + this.networkScale = scale; + }; + /** - * unselect the edges connected to the node that is being selected + * set the velocity at 0. Is called when this node is contained in another during clustering + */ + Node.prototype.clearVelocity = function() { + this.vx = 0; + this.vy = 0; + }; + + + /** + * Basic preservation of (kinectic) energy * - * @param {Node} node - * @private + * @param massBeforeClustering */ - _unselectConnectedEdges : function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.unselect(); - this._removeFromSelection(edge); - } - }, + Node.prototype.updateVelocity = function(massBeforeClustering) { + var energyBefore = this.vx * this.vx * massBeforeClustering; + //this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.mass) : Math.sqrt(energyBefore/this.mass); + this.vx = Math.sqrt(energyBefore/this.mass); + energyBefore = this.vy * this.vy * massBeforeClustering; + //this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.mass) : Math.sqrt(energyBefore/this.mass); + this.vy = Math.sqrt(energyBefore/this.mass); + }; + module.exports = Node; +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @param {Boolean} append - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private + * Popup is a class to create a popup window with some text + * @param {Element} container The container object. + * @param {Number} [x] + * @param {Number} [y] + * @param {String} [text] + * @param {Object} [style] An object containing borderColor, + * backgroundColor, etc. */ - _selectObject : function(object, append, doNotTrigger, highlightEdges) { - if (doNotTrigger === undefined) { - doNotTrigger = false; + function Popup(container, x, y, text, style) { + if (container) { + this.container = container; } - if (highlightEdges === undefined) { - highlightEdges = true; + else { + this.container = document.body; } - if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { - this._unselectAll(true); + // x, y and text are optional, see if a style object was passed in their place + if (style === undefined) { + if (typeof x === "object") { + style = x; + x = undefined; + } else if (typeof text === "object") { + style = text; + text = undefined; + } else { + // for backwards compatibility, in case clients other than Network are creating Popup directly + style = { + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' + } + } + } } - if (object.selected == false) { - object.select(); - this._addToSelection(object); - if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { - this._selectConnectedEdges(object); - } + this.x = 0; + this.y = 0; + this.padding = 5; + + if (x !== undefined && y !== undefined ) { + this.setPosition(x, y); } - else { - object.unselect(); - this._removeFromSelection(object); + if (text !== undefined) { + this.setText(text); } - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } - }, + // create the frame + this.frame = document.createElement("div"); + var styleAttr = this.frame.style; + styleAttr.position = "absolute"; + styleAttr.visibility = "hidden"; + styleAttr.border = "1px solid " + style.color.border; + styleAttr.color = style.fontColor; + styleAttr.fontSize = style.fontSize + "px"; + styleAttr.fontFamily = style.fontFace; + styleAttr.padding = this.padding + "px"; + styleAttr.backgroundColor = style.color.background; + styleAttr.borderRadius = "3px"; + styleAttr.MozBorderRadius = "3px"; + styleAttr.WebkitBorderRadius = "3px"; + styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; + styleAttr.whiteSpace = "nowrap"; + this.container.appendChild(this.frame); + } + /** + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window + */ + Popup.prototype.setPosition = function(x, y) { + this.x = parseInt(x); + this.y = parseInt(y); + }; /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @private + * Set the text for the popup window. This can be HTML code + * @param {string} text */ - _blurObject : function(object) { - if (object.hover == true) { - object.hover = false; - this.emit("blurNode",{node:object.id}); - } - }, + Popup.prototype.setText = function(text) { + this.frame.innerHTML = text; + }; /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @private + * Show the popup window + * @param {boolean} show Optional. Show or hide the window */ - _hoverObject : function(object) { - if (object.hover == false) { - object.hover = true; - this._addToHover(object); - if (object instanceof Node) { - this.emit("hoverNode",{node:object.id}); - } + Popup.prototype.show = function (show) { + if (show === undefined) { + show = true; } - if (object instanceof Node) { - this._hoverConnectedEdges(object); - } - }, + if (show) { + var height = this.frame.clientHeight; + var width = this.frame.clientWidth; + var maxHeight = this.frame.parentNode.clientHeight; + var maxWidth = this.frame.parentNode.clientWidth; - /** - * handles the selection part of the touch, only for navigation controls elements; - * Touch is triggered before tap, also before hold. Hold triggers after a while. - * This is the most responsive solution - * - * @param {Object} pointer - * @private - */ - _handleTouch : function(pointer) { - }, + var top = (this.y - height); + if (top + height + this.padding > maxHeight) { + top = maxHeight - height - this.padding; + } + if (top < this.padding) { + top = this.padding; + } + var left = this.x; + if (left + width + this.padding > maxWidth) { + left = maxWidth - width - this.padding; + } + if (left < this.padding) { + left = this.padding; + } - /** - * handles the selection part of the tap; - * - * @param {Object} pointer - * @private - */ - _handleTap : function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node,false); + this.frame.style.left = left + "px"; + this.frame.style.top = top + "px"; + this.frame.style.visibility = "visible"; } else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge,false); - } - else { - this._unselectAll(); - } + this.hide(); } - this.emit("click", this.getSelection()); - this._redraw(); - }, - + }; /** - * handles the selection part of the double tap and opens a cluster if needed - * - * @param {Object} pointer - * @private + * Hide the popup window */ - _handleDoubleTap : function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null && node !== undefined) { - // we reset the areaCenter here so the opening of the node will occur - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; - this.openCluster(node); - } - this.emit("doubleClick", this.getSelection()); - }, + Popup.prototype.hide = function () { + this.frame.style.visibility = "hidden"; + }; + module.exports = Popup; - /** - * Handle the onHold selection part - * - * @param pointer - * @private - */ - _handleOnHold : function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node,true); - } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge,true); - } - } - this._redraw(); - }, +/***/ }, +/* 32 */ +/***/ function(module, exports, __webpack_require__) { /** - * handle the onRelease event. These functions are here for the navigation controls module. + * Parse a text source containing data in DOT language into a JSON object. + * The object contains two lists: one with nodes and one with edges. * - * @private + * DOT language reference: http://www.graphviz.org/doc/info/lang.html + * + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graph An object containing two parameters: + * {Object[]} nodes + * {Object[]} edges */ - _handleOnRelease : function(pointer) { + function parseDOT (data) { + dot = data; + return parseGraph(); + } + + // token types enumeration + var TOKENTYPE = { + NULL : 0, + DELIMITER : 1, + IDENTIFIER: 2, + UNKNOWN : 3 + }; - }, + // map with all delimiters + var DELIMITERS = { + '{': true, + '}': true, + '[': true, + ']': true, + ';': true, + '=': true, + ',': true, + '->': true, + '--': true + }; + var dot = ''; // current dot file + var index = 0; // current index in dot file + var c = ''; // current token character in expr + var token = ''; // current token + var tokenType = TOKENTYPE.NULL; // type of the token /** - * - * retrieve the currently selected objects - * @return {Number[] | String[]} selection An array with the ids of the - * selected nodes. + * Get the first character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. */ - getSelection : function() { - var nodeIds = this.getSelectedNodes(); - var edgeIds = this.getSelectedEdges(); - return {nodes:nodeIds, edges:edgeIds}; - }, + function first() { + index = 0; + c = dot.charAt(0); + } /** - * - * retrieve the currently selected nodes - * @return {String} selection An array with the ids of the - * selected nodes. + * Get the next character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. */ - getSelectedNodes : function() { - var idArray = []; - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - idArray.push(nodeId); - } - } - return idArray - }, + function next() { + index++; + c = dot.charAt(index); + } /** - * - * retrieve the currently selected edges - * @return {Array} selection An array with the ids of the - * selected nodes. + * Preview the next character from the dot file. + * @return {String} cNext */ - getSelectedEdges : function() { - var idArray = []; - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - idArray.push(edgeId); - } - } - return idArray; - }, - + function nextPreview() { + return dot.charAt(index + 1); + } /** - * select zero or more nodes - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. + * Test whether given character is alphabetic or numeric + * @param {String} c + * @return {Boolean} isAlphaNumeric */ - setSelection : function(selection) { - var i, iMax, id; - - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; + var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; + function isAlphaNumeric(c) { + return regexAlphaNumeric.test(c); + } - // first unselect any selected node - this._unselectAll(true); - - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; + /** + * Merge all properties of object b into object b + * @param {Object} a + * @param {Object} b + * @return {Object} a + */ + function merge (a, b) { + if (!a) { + a = {}; + } - var node = this.nodes[id]; - if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); + if (b) { + for (var name in b) { + if (b.hasOwnProperty(name)) { + a[name] = b[name]; + } } - this._selectObject(node,true,true); } - - console.log("setSelection is deprecated. Please use selectNodes instead.") - - this.redraw(); - }, - + return a; + } /** - * select zero or more nodes with the option to highlight edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - * @param {boolean} [highlightEdges] + * Set a value in an object, where the provided parameter name can be a + * path with nested parameters. For example: + * + * var obj = {a: 2}; + * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} + * + * @param {Object} obj + * @param {String} path A parameter name or dot-separated parameter path, + * like "color.highlight.border". + * @param {*} value */ - selectNodes : function(selection, highlightEdges) { - var i, iMax, id; - - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; - - // first unselect any selected node - this._unselectAll(true); - - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; - - var node = this.nodes[id]; - if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); + function setValue(obj, path, value) { + var keys = path.split('.'); + var o = obj; + while (keys.length) { + var key = keys.shift(); + if (keys.length) { + // this isn't the end point + if (!o[key]) { + o[key] = {}; + } + o = o[key]; + } + else { + // this is the end point + o[key] = value; } - this._selectObject(node,true,true,highlightEdges); } - this.redraw(); - }, - + } /** - * select zero or more edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. + * Add a node to a graph object. If there is already a node with + * the same id, their attributes will be merged. + * @param {Object} graph + * @param {Object} node */ - selectEdges : function(selection) { - var i, iMax, id; - - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; - - // first unselect any selected node - this._unselectAll(true); - - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; + function addNode(graph, node) { + var i, len; + var current = null; - var edge = this.edges[id]; - if (!edge) { - throw new RangeError('Edge with id "' + id + '" not found'); - } - this._selectObject(edge,true,true,highlightEdges); + // find root graph (in case of subgraph) + var graphs = [graph]; // list with all graphs from current graph to root graph + var root = graph; + while (root.parent) { + graphs.push(root.parent); + root = root.parent; } - this.redraw(); - }, - /** - * Validate the selection: remove ids of nodes which no longer exist - * @private - */ - _updateSelection : function () { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (!this.nodes.hasOwnProperty(nodeId)) { - delete this.selectionObj.nodes[nodeId]; + // find existing node (at root level) by its id + if (root.nodes) { + for (i = 0, len = root.nodes.length; i < len; i++) { + if (node.id === root.nodes[i].id) { + current = root.nodes[i]; + break; } } } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - if (!this.edges.hasOwnProperty(edgeId)) { - delete this.selectionObj.edges[edgeId]; - } + + if (!current) { + // this is a new node + current = { + id: node.id + }; + if (graph.node) { + // clone default attributes + current.attr = merge(current.attr, graph.node); } } - } -}; - + // add node to this (sub)graph and all its parent graphs + for (i = graphs.length - 1; i >= 0; i--) { + var g = graphs[i]; -/** - * Created by Alex on 1/22/14. - */ - -var NavigationMixin = { + if (!g.nodes) { + g.nodes = []; + } + if (g.nodes.indexOf(current) == -1) { + g.nodes.push(current); + } + } - _cleanNavigation : function() { - // clean up previosu navigation items - var wrapper = document.getElementById('network-navigation_wrapper'); - if (wrapper != null) { - this.containerElement.removeChild(wrapper); + // merge attributes + if (node.attr) { + current.attr = merge(current.attr, node.attr); } - document.onmouseup = null; - }, + } /** - * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation - * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent - * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. - * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. - * - * @private + * Add an edge to a graph object + * @param {Object} graph + * @param {Object} edge */ - _loadNavigationElements : function() { - this._cleanNavigation(); - - this.navigationDivs = {}; - var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; - var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','zoomExtent']; - - this.navigationDivs['wrapper'] = document.createElement('div'); - this.navigationDivs['wrapper'].id = "network-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; i < navigationDivs.length; i++) { - this.navigationDivs[navigationDivs[i]] = document.createElement('div'); - this.navigationDivs[navigationDivs[i]].id = "network-navigation_" + navigationDivs[i]; - this.navigationDivs[navigationDivs[i]].className = "network-navigation " + navigationDivs[i]; - this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); - this.navigationDivs[navigationDivs[i]].onmousedown = this[navigationDivActions[i]].bind(this); + function addEdge(graph, edge) { + if (!graph.edges) { + graph.edges = []; } - - document.onmouseup = this._stopMovement.bind(this); - }, + graph.edges.push(edge); + if (graph.edge) { + var attr = merge({}, graph.edge); // clone default attributes + edge.attr = merge(attr, edge.attr); // merge attributes + } + } /** - * this stops all movement induced by the navigation buttons - * - * @private + * Create an edge to a graph object + * @param {Object} graph + * @param {String | Number | Object} from + * @param {String | Number | Object} to + * @param {String} type + * @param {Object | null} attr + * @return {Object} edge */ - _stopMovement : function() { - this._xStopMoving(); - this._yStopMoving(); - this._stopZoom(); - }, - + function createEdge(graph, from, to, type, attr) { + var edge = { + from: from, + to: to, + type: type + }; - /** - * stops the actions performed by page up and down etc. - * - * @param event - * @private - */ - _preventDefault : function(event) { - if (event !== undefined) { - if (event.preventDefault) { - event.preventDefault(); - } else { - event.returnValue = false; - } + if (graph.edge) { + edge.attr = merge({}, graph.edge); // clone default attributes } - }, + edge.attr = merge(edge.attr || {}, attr); // merge attributes + return edge; + } /** - * move the screen up - * By using the increments, instead of adding a fixed number to the translation, we keep fluent and - * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently - * To avoid this behaviour, we do the translation in the start loop. - * - * @private + * Get next token in the current dot file. + * The token and token type are available as token and tokenType */ - _moveUp : function(event) { - this.yIncrement = this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['up'].className += " active"; - } - }, + function getToken() { + tokenType = TOKENTYPE.NULL; + token = ''; + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); + } - /** - * move the screen down - * @private - */ - _moveDown : function(event) { - this.yIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['down'].className += " active"; - } - }, + do { + var isComment = false; + // skip comment + if (c == '#') { + // find the previous non-space character + var i = index - 1; + while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { + i--; + } + if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { + // the # is at the start of a line, this is indeed a line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; + } + } + if (c == '/' && nextPreview() == '/') { + // skip line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; + } + if (c == '/' && nextPreview() == '*') { + // skip block comment + while (c != '') { + if (c == '*' && nextPreview() == '/') { + // end of block comment found. skip these last two characters + next(); + next(); + break; + } + else { + next(); + } + } + isComment = true; + } - /** - * move the screen left - * @private - */ - _moveLeft : function(event) { - this.xIncrement = this.constants.keyboard.speed.x; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['left'].className += " active"; + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); + } } - }, - + while (isComment); - /** - * move the screen right - * @private - */ - _moveRight : function(event) { - this.xIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['right'].className += " active"; + // check for end of dot file + if (c == '') { + // token is still empty + tokenType = TOKENTYPE.DELIMITER; + return; } - }, + // check for delimiters consisting of 2 characters + var c2 = c + nextPreview(); + if (DELIMITERS[c2]) { + tokenType = TOKENTYPE.DELIMITER; + token = c2; + next(); + next(); + return; + } - /** - * Zoom in, using the same method as the movement. - * @private - */ - _zoomIn : function(event) { - this.zoomIncrement = this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['zoomIn'].className += " active"; + // check for delimiters consisting of 1 character + if (DELIMITERS[c]) { + tokenType = TOKENTYPE.DELIMITER; + token = c; + next(); + return; } - }, + // check for an identifier (number or string) + // TODO: more precise parsing of numbers/strings (and the port separator ':') + if (isAlphaNumeric(c) || c == '-') { + token += c; + next(); - /** - * Zoom out - * @private - */ - _zoomOut : function() { - this.zoomIncrement = -this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['zoomOut'].className += " active"; + while (isAlphaNumeric(c)) { + token += c; + next(); + } + if (token == 'false') { + token = false; // convert to boolean + } + else if (token == 'true') { + token = true; // convert to boolean + } + else if (!isNaN(Number(token))) { + token = Number(token); // convert to number + } + tokenType = TOKENTYPE.IDENTIFIER; + return; } - }, - - /** - * Stop zooming and unhighlight the zoom controls - * @private - */ - _stopZoom : function() { - this.zoomIncrement = 0; - if (this.navigationDivs) { - this.navigationDivs['zoomIn'].className = this.navigationDivs['zoomIn'].className.replace(" active",""); - this.navigationDivs['zoomOut'].className = this.navigationDivs['zoomOut'].className.replace(" active",""); + // check for a string enclosed by double quotes + if (c == '"') { + next(); + while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { + token += c; + if (c == '"') { // skip the escape character + next(); + } + next(); + } + if (c != '"') { + throw newSyntaxError('End of string " expected'); + } + next(); + tokenType = TOKENTYPE.IDENTIFIER; + return; } - }, + // something unknown is found, wrong characters, a syntax error + tokenType = TOKENTYPE.UNKNOWN; + while (c != '') { + token += c; + next(); + } + throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); + } /** - * Stop moving in the Y direction and unHighlight the up and down - * @private + * Parse a graph. + * @returns {Object} graph */ - _yStopMoving : function() { - this.yIncrement = 0; - if (this.navigationDivs) { - this.navigationDivs['up'].className = this.navigationDivs['up'].className.replace(" active",""); - this.navigationDivs['down'].className = this.navigationDivs['down'].className.replace(" active",""); - } - }, + function parseGraph() { + var graph = {}; + first(); + getToken(); - /** - * Stop moving in the X direction and unHighlight left and right. - * @private - */ - _xStopMoving : function() { - this.xIncrement = 0; - if (this.navigationDivs) { - this.navigationDivs['left'].className = this.navigationDivs['left'].className.replace(" active",""); - this.navigationDivs['right'].className = this.navigationDivs['right'].className.replace(" active",""); + // optional strict keyword + if (token == 'strict') { + graph.strict = true; + getToken(); } - } + // graph or digraph keyword + if (token == 'graph' || token == 'digraph') { + graph.type = token; + getToken(); + } -}; + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + graph.id = token; + getToken(); + } -/** - * Created by Alex on 2/10/14. - */ + // open angle bracket + if (token != '{') { + throw newSyntaxError('Angle bracket { expected'); + } + getToken(); + // statements + parseStatements(graph); -var networkMixinLoaders = { + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); + } + getToken(); - /** - * Load a mixin into the network object - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private - */ - _loadMixin: function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - Network.prototype[mixinFunction] = sourceVariable[mixinFunction]; - } + // end of file + if (token !== '') { + throw newSyntaxError('End of file expected'); } - }, + getToken(); + + // remove temporary default properties + delete graph.node; + delete graph.edge; + delete graph.graph; + return graph; + } /** - * removes a mixin from the network object. - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private + * Parse a list with statements. + * @param {Object} graph */ - _clearMixin: function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - Network.prototype[mixinFunction] = undefined; + function parseStatements (graph) { + while (token !== '' && token != '}') { + parseStatement(graph); + if (token == ';') { + getToken(); } } - }, - + } /** - * Mixin the physics system and initialize the parameters required. - * - * @private + * Parse a single statement. Can be a an attribute statement, node + * statement, a series of node statements and edge statements, or a + * parameter. + * @param {Object} graph */ - _loadPhysicsSystem: function () { - this._loadMixin(physicsMixin); - this._loadSelectedForceSolver(); - if (this.constants.configurePhysics == true) { - this._loadPhysicsConfiguration(); + function parseStatement(graph) { + // parse subgraph + var subgraph = parseSubgraph(graph); + if (subgraph) { + // edge statements + parseEdge(graph, subgraph); + + return; } - }, + // parse an attribute statement + var attr = parseAttributeStatement(graph); + if (attr) { + return; + } - /** - * Mixin the cluster system and initialize the parameters required. - * - * @private - */ - _loadClusterSystem: function () { - this.clusterSession = 0; - this.hubThreshold = 5; - this._loadMixin(ClusterMixin); - }, + // parse node + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); + } + var id = token; // id can be a string or a number + getToken(); + if (token == '=') { + // id statement + getToken(); + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); + } + graph[id] = token; + getToken(); + // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " + } + else { + parseNodeStatement(graph, id); + } + } /** - * Mixin the sector system and initialize the parameters required - * - * @private + * Parse a subgraph + * @param {Object} graph parent graph object + * @return {Object | null} subgraph */ - _loadSectorSystem: function () { - this.sectors = {}; - this.activeSector = ["default"]; - this.sectors["active"] = {}; - this.sectors["active"]["default"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; - this.sectors["frozen"] = {}; - this.sectors["support"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; + function parseSubgraph (graph) { + var subgraph = null; - this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields + // optional subgraph keyword + if (token == 'subgraph') { + subgraph = {}; + subgraph.type = 'subgraph'; + getToken(); - this._loadMixin(SectorMixin); - }, + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + subgraph.id = token; + getToken(); + } + } + // open angle bracket + if (token == '{') { + getToken(); - /** - * Mixin the selection system and initialize the parameters required - * - * @private - */ - _loadSelectionSystem: function () { - this.selectionObj = {nodes: {}, edges: {}}; + if (!subgraph) { + subgraph = {}; + } + subgraph.parent = graph; + subgraph.node = graph.node; + subgraph.edge = graph.edge; + subgraph.graph = graph.graph; - this._loadMixin(SelectionMixin); - }, + // statements + parseStatements(subgraph); + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); + } + getToken(); - /** - * Mixin the navigationUI (User Interface) system and initialize the parameters required - * - * @private - */ - _loadManipulationSystem: function () { - // reset global variables -- these are used by the selection of nodes and edges. - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; + // remove temporary default properties + delete subgraph.node; + delete subgraph.edge; + delete subgraph.graph; + delete subgraph.parent; - if (this.constants.dataManipulation.enabled == true) { - // load the manipulator HTML elements. All styling done in css. - if (this.manipulationDiv === undefined) { - this.manipulationDiv = document.createElement('div'); - this.manipulationDiv.className = 'network-manipulationDiv'; - this.manipulationDiv.id = 'network-manipulationDiv'; - if (this.editMode == true) { - this.manipulationDiv.style.display = "block"; - } - else { - this.manipulationDiv.style.display = "none"; - } - this.containerElement.insertBefore(this.manipulationDiv, this.frame); + // register at the parent graph + if (!graph.subgraphs) { + graph.subgraphs = []; } + graph.subgraphs.push(subgraph); + } - if (this.editModeDiv === undefined) { - this.editModeDiv = document.createElement('div'); - this.editModeDiv.className = 'network-manipulation-editMode'; - this.editModeDiv.id = 'network-manipulation-editMode'; - if (this.editMode == true) { - this.editModeDiv.style.display = "none"; - } - else { - this.editModeDiv.style.display = "block"; - } - this.containerElement.insertBefore(this.editModeDiv, this.frame); - } + return subgraph; + } - if (this.closeDiv === undefined) { - this.closeDiv = document.createElement('div'); - this.closeDiv.className = 'network-manipulation-closeDiv'; - this.closeDiv.id = 'network-manipulation-closeDiv'; - this.closeDiv.style.display = this.manipulationDiv.style.display; - this.containerElement.insertBefore(this.closeDiv, this.frame); - } + /** + * parse an attribute statement like "node [shape=circle fontSize=16]". + * Available keywords are 'node', 'edge', 'graph'. + * The previous list with default attributes will be replaced + * @param {Object} graph + * @returns {String | null} keyword Returns the name of the parsed attribute + * (node, edge, graph), or null if nothing + * is parsed. + */ + function parseAttributeStatement (graph) { + // attribute statements + if (token == 'node') { + getToken(); - // load the manipulation functions - this._loadMixin(manipulationMixin); + // node attributes + graph.node = parseAttributeList(); + return 'node'; + } + else if (token == 'edge') { + getToken(); - // create the manipulator toolbar - this._createManipulatorBar(); + // edge attributes + graph.edge = parseAttributeList(); + return 'edge'; } - else { - if (this.manipulationDiv !== undefined) { - // removes all the bindings and overloads - this._createManipulatorBar(); - // remove the manipulation divs - this.containerElement.removeChild(this.manipulationDiv); - this.containerElement.removeChild(this.editModeDiv); - this.containerElement.removeChild(this.closeDiv); + else if (token == 'graph') { + getToken(); - this.manipulationDiv = undefined; - this.editModeDiv = undefined; - this.closeDiv = undefined; - // remove the mixin functions - this._clearMixin(manipulationMixin); - } + // graph attributes + graph.graph = parseAttributeList(); + return 'graph'; } - }, + return null; + } /** - * Mixin the navigation (User Interface) system and initialize the parameters required - * - * @private + * parse a node statement + * @param {Object} graph + * @param {String | Number} id */ - _loadNavigationControls: function () { - this._loadMixin(NavigationMixin); - - // the clean function removes the button divs, this is done to remove the bindings. - this._cleanNavigation(); - if (this.constants.navigation.enabled == true) { - this._loadNavigationElements(); + function parseNodeStatement(graph, id) { + // node statement + var node = { + id: id + }; + var attr = parseAttributeList(); + if (attr) { + node.attr = attr; } - }, + addNode(graph, node); + // edge statements + parseEdge(graph, id); + } /** - * Mixin the hierarchical layout system. - * - * @private + * Parse an edge or a series of edges + * @param {Object} graph + * @param {String | Number} from Id of the from node */ - _loadHierarchySystem: function () { - this._loadMixin(HierarchicalLayoutMixin); - } + function parseEdge(graph, from) { + while (token == '->' || token == '--') { + var to; + var type = token; + getToken(); + + var subgraph = parseSubgraph(graph); + if (subgraph) { + to = subgraph; + } + else { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier or subgraph expected'); + } + to = token; + addNode(graph, { + id: to + }); + getToken(); + } -}; + // parse edge attributes + var attr = parseAttributeList(); -/** - * @constructor Network - * Create a network visualization, displaying nodes and edges. - * - * @param {Element} container The DOM element in which the Network will - * be created. Normally a div element. - * @param {Object} data An object containing parameters - * {Array} nodes - * {Array} edges - * @param {Object} options Options - */ -function Network (container, data, options) { - if (!(this instanceof Network)) { - throw new SyntaxError('Constructor must be called with the new operator'); + // create edge + var edge = createEdge(graph, from, to, type, attr); + addEdge(graph, edge); + + from = to; + } } - this._initializeMixinLoaders(); - - // create variables and set default values - this.containerElement = container; - this.width = '100%'; - this.height = '100%'; - - // render and calculation settings - this.renderRefreshRate = 60; // hz (fps) - 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.50; // discrete stepsize of the simulation - - this.stabilize = true; // stabilize before displaying the network - this.selectable = true; - this.initializing = true; - - // these functions are triggered when the dataset is edited - this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; - - - // set constant values - this.constants = { - nodes: { - radiusMin: 5, - radiusMax: 20, - radius: 5, - shape: 'ellipse', - image: undefined, - widthMin: 16, // px - widthMax: 64, // px - fixed: false, - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - level: -1, - color: { - border: '#2B7CE9', - background: '#97C2FC', - highlight: { - border: '#2B7CE9', - background: '#D2E5FF' - }, - hover: { - border: '#2B7CE9', - background: '#D2E5FF' - } - }, - borderColor: '#2B7CE9', - backgroundColor: '#97C2FC', - highlightColor: '#D2E5FF', - group: undefined - }, - edges: { - widthMin: 1, - widthMax: 15, - width: 1, - widthSelectionMultiplier: 2, - hoverWidth: 1.5, - style: 'line', - color: { - color:'#848484', - highlight:'#848484', - hover: '#848484' - }, - fontColor: '#343434', - fontSize: 14, // px - fontFace: 'arial', - fontFill: 'white', - arrowScaleFactor: 1, - dash: { - length: 10, - gap: 5, - altLength: undefined - } - }, - configurePhysics:false, - physics: { - barnesHut: { - enabled: true, - theta: 1 / 0.6, // inverted to save time during calculation - gravitationalConstant: -2000, - centralGravity: 0.3, - springLength: 95, - springConstant: 0.04, - damping: 0.09 - }, - repulsion: { - centralGravity: 0.1, - springLength: 200, - springConstant: 0.05, - nodeDistance: 100, - damping: 0.09 - }, - hierarchicalRepulsion: { - enabled: false, - centralGravity: 0.5, - springLength: 150, - springConstant: 0.01, - nodeDistance: 60, - damping: 0.09 - }, - damping: null, - centralGravity: null, - springLength: null, - springConstant: null - }, - clustering: { // Per Node in Cluster = PNiC - enabled: false, // (Boolean) | global on/off switch for clustering. - initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. - clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes - reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this - chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). - clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. - sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. - screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node. - fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px). - maxFontSize: 1000, - forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). - distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). - edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. - nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster. - height: 1, // (px PNiC) | growth of the height per node in cluster. - radius: 1}, // (px PNiC) | growth of the radius per node in cluster. - maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. - activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. - clusterLevelDifference: 2 - }, - navigation: { - enabled: false - }, - keyboard: { - enabled: false, - speed: {x: 10, y: 10, zoom: 0.02} - }, - dataManipulation: { - enabled: false, - initiallyVisible: false - }, - hierarchicalLayout: { - enabled:false, - levelSeparation: 150, - nodeSpacing: 100, - direction: "UD" // UD, DU, LR, RL - }, - freezeForStabilization: false, - smoothCurves: true, - maxVelocity: 10, - minVelocity: 0.1, // px/s - stabilizationIterations: 1000, // maximum number of iteration to stabilize - labels:{ - add:"Add Node", - edit:"Edit", - 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).", - editBoundError:"No edit function has been bound to this button.", - deleteError:"The function for delete does not support two arguments (data, callback).", - deleteClusterError:"Clusters cannot be deleted." - }, - tooltip: { - delay: 300, - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' - } - }, - dragNetwork: true, - dragNodes: true, - zoomable: true, - hover: false - }; - this.hoverObj = {nodes:{},edges:{}}; - - - // Node variables - var network = this; - this.groups = new Groups(); // object with groups - this.images = new Images(); // object with images - this.images.setOnloadCallback(function () { - network._redraw(); - }); - - // keyboard navigation variables - this.xIncrement = 0; - this.yIncrement = 0; - this.zoomIncrement = 0; - - // loading all the mixins: - // load the force calculation functions, grouped under the physics system. - this._loadPhysicsSystem(); - // create a frame and canvas - this._create(); - // load the sector system. (mandatory, fully integrated with Network) - this._loadSectorSystem(); - // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) - this._loadClusterSystem(); - // load the selection system. (mandatory, required by Network) - this._loadSelectionSystem(); - // load the selection system. (mandatory, required by Network) - this._loadHierarchySystem(); - - // apply options - this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); - this._setScale(1); - this.setOptions(options); - - // other vars - this.freezeSimulation = false;// freeze the simulation - this.cachedFunctions = {}; - - // containers for nodes and edges - this.calculationNodes = {}; - this.calculationNodeIndices = []; - this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation - this.nodes = {}; // object with Node objects - this.edges = {}; // object with Edge objects - - // position and scale variables and objects - this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw. - this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw - this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw - this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action - this.scale = 1; // defining the global scale variable in the constructor - this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out - - // datasets or dataviews - this.nodesData = null; // A DataSet or DataView - this.edgesData = null; // A DataSet or DataView - - // create event listeners used to subscribe on the DataSets of the nodes and edges - this.nodesListeners = { - 'add': function (event, params) { - network._addNodes(params.items); - network.start(); - }, - 'update': function (event, params) { - network._updateNodes(params.items); - network.start(); - }, - 'remove': function (event, params) { - network._removeNodes(params.items); - network.start(); - } - }; - this.edgesListeners = { - 'add': function (event, params) { - network._addEdges(params.items); - network.start(); - }, - 'update': function (event, params) { - network._updateEdges(params.items); - network.start(); - }, - 'remove': function (event, params) { - network._removeEdges(params.items); - network.start(); - } - }; - - // properties for the animation - this.moving = true; - this.timer = undefined; // Scheduling function. Is definded in this.start(); - - // load data (the disable start variable will be the same as the enabled clustering) - this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled); - - // hierarchical layout - this.initializing = false; - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); - } - else { - // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here. - if (this.stabilize == false) { - this.zoomExtent(true,this.constants.clustering.enabled); - } - } - - // if clustering is disabled, the simulation will have started in the setData function - if (this.constants.clustering.enabled) { - this.startWithClustering(); - } -} - -// Extend Network with an Emitter mixin -Emitter(Network.prototype); + /** + * Parse a set with attributes, + * for example [label="1.000", shape=solid] + * @return {Object | null} attr + */ + function parseAttributeList() { + var attr = null; -/** - * Get the script path where the vis.js library is located - * - * @returns {string | null} path Path or null when not found. Path does not - * end with a slash. - * @private - */ -Network.prototype._getScriptPath = function() { - var scripts = document.getElementsByTagName( 'script' ); + while (token == '[') { + getToken(); + attr = {}; + while (token !== '' && token != ']') { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute name expected'); + } + var name = token; - // find script named vis.js or vis.min.js - for (var i = 0; i < scripts.length; i++) { - var src = scripts[i].src; - var match = src && /\/?vis(.min)?\.js$/.exec(src); - if (match) { - // return path without the script name - return src.substring(0, src.length - match[0].length); - } - } + getToken(); + if (token != '=') { + throw newSyntaxError('Equal sign = expected'); + } + getToken(); - return null; -}; + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute value expected'); + } + var value = token; + setValue(attr, name, value); // name can be a path + getToken(); + if (token ==',') { + getToken(); + } + } -/** - * Find the center position of the network - * @private - */ -Network.prototype._getRange = function() { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (minX > (node.x)) {minX = node.x;} - if (maxX < (node.x)) {maxX = node.x;} - if (minY > (node.y)) {minY = node.y;} - if (maxY < (node.y)) {maxY = node.y;} + if (token != ']') { + throw newSyntaxError('Bracket ] expected'); + } + getToken(); } - } - if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { - minY = 0, maxY = 0, minX = 0, maxX = 0; - } - return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; -}; - - -/** - * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; - * @returns {{x: number, y: number}} - * @private - */ -Network.prototype._findCenter = function(range) { - return {x: (0.5 * (range.maxX + range.minX)), - y: (0.5 * (range.maxY + range.minY))}; -}; - - -/** - * center the network - * - * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; - */ -Network.prototype._centerNetwork = function(range) { - var center = this._findCenter(range); - - center.x *= this.scale; - center.y *= this.scale; - center.x -= 0.5 * this.frame.canvas.clientWidth; - center.y -= 0.5 * this.frame.canvas.clientHeight; - - this._setTranslation(-center.x,-center.y); // set at 0,0 -}; - -/** - * This function zooms out to fit all data on screen based on amount of nodes - * - * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; - * @param {Boolean} [disableStart] | If true, start is not called. - */ -Network.prototype.zoomExtent = function(initialZoom, disableStart) { - if (initialZoom === undefined) { - initialZoom = false; + return attr; } - if (disableStart === undefined) { - disableStart = false; + + /** + * Create a syntax error with extra information on current token and index. + * @param {String} message + * @returns {SyntaxError} err + */ + function newSyntaxError(message) { + return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); } - var range = this._getRange(); - var zoomLevel; + /** + * Chop off text after a maximum length + * @param {String} text + * @param {Number} maxLength + * @returns {String} + */ + function chop (text, maxLength) { + return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); + } - if (initialZoom == true) { - var numberOfNodes = this.nodeIndices.length; - if (this.constants.smoothCurves == true) { - if (this.constants.clustering.enabled == true && - numberOfNodes >= this.constants.clustering.initialMaxNodes) { - zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - else { - zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } + /** + * Execute a function fn for each pair of elements in two arrays + * @param {Array | *} array1 + * @param {Array | *} array2 + * @param {function} fn + */ + function forEach2(array1, array2, fn) { + if (array1 instanceof Array) { + array1.forEach(function (elem1) { + if (array2 instanceof Array) { + array2.forEach(function (elem2) { + fn(elem1, elem2); + }); + } + else { + fn(elem1, array2); + } + }); } else { - if (this.constants.clustering.enabled == true && - numberOfNodes >= this.constants.clustering.initialMaxNodes) { - zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + if (array2 instanceof Array) { + array2.forEach(function (elem2) { + fn(array1, elem2); + }); } else { - zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + fn(array1, array2); } } - - // correct for larger canvasses. - var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600); - zoomLevel *= factor; } - else { - var xDistance = (Math.abs(range.minX) + Math.abs(range.maxX)) * 1.1; - var yDistance = (Math.abs(range.minY) + Math.abs(range.maxY)) * 1.1; - var xZoomLevel = this.frame.canvas.clientWidth / xDistance; - var yZoomLevel = this.frame.canvas.clientHeight / yDistance; + /** + * Convert a string containing a graph in DOT language into a map containing + * with nodes and edges in the format of graph. + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graphData + */ + function DOTToGraph (data) { + // parse the DOT file + var dotData = parseDOT(data); + var graphData = { + nodes: [], + edges: [], + options: {} + }; + + // copy the nodes + if (dotData.nodes) { + dotData.nodes.forEach(function (dotNode) { + var graphNode = { + id: dotNode.id, + label: String(dotNode.label || dotNode.id) + }; + merge(graphNode, dotNode.attr); + if (graphNode.image) { + graphNode.shape = 'image'; + } + graphData.nodes.push(graphNode); + }); + } - zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; - } + // copy the edges + if (dotData.edges) { + /** + * Convert an edge in DOT format to an edge with VisGraph format + * @param {Object} dotEdge + * @returns {Object} graphEdge + */ + function convertEdge(dotEdge) { + var graphEdge = { + from: dotEdge.from, + to: dotEdge.to + }; + merge(graphEdge, dotEdge.attr); + graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; + return graphEdge; + } - if (zoomLevel > 1.0) { - zoomLevel = 1.0; - } + dotData.edges.forEach(function (dotEdge) { + var from, to; + if (dotEdge.from instanceof Object) { + from = dotEdge.from.nodes; + } + else { + from = { + id: dotEdge.from + } + } + if (dotEdge.to instanceof Object) { + to = dotEdge.to.nodes; + } + else { + to = { + id: dotEdge.to + } + } - this._setScale(zoomLevel); - this._centerNetwork(range); - if (disableStart == false) { - this.moving = true; - this.start(); - } -}; + if (dotEdge.from instanceof Object && dotEdge.from.edges) { + dotEdge.from.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } + forEach2(from, to, function (from, to) { + var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); -/** - * Update the this.nodeIndices with the most recent node index list - * @private - */ -Network.prototype._updateNodeIndexList = function() { - this._clearNodeIndexList(); - for (var idx in this.nodes) { - if (this.nodes.hasOwnProperty(idx)) { - this.nodeIndices.push(idx); + if (dotEdge.to instanceof Object && dotEdge.to.edges) { + dotEdge.to.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } + }); } - } -}; + // copy the options + if (dotData.attr) { + graphData.options = dotData.attr; + } -/** - * Set nodes and edges, and optionally options as well. - * - * @param {Object} data Object containing parameters: - * {Array | DataSet | DataView} [nodes] Array with nodes - * {Array | DataSet | DataView} [edges] Array with edges - * {String} [dot] String containing data in DOT format - * {Options} [options] Object with options - * @param {Boolean} [disableStart] | optional: disable the calling of the start function. - */ -Network.prototype.setData = function(data, disableStart) { - if (disableStart === undefined) { - disableStart = false; + return graphData; } - if (data && data.dot && (data.nodes || data.edges)) { - throw new SyntaxError('Data must contain either parameter "dot" or ' + - ' parameter pair "nodes" and "edges", but not both.'); - } + // exports + exports.parseDOT = parseDOT; + exports.DOTToGraph = DOTToGraph; - // set options - this.setOptions(data && data.options); - // set all data - if (data && data.dot) { - // parse DOT file - if(data && data.dot) { - var dotData = vis.util.DOTToGraph(data.dot); - this.setData(dotData); - return; - } - } - else { - this._setNodes(data && data.nodes); - this._setEdges(data && data.edges); - } +/***/ }, +/* 33 */ +/***/ function(module, exports, __webpack_require__) { - this._putDataInSector(); + /** + * @prototype Point3d + * @param {Number} [x] + * @param {Number} [y] + * @param {Number} [z] + */ + function Point3d(x, y, z) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + this.z = z !== undefined ? z : 0; + }; - if (!disableStart) { - // find a stable position or start animating to a stable position - if (this.stabilize) { - var me = this; - setTimeout(function() {me._stabilize(); me.start();},0) - } - else { - this.start(); - } - } -}; + /** + * Subtract the two provided points, returns a-b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a-b + */ + Point3d.subtract = function(a, b) { + var sub = new Point3d(); + sub.x = a.x - b.x; + sub.y = a.y - b.y; + sub.z = a.z - b.z; + return sub; + }; -/** - * Set options - * @param {Object} options - * @param {Boolean} [initializeView] | set zoom and translation to default. - */ -Network.prototype.setOptions = function (options) { - if (options) { - var prop; - // retrieve parameter values - if (options.width !== undefined) {this.width = options.width;} - if (options.height !== undefined) {this.height = options.height;} - if (options.stabilize !== undefined) {this.stabilize = options.stabilize;} - if (options.selectable !== undefined) {this.selectable = options.selectable;} - if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;} - 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.dragNetwork !== undefined) {this.constants.dragNetwork = options.dragNetwork;} - 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;} + /** + * Add the two provided points, returns a+b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a+b + */ + Point3d.add = function(a, b) { + var sum = new Point3d(); + sum.x = a.x + b.x; + sum.y = a.y + b.y; + sum.z = a.z + b.z; + return sum; + }; - // TODO: deprecated since version 3.0.0. Cleanup some day - if (options.dragGraph !== undefined) { - throw new Error('Option dragGraph is renamed to dragNetwork'); - } + /** + * Calculate the average of two 3d points + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} The average, (a+b)/2 + */ + Point3d.avg = function(a, b) { + return new Point3d( + (a.x + b.x) / 2, + (a.y + b.y) / 2, + (a.z + b.z) / 2 + ); + }; - if (options.labels !== undefined) { - for (prop in options.labels) { - if (options.labels.hasOwnProperty(prop)) { - this.constants.labels[prop] = options.labels[prop]; - } - } - } + /** + * Calculate the cross product of the two provided points, returns axb + * Documentation: http://en.wikipedia.org/wiki/Cross_product + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} cross product axb + */ + Point3d.crossProduct = function(a, b) { + var crossproduct = new Point3d(); - if (options.onAdd) { - this.triggerFunctions.add = options.onAdd; - } + crossproduct.x = a.y * b.z - a.z * b.y; + crossproduct.y = a.z * b.x - a.x * b.z; + crossproduct.z = a.x * b.y - a.y * b.x; - if (options.onEdit) { - this.triggerFunctions.edit = options.onEdit; - } + return crossproduct; + }; - if (options.onEditEdge) { - this.triggerFunctions.editEdge = options.onEditEdge; - } - - if (options.onConnect) { - this.triggerFunctions.connect = options.onConnect; - } - if (options.onDelete) { - this.triggerFunctions.del = options.onDelete; - } + /** + * Rtrieve the length of the vector (or the distance from this point to the origin + * @return {Number} length + */ + Point3d.prototype.length = function() { + return Math.sqrt( + this.x * this.x + + this.y * this.y + + this.z * this.z + ); + }; - if (options.physics) { - if (options.physics.barnesHut) { - this.constants.physics.barnesHut.enabled = true; - for (prop in options.physics.barnesHut) { - if (options.physics.barnesHut.hasOwnProperty(prop)) { - this.constants.physics.barnesHut[prop] = options.physics.barnesHut[prop]; - } - } - } + module.exports = Point3d; - if (options.physics.repulsion) { - this.constants.physics.barnesHut.enabled = false; - for (prop in options.physics.repulsion) { - if (options.physics.repulsion.hasOwnProperty(prop)) { - this.constants.physics.repulsion[prop] = options.physics.repulsion[prop]; - } - } - } - if (options.physics.hierarchicalRepulsion) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - for (prop in options.physics.hierarchicalRepulsion) { - if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) { - this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop]; - } - } - } - } +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { - if (options.hierarchicalLayout) { - this.constants.hierarchicalLayout.enabled = true; - for (prop in options.hierarchicalLayout) { - if (options.hierarchicalLayout.hasOwnProperty(prop)) { - this.constants.hierarchicalLayout[prop] = options.hierarchicalLayout[prop]; - } - } - } - else if (options.hierarchicalLayout !== undefined) { - this.constants.hierarchicalLayout.enabled = false; - } + /** + * @prototype Point2d + * @param {Number} [x] + * @param {Number} [y] + */ + Point2d = function (x, y) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + }; - if (options.clustering) { - this.constants.clustering.enabled = true; - for (prop in options.clustering) { - if (options.clustering.hasOwnProperty(prop)) { - this.constants.clustering[prop] = options.clustering[prop]; - } - } - } - else if (options.clustering !== undefined) { - this.constants.clustering.enabled = false; - } + module.exports = Point2d; - if (options.navigation) { - this.constants.navigation.enabled = true; - for (prop in options.navigation) { - if (options.navigation.hasOwnProperty(prop)) { - this.constants.navigation[prop] = options.navigation[prop]; - } - } - } - else if (options.navigation !== undefined) { - this.constants.navigation.enabled = false; - } - if (options.keyboard) { - this.constants.keyboard.enabled = true; - for (prop in options.keyboard) { - if (options.keyboard.hasOwnProperty(prop)) { - this.constants.keyboard[prop] = options.keyboard[prop]; - } - } - } - else if (options.keyboard !== undefined) { - this.constants.keyboard.enabled = false; - } +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { - if (options.dataManipulation) { - this.constants.dataManipulation.enabled = true; - for (prop in options.dataManipulation) { - if (options.dataManipulation.hasOwnProperty(prop)) { - this.constants.dataManipulation[prop] = options.dataManipulation[prop]; - } - } - this.editMode = this.constants.dataManipulation.initiallyVisible; - } - else if (options.dataManipulation !== undefined) { - this.constants.dataManipulation.enabled = false; - } + var DataView = __webpack_require__(4); - // TODO: work out these options and document them - if (options.edges) { - for (prop in options.edges) { - if (options.edges.hasOwnProperty(prop)) { - if (typeof options.edges[prop] != "object") { - this.constants.edges[prop] = options.edges[prop]; - } - } - } + /** + * @class Filter + * + * @param {DataSet} data The google data table + * @param {Number} column The index of the column to be filtered + * @param {Graph} graph The graph + */ + function Filter (data, column, graph) { + this.data = data; + this.column = column; + this.graph = graph; // the parent graph + this.index = undefined; + this.value = undefined; - if (options.edges.color !== undefined) { - if (util.isString(options.edges.color)) { - this.constants.edges.color = {}; - this.constants.edges.color.color = options.edges.color; - this.constants.edges.color.highlight = options.edges.color; - this.constants.edges.color.hover = options.edges.color; - } - else { - if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;} - if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} - if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} - } - } + // read all distinct values and select the first one + this.values = graph.getDistinctValues(data.get(), this.column); - if (!options.edges.fontColor) { - if (options.edges.color !== undefined) { - if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;} - else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;} - } - } + // sort both numeric and string values correctly + this.values.sort(function (a, b) { + return a > b ? 1 : a < b ? -1 : 0; + }); - // Added to support dashed lines - // David Jordan - // 2012-08-08 - if (options.edges.dash) { - if (options.edges.dash.length !== undefined) { - this.constants.edges.dash.length = options.edges.dash.length; - } - if (options.edges.dash.gap !== undefined) { - this.constants.edges.dash.gap = options.edges.dash.gap; - } - if (options.edges.dash.altLength !== undefined) { - this.constants.edges.dash.altLength = options.edges.dash.altLength; - } - } + if (this.values.length > 0) { + this.selectValue(0); } - if (options.nodes) { - for (prop in options.nodes) { - if (options.nodes.hasOwnProperty(prop)) { - this.constants.nodes[prop] = options.nodes[prop]; - } - } + // create an array with the filtered datapoints. this will be loaded afterwards + this.dataPoints = []; - if (options.nodes.color) { - this.constants.nodes.color = util.parseColor(options.nodes.color); - } + this.loaded = false; + this.onLoadCallback = undefined; - /* - if (options.nodes.widthMin) this.constants.nodes.radiusMin = options.nodes.widthMin; - if (options.nodes.widthMax) this.constants.nodes.radiusMax = options.nodes.widthMax; - */ + if (graph.animationPreload) { + this.loaded = false; + this.loadInBackground(); } - if (options.groups) { - for (var groupname in options.groups) { - if (options.groups.hasOwnProperty(groupname)) { - var group = options.groups[groupname]; - this.groups.add(groupname, group); - } - } + else { + this.loaded = true; } + }; - if (options.tooltip) { - for (prop in options.tooltip) { - if (options.tooltip.hasOwnProperty(prop)) { - this.constants.tooltip[prop] = options.tooltip[prop]; - } - } - if (options.tooltip.color) { - this.constants.tooltip.color = util.parseColor(options.tooltip.color); - } - } - } + /** + * Return the label + * @return {string} label + */ + Filter.prototype.isLoaded = function() { + return this.loaded; + }; - // (Re)loading the mixins that can be enabled or disabled in the options. - // load the force calculation functions, grouped under the physics system. - this._loadPhysicsSystem(); - // load the navigation system. - this._loadNavigationControls(); - // load the data manipulation system - this._loadManipulationSystem(); - // configure the smooth curves - this._configureSmoothCurves(); + /** + * Return the loaded progress + * @return {Number} percentage between 0 and 100 + */ + Filter.prototype.getLoadedProgress = function() { + var len = this.values.length; - // bind keys. If disabled, this will not do anything; - this._createKeyBinds(); - this.setSize(this.width, this.height); - this.moving = true; - this.start(); + var i = 0; + while (this.dataPoints[i]) { + i++; + } -}; + return Math.round(i / len * 100); + }; -/** - * Create the main frame for the Network. - * This function is executed once when a Network object is created. The frame - * contains a canvas, and this canvas contains all objects like the axis and - * nodes. - * @private - */ -Network.prototype._create = function () { - // remove all elements from the container element. - while (this.containerElement.hasChildNodes()) { - this.containerElement.removeChild(this.containerElement.firstChild); - } - this.frame = document.createElement('div'); - this.frame.className = 'network-frame'; - this.frame.style.position = 'relative'; - this.frame.style.overflow = 'hidden'; - - // create the network canvas (HTML canvas element) - this.frame.canvas = document.createElement( 'canvas' ); - this.frame.canvas.style.position = 'relative'; - this.frame.appendChild(this.frame.canvas); - if (!this.frame.canvas.getContext) { - var noCanvas = document.createElement( 'DIV' ); - noCanvas.style.color = 'red'; - noCanvas.style.fontWeight = 'bold' ; - noCanvas.style.padding = '10px'; - noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; - this.frame.canvas.appendChild(noCanvas); - } + /** + * Return the label + * @return {string} label + */ + Filter.prototype.getLabel = function() { + return this.graph.filterLabel; + }; - var me = this; - this.drag = {}; - this.pinch = {}; - this.hammer = Hammer(this.frame.canvas, { - prevent_default: true - }); - this.hammer.on('tap', me._onTap.bind(me) ); - this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); - this.hammer.on('hold', me._onHold.bind(me) ); - this.hammer.on('pinch', me._onPinch.bind(me) ); - this.hammer.on('touch', me._onTouch.bind(me) ); - this.hammer.on('dragstart', me._onDragStart.bind(me) ); - this.hammer.on('drag', me._onDrag.bind(me) ); - this.hammer.on('dragend', me._onDragEnd.bind(me) ); - this.hammer.on('release', me._onRelease.bind(me) ); - this.hammer.on('mousewheel',me._onMouseWheel.bind(me) ); - this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF - this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); - - // add the frame to the container element - this.containerElement.appendChild(this.frame); - -}; + /** + * Return the columnIndex of the filter + * @return {Number} columnIndex + */ + Filter.prototype.getColumn = function() { + return this.column; + }; -/** - * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin - * @private - */ -Network.prototype._createKeyBinds = function() { - var me = this; - this.mousetrap = mousetrap; - - this.mousetrap.reset(); - - if (this.constants.keyboard.enabled == true) { - this.mousetrap.bind("up", this._moveUp.bind(me) , "keydown"); - this.mousetrap.bind("up", this._yStopMoving.bind(me), "keyup"); - this.mousetrap.bind("down", this._moveDown.bind(me) , "keydown"); - this.mousetrap.bind("down", this._yStopMoving.bind(me), "keyup"); - this.mousetrap.bind("left", this._moveLeft.bind(me) , "keydown"); - this.mousetrap.bind("left", this._xStopMoving.bind(me), "keyup"); - this.mousetrap.bind("right",this._moveRight.bind(me), "keydown"); - this.mousetrap.bind("right",this._xStopMoving.bind(me), "keyup"); - this.mousetrap.bind("=", this._zoomIn.bind(me), "keydown"); - this.mousetrap.bind("=", this._stopZoom.bind(me), "keyup"); - this.mousetrap.bind("-", this._zoomOut.bind(me), "keydown"); - this.mousetrap.bind("-", this._stopZoom.bind(me), "keyup"); - this.mousetrap.bind("[", this._zoomIn.bind(me), "keydown"); - this.mousetrap.bind("[", this._stopZoom.bind(me), "keyup"); - this.mousetrap.bind("]", this._zoomOut.bind(me), "keydown"); - this.mousetrap.bind("]", this._stopZoom.bind(me), "keyup"); - this.mousetrap.bind("pageup",this._zoomIn.bind(me), "keydown"); - this.mousetrap.bind("pageup",this._stopZoom.bind(me), "keyup"); - this.mousetrap.bind("pagedown",this._zoomOut.bind(me),"keydown"); - this.mousetrap.bind("pagedown",this._stopZoom.bind(me), "keyup"); - } + /** + * Return the currently selected value. Returns undefined if there is no selection + * @return {*} value + */ + Filter.prototype.getSelectedValue = function() { + if (this.index === undefined) + return undefined; - if (this.constants.dataManipulation.enabled == true) { - this.mousetrap.bind("escape",this._createManipulatorBar.bind(me)); - this.mousetrap.bind("del",this._deleteSelected.bind(me)); - } -}; + return this.values[this.index]; + }; -/** - * Get the pointer location from a touch location - * @param {{pageX: Number, pageY: Number}} touch - * @return {{x: Number, y: Number}} pointer - * @private - */ -Network.prototype._getPointer = function (touch) { - return { - x: touch.pageX - vis.util.getAbsoluteLeft(this.frame.canvas), - y: touch.pageY - vis.util.getAbsoluteTop(this.frame.canvas) + /** + * Retrieve all values of the filter + * @return {Array} values + */ + Filter.prototype.getValues = function() { + return this.values; }; -}; -/** - * On start of a touch gesture, store the pointer - * @param event - * @private - */ -Network.prototype._onTouch = function (event) { - this.drag.pointer = this._getPointer(event.gesture.center); - this.drag.pinched = false; - this.pinch.scale = this._getScale(); + /** + * Retrieve one value of the filter + * @param {Number} index + * @return {*} value + */ + Filter.prototype.getValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; + + return this.values[index]; + }; - this._handleTouch(this.drag.pointer); -}; -/** - * handle drag start event - * @private - */ -Network.prototype._onDragStart = function () { - this._handleDragStart(); -}; + /** + * Retrieve the (filtered) dataPoints for the currently selected filter index + * @param {Number} [index] (optional) + * @return {Array} dataPoints + */ + Filter.prototype._getDataPoints = function(index) { + if (index === undefined) + index = this.index; + if (index === undefined) + return []; -/** - * This function is called by _onDragStart. - * It is separated out because we can then overload it for the datamanipulation system. - * - * @private - */ -Network.prototype._handleDragStart = function() { - var drag = this.drag; - var node = this._getNodeAt(drag.pointer); - // note: drag.pointer is set in _onTouch to get the initial touch location - - drag.dragging = true; - drag.selection = []; - drag.translation = this._getTranslation(); - drag.nodeId = null; - - if (node != null) { - drag.nodeId = node.id; - // select the clicked node if not yet selected - if (!node.isSelected()) { - this._selectObject(node,false); + var dataPoints; + if (this.dataPoints[index]) { + dataPoints = this.dataPoints[index]; } + else { + var f = {}; + f.column = this.column; + f.value = this.values[index]; - // create an array with the selected nodes and their original location and status - for (var objectId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(objectId)) { - var object = this.selectionObj.nodes[objectId]; - var s = { - id: object.id, - node: object, + var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); + dataPoints = this.graph._getDataPoints(dataView); - // store original x, y, xFixed and yFixed, make the node temporarily Fixed - x: object.x, - y: object.y, - xFixed: object.xFixed, - yFixed: object.yFixed - }; + this.dataPoints[index] = dataPoints; + } - object.xFixed = true; - object.yFixed = true; + return dataPoints; + }; - drag.selection.push(s); - } - } - } -}; -/** - * handle drag event - * @private - */ -Network.prototype._onDrag = function (event) { - this._handleOnDrag(event) -}; + /** + * Set a callback function when the filter is fully loaded. + */ + Filter.prototype.setOnLoadCallback = function(callback) { + this.onLoadCallback = callback; + }; -/** - * This function is called by _onDrag. - * It is separated out because we can then overload it for the datamanipulation system. - * - * @private - */ -Network.prototype._handleOnDrag = function(event) { - if (this.drag.pinched) { - return; - } + /** + * Add a value to the list with available values for this filter + * No double entries will be created. + * @param {Number} index + */ + Filter.prototype.selectValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; - var pointer = this._getPointer(event.gesture.center); + this.index = index; + this.value = this.values[index]; + }; - var me = this, - drag = this.drag, - selection = drag.selection; - 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; + /** + * Load all filtered rows in the background one by one + * Start this method without providing an index! + */ + Filter.prototype.loadInBackground = function(index) { + if (index === undefined) + index = 0; - // update position of all selected nodes - selection.forEach(function (s) { - var node = s.node; + var frame = this.graph.frame; - if (!s.xFixed) { - node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX); - } + if (index < this.values.length) { + var dataPointsTemp = this._getDataPoints(index); + //this.graph.redrawInfo(); // TODO: not neat - if (!s.yFixed) { - node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY); + // create a progress box + if (frame.progress === undefined) { + frame.progress = document.createElement('DIV'); + frame.progress.style.position = 'absolute'; + frame.progress.style.color = 'gray'; + frame.appendChild(frame.progress); } - }); + var progress = this.getLoadedProgress(); + frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; + // TODO: this is no nice solution... + frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider + frame.progress.style.left = 10 + 'px'; - // start _animationStep if not yet running - if (!this.moving) { - this.moving = true; - this.start(); - } - } - else { - if (this.constants.dragNetwork == true) { - // move the network - var diffX = pointer.x - this.drag.pointer.x; - var diffY = pointer.y - this.drag.pointer.y; - - this._setTranslation( - this.drag.translation.x + diffX, - this.drag.translation.y + diffY); - this._redraw(); - this.moving = true; - this.start(); + var me = this; + setTimeout(function() {me.loadInBackground(index+1);}, 10); + this.loaded = false; } - } -}; - -/** - * handle drag start event - * @private - */ -Network.prototype._onDragEnd = function () { - this.drag.dragging = false; - var selection = this.drag.selection; - if (selection) { - selection.forEach(function (s) { - // restore original xFixed and yFixed - s.node.xFixed = s.xFixed; - s.node.yFixed = s.yFixed; - }); - } -}; + else { + this.loaded = true; -/** - * handle tap/click event: select/unselect a node - * @private - */ -Network.prototype._onTap = function (event) { - var pointer = this._getPointer(event.gesture.center); - this.pointerPosition = pointer; - this._handleTap(pointer); + // remove the progress box + if (frame.progress !== undefined) { + frame.removeChild(frame.progress); + frame.progress = undefined; + } -}; + if (this.onLoadCallback) + this.onLoadCallback(); + } + }; + module.exports = Filter; -/** - * handle doubletap event - * @private - */ -Network.prototype._onDoubleTap = function (event) { - var pointer = this._getPointer(event.gesture.center); - this._handleDoubleTap(pointer); -}; +/***/ }, +/* 36 */ +/***/ function(module, exports, __webpack_require__) { -/** - * handle long tap event: multi select nodes - * @private - */ -Network.prototype._onHold = function (event) { - var pointer = this._getPointer(event.gesture.center); - this.pointerPosition = pointer; - this._handleOnHold(pointer); -}; + /** + * @prototype StepNumber + * The class StepNumber is an iterator for Numbers. You provide a start and end + * value, and a best step size. StepNumber itself rounds to fixed values and + * a finds the step that best fits the provided step. + * + * If prettyStep is true, the step size is chosen as close as possible to the + * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... + * + * Example usage: + * var step = new StepNumber(0, 10, 2.5, true); + * step.start(); + * while (!step.end()) { + * alert(step.getCurrent()); + * step.next(); + * } + * + * Version: 1.0 + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + function StepNumber(start, end, step, prettyStep) { + // set default values + this._start = 0; + this._end = 0; + this._step = 1; + this.prettyStep = true; + this.precision = 5; + + this._current = 0; + this.setRange(start, end, step, prettyStep); + }; -/** - * handle the release of the screen - * - * @private - */ -Network.prototype._onRelease = function (event) { - var pointer = this._getPointer(event.gesture.center); - this._handleOnRelease(pointer); -}; + /** + * Set a new range: start, end and step. + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + StepNumber.prototype.setRange = function(start, end, step, prettyStep) { + this._start = start ? start : 0; + this._end = end ? end : 0; -/** - * Handle pinch event - * @param event - * @private - */ -Network.prototype._onPinch = function (event) { - var pointer = this._getPointer(event.gesture.center); + this.setStep(step, prettyStep); + }; - this.drag.pinched = true; - if (!('scale' in this.pinch)) { - this.pinch.scale = 1; - } + /** + * Set a new step size + * @param {Number} step New step size. Must be a positive value + * @param {boolean} prettyStep Optional. If true, the provided step is rounded + * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + StepNumber.prototype.setStep = function(step, prettyStep) { + if (step === undefined || step <= 0) + return; - // TODO: enabled moving while pinching? - var scale = this.pinch.scale * event.gesture.scale; - this._zoom(scale, pointer) -}; + if (prettyStep !== undefined) + this.prettyStep = prettyStep; -/** - * Zoom the network in or out - * @param {Number} scale a number around 1, and between 0.01 and 10 - * @param {{x: Number, y: Number}} pointer Position on screen - * @return {Number} appliedScale scale is limited within the boundaries - * @private - */ -Network.prototype._zoom = function(scale, pointer) { - if (this.constants.zoomable == true) { - var scaleOld = this._getScale(); - if (scale < 0.00001) { - scale = 0.00001; - } - if (scale > 10) { - scale = 10; - } - // + this.frame.canvas.clientHeight / 2 - var translation = this._getTranslation(); + if (this.prettyStep === true) + this._step = StepNumber.calculatePrettyStep(step); + else + this._step = step; + }; - var scaleFrac = scale / scaleOld; - var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; - var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; + /** + * Calculate a nice step size, closest to the desired step size. + * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an + * integer Number. For example 1, 2, 5, 10, 20, 50, etc... + * @param {Number} step Desired step size + * @return {Number} Nice step size + */ + StepNumber.calculatePrettyStep = function (step) { + var log10 = function (x) {return Math.log(x) / Math.LN10;}; - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; + // try three steps (multiple of 1, 2, or 5 + var step1 = Math.pow(10, Math.round(log10(step))), + step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), + step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); - this._setScale(scale); - this._setTranslation(tx, ty); - this.updateClustersDefault(); - this._redraw(); + // choose the best step (closest to minimum step) + var prettyStep = step1; + if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; + if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; - if (scaleOld < scale) { - this.emit("zoom", {direction:"+"}); - } - else { - this.emit("zoom", {direction:"-"}); + // for safety + if (prettyStep <= 0) { + prettyStep = 1; } - return scale; - } -}; + return prettyStep; + }; + /** + * returns the current value of the step + * @return {Number} current value + */ + StepNumber.prototype.getCurrent = function () { + return parseFloat(this._current.toPrecision(this.precision)); + }; -/** - * Event handler for mouse wheel event, used to zoom the timeline - * See http://adomas.org/javascript-mouse-wheel/ - * https://github.com/EightMedia/hammer.js/issues/256 - * @param {MouseEvent} event - * @private - */ -Network.prototype._onMouseWheel = function(event) { - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta/120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail/3; - } + /** + * returns the current step size + * @return {Number} current step size + */ + StepNumber.prototype.getStep = function () { + return this._step; + }; - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { + /** + * Set the current value to the largest value smaller than start, which + * is a multiple of the step size + */ + StepNumber.prototype.start = function() { + this._current = this._start - this._start % this._step; + }; - // calculate the new scale - var scale = this._getScale(); - var zoom = delta / 10; - if (delta < 0) { - zoom = zoom / (1 - zoom); - } - scale *= (1 + zoom); + /** + * Do a step, add the step size to the current value + */ + StepNumber.prototype.next = function () { + this._current += this._step; + }; - // calculate the pointer location - var gesture = util.fakeGesture(this, event); - var pointer = this._getPointer(gesture.center); + /** + * Returns true whether the end is reached + * @return {boolean} True if the current value has passed the end value. + */ + StepNumber.prototype.end = function () { + return (this._current > this._end); + }; - // apply the new scale - this._zoom(scale, pointer); - } + module.exports = StepNumber; - // Prevent default actions caused by mouse wheel. - event.preventDefault(); -}; +/***/ }, +/* 37 */ +/***/ function(module, exports, __webpack_require__) { -/** - * Mouse move handler for checking whether the title moves over a node with a title. - * @param {Event} event - * @private - */ -Network.prototype._onMouseMoveTitle = function (event) { - var gesture = util.fakeGesture(this, event); - var pointer = this._getPointer(gesture.center); + /** + * Canvas shapes used by Network + */ + if (typeof CanvasRenderingContext2D !== 'undefined') { - // check if the previously selected node is still selected - if (this.popupObj) { - this._checkHidePopup(pointer); - } + /** + * Draw a circle shape + */ + CanvasRenderingContext2D.prototype.circle = function(x, y, r) { + this.beginPath(); + this.arc(x, y, r, 0, 2*Math.PI, false); + }; - // start a timeout that will check if the mouse is positioned above - // an element - var me = this; - var checkShow = function() { - me._checkShowPopup(pointer); - }; - if (this.popupTimer) { - clearInterval(this.popupTimer); // stop any running calculationTimer - } - if (!this.drag.dragging) { - this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay); - } + /** + * Draw a square shape + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r size, width and height of the square + */ + CanvasRenderingContext2D.prototype.square = function(x, y, r) { + this.beginPath(); + this.rect(x - r, y - r, r * 2, r * 2); + }; + /** + * Draw a triangle shape + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius, half the length of the sides of the triangle + */ + CanvasRenderingContext2D.prototype.triangle = function(x, y, r) { + // http://en.wikipedia.org/wiki/Equilateral_triangle + this.beginPath(); + + var s = r * 2; + var s2 = s / 2; + var ir = Math.sqrt(3) / 6 * s; // radius of inner circle + var h = Math.sqrt(s * s - s2 * s2); // height + + this.moveTo(x, y - (h - ir)); + this.lineTo(x + s2, y + ir); + this.lineTo(x - s2, y + ir); + this.lineTo(x, y - (h - ir)); + this.closePath(); + }; - /** - * Adding hover highlights - */ - if (this.constants.hover == true) { - // removing all hover highlights - for (var edgeId in this.hoverObj.edges) { - if (this.hoverObj.edges.hasOwnProperty(edgeId)) { - this.hoverObj.edges[edgeId].hover = false; - delete this.hoverObj.edges[edgeId]; - } - } + /** + * Draw a triangle shape in downward orientation + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius + */ + CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) { + // http://en.wikipedia.org/wiki/Equilateral_triangle + this.beginPath(); + + var s = r * 2; + var s2 = s / 2; + var ir = Math.sqrt(3) / 6 * s; // radius of inner circle + var h = Math.sqrt(s * s - s2 * s2); // height + + this.moveTo(x, y + (h - ir)); + this.lineTo(x + s2, y - ir); + this.lineTo(x - s2, y - ir); + this.lineTo(x, y + (h - ir)); + this.closePath(); + }; - // adding hover highlights - var obj = this._getNodeAt(pointer); - if (obj == null) { - obj = this._getEdgeAt(pointer); - } - if (obj != null) { - this._hoverObject(obj); - } + /** + * Draw a star shape, a star with 5 points + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius, half the length of the sides of the triangle + */ + CanvasRenderingContext2D.prototype.star = function(x, y, r) { + // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ + this.beginPath(); - // removing all node hover highlights except for the selected one. - for (var nodeId in this.hoverObj.nodes) { - if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { - if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) { - this._blurObject(this.hoverObj.nodes[nodeId]); - delete this.hoverObj.nodes[nodeId]; - } + for (var n = 0; n < 10; n++) { + var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5; + this.lineTo( + x + radius * Math.sin(n * 2 * Math.PI / 10), + y - radius * Math.cos(n * 2 * Math.PI / 10) + ); } - } - this.redraw(); - } -}; -/** - * Check if there is an element on the given position in the network - * (a node or edge). If so, and if this element has a title, - * show a popup window with its title. - * - * @param {{x:Number, y:Number}} pointer - * @private - */ -Network.prototype._checkShowPopup = function (pointer) { - var obj = { - left: this._XconvertDOMtoCanvas(pointer.x), - top: this._YconvertDOMtoCanvas(pointer.y), - right: this._XconvertDOMtoCanvas(pointer.x), - bottom: this._YconvertDOMtoCanvas(pointer.y) - }; + this.closePath(); + }; - var id; - var lastPopupNode = this.popupObj; + /** + * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas + */ + CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { + var r2d = Math.PI/180; + if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x + if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y + this.beginPath(); + this.moveTo(x+r,y); + this.lineTo(x+w-r,y); + this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false); + this.lineTo(x+w,y+h-r); + this.arc(x+w-r,y+h-r,r,0,r2d*90,false); + this.lineTo(x+r,y+h); + this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false); + this.lineTo(x,y+r); + this.arc(x+r,y+r,r,r2d*180,r2d*270,false); + }; - if (this.popupObj == undefined) { - // search the nodes for overlap, select the top one in case of multiple nodes - var nodes = this.nodes; - for (id in nodes) { - if (nodes.hasOwnProperty(id)) { - var node = nodes[id]; - if (node.getTitle() !== undefined && node.isOverlappingWith(obj)) { - this.popupObj = node; - break; - } - } - } - } + /** + * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + */ + CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) { + var kappa = .5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + this.beginPath(); + this.moveTo(x, ym); + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + }; - if (this.popupObj === undefined) { - // search the edges for overlap - var edges = this.edges; - for (id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - if (edge.connected && (edge.getTitle() !== undefined) && - edge.isOverlappingWith(obj)) { - this.popupObj = edge; - break; - } - } - } - } - if (this.popupObj) { - // show popup message window - if (this.popupObj != lastPopupNode) { - var me = this; - if (!me.popup) { - me.popup = new Popup(me.frame, me.constants.tooltip); - } - // adjust a small offset such that the mouse cursor is located in the - // bottom left location of the popup, and you can easily move over the - // popup area - me.popup.setPosition(pointer.x - 3, pointer.y - 3); - me.popup.setText(me.popupObj.getTitle()); - me.popup.show(); - } - } - else { - if (this.popup) { - this.popup.hide(); - } - } -}; + /** + * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + */ + CanvasRenderingContext2D.prototype.database = function(x, y, w, h) { + var f = 1/3; + var wEllipse = w; + var hEllipse = h * f; + var kappa = .5522848, + ox = (wEllipse / 2) * kappa, // control point offset horizontal + oy = (hEllipse / 2) * kappa, // control point offset vertical + xe = x + wEllipse, // x-end + ye = y + hEllipse, // y-end + xm = x + wEllipse / 2, // x-middle + ym = y + hEllipse / 2, // y-middle + ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse + yeb = y + h; // y-end, bottom ellipse -/** - * Check if the popup must be hided, which is the case when the mouse is no - * longer hovering on the object - * @param {{x:Number, y:Number}} pointer - * @private - */ -Network.prototype._checkHidePopup = function (pointer) { - if (!this.popupObj || !this._getNodeAt(pointer) ) { - this.popupObj = undefined; - if (this.popup) { - this.popup.hide(); - } - } -}; + this.beginPath(); + this.moveTo(xe, ym); + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); -/** - * Set a new size for the network - * @param {string} width Width in pixels or percentage (for example '800px' - * or '50%') - * @param {string} height Height in pixels or percentage (for example '400px' - * or '30%') - */ -Network.prototype.setSize = function(width, height) { - this.frame.style.width = width; - this.frame.style.height = height; + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - this.frame.canvas.style.width = '100%'; - this.frame.canvas.style.height = '100%'; + this.lineTo(xe, ymb); - this.frame.canvas.width = this.frame.canvas.clientWidth; - this.frame.canvas.height = this.frame.canvas.clientHeight; + this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); + this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); - if (this.manipulationDiv !== undefined) { - this.manipulationDiv.style.width = this.frame.canvas.clientWidth + "px"; - } - if (this.navigationDivs !== undefined) { - if (this.navigationDivs['wrapper'] !== undefined) { - this.navigationDivs['wrapper'].style.width = this.frame.canvas.clientWidth + "px"; - this.navigationDivs['wrapper'].style.height = this.frame.canvas.clientHeight + "px"; - } - } + this.lineTo(x, ym); + }; - this.emit('resize', {width:this.frame.canvas.width,height:this.frame.canvas.height}); -}; -/** - * Set a data set with nodes for the network - * @param {Array | DataSet | DataView} nodes The data containing the nodes. - * @private - */ -Network.prototype._setNodes = function(nodes) { - var oldNodesData = this.nodesData; + /** + * Draw an arrow point (no line) + */ + CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) { + // tail + var xt = x - length * Math.cos(angle); + var yt = y - length * Math.sin(angle); + + // inner tail + // TODO: allow to customize different shapes + var xi = x - length * 0.9 * Math.cos(angle); + var yi = y - length * 0.9 * Math.sin(angle); + + // left + var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); + var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); + + // right + var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); + var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); + + this.beginPath(); + this.moveTo(x, y); + this.lineTo(xl, yl); + this.lineTo(xi, yi); + this.lineTo(xr, yr); + this.closePath(); + }; - if (nodes instanceof DataSet || nodes instanceof DataView) { - this.nodesData = nodes; - } - else if (nodes instanceof Array) { - this.nodesData = new DataSet(); - this.nodesData.add(nodes); - } - else if (!nodes) { - this.nodesData = new DataSet(); - } - else { - throw new TypeError('Array or DataSet expected'); - } + /** + * Sets up the dashedLine functionality for drawing + * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas + * @author David Jordan + * @date 2012-08-08 + */ + CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ + if (!dashArray) dashArray=[10,5]; + if (dashLength==0) dashLength = 0.001; // Hack for Safari + var dashCount = dashArray.length; + this.moveTo(x, y); + var dx = (x2-x), dy = (y2-y); + var slope = dy/dx; + var distRemaining = Math.sqrt( dx*dx + dy*dy ); + var dashIndex=0, draw=true; + while (distRemaining>=0.1){ + var dashLength = dashArray[dashIndex++%dashCount]; + if (dashLength > distRemaining) dashLength = distRemaining; + var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); + if (dx<0) xStep = -xStep; + x += xStep; + y += slope*xStep; + this[draw ? 'lineTo' : 'moveTo'](x,y); + distRemaining -= dashLength; + draw = !draw; + } + }; - if (oldNodesData) { - // unsubscribe from old dataset - util.forEach(this.nodesListeners, function (callback, event) { - oldNodesData.off(event, callback); - }); + // TODO: add diamond shape } - // remove drawn nodes - this.nodes = {}; - - if (this.nodesData) { - // subscribe to new dataset - var me = this; - util.forEach(this.nodesListeners, function (callback, event) { - me.nodesData.on(event, callback); - }); - // draw all new nodes - var ids = this.nodesData.getIds(); - this._addNodes(ids); - } - this._updateSelection(); -}; +/***/ }, +/* 38 */ +/***/ function(module, exports, __webpack_require__) { -/** - * Add nodes - * @param {Number[] | String[]} ids - * @private - */ -Network.prototype._addNodes = function(ids) { - var id; - for (var i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - var data = this.nodesData.get(id); - var node = new Node(data, this.images, this.groups, this.constants); - this.nodes[id] = node; // note: this may replace an existing node - - if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) { - var radius = 10 * 0.1*ids.length; - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - } - this.moving = true; - } - this._updateNodeIndexList(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); + // Only load hammer.js when in a browser environment + // (loading hammer.js in a node.js environment gives errors) + if (typeof window !== 'undefined') { + module.exports = __webpack_require__(49); } - this._updateCalculationNodes(); - this._reconnectEdges(); - this._updateValueRange(this.nodes); - this.updateLabels(); -}; - -/** - * Update existing nodes, or create them when not yet existing - * @param {Number[] | String[]} ids - * @private - */ -Network.prototype._updateNodes = function(ids) { - var nodes = this.nodes, - nodesData = this.nodesData; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var node = nodes[id]; - var data = nodesData.get(id); - if (node) { - // update node - node.setProperties(data, this.constants); - } - else { - // create node - node = new Node(properties, this.images, this.groups, this.constants); - nodes[id] = node; + else { + module.exports = function () { + throw Error('hammer.js is only available in a browser, not in node.js.'); } } - this.moving = true; - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateNodeIndexList(); - this._reconnectEdges(); - this._updateValueRange(nodes); -}; -/** - * Remove existing nodes. If nodes do not exist, the method will just ignore it. - * @param {Number[] | String[]} ids - * @private - */ -Network.prototype._removeNodes = function(ids) { - var nodes = this.nodes; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - delete nodes[id]; - } - this._updateNodeIndexList(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); - this._reconnectEdges(); - this._updateSelection(); - this._updateValueRange(nodes); -}; -/** - * Load edges by reading the data table - * @param {Array | DataSet | DataView} edges The data containing the edges. - * @private - * @private - */ -Network.prototype._setEdges = function(edges) { - var oldEdgesData = this.edgesData; +/***/ }, +/* 39 */ +/***/ function(module, exports, __webpack_require__) { - if (edges instanceof DataSet || edges instanceof DataView) { - this.edgesData = edges; - } - else if (edges instanceof Array) { - this.edgesData = new DataSet(); - this.edgesData.add(edges); - } - else if (!edges) { - this.edgesData = new DataSet(); - } - else { - throw new TypeError('Array or DataSet expected'); - } + // first check if moment.js is already loaded in the browser window, if so, + // use this instance. Else, load via commonjs. + module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(51); - if (oldEdgesData) { - // unsubscribe from old dataset - util.forEach(this.edgesListeners, function (callback, event) { - oldEdgesData.off(event, callback); - }); - } - // remove drawn edges - this.edges = {}; +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { - if (this.edgesData) { - // subscribe to new dataset - var me = this; - util.forEach(this.edgesListeners, function (callback, event) { - me.edgesData.on(event, callback); - }); + var PhysicsMixin = __webpack_require__(50); + var ClusterMixin = __webpack_require__(43); + var SectorsMixin = __webpack_require__(44); + var SelectionMixin = __webpack_require__(45); + var ManipulationMixin = __webpack_require__(46); + var NavigationMixin = __webpack_require__(47); + var HierarchicalLayoutMixin = __webpack_require__(48); - // draw all new nodes - var ids = this.edgesData.getIds(); - this._addEdges(ids); - } + /** + * Load a mixin into the network object + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private + */ + exports._loadMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = sourceVariable[mixinFunction]; + } + } + }; - this._reconnectEdges(); -}; -/** - * Add edges - * @param {Number[] | String[]} ids - * @private - */ -Network.prototype._addEdges = function (ids) { - var edges = this.edges, - edgesData = this.edgesData; + /** + * removes a mixin from the network object. + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private + */ + exports._clearMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = undefined; + } + } + }; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var oldEdge = edges[id]; - if (oldEdge) { - oldEdge.disconnect(); + /** + * Mixin the physics system and initialize the parameters required. + * + * @private + */ + exports._loadPhysicsSystem = function () { + this._loadMixin(PhysicsMixin); + this._loadSelectedForceSolver(); + if (this.constants.configurePhysics == true) { + this._loadPhysicsConfiguration(); } + }; - var data = edgesData.get(id, {"showInternalIds" : true}); - edges[id] = new Edge(data, this, this.constants); - } - this.moving = true; - this._updateValueRange(edges); - this._createBezierNodes(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); -}; + /** + * Mixin the cluster system and initialize the parameters required. + * + * @private + */ + exports._loadClusterSystem = function () { + this.clusterSession = 0; + this.hubThreshold = 5; + this._loadMixin(ClusterMixin); + }; -/** - * Update existing edges, or create them when not yet existing - * @param {Number[] | String[]} ids - * @private - */ -Network.prototype._updateEdges = function (ids) { - var edges = this.edges, - edgesData = this.edgesData; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - - var data = edgesData.get(id); - var edge = edges[id]; - if (edge) { - // update edge - edge.disconnect(); - edge.setProperties(data, this.constants); - edge.connect(); - } - else { - // create edge - edge = new Edge(data, this, this.constants); - this.edges[id] = edge; - } - } - this._createBezierNodes(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this.moving = true; - this._updateValueRange(edges); -}; + /** + * Mixin the sector system and initialize the parameters required + * + * @private + */ + exports._loadSectorSystem = function () { + this.sectors = {}; + this.activeSector = ["default"]; + this.sectors["active"] = {}; + this.sectors["active"]["default"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; + this.sectors["frozen"] = {}; + this.sectors["support"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; -/** - * Remove existing edges. Non existing ids will be ignored - * @param {Number[] | String[]} ids - * @private - */ -Network.prototype._removeEdges = function (ids) { - var edges = this.edges; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var edge = edges[id]; - if (edge) { - if (edge.via != null) { - delete this.sectors['support']['nodes'][edge.via.id]; - } - edge.disconnect(); - delete edges[id]; - } - } + this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields - this.moving = true; - this._updateValueRange(edges); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); -}; + this._loadMixin(SectorsMixin); + }; -/** - * Reconnect all edges - * @private - */ -Network.prototype._reconnectEdges = function() { - var id, - nodes = this.nodes, - edges = this.edges; - for (id in nodes) { - if (nodes.hasOwnProperty(id)) { - nodes[id].edges = []; - } - } - for (id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - edge.from = null; - edge.to = null; - edge.connect(); - } - } -}; + /** + * Mixin the selection system and initialize the parameters required + * + * @private + */ + exports._loadSelectionSystem = function () { + this.selectionObj = {nodes: {}, edges: {}}; -/** - * Update the values of all object in the given array according to the current - * value range of the objects in the array. - * @param {Object} obj An object containing a set of Edges or Nodes - * The objects must have a method getValue() and - * setValueRange(min, max). - * @private - */ -Network.prototype._updateValueRange = function(obj) { - var id; + this._loadMixin(SelectionMixin); + }; + + + /** + * Mixin the navigationUI (User Interface) system and initialize the parameters required + * + * @private + */ + exports._loadManipulationSystem = function () { + // reset global variables -- these are used by the selection of nodes and edges. + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; - // determine the range of the objects - var valueMin = undefined; - var valueMax = undefined; - for (id in obj) { - if (obj.hasOwnProperty(id)) { - var value = obj[id].getValue(); - if (value !== undefined) { - valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin); - valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax); + if (this.constants.dataManipulation.enabled == true) { + // load the manipulator HTML elements. All styling done in css. + if (this.manipulationDiv === undefined) { + this.manipulationDiv = document.createElement('div'); + this.manipulationDiv.className = 'network-manipulationDiv'; + this.manipulationDiv.id = 'network-manipulationDiv'; + if (this.editMode == true) { + this.manipulationDiv.style.display = "block"; + } + else { + this.manipulationDiv.style.display = "none"; + } + this.containerElement.insertBefore(this.manipulationDiv, this.frame); } - } - } - // adjust the range of all objects - if (valueMin !== undefined && valueMax !== undefined) { - for (id in obj) { - if (obj.hasOwnProperty(id)) { - obj[id].setValueRange(valueMin, valueMax); + if (this.editModeDiv === undefined) { + this.editModeDiv = document.createElement('div'); + this.editModeDiv.className = 'network-manipulation-editMode'; + this.editModeDiv.id = 'network-manipulation-editMode'; + if (this.editMode == true) { + this.editModeDiv.style.display = "none"; + } + else { + this.editModeDiv.style.display = "block"; + } + this.containerElement.insertBefore(this.editModeDiv, this.frame); } - } - } -}; -/** - * Redraw the network with the current data - * chart will be resized too. - */ -Network.prototype.redraw = function() { - this.setSize(this.width, this.height); - this._redraw(); -}; + if (this.closeDiv === undefined) { + this.closeDiv = document.createElement('div'); + this.closeDiv.className = 'network-manipulation-closeDiv'; + this.closeDiv.id = 'network-manipulation-closeDiv'; + this.closeDiv.style.display = this.manipulationDiv.style.display; + this.containerElement.insertBefore(this.closeDiv, this.frame); + } -/** - * Redraw the network with the current data - * @private - */ -Network.prototype._redraw = function() { - var ctx = this.frame.canvas.getContext('2d'); - // clear the canvas - var w = this.frame.canvas.width; - var h = this.frame.canvas.height; - ctx.clearRect(0, 0, w, h); + // load the manipulation functions + this._loadMixin(ManipulationMixin); - // set scaling and translation - ctx.save(); - ctx.translate(this.translation.x, this.translation.y); - ctx.scale(this.scale, this.scale); + // create the manipulator toolbar + this._createManipulatorBar(); + } + else { + if (this.manipulationDiv !== undefined) { + // removes all the bindings and overloads + this._createManipulatorBar(); + // remove the manipulation divs + this.containerElement.removeChild(this.manipulationDiv); + this.containerElement.removeChild(this.editModeDiv); + this.containerElement.removeChild(this.closeDiv); - 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.manipulationDiv = undefined; + this.editModeDiv = undefined; + this.closeDiv = undefined; + // remove the mixin functions + this._clearMixin(ManipulationMixin); + } + } }; - 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"); - // restore original scaling and translation - ctx.restore(); -}; - -/** - * Set the translation of the network - * @param {Number} offsetX Horizontal offset - * @param {Number} offsetY Vertical offset - * @private - */ -Network.prototype._setTranslation = function(offsetX, offsetY) { - if (this.translation === undefined) { - this.translation = { - x: 0, - y: 0 - }; - } - - if (offsetX !== undefined) { - this.translation.x = offsetX; - } - if (offsetY !== undefined) { - this.translation.y = offsetY; - } - - this.emit('viewChanged'); -}; + /** + * Mixin the navigation (User Interface) system and initialize the parameters required + * + * @private + */ + exports._loadNavigationControls = function () { + this._loadMixin(NavigationMixin); -/** - * Get the translation of the network - * @return {Object} translation An object with parameters x and y, both a number - * @private - */ -Network.prototype._getTranslation = function() { - return { - x: this.translation.x, - y: this.translation.y + // the clean function removes the button divs, this is done to remove the bindings. + this._cleanNavigation(); + if (this.constants.navigation.enabled == true) { + this._loadNavigationElements(); + } }; -}; -/** - * Scale the network - * @param {Number} scale Scaling factor 1.0 is unscaled - * @private - */ -Network.prototype._setScale = function(scale) { - this.scale = scale; -}; -/** - * Get the current scale of the network - * @return {Number} scale Scaling factor 1.0 is unscaled - * @private - */ -Network.prototype._getScale = function() { - return this.scale; -}; + /** + * Mixin the hierarchical layout system. + * + * @private + */ + exports._loadHierarchySystem = function () { + this._loadMixin(HierarchicalLayoutMixin); + }; -/** - * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to - * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) - * @param {number} x - * @returns {number} - * @private - */ -Network.prototype._XconvertDOMtoCanvas = function(x) { - return (x - this.translation.x) / this.scale; -}; -/** - * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to - * the X coordinate in DOM-space (coordinate point in browser relative to the container div) - * @param {number} x - * @returns {number} - * @private - */ -Network.prototype._XconvertCanvasToDOM = function(x) { - return x * this.scale + this.translation.x; -}; +/***/ }, +/* 41 */ +/***/ function(module, exports, __webpack_require__) { -/** - * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to - * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) - * @param {number} y - * @returns {number} - * @private - */ -Network.prototype._YconvertDOMtoCanvas = function(y) { - return (y - this.translation.y) / this.scale; -}; + + /** + * Expose `Emitter`. + */ -/** - * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to - * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) - * @param {number} y - * @returns {number} - * @private - */ -Network.prototype._YconvertCanvasToDOM = function(y) { - return y * this.scale + this.translation.y ; -}; + module.exports = Emitter; + /** + * Initialize a new `Emitter`. + * + * @api public + */ -/** - * - * @param {object} pos = {x: number, y: number} - * @returns {{x: number, y: number}} - * @constructor - */ -Network.prototype.canvasToDOM = function(pos) { - return {x:this._XconvertCanvasToDOM(pos.x),y:this._YconvertCanvasToDOM(pos.y)}; -} + function Emitter(obj) { + if (obj) return mixin(obj); + }; -/** - * - * @param {object} pos = {x: number, y: number} - * @returns {{x: number, y: number}} - * @constructor - */ -Network.prototype.DOMtoCanvas = function(pos) { - return {x:this._XconvertDOMtoCanvas(pos.x),y:this._YconvertDOMtoCanvas(pos.y)}; -} + /** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ -/** - * Redraw all nodes - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @param {Boolean} [alwaysShow] - * @private - */ -Network.prototype._drawNodes = function(ctx,alwaysShow) { - if (alwaysShow === undefined) { - alwaysShow = false; + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; } - // first draw the unselected nodes - var nodes = this.nodes; - var selected = []; + /** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); - if (nodes[id].isSelected()) { - selected.push(id); - } - else { - if (nodes[id].inArea() || alwaysShow) { - nodes[id].draw(ctx); - } - } - } - } + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; + }; - // draw the selected nodes on top - for (var s = 0, sMax = selected.length; s < sMax; s++) { - if (nodes[selected[s]].inArea() || alwaysShow) { - nodes[selected[s]].draw(ctx); - } - } -}; + /** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ -/** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @private - */ -Network.prototype._drawEdges = function(ctx) { - var edges = this.edges; - for (var id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - edge.setScale(this.scale); - if (edge.connected) { - edges[id].draw(ctx); - } - } - } -}; + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; -/** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @private - */ -Network.prototype._drawControlNodes = function(ctx) { - var edges = this.edges; - for (var id in edges) { - if (edges.hasOwnProperty(id)) { - edges[id]._drawControlNodes(ctx); + function on() { + self.off(event, on); + fn.apply(this, arguments); } - } -}; -/** - * Find a stable position for all nodes - * @private - */ -Network.prototype._stabilize = function() { - if (this.constants.freezeForStabilization == true) { - this._freezeDefinedNodes(); - } + on.fn = fn; + this.on(event, on); + return this; + }; - // find stable position - var count = 0; - while (this.moving && count < this.constants.stabilizationIterations) { - this._physicsTick(); - count++; - } - this.zoomExtent(false,true); - if (this.constants.freezeForStabilization == true) { - this._restoreFrozenNodes(); - } - this.emit("stabilized",{iterations:count}); -}; + /** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ -/** - * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization - * because only the supportnodes for the smoothCurves have to settle. - * - * @private - */ -Network.prototype._freezeDefinedNodes = function() { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - if (nodes[id].x != null && nodes[id].y != null) { - nodes[id].fixedData.x = nodes[id].xFixed; - nodes[id].fixedData.y = nodes[id].yFixed; - nodes[id].xFixed = true; - nodes[id].yFixed = true; - } - } - } -}; + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; -/** - * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. - * - * @private - */ -Network.prototype._restoreFrozenNodes = function() { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - if (nodes[id].fixedData.x != null) { - nodes[id].xFixed = nodes[id].fixedData.x; - nodes[id].yFixed = nodes[id].fixedData.y; - } + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; } - } -}; + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; -/** - * Check if any of the nodes is still moving - * @param {number} vmin the minimum velocity considered as 'moving' - * @return {boolean} true if moving, false if non of the nodes is moving - * @private - */ -Network.prototype._isMoving = function(vmin) { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) { - return true; + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; } - } - return false; -}; - -/** - * /** - * Perform one discrete step for all nodes - * - * @private - */ -Network.prototype._discreteStepNodes = function() { - var interval = this.physicsDiscreteStepsize; - var nodes = this.nodes; - var nodeId; - var nodesPresent = false; - - if (this.constants.maxVelocity > 0) { - for (nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity); - nodesPresent = true; - } - } - } - else { - for (nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].discreteStep(interval); - nodesPresent = true; + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; } } - } + return this; + }; - if (nodesPresent == true) { - var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05); - if (vminCorrected > 0.5*this.constants.maxVelocity) { - this.moving = true; - } - else { - this.moving = this._isMoving(vminCorrected); - } - } -}; + /** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ -/** - * A single simulation step (or "tick") in the physics simulation - * - * @private - */ -Network.prototype._physicsTick = function() { - if (!this.freezeSimulation) { - if (this.moving) { - this._doInAllActiveSectors("_initializeForceCalculation"); - this._doInAllActiveSectors("_discreteStepNodes"); - if (this.constants.smoothCurves) { - this._doInSupportSector("_discreteStepNodes"); + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks[event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); } - this._findCenter(this._getRange()) } - } -}; - - -/** - * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick. - * It reschedules itself at the beginning of the function - * - * @private - */ -Network.prototype._animationStep = function() { - // reset the timer so a new scheduled animation step can be set - this.timer = undefined; - // handle the keyboad movement - this._handleNavigation(); - - // this schedules a new animation step - this.start(); - - // start the physics simulation - var calculationTime = Date.now(); - var maxSteps = 1; - this._physicsTick(); - var timeRequired = Date.now() - calculationTime; - while (timeRequired < 0.9*(this.renderTimestep - this.renderTime) && maxSteps < this.maxPhysicsTicksPerRender) { - this._physicsTick(); - timeRequired = Date.now() - calculationTime; - maxSteps++; - } - // start the rendering process - var renderTime = Date.now(); - this._redraw(); - this.renderTime = Date.now() - renderTime; -}; -if (typeof window !== 'undefined') { - window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; -} + return this; + }; -/** - * Schedule a animation step with the refreshrate interval. - */ -Network.prototype.start = function() { - if (this.moving || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0) { - if (!this.timer) { - var ua = navigator.userAgent.toLowerCase(); + /** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ - var requiresTimeout = false; - if (ua.indexOf('msie 9.0') != -1) { // IE 9 - requiresTimeout = true; - } - else if (ua.indexOf('safari') != -1) { // safari - if (ua.indexOf('chrome') <= -1) { - requiresTimeout = true; - } - } + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; + }; - if (requiresTimeout == true) { - this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function - } - else{ - this.timer = window.requestAnimationFrame(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function - } - } - } - else { - this._redraw(); - } -}; + /** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; -/** - * Move the network according to the keyboard presses. - * - * @private - */ -Network.prototype._handleNavigation = function() { - if (this.xIncrement != 0 || this.yIncrement != 0) { - var translation = this._getTranslation(); - this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement); - } - if (this.zoomIncrement != 0) { - var center = { - x: this.frame.canvas.clientWidth / 2, - y: this.frame.canvas.clientHeight / 2 - }; - this._zoom(this.scale*(1 + this.zoomIncrement), center); - } -}; +/***/ }, +/* 42 */ +/***/ function(module, exports, __webpack_require__) { -/** - * Freeze the _animationStep - */ -Network.prototype.toggleFreeze = function() { - if (this.freezeSimulation == false) { - this.freezeSimulation = true; - } - else { - this.freezeSimulation = false; - this.start(); - } -}; + /** + * Copyright 2012 Craig Campbell + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Mousetrap is a simple keyboard shortcut library for Javascript with + * no external dependencies + * + * @version 1.1.2 + * @url craig.is/killing/mice + */ + /** + * mapping of special keycodes to their corresponding keys + * + * everything in this dictionary cannot use keypress events + * so it has to be here to map to the correct keycodes for + * keyup/keydown events + * + * @type {Object} + */ + var _MAP = { + 8: 'backspace', + 9: 'tab', + 13: 'enter', + 16: 'shift', + 17: 'ctrl', + 18: 'alt', + 20: 'capslock', + 27: 'esc', + 32: 'space', + 33: 'pageup', + 34: 'pagedown', + 35: 'end', + 36: 'home', + 37: 'left', + 38: 'up', + 39: 'right', + 40: 'down', + 45: 'ins', + 46: 'del', + 91: 'meta', + 93: 'meta', + 224: 'meta' + }, -/** - * This function cleans the support nodes if they are not needed and adds them when they are. - * - * @param {boolean} [disableStart] - * @private - */ -Network.prototype._configureSmoothCurves = function(disableStart) { - if (disableStart === undefined) { - disableStart = true; - } + /** + * mapping for special characters so they can support + * + * this dictionary is only used incase you want to bind a + * keyup or keydown event to one of these keys + * + * @type {Object} + */ + _KEYCODE_MAP = { + 106: '*', + 107: '+', + 109: '-', + 110: '.', + 111 : '/', + 186: ';', + 187: '=', + 188: ',', + 189: '-', + 190: '.', + 191: '/', + 192: '`', + 219: '[', + 220: '\\', + 221: ']', + 222: '\'' + }, - if (this.constants.smoothCurves == true) { - this._createBezierNodes(); - } - else { - // delete the support nodes - this.sectors['support']['nodes'] = {}; - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.edges[edgeId].smooth = false; - this.edges[edgeId].via = null; - } - } - } - this._updateCalculationNodes(); - if (!disableStart) { - this.moving = true; - this.start(); - } -}; + /** + * this is a mapping of keys that require shift on a US keypad + * back to the non shift equivelents + * + * this is so you can use keyup events with these keys + * + * note that this will only work reliably on US keyboards + * + * @type {Object} + */ + _SHIFT_MAP = { + '~': '`', + '!': '1', + '@': '2', + '#': '3', + '$': '4', + '%': '5', + '^': '6', + '&': '7', + '*': '8', + '(': '9', + ')': '0', + '_': '-', + '+': '=', + ':': ';', + '\"': '\'', + '<': ',', + '>': '.', + '?': '/', + '|': '\\' + }, + /** + * this is a list of special strings you can use to map + * to modifier keys when you specify your keyboard shortcuts + * + * @type {Object} + */ + _SPECIAL_ALIASES = { + 'option': 'alt', + 'command': 'meta', + 'return': 'enter', + 'escape': 'esc' + }, -/** - * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but - * are used for the force calculation. - * - * @private - */ -Network.prototype._createBezierNodes = function() { - if (this.constants.smoothCurves == true) { - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.via == null) { - edge.smooth = true; - var nodeId = "edgeId:".concat(edge.id); - this.sectors['support']['nodes'][nodeId] = new Node( - {id:nodeId, - mass:1, - shape:'circle', - image:"", - internalMultiplier:1 - },{},{},this.constants); - edge.via = this.sectors['support']['nodes'][nodeId]; - edge.via.parentEdgeId = edge.id; - edge.positionBezierNode(); - } - } - } - } -}; + /** + * variable to store the flipped version of _MAP from above + * needed to check if we should use keypress or not when no action + * is specified + * + * @type {Object|undefined} + */ + _REVERSE_MAP, + + /** + * a list of all the callbacks setup via Mousetrap.bind() + * + * @type {Object} + */ + _callbacks = {}, + + /** + * direct map of string combinations to callbacks used for trigger() + * + * @type {Object} + */ + _direct_map = {}, + + /** + * keeps track of what level each sequence is at since multiple + * sequences can start out with the same sequence + * + * @type {Object} + */ + _sequence_levels = {}, + + /** + * variable to store the setTimeout call + * + * @type {null|number} + */ + _reset_timer, + + /** + * temporary state where we will ignore the next keyup + * + * @type {boolean|string} + */ + _ignore_next_keyup = false, + + /** + * are we currently inside of a sequence? + * type of action ("keyup" or "keydown" or "keypress") or false + * + * @type {boolean|string} + */ + _inside_sequence = false; -/** - * load the functions that load the mixins into the prototype. - * - * @private - */ -Network.prototype._initializeMixinLoaders = function () { - for (var mixinFunction in networkMixinLoaders) { - if (networkMixinLoaders.hasOwnProperty(mixinFunction)) { - Network.prototype[mixinFunction] = networkMixinLoaders[mixinFunction]; + /** + * loop through the f keys, f1 to f19 and add them to the map + * programatically + */ + for (var i = 1; i < 20; ++i) { + _MAP[111 + i] = 'f' + i; } - } -}; -/** - * Load the XY positions of the nodes into the dataset. - */ -Network.prototype.storePosition = function() { - var dataArray = []; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - var allowedToMoveX = !this.nodes.xFixed; - var allowedToMoveY = !this.nodes.yFixed; - if (this.nodesData._data[nodeId].x != Math.round(node.x) || this.nodesData._data[nodeId].y != Math.round(node.y)) { - dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY}); - } + /** + * loop through to map numbers on the numeric keypad + */ + for (i = 0; i <= 9; ++i) { + _MAP[i + 96] = i; } - } - this.nodesData.update(dataArray); -}; + /** + * cross browser add event method + * + * @param {Element|HTMLDocument} object + * @param {string} type + * @param {Function} callback + * @returns void + */ + function _addEvent(object, type, callback) { + if (object.addEventListener) { + return object.addEventListener(type, callback, false); + } -/** - * Center a node in view. - * - * @param {Number} nodeId - * @param {Number} [zoomLevel] - */ -Network.prototype.focusOnNode = function (nodeId, zoomLevel) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (zoomLevel === undefined) { - zoomLevel = this._getScale(); + object.attachEvent('on' + type, callback); } - var nodePosition= {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y}; - var requiredScale = zoomLevel; - this._setScale(requiredScale); - - var canvasCenter = this.DOMtoCanvas({x:0.5 * this.frame.canvas.width,y:0.5 * this.frame.canvas.height}); - var translation = this._getTranslation(); + /** + * takes the event and returns the key character + * + * @param {Event} e + * @return {string} + */ + function _characterFromEvent(e) { - var distanceFromCenter = {x:canvasCenter.x - nodePosition.x, - y:canvasCenter.y - nodePosition.y}; + // for keypress events we should return the character as is + if (e.type == 'keypress') { + return String.fromCharCode(e.which); + } - this._setTranslation(translation.x + requiredScale * distanceFromCenter.x, - translation.y + requiredScale * distanceFromCenter.y); - this.redraw(); - } - else { - console.log("This nodeId cannot be found.") - } -}; + // for non keypress events the special maps are needed + if (_MAP[e.which]) { + return _MAP[e.which]; + } + if (_KEYCODE_MAP[e.which]) { + return _KEYCODE_MAP[e.which]; + } + // if it is not in the special map + return String.fromCharCode(e.which).toLowerCase(); + } + /** + * should we stop this event before firing off callbacks + * + * @param {Event} e + * @return {boolean} + */ + function _stop(e) { + var element = e.target || e.srcElement, + tag_name = element.tagName; + // if the element has the class "mousetrap" then no need to stop + if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) { + return false; + } + // stop for input, select, and textarea + return tag_name == 'INPUT' || tag_name == 'SELECT' || tag_name == 'TEXTAREA' || (element.contentEditable && element.contentEditable == 'true'); + } + /** + * checks if two arrays are equal + * + * @param {Array} modifiers1 + * @param {Array} modifiers2 + * @returns {boolean} + */ + function _modifiersMatch(modifiers1, modifiers2) { + return modifiers1.sort().join(',') === modifiers2.sort().join(','); + } + /** + * resets all sequence counters except for the ones passed in + * + * @param {Object} do_not_reset + * @returns void + */ + function _resetSequences(do_not_reset) { + do_not_reset = do_not_reset || {}; + var active_sequences = false, + key; + for (key in _sequence_levels) { + if (do_not_reset[key]) { + active_sequences = true; + continue; + } + _sequence_levels[key] = 0; + } -/** - * @constructor Graph3d - * Graph3d displays data in 3d. - * - * Graph3d is developed in javascript as a Google Visualization Chart. - * - * @param {Element} container The DOM element in which the Graph3d will - * be created. Normally a div element. - * @param {DataSet | DataView | Array} [data] - * @param {Object} [options] - */ -function Graph3d(container, data, options) { - if (!(this instanceof Graph3d)) { - throw new SyntaxError('Constructor must be called with the new operator'); - } + if (!active_sequences) { + _inside_sequence = false; + } + } - // create variables and set default values - this.containerElement = container; - this.width = '400px'; - this.height = '400px'; - this.margin = 10; // px - this.defaultXCenter = '55%'; - this.defaultYCenter = '50%'; - - this.xLabel = 'x'; - this.yLabel = 'y'; - this.zLabel = 'z'; - this.filterLabel = 'time'; - this.legendLabel = 'value'; - - this.style = Graph3d.STYLE.DOT; - this.showPerspective = true; - this.showGrid = true; - this.keepAspectRatio = true; - this.showShadow = false; - this.showGrayBottom = false; // TODO: this does not work correctly - this.showTooltip = false; - this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube' - - this.animationInterval = 1000; // milliseconds - this.animationPreload = false; - - this.camera = new Graph3d.Camera(); - this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? - - this.dataTable = null; // The original data table - this.dataPoints = null; // The table with point objects - - // the column indexes - this.colX = undefined; - this.colY = undefined; - this.colZ = undefined; - this.colValue = undefined; - this.colFilter = undefined; - - this.xMin = 0; - this.xStep = undefined; // auto by default - this.xMax = 1; - this.yMin = 0; - this.yStep = undefined; // auto by default - this.yMax = 1; - this.zMin = 0; - this.zStep = undefined; // auto by default - this.zMax = 1; - this.valueMin = 0; - this.valueMax = 1; - this.xBarWidth = 1; - this.yBarWidth = 1; - // TODO: customize axis range - - // constants - this.colorAxis = '#4D4D4D'; - this.colorGrid = '#D3D3D3'; - this.colorDot = '#7DC1FF'; - this.colorDotBorder = '#3267D2'; - - // create a frame and canvas - this.create(); - - // apply options (also when undefined) - this.setOptions(options); - - // apply data - if (data) { - this.setData(data); - } -} + /** + * finds all callbacks that match based on the keycode, modifiers, + * and action + * + * @param {string} character + * @param {Array} modifiers + * @param {string} action + * @param {boolean=} remove - should we remove any matches + * @param {string=} combination + * @returns {Array} + */ + function _getMatches(character, modifiers, action, remove, combination) { + var i, + callback, + matches = []; -// Extend Graph3d with an Emitter mixin -Emitter(Graph3d.prototype); + // if there are no events related to this keycode + if (!_callbacks[character]) { + return []; + } -/** - * @class Camera - * The camera is mounted on a (virtual) camera arm. The camera arm can rotate - * The camera is always looking in the direction of the origin of the arm. - * This way, the camera always rotates around one fixed point, the location - * of the camera arm. - * - * Documentation: - * http://en.wikipedia.org/wiki/3D_projection - */ -Graph3d.Camera = function () { - this.armLocation = new Point3d(); - this.armRotation = {}; - this.armRotation.horizontal = 0; - this.armRotation.vertical = 0; - this.armLength = 1.7; + // if a modifier key is coming up on its own we should allow it + if (action == 'keyup' && _isModifier(character)) { + modifiers = [character]; + } - this.cameraLocation = new Point3d(); - this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); + // loop through all callbacks for the key that was pressed + // and see if any of them match + for (i = 0; i < _callbacks[character].length; ++i) { + callback = _callbacks[character][i]; - this.calculateCameraOrientation(); -}; + // if this is a sequence but it is not at the right level + // then move onto the next match + if (callback.seq && _sequence_levels[callback.seq] != callback.level) { + continue; + } -/** - * Set the location (origin) of the arm - * @param {Number} x Normalized value of x - * @param {Number} y Normalized value of y - * @param {Number} z Normalized value of z - */ -Graph3d.Camera.prototype.setArmLocation = function(x, y, z) { - this.armLocation.x = x; - this.armLocation.y = y; - this.armLocation.z = z; + // if the action we are looking for doesn't match the action we got + // then we should keep going + if (action != callback.action) { + continue; + } - this.calculateCameraOrientation(); -}; + // if this is a keypress event that means that we need to only + // look at the character, otherwise check the modifiers as + // well + if (action == 'keypress' || _modifiersMatch(modifiers, callback.modifiers)) { -/** - * Set the rotation of the camera arm - * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. - * Optional, can be left undefined. - * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI - * if vertical=0.5*PI, the graph is shown from the - * top. Optional, can be left undefined. - */ -Graph3d.Camera.prototype.setArmRotation = function(horizontal, vertical) { - if (horizontal !== undefined) { - this.armRotation.horizontal = horizontal; - } + // remove is used so if you change your mind and call bind a + // second time with a new function the first one is overwritten + if (remove && callback.combo == combination) { + _callbacks[character].splice(i, 1); + } - if (vertical !== undefined) { - this.armRotation.vertical = vertical; - if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; - if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; - } + matches.push(callback); + } + } - if (horizontal !== undefined || vertical !== undefined) { - this.calculateCameraOrientation(); - } -}; + return matches; + } -/** - * Retrieve the current arm rotation - * @return {object} An object with parameters horizontal and vertical - */ -Graph3d.Camera.prototype.getArmRotation = function() { - var rot = {}; - rot.horizontal = this.armRotation.horizontal; - rot.vertical = this.armRotation.vertical; + /** + * takes a key event and figures out what the modifiers are + * + * @param {Event} e + * @returns {Array} + */ + function _eventModifiers(e) { + var modifiers = []; - return rot; -}; + if (e.shiftKey) { + modifiers.push('shift'); + } -/** - * Set the (normalized) length of the camera arm. - * @param {Number} length A length between 0.71 and 5.0 - */ -Graph3d.Camera.prototype.setArmLength = function(length) { - if (length === undefined) - return; + if (e.altKey) { + modifiers.push('alt'); + } - this.armLength = length; + if (e.ctrlKey) { + modifiers.push('ctrl'); + } - // Radius must be larger than the corner of the graph, - // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the - // graph - if (this.armLength < 0.71) this.armLength = 0.71; - if (this.armLength > 5.0) this.armLength = 5.0; + if (e.metaKey) { + modifiers.push('meta'); + } - this.calculateCameraOrientation(); -}; + return modifiers; + } -/** - * Retrieve the arm length - * @return {Number} length - */ -Graph3d.Camera.prototype.getArmLength = function() { - return this.armLength; -}; + /** + * actually calls the callback function + * + * if your callback function returns false this will use the jquery + * convention - prevent default and stop propogation on the event + * + * @param {Function} callback + * @param {Event} e + * @returns void + */ + function _fireCallback(callback, e) { + if (callback(e) === false) { + if (e.preventDefault) { + e.preventDefault(); + } -/** - * Retrieve the camera location - * @return {Point3d} cameraLocation - */ -Graph3d.Camera.prototype.getCameraLocation = function() { - return this.cameraLocation; -}; + if (e.stopPropagation) { + e.stopPropagation(); + } -/** - * Retrieve the camera rotation - * @return {Point3d} cameraRotation - */ -Graph3d.Camera.prototype.getCameraRotation = function() { - return this.cameraRotation; -}; + e.returnValue = false; + e.cancelBubble = true; + } + } -/** - * Calculate the location and rotation of the camera based on the - * position and orientation of the camera arm - */ -Graph3d.Camera.prototype.calculateCameraOrientation = function() { - // calculate location of the camera - this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); - this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); - this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); - - // calculate rotation of the camera - this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; - this.cameraRotation.y = 0; - this.cameraRotation.z = -this.armRotation.horizontal; -}; + /** + * handles a character key event + * + * @param {string} character + * @param {Event} e + * @returns void + */ + function _handleCharacter(character, e) { -/** - * Calculate the scaling values, dependent on the range in x, y, and z direction - */ -Graph3d.prototype._setScale = function() { - this.scale = new Point3d(1 / (this.xMax - this.xMin), - 1 / (this.yMax - this.yMin), - 1 / (this.zMax - this.zMin)); + // if this event should not happen stop here + if (_stop(e)) { + return; + } - // keep aspect ration between x and y scale if desired - if (this.keepAspectRatio) { - if (this.scale.x < this.scale.y) { - //noinspection JSSuspiciousNameCombination - this.scale.y = this.scale.x; - } - else { - //noinspection JSSuspiciousNameCombination - this.scale.x = this.scale.y; + var callbacks = _getMatches(character, _eventModifiers(e), e.type), + i, + do_not_reset = {}, + processed_sequence_callback = false; + + // loop through matching callbacks for this key event + for (i = 0; i < callbacks.length; ++i) { + + // fire for all sequence callbacks + // this is because if for example you have multiple sequences + // bound such as "g i" and "g t" they both need to fire the + // callback for matching g cause otherwise you can only ever + // match the first one + if (callbacks[i].seq) { + processed_sequence_callback = true; + + // keep a list of which sequences were matches for later + do_not_reset[callbacks[i].seq] = 1; + _fireCallback(callbacks[i].callback, e); + continue; + } + + // if there were no sequence matches but we are still here + // that means this is a regular match so we should fire that + if (!processed_sequence_callback && !_inside_sequence) { + _fireCallback(callbacks[i].callback, e); + } + } + + // if you are inside of a sequence and the key you are pressing + // is not a modifier key then we should reset all sequences + // that were not matched by this key event + if (e.type == _inside_sequence && !_isModifier(character)) { + _resetSequences(do_not_reset); + } } - } - // scale the vertical axis - this.scale.z *= this.verticalRatio; - // TODO: can this be automated? verticalRatio? + /** + * handles a keydown event + * + * @param {Event} e + * @returns void + */ + function _handleKey(e) { - // determine scale for (optional) value - this.scale.value = 1 / (this.valueMax - this.valueMin); + // normalize e.which for key events + // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion + e.which = typeof e.which == "number" ? e.which : e.keyCode; - // position the camera arm - var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x; - var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y; - var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z; - this.camera.setArmLocation(xCenter, yCenter, zCenter); -}; + var character = _characterFromEvent(e); + // no character found then stop + if (!character) { + return; + } -/** - * Convert a 3D location to a 2D location on screen - * http://en.wikipedia.org/wiki/3D_projection - * @param {Point3d} point3d A 3D point with parameters x, y, z - * @return {Point2d} point2d A 2D point with parameters x, y - */ -Graph3d.prototype._convert3Dto2D = function(point3d) { - var translation = this._convertPointToTranslation(point3d); - return this._convertTranslationToScreen(translation); -}; + if (e.type == 'keyup' && _ignore_next_keyup == character) { + _ignore_next_keyup = false; + return; + } -/** - * Convert a 3D location its translation seen from the camera - * http://en.wikipedia.org/wiki/3D_projection - * @param {Point3d} point3d A 3D point with parameters x, y, z - * @return {Point3d} translation A 3D point with parameters x, y, z This is - * the translation of the point, seen from the - * camera - */ -Graph3d.prototype._convertPointToTranslation = function(point3d) { - var ax = point3d.x * this.scale.x, - ay = point3d.y * this.scale.y, - az = point3d.z * this.scale.z, - - cx = this.camera.getCameraLocation().x, - cy = this.camera.getCameraLocation().y, - cz = this.camera.getCameraLocation().z, - - // calculate angles - sinTx = Math.sin(this.camera.getCameraRotation().x), - cosTx = Math.cos(this.camera.getCameraRotation().x), - sinTy = Math.sin(this.camera.getCameraRotation().y), - cosTy = Math.cos(this.camera.getCameraRotation().y), - sinTz = Math.sin(this.camera.getCameraRotation().z), - cosTz = Math.cos(this.camera.getCameraRotation().z), - - // calculate translation - dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), - dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)), - dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx)); - - return new Point3d(dx, dy, dz); -}; + _handleCharacter(character, e); + } -/** - * Convert a translation point to a point on the screen - * @param {Point3d} translation A 3D point with parameters x, y, z This is - * the translation of the point, seen from the - * camera - * @return {Point2d} point2d A 2D point with parameters x, y - */ -Graph3d.prototype._convertTranslationToScreen = function(translation) { - var ex = this.eye.x, - ey = this.eye.y, - ez = this.eye.z, - dx = translation.x, - dy = translation.y, - dz = translation.z; - - // calculate position on screen from translation - var bx; - var by; - if (this.showPerspective) { - bx = (dx - ex) * (ez / dz); - by = (dy - ey) * (ez / dz); - } - else { - bx = dx * -(ez / this.camera.getArmLength()); - by = dy * -(ez / this.camera.getArmLength()); - } + /** + * determines if the keycode specified is a modifier key or not + * + * @param {string} key + * @returns {boolean} + */ + function _isModifier(key) { + return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta'; + } - // shift and scale the point to the center of the screen - // use the width of the graph to scale both horizontally and vertically. - return new Point2d( - this.xcenter + bx * this.frame.canvas.clientWidth, - this.ycenter - by * this.frame.canvas.clientWidth); -}; + /** + * called to set a 1 second timeout on the specified sequence + * + * this is so after each key press in the sequence you have 1 second + * to press the next key before you have to start over + * + * @returns void + */ + function _resetSequenceTimer() { + clearTimeout(_reset_timer); + _reset_timer = setTimeout(_resetSequences, 1000); + } -/** - * Set the background styling for the graph - * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor - */ -Graph3d.prototype._setBackgroundColor = function(backgroundColor) { - var fill = 'white'; - var stroke = 'gray'; - var strokeWidth = 1; - - if (typeof(backgroundColor) === 'string') { - fill = backgroundColor; - stroke = 'none'; - strokeWidth = 0; - } - else if (typeof(backgroundColor) === 'object') { - if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; - if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; - if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; - } - else if (backgroundColor === undefined) { - // use use defaults - } - else { - throw 'Unsupported type of backgroundColor'; - } + /** + * reverses the map lookup so that we can look for specific keys + * to see what can and can't use keypress + * + * @return {Object} + */ + function _getReverseMap() { + if (!_REVERSE_MAP) { + _REVERSE_MAP = {}; + for (var key in _MAP) { + + // pull out the numeric keypad from here cause keypress should + // be able to detect the keys from the character + if (key > 95 && key < 112) { + continue; + } - this.frame.style.backgroundColor = fill; - this.frame.style.borderColor = stroke; - this.frame.style.borderWidth = strokeWidth + 'px'; - this.frame.style.borderStyle = 'solid'; -}; - - -/// enumerate the available styles -Graph3d.STYLE = { - BAR: 0, - BARCOLOR: 1, - BARSIZE: 2, - DOT : 3, - DOTLINE : 4, - DOTCOLOR: 5, - DOTSIZE: 6, - GRID : 7, - LINE: 8, - SURFACE : 9 -}; + if (_MAP.hasOwnProperty(key)) { + _REVERSE_MAP[_MAP[key]] = key; + } + } + } + return _REVERSE_MAP; + } -/** - * Retrieve the style index from given styleName - * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' - * @return {Number} styleNumber Enumeration value representing the style, or -1 - * when not found - */ -Graph3d.prototype._getStyleNumber = function(styleName) { - switch (styleName) { - case 'dot': return Graph3d.STYLE.DOT; - case 'dot-line': return Graph3d.STYLE.DOTLINE; - case 'dot-color': return Graph3d.STYLE.DOTCOLOR; - case 'dot-size': return Graph3d.STYLE.DOTSIZE; - case 'line': return Graph3d.STYLE.LINE; - case 'grid': return Graph3d.STYLE.GRID; - case 'surface': return Graph3d.STYLE.SURFACE; - case 'bar': return Graph3d.STYLE.BAR; - case 'bar-color': return Graph3d.STYLE.BARCOLOR; - case 'bar-size': return Graph3d.STYLE.BARSIZE; - } + /** + * picks the best action based on the key combination + * + * @param {string} key - character for key + * @param {Array} modifiers + * @param {string=} action passed in + */ + function _pickBestAction(key, modifiers, action) { - return -1; -}; + // if no action was picked in we should try to pick the one + // that we think would work best for this key + if (!action) { + action = _getReverseMap()[key] ? 'keydown' : 'keypress'; + } -/** - * Determine the indexes of the data columns, based on the given style and data - * @param {DataSet} data - * @param {Number} style - */ -Graph3d.prototype._determineColumnIndexes = function(data, style) { - if (this.style === Graph3d.STYLE.DOT || - this.style === Graph3d.STYLE.DOTLINE || - this.style === Graph3d.STYLE.LINE || - this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE || - this.style === Graph3d.STYLE.BAR) { - // 3 columns expected, and optionally a 4th with filter values - this.colX = 0; - this.colY = 1; - this.colZ = 2; - this.colValue = undefined; + // modifier keys don't work as expected with keypress, + // switch to keydown + if (action == 'keypress' && modifiers.length) { + action = 'keydown'; + } - if (data.getNumberOfColumns() > 3) { - this.colFilter = 3; + return action; } - } - else if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - // 4 columns expected, and optionally a 5th with filter values - this.colX = 0; - this.colY = 1; - this.colZ = 2; - this.colValue = 3; - - if (data.getNumberOfColumns() > 4) { - this.colFilter = 4; - } - } - else { - throw 'Unknown style "' + this.style + '"'; - } -}; - -Graph3d.prototype.getNumberOfRows = function(data) { - return data.length; -} + /** + * binds a key sequence to an event + * + * @param {string} combo - combo specified in bind call + * @param {Array} keys + * @param {Function} callback + * @param {string=} action + * @returns void + */ + function _bindSequence(combo, keys, callback, action) { + + // start off by adding a sequence level record for this combination + // and setting the level to 0 + _sequence_levels[combo] = 0; + + // if there is no action pick the best one for the first key + // in the sequence + if (!action) { + action = _pickBestAction(keys[0], []); + } + + /** + * callback to increase the sequence level for this sequence and reset + * all other sequences that were active + * + * @param {Event} e + * @returns void + */ + var _increaseSequence = function(e) { + _inside_sequence = action; + ++_sequence_levels[combo]; + _resetSequenceTimer(); + }, -Graph3d.prototype.getNumberOfColumns = function(data) { - var counter = 0; - for (var column in data[0]) { - if (data[0].hasOwnProperty(column)) { - counter++; - } - } - return counter; -} + /** + * wraps the specified callback inside of another function in order + * to reset all sequence counters as soon as this sequence is done + * + * @param {Event} e + * @returns void + */ + _callbackAndReset = function(e) { + _fireCallback(callback, e); + + // we should ignore the next key up if the action is key down + // or keypress. this is so if you finish a sequence and + // release the key the final key will not trigger a keyup + if (action !== 'keyup') { + _ignore_next_keyup = _characterFromEvent(e); + } + // weird race condition if a sequence ends with the key + // another sequence begins with + setTimeout(_resetSequences, 10); + }, + i; -Graph3d.prototype.getDistinctValues = function(data, column) { - var distinctValues = []; - for (var i = 0; i < data.length; i++) { - if (distinctValues.indexOf(data[i][column]) == -1) { - distinctValues.push(data[i][column]); + // loop through keys one at a time and bind the appropriate callback + // function. for any key leading up to the final one it should + // increase the sequence. after the final, it should reset all sequences + for (i = 0; i < keys.length; ++i) { + _bindSingle(keys[i], i < keys.length - 1 ? _increaseSequence : _callbackAndReset, action, combo, i); + } } - } - return distinctValues; -} + /** + * binds a single keyboard combination + * + * @param {string} combination + * @param {Function} callback + * @param {string=} action + * @param {string=} sequence_name - name of sequence if part of sequence + * @param {number=} level - what part of the sequence the command is + * @returns void + */ + function _bindSingle(combination, callback, action, sequence_name, level) { -Graph3d.prototype.getColumnRange = function(data,column) { - var minMax = {min:data[0][column],max:data[0][column]}; - for (var i = 0; i < data.length; i++) { - if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } - if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } - } - return minMax; -}; + // make sure multiple spaces in a row become a single space + combination = combination.replace(/\s+/g, ' '); -/** - * Initialize the data from the data table. Calculate minimum and maximum values - * and column index values - * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph. - * @param {Number} style Style Number - */ -Graph3d.prototype._dataInitialize = function (rawData, style) { - var me = this; + var sequence = combination.split(' '), + i, + key, + keys, + modifiers = []; - // unsubscribe from the dataTable - if (this.dataSet) { - this.dataSet.off('*', this._onChange); - } + // if this pattern is a sequence of keys then run through this method + // to reprocess each pattern one key at a time + if (sequence.length > 1) { + return _bindSequence(combination, sequence, callback, action); + } - if (rawData === undefined) - return; + // take the keys from this pattern and figure out what the actual + // pattern is all about + keys = combination === '+' ? ['+'] : combination.split('+'); - if (Array.isArray(rawData)) { - rawData = new DataSet(rawData); - } + for (i = 0; i < keys.length; ++i) { + key = keys[i]; - var data; - if (rawData instanceof DataSet || rawData instanceof DataView) { - data = rawData.get(); - } - else { - throw new Error('Array, DataSet, or DataView expected'); - } + // normalize key names + if (_SPECIAL_ALIASES[key]) { + key = _SPECIAL_ALIASES[key]; + } - if (data.length == 0) - return; + // if this is not a keypress event then we should + // be smart about using shift keys + // this will only work for US keyboards however + if (action && action != 'keypress' && _SHIFT_MAP[key]) { + key = _SHIFT_MAP[key]; + modifiers.push('shift'); + } - this.dataSet = rawData; - this.dataTable = data; + // if this key is a modifier then add it to the list of modifiers + if (_isModifier(key)) { + modifiers.push(key); + } + } - // subscribe to changes in the dataset - this._onChange = function () { - me.setData(me.dataSet); - }; - this.dataSet.on('*', this._onChange); + // depending on what the key combination is + // we will try to pick the best event for it + action = _pickBestAction(key, modifiers, action); - // _determineColumnIndexes - // getNumberOfRows (points) - // getNumberOfColumns (x,y,z,v,t,t1,t2...) - // getDistinctValues (unique values?) - // getColumnRange + // make sure to initialize array if this is the first time + // a callback is added for this key + if (!_callbacks[key]) { + _callbacks[key] = []; + } - // determine the location of x,y,z,value,filter columns - this.colX = 'x'; - this.colY = 'y'; - this.colZ = 'z'; - this.colValue = 'style'; - this.colFilter = 'filter'; + // remove an existing match if there is one + _getMatches(key, modifiers, action, !sequence_name, combination); + // add this call back to the array + // if it is a sequence put it at the beginning + // if not put it at the end + // + // this is important because the way these are processed expects + // the sequence ones to come first + _callbacks[key][sequence_name ? 'unshift' : 'push']({ + callback: callback, + modifiers: modifiers, + action: action, + seq: sequence_name, + level: level, + combo: combination + }); + } + /** + * binds multiple combinations to the same callback + * + * @param {Array} combinations + * @param {Function} callback + * @param {string|undefined} action + * @returns void + */ + function _bindMultiple(combinations, callback, action) { + for (var i = 0; i < combinations.length; ++i) { + _bindSingle(combinations[i], callback, action); + } + } + + // start! + _addEvent(document, 'keypress', _handleKey); + _addEvent(document, 'keydown', _handleKey); + _addEvent(document, 'keyup', _handleKey); + + var mousetrap = { + + /** + * binds an event to mousetrap + * + * can be a single key, a combination of keys separated with +, + * a comma separated list of keys, an array of keys, or + * a sequence of keys separated by spaces + * + * be sure to list the modifier keys first to make sure that the + * correct key ends up getting bound (the last key in the pattern) + * + * @param {string|Array} keys + * @param {Function} callback + * @param {string=} action - 'keypress', 'keydown', or 'keyup' + * @returns void + */ + bind: function(keys, callback, action) { + _bindMultiple(keys instanceof Array ? keys : [keys], callback, action); + _direct_map[keys + ':' + action] = callback; + return this; + }, - // check if a filter column is provided - if (data[0].hasOwnProperty('filter')) { - if (this.dataFilter === undefined) { - this.dataFilter = new Filter(rawData, this.colFilter, this); - this.dataFilter.setOnLoadCallback(function() {me.redraw();}); - } - } + /** + * unbinds an event to mousetrap + * + * the unbinding sets the callback function of the specified key combo + * to an empty function and deletes the corresponding key in the + * _direct_map dict. + * + * the keycombo+action has to be exactly the same as + * it was defined in the bind method + * + * TODO: actually remove this from the _callbacks dictionary instead + * of binding an empty function + * + * @param {string|Array} keys + * @param {string} action + * @returns void + */ + unbind: function(keys, action) { + if (_direct_map[keys + ':' + action]) { + delete _direct_map[keys + ':' + action]; + this.bind(keys, function() {}, action); + } + return this; + }, + /** + * triggers an event that has already been bound + * + * @param {string} keys + * @param {string=} action + * @returns void + */ + trigger: function(keys, action) { + _direct_map[keys + ':' + action](); + return this; + }, - var withBars = this.style == Graph3d.STYLE.BAR || - this.style == Graph3d.STYLE.BARCOLOR || - this.style == Graph3d.STYLE.BARSIZE; + /** + * resets the library back to its initial state. this is useful + * if you want to clear out the current keyboard shortcuts and bind + * new ones - for example if you switch to another page + * + * @returns void + */ + reset: function() { + _callbacks = {}; + _direct_map = {}; + return this; + } + }; - // determine barWidth from data - if (withBars) { - if (this.defaultXBarWidth !== undefined) { - this.xBarWidth = this.defaultXBarWidth; - } - else { - var dataX = this.getDistinctValues(data,this.colX); - this.xBarWidth = (dataX[1] - dataX[0]) || 1; - } + module.exports = mousetrap; - if (this.defaultYBarWidth !== undefined) { - this.yBarWidth = this.defaultYBarWidth; - } - else { - var dataY = this.getDistinctValues(data,this.colY); - this.yBarWidth = (dataY[1] - dataY[0]) || 1; - } - } - // calculate minimums and maximums - var xRange = this.getColumnRange(data,this.colX); - if (withBars) { - xRange.min -= this.xBarWidth / 2; - xRange.max += this.xBarWidth / 2; - } - this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min; - this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max; - if (this.xMax <= this.xMin) this.xMax = this.xMin + 1; - this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5; - - var yRange = this.getColumnRange(data,this.colY); - if (withBars) { - yRange.min -= this.yBarWidth / 2; - yRange.max += this.yBarWidth / 2; - } - this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min; - this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max; - if (this.yMax <= this.yMin) this.yMax = this.yMin + 1; - this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5; - - var zRange = this.getColumnRange(data,this.colZ); - this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min; - this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max; - if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; - this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; - - if (this.colValue !== undefined) { - var valueRange = this.getColumnRange(data,this.colValue); - this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; - this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; - if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; - } - // set the scale dependent on the ranges. - this._setScale(); -}; +/***/ }, +/* 43 */ +/***/ function(module, exports, __webpack_require__) { + /** + * Creation of the ClusterMixin var. + * + * This contains all the functions the Network object can use to employ clustering + */ + /** + * This is only called in the constructor of the network object + * + */ + exports.startWithClustering = function() { + // cluster if the data set is big + this.clusterToFit(this.constants.clustering.initialMaxNodes, true); -/** - * Filter the data based on the current filter - * @param {Array} data - * @return {Array} dataPoints Array with point objects which can be drawn on screen - */ -Graph3d.prototype._getDataPoints = function (data) { - // TODO: store the created matrix dataPoints in the filters instead of reloading each time - var x, y, i, z, obj, point; + // updates the lables after clustering + this.updateLabels(); - var dataPoints = []; + // this is called here because if clusterin is disabled, the start and stabilize are called in + // the setData function. + if (this.stabilize) { + this._stabilize(); + } + this.start(); + }; - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - // copy all values from the google data table to a matrix - // the provided values are supposed to form a grid of (x,y) positions + /** + * This function clusters until the initialMaxNodes has been reached + * + * @param {Number} maxNumberOfNodes + * @param {Boolean} reposition + */ + exports.clusterToFit = function(maxNumberOfNodes, reposition) { + var numberOfNodes = this.nodeIndices.length; - // create two lists with all present x and y values - var dataX = []; - var dataY = []; - for (i = 0; i < this.getNumberOfRows(data); i++) { - x = data[i][this.colX] || 0; - y = data[i][this.colY] || 0; + var maxLevels = 50; + var level = 0; - if (dataX.indexOf(x) === -1) { - dataX.push(x); + // we first cluster the hubs, then we pull in the outliers, repeat + while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { + if (level % 3 == 0) { + this.forceAggregateHubs(true); + this.normalizeClusterLevels(); } - if (dataY.indexOf(y) === -1) { - dataY.push(y); + else { + this.increaseClusterLevel(); // this also includes a cluster normalization } - } - function sortNumber(a, b) { - return a - b; + numberOfNodes = this.nodeIndices.length; + level += 1; } - dataX.sort(sortNumber); - dataY.sort(sortNumber); - // create a grid, a 2d matrix, with all values. - var dataMatrix = []; // temporary data matrix - for (i = 0; i < data.length; i++) { - x = data[i][this.colX] || 0; - y = data[i][this.colY] || 0; - z = data[i][this.colZ] || 0; + // after the clustering we reposition the nodes to reduce the initial chaos + if (level > 0 && reposition == true) { + this.repositionNodes(); + } + this._updateCalculationNodes(); + }; - var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer - var yIndex = dataY.indexOf(y); + /** + * This function can be called to open up a specific cluster. It is only called by + * It will unpack the cluster back one level. + * + * @param node | Node object: cluster to open. + */ + exports.openCluster = function(node) { + var isMovingBeforeClustering = this.moving; + if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && + !(this._sector() == "default" && this.nodeIndices.length == 1)) { + // this loads a new sector, loads the nodes and edges and nodeIndices of it. + this._addSector(node); + var level = 0; - if (dataMatrix[xIndex] === undefined) { - dataMatrix[xIndex] = []; + // we decluster until we reach a decent number of nodes + while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { + this.decreaseClusterLevel(); + level += 1; } - var point3d = new Point3d(); - point3d.x = x; - point3d.y = y; - point3d.z = z; - - obj = {}; - obj.point = point3d; - obj.trans = undefined; - obj.screen = undefined; - obj.bottom = new Point3d(x, y, this.zMin); - - dataMatrix[xIndex][yIndex] = obj; - - dataPoints.push(obj); } - - // fill in the pointers to the neighbors. - for (x = 0; x < dataMatrix.length; x++) { - for (y = 0; y < dataMatrix[x].length; y++) { - if (dataMatrix[x][y]) { - dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined; - dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined; - dataMatrix[x][y].pointCross = - (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ? - dataMatrix[x+1][y+1] : - undefined; - } - } + else { + this._expandClusterNode(node,false,true); + + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this._updateCalculationNodes(); + this.updateLabels(); } - } - else { // 'dot', 'dot-line', etc. - // copy all values from the google data table to a list with Point3d objects - for (i = 0; i < data.length; i++) { - point = new Point3d(); - point.x = data[i][this.colX] || 0; - point.y = data[i][this.colY] || 0; - point.z = data[i][this.colZ] || 0; - if (this.colValue !== undefined) { - point.value = data[i][this.colValue] || 0; - } + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + }; - obj = {}; - obj.point = point; - obj.bottom = new Point3d(point.x, point.y, this.zMin); - obj.trans = undefined; - obj.screen = undefined; - dataPoints.push(obj); + /** + * This calls the updateClustes with default arguments + */ + exports.updateClustersDefault = function() { + if (this.constants.clustering.enabled == true) { + this.updateClusters(0,false,false); } - } + }; - return dataPoints; -}; + /** + * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will + * be clustered with their connected node. This can be repeated as many times as needed. + * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. + */ + exports.increaseClusterLevel = function() { + this.updateClusters(-1,false,true); + }; + /** + * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will + * be unpacked if they are a cluster. This can be repeated as many times as needed. + * This can be called externally (by a key-bind for instance) to look into clusters without zooming. + */ + exports.decreaseClusterLevel = function() { + this.updateClusters(1,false,true); + }; -/** - * Append suffix 'px' to provided value x - * @param {int} x An integer value - * @return {string} the string value of x, followed by the suffix 'px' - */ -Graph3d.px = function(x) { - return x + 'px'; -}; + /** + * This is the main clustering function. It clusters and declusters on zoom or forced + * This function clusters on zoom, it can be called with a predefined zoom direction + * If out, check if we can form clusters, if in, check if we can open clusters. + * This function is only called from _zoom() + * + * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn + * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} doNotStart | if true do not call start + * + */ + exports.updateClusters = function(zoomDirection,recursive,force,doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; -/** - * Create the main frame for the Graph3d. - * This function is executed once when a Graph3d object is created. The frame - * contains a canvas, and this canvas contains all objects like the axis and - * nodes. - */ -Graph3d.prototype.create = function () { - // remove all elements from the container element. - while (this.containerElement.hasChildNodes()) { - this.containerElement.removeChild(this.containerElement.firstChild); - } + // on zoom out collapse the sector if the scale is at the level the sector was made + if (this.previousScale > this.scale && zoomDirection == 0) { + this._collapseSector(); + } - this.frame = document.createElement('div'); - this.frame.style.position = 'relative'; - this.frame.style.overflow = 'hidden'; - - // create the graph canvas (HTML canvas element) - this.frame.canvas = document.createElement( 'canvas' ); - this.frame.canvas.style.position = 'relative'; - this.frame.appendChild(this.frame.canvas); - //if (!this.frame.canvas.getContext) { - { - var noCanvas = document.createElement( 'DIV' ); - noCanvas.style.color = 'red'; - noCanvas.style.fontWeight = 'bold' ; - noCanvas.style.padding = '10px'; - noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; - this.frame.canvas.appendChild(noCanvas); - } + // check if we zoom in or out + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + // forming clusters when forced pulls outliers in. When not forced, the edge length of the + // outer nodes determines if it is being clustered + this._formClusters(force); + } + else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in + if (force == true) { + // _openClusters checks for each node if the formationScale of the cluster is smaller than + // the current scale and if so, declusters. When forced, all clusters are reduced by one step + this._openClusters(recursive,force); + } + else { + // if a cluster takes up a set percentage of the active window + this._openClustersBySize(); + } + } + this._updateNodeIndexList(); - this.frame.filter = document.createElement( 'div' ); - this.frame.filter.style.position = 'absolute'; - this.frame.filter.style.bottom = '0px'; - this.frame.filter.style.left = '0px'; - this.frame.filter.style.width = '100%'; - this.frame.appendChild(this.frame.filter); + // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs + if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { + this._aggregateHubs(force); + this._updateNodeIndexList(); + } - // add event listeners to handle moving and zooming the contents - var me = this; - var onmousedown = function (event) {me._onMouseDown(event);}; - var ontouchstart = function (event) {me._onTouchStart(event);}; - var onmousewheel = function (event) {me._onWheel(event);}; - var ontooltip = function (event) {me._onTooltip(event);}; - // TODO: these events are never cleaned up... can give a 'memory leakage' + // we now reduce chains. + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + this.handleChains(); + this._updateNodeIndexList(); + } - G3DaddEventListener(this.frame.canvas, 'keydown', onkeydown); - G3DaddEventListener(this.frame.canvas, 'mousedown', onmousedown); - G3DaddEventListener(this.frame.canvas, 'touchstart', ontouchstart); - G3DaddEventListener(this.frame.canvas, 'mousewheel', onmousewheel); - G3DaddEventListener(this.frame.canvas, 'mousemove', ontooltip); + this.previousScale = this.scale; - // add the new graph to the container element - this.containerElement.appendChild(this.frame); -}; + // rest of the update the index list, dynamic edges and labels + this._updateDynamicEdges(); + this.updateLabels(); + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place + this.clusterSession += 1; + // if clusters have been made, we normalize the cluster level + this.normalizeClusterLevels(); + } -/** - * Set a new size for the graph - * @param {string} width Width in pixels or percentage (for example '800px' - * or '50%') - * @param {string} height Height in pixels or percentage (for example '400px' - * or '30%') - */ -Graph3d.prototype.setSize = function(width, height) { - this.frame.style.width = width; - this.frame.style.height = height; + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + } - this._resizeCanvas(); -}; + this._updateCalculationNodes(); + }; -/** - * Resize the canvas to the current size of the frame - */ -Graph3d.prototype._resizeCanvas = function() { - this.frame.canvas.style.width = '100%'; - this.frame.canvas.style.height = '100%'; + /** + * This function handles the chains. It is called on every updateClusters(). + */ + exports.handleChains = function() { + // after clustering we check how many chains there are + var chainPercentage = this._getChainFraction(); + if (chainPercentage > this.constants.clustering.chainThreshold) { + this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) - this.frame.canvas.width = this.frame.canvas.clientWidth; - this.frame.canvas.height = this.frame.canvas.clientHeight; + } + }; - // adjust with for margin - this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px'; -}; + /** + * this functions starts clustering by hubs + * The minimum hub threshold is set globally + * + * @private + */ + exports._aggregateHubs = function(force) { + this._getHubSize(); + this._formClustersByHub(force,false); + }; -/** - * Start animation - */ -Graph3d.prototype.animationStart = function() { - if (!this.frame.filter || !this.frame.filter.slider) - throw 'No animation available'; - this.frame.filter.slider.play(); -}; + /** + * This function is fired by keypress. It forces hubs to form. + * + */ + exports.forceAggregateHubs = function(doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; + this._aggregateHubs(true); -/** - * Stop animation - */ -Graph3d.prototype.animationStop = function() { - if (!this.frame.filter || !this.frame.filter.slider) return; + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this.updateLabels(); - this.frame.filter.slider.stop(); -}; + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; + } + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + } + }; -/** - * Resize the center position based on the current values in this.defaultXCenter - * and this.defaultYCenter (which are strings with a percentage or a value - * in pixels). The center positions are the variables this.xCenter - * and this.yCenter - */ -Graph3d.prototype._resizeCenter = function() { - // calculate the horizontal center position - if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') { - this.xcenter = - parseFloat(this.defaultXCenter) / 100 * - this.frame.canvas.clientWidth; - } - else { - this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px - } + /** + * If a cluster takes up more than a set percentage of the screen, open the cluster + * + * @private + */ + exports._openClustersBySize = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.inView() == true) { + if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + this.openCluster(node); + } + } + } + } + }; - // calculate the vertical center position - if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') { - this.ycenter = - parseFloat(this.defaultYCenter) / 100 * - (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); - } - else { - this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px - } -}; -/** - * Set the rotation and distance of the camera - * @param {Object} pos An object with the camera position. The object - * contains three parameters: - * - horizontal {Number} - * The horizontal rotation, between 0 and 2*PI. - * Optional, can be left undefined. - * - vertical {Number} - * The vertical rotation, between 0 and 0.5*PI - * if vertical=0.5*PI, the graph is shown from the - * top. Optional, can be left undefined. - * - distance {Number} - * The (normalized) distance of the camera to the - * center of the graph, a value between 0.71 and 5.0. - * Optional, can be left undefined. - */ -Graph3d.prototype.setCameraPosition = function(pos) { - if (pos === undefined) { - return; - } + /** + * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it + * has to be opened based on the current zoom level. + * + * @private + */ + exports._openClusters = function(recursive,force) { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + this._expandClusterNode(node,recursive,force); + this._updateCalculationNodes(); + } + }; - if (pos.horizontal !== undefined && pos.vertical !== undefined) { - this.camera.setArmRotation(pos.horizontal, pos.vertical); - } + /** + * This function checks if a node has to be opened. This is done by checking the zoom level. + * If the node contains child nodes, this function is recursively called on the child nodes as well. + * This recursive behaviour is optional and can be set by the recursive argument. + * + * @param {Node} parentNode | to check for cluster and expand + * @param {Boolean} recursive | enabled or disable recursive calling + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released + * @private + */ + exports._expandClusterNode = function(parentNode, recursive, force, openAll) { + // first check if node is a cluster + if (parentNode.clusterSize > 1) { + // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 + if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { + openAll = true; + } + recursive = openAll ? true : recursive; - if (pos.distance !== undefined) { - this.camera.setArmLength(pos.distance); - } + // if the last child has been added on a smaller scale than current scale decluster + if (parentNode.formationScale < this.scale || force == true) { + // we will check if any of the contained child nodes should be removed from the cluster + for (var containedNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { + var childNode = parentNode.containedNodes[containedNodeId]; - this.redraw(); -}; + // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that + // the largest cluster is the one that comes from outside + if (force == true) { + if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] + || openAll) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + else { + if (this._nodeInActiveArea(parentNode)) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + } + } + } + } + }; + /** + * ONLY CALLED FROM _expandClusterNode + * + * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove + * the child node from the parent contained_node object and put it back into the global nodes object. + * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. + * + * @param {Node} parentNode | the parent node + * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node + * @param {Boolean} recursive | This will also check if the child needs to be expanded. + * With force and recursive both true, the entire cluster is unpacked + * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent + * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released + * @private + */ + exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { + var childNode = parentNode.containedNodes[containedNodeId]; -/** - * Retrieve the current camera rotation - * @return {object} An object with parameters horizontal, vertical, and - * distance - */ -Graph3d.prototype.getCameraPosition = function() { - var pos = this.camera.getArmRotation(); - pos.distance = this.camera.getArmLength(); - return pos; -}; - -/** - * Load data into the 3D Graph - */ -Graph3d.prototype._readData = function(data) { - // read the data - this._dataInitialize(data, this.style); - - - if (this.dataFilter) { - // apply filtering - this.dataPoints = this.dataFilter._getDataPoints(); - } - else { - // no filtering. load all data - this.dataPoints = this._getDataPoints(this.dataTable); - } - - // draw the filter - this._redrawFilter(); -}; - -/** - * Replace the dataset of the Graph3d - * @param {Array | DataSet | DataView} data - */ -Graph3d.prototype.setData = function (data) { - this._readData(data); - this.redraw(); + // if child node has been added on smaller scale than current, kick out + if (childNode.formationScale < this.scale || force == true) { + // unselect all selected items + this._unselectAll(); - // start animation when option is true - if (this.animationAutoStart && this.dataFilter) { - this.animationStart(); - } -}; + // put the child node back in the global nodes object + this.nodes[containedNodeId] = childNode; -/** - * Update the options. Options will be merged with current options - * @param {Object} options - */ -Graph3d.prototype.setOptions = function (options) { - var cameraPosition = undefined; - - this.animationStop(); - - if (options !== undefined) { - // retrieve parameter values - if (options.width !== undefined) this.width = options.width; - if (options.height !== undefined) this.height = options.height; - - if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter; - if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter; - - if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel; - if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel; - if (options.xLabel !== undefined) this.xLabel = options.xLabel; - if (options.yLabel !== undefined) this.yLabel = options.yLabel; - if (options.zLabel !== undefined) this.zLabel = options.zLabel; - - if (options.style !== undefined) { - var styleNumber = this._getStyleNumber(options.style); - if (styleNumber !== -1) { - this.style = styleNumber; - } - } - if (options.showGrid !== undefined) this.showGrid = options.showGrid; - if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective; - if (options.showShadow !== undefined) this.showShadow = options.showShadow; - if (options.tooltip !== undefined) this.showTooltip = options.tooltip; - if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls; - if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio; - if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio; - - if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval; - if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload; - if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart; - - if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; - if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; - - if (options.xMin !== undefined) this.defaultXMin = options.xMin; - if (options.xStep !== undefined) this.defaultXStep = options.xStep; - if (options.xMax !== undefined) this.defaultXMax = options.xMax; - if (options.yMin !== undefined) this.defaultYMin = options.yMin; - if (options.yStep !== undefined) this.defaultYStep = options.yStep; - if (options.yMax !== undefined) this.defaultYMax = options.yMax; - if (options.zMin !== undefined) this.defaultZMin = options.zMin; - if (options.zStep !== undefined) this.defaultZStep = options.zStep; - if (options.zMax !== undefined) this.defaultZMax = options.zMax; - if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; - if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; - - if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; - - if (cameraPosition !== undefined) { - this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); - this.camera.setArmLength(cameraPosition.distance); - } - else { - this.camera.setArmRotation(1.0, 0.5); - this.camera.setArmLength(1.7); - } - } + // release the contained edges from this childNode back into the global edges + this._releaseContainedEdges(parentNode,childNode); - this._setBackgroundColor(options && options.backgroundColor); + // reconnect rerouted edges to the childNode + this._connectEdgeBackToChild(parentNode,childNode); - this.setSize(this.width, this.height); + // validate all edges in dynamicEdges + this._validateEdges(parentNode); - // re-load the data - if (this.dataTable) { - this.setData(this.dataTable); - } + // undo the changes from the clustering operation on the parent node + parentNode.mass -= childNode.mass; + parentNode.clusterSize -= childNode.clusterSize; + parentNode.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); + parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; - // start animation when option is true - if (this.animationAutoStart && this.dataFilter) { - this.animationStart(); - } -}; + // place the child node near the parent, not at the exact same location to avoid chaos in the system + childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); + childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); -/** - * Redraw the Graph. - */ -Graph3d.prototype.redraw = function() { - if (this.dataPoints === undefined) { - throw 'Error: graph data not initialized'; - } + // remove node from the list + delete parentNode.containedNodes[containedNodeId]; - this._resizeCanvas(); - this._resizeCenter(); - this._redrawSlider(); - this._redrawClear(); - this._redrawAxis(); + // check if there are other childs with this clusterSession in the parent. + var othersPresent = false; + for (var childNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { + if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { + othersPresent = true; + break; + } + } + } + // if there are no others, remove the cluster session from the list + if (othersPresent == false) { + parentNode.clusterSessions.pop(); + } - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - this._redrawDataGrid(); - } - else if (this.style === Graph3d.STYLE.LINE) { - this._redrawDataLine(); - } - else if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - this._redrawDataBar(); - } - else { - // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE - this._redrawDataDot(); - } + this._repositionBezierNodes(childNode); + // this._repositionBezierNodes(parentNode); - this._redrawInfo(); - this._redrawLegend(); -}; + // remove the clusterSession from the child node + childNode.clusterSession = 0; -/** - * Clear the canvas before redrawing - */ -Graph3d.prototype._redrawClear = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); - ctx.clearRect(0, 0, canvas.width, canvas.height); -}; + // restart the simulation to reorganise all nodes + this.moving = true; + } + // check if a further expansion step is possible if recursivity is enabled + if (recursive == true) { + this._expandClusterNode(childNode,recursive,force,openAll); + } + }; -/** - * Redraw the legend showing the colors - */ -Graph3d.prototype._redrawLegend = function() { - var y; - if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE) { + /** + * position the bezier nodes at the center of the edges + * + * @param node + * @private + */ + exports._repositionBezierNodes = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + node.dynamicEdges[i].positionBezierNode(); + } + }; - var dotSize = this.frame.clientWidth * 0.02; - var widthMin, widthMax; - if (this.style === Graph3d.STYLE.DOTSIZE) { - widthMin = dotSize / 2; // px - widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function + /** + * This function checks if any nodes at the end of their trees have edges below a threshold length + * This function is called only from updateClusters() + * forceLevelCollapse ignores the length of the edge and collapses one level + * This means that a node with only one edge will be clustered with its connected node + * + * @private + * @param {Boolean} force + */ + exports._formClusters = function(force) { + if (force == false) { + this._formClustersByZoom(); } else { - widthMin = 20; // px - widthMax = 20; // px + this._forceClustersByZoom(); } + }; - var height = Math.max(this.frame.clientHeight * 0.25, 100); - var top = this.margin; - var right = this.frame.clientWidth - this.margin; - var left = right - widthMax; - var bottom = top + height; - } - - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - ctx.lineWidth = 1; - ctx.font = '14px arial'; // TODO: put in options - - if (this.style === Graph3d.STYLE.DOTCOLOR) { - // draw the color bar - var ymin = 0; - var ymax = height; // Todo: make height customizable - for (y = ymin; y < ymax; y++) { - var f = (y - ymin) / (ymax - ymin); - //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function - var hue = f * 240; - var color = this._hsv2rgb(hue, 1, 1); + /** + * This function handles the clustering by zooming out, this is based on a minimum edge distance + * + * @private + */ + exports._formClustersByZoom = function() { + var dx,dy,length, + minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - ctx.strokeStyle = color; - ctx.beginPath(); - ctx.moveTo(left, top + y); - ctx.lineTo(right, top + y); - ctx.stroke(); - } + // check if any edges are shorter than minLength and start the clustering + // the clustering favours the node with the larger mass + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); - ctx.strokeStyle = this.colorAxis; - ctx.strokeRect(left, top, widthMax, height); - } - if (this.style === Graph3d.STYLE.DOTSIZE) { - // draw border around color bar - ctx.strokeStyle = this.colorAxis; - ctx.fillStyle = this.colorDot; - ctx.beginPath(); - ctx.moveTo(left, top); - ctx.lineTo(right, top); - ctx.lineTo(right - widthMax + widthMin, bottom); - ctx.lineTo(left, bottom); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - } + if (length < minLength) { + // first check which node is larger + var parentNode = edge.from; + var childNode = edge.to; + if (edge.to.mass > edge.from.mass) { + parentNode = edge.to; + childNode = edge.from; + } - if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE) { - // print values along the color bar - var gridLineLen = 5; // px - var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true); - step.start(); - if (step.getCurrent() < this.valueMin) { - step.next(); + if (childNode.dynamicEdgesLength == 1) { + this._addToCluster(parentNode,childNode,false); + } + else if (parentNode.dynamicEdgesLength == 1) { + this._addToCluster(childNode,parentNode,false); + } + } + } + } + } } - while (!step.end()) { - y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height; + }; - ctx.beginPath(); - ctx.moveTo(left - gridLineLen, y); - ctx.lineTo(left, y); - ctx.stroke(); + /** + * This function forces the network to cluster all nodes with only one connecting edge to their + * connected node. + * + * @private + */ + exports._forceClustersByZoom = function() { + for (var nodeId in this.nodes) { + // another node could have absorbed this child. + if (this.nodes.hasOwnProperty(nodeId)) { + var childNode = this.nodes[nodeId]; - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); + // the edges can be swallowed by another decrease + if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { + var edge = childNode.dynamicEdges[0]; + var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; - step.next(); + // group to the largest node + if (childNode.id != parentNode.id) { + if (parentNode.mass > childNode.mass) { + this._addToCluster(parentNode,childNode,true); + } + else { + this._addToCluster(childNode,parentNode,true); + } + } + } + } } + }; - ctx.textAlign = 'right'; - ctx.textBaseline = 'top'; - var label = this.legendLabel; - ctx.fillText(label, right, bottom + this.margin); - } -}; - -/** - * Redraw the filter - */ -Graph3d.prototype._redrawFilter = function() { - this.frame.filter.innerHTML = ''; - - if (this.dataFilter) { - var options = { - 'visible': this.showAnimationControls - }; - var slider = new Slider(this.frame.filter, options); - this.frame.filter.slider = slider; - - // TODO: css here is not nice here... - this.frame.filter.style.padding = '10px'; - //this.frame.filter.style.backgroundColor = '#EFEFEF'; - - slider.setValues(this.dataFilter.values); - slider.setPlayInterval(this.animationInterval); - // create an event handler - var me = this; - var onchange = function () { - var index = slider.getIndex(); + /** + * To keep the nodes of roughly equal size we normalize the cluster levels. + * This function clusters a node to its smallest connected neighbour. + * + * @param node + * @private + */ + exports._clusterToSmallestNeighbour = function(node) { + var smallestNeighbour = -1; + var smallestNeighbourNode = null; + for (var i = 0; i < node.dynamicEdges.length; i++) { + if (node.dynamicEdges[i] !== undefined) { + var neighbour = null; + if (node.dynamicEdges[i].fromId != node.id) { + neighbour = node.dynamicEdges[i].from; + } + else if (node.dynamicEdges[i].toId != node.id) { + neighbour = node.dynamicEdges[i].to; + } - me.dataFilter.selectValue(index); - me.dataPoints = me.dataFilter._getDataPoints(); - me.redraw(); - }; - slider.setOnChangeCallback(onchange); - } - else { - this.frame.filter.slider = undefined; - } -}; + if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { + smallestNeighbour = neighbour.clusterSessions.length; + smallestNeighbourNode = neighbour; + } + } + } -/** - * Redraw the slider - */ -Graph3d.prototype._redrawSlider = function() { - if ( this.frame.filter.slider !== undefined) { - this.frame.filter.slider.redraw(); - } -}; + if (neighbour != null && this.nodes[neighbour.id] !== undefined) { + this._addToCluster(neighbour, node, true); + } + }; -/** - * Redraw common information - */ -Graph3d.prototype._redrawInfo = function() { - if (this.dataFilter) { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); + /** + * This function forms clusters from hubs, it loops over all nodes + * + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @private + */ + exports._formClustersByHub = function(force, onlyEqual) { + // we loop over all nodes in the list + for (var nodeId in this.nodes) { + // we check if it is still available since it can be used by the clustering in this loop + if (this.nodes.hasOwnProperty(nodeId)) { + this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); + } + } + }; - ctx.font = '14px arial'; // TODO: put in options - ctx.lineStyle = 'gray'; - ctx.fillStyle = 'gray'; - ctx.textAlign = 'left'; - ctx.textBaseline = 'top'; - - var x = this.margin; - var y = this.margin; - ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y); - } -}; + /** + * This function forms a cluster from a specific preselected hub node + * + * @param {Node} hubNode | the node we will cluster as a hub + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @param {Number} [absorptionSizeOffset] | + * @private + */ + exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSizeOffset) { + if (absorptionSizeOffset === undefined) { + absorptionSizeOffset = 0; + } + // we decide if the node is a hub + if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || + (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { + // initialize variables + var dx,dy,length; + var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + var allowCluster = false; + // we create a list of edges because the dynamicEdges change over the course of this loop + var edgesIdarray = []; + var amountOfInitialEdges = hubNode.dynamicEdges.length; + for (var j = 0; j < amountOfInitialEdges; j++) { + edgesIdarray.push(hubNode.dynamicEdges[j].id); + } -/** - * Redraw the axis - */ -Graph3d.prototype._redrawAxis = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - from, to, step, prettyStep, - text, xText, yText, zText, - offset, xOffset, yOffset, - xMin2d, xMax2d; - - // TODO: get the actual rendered style of the containerElement - //ctx.font = this.containerElement.style.font; - ctx.font = 24 / this.camera.getArmLength() + 'px arial'; - - // calculate the length for the short grid lines - var gridLenX = 0.025 / this.scale.x; - var gridLenY = 0.025 / this.scale.y; - var textMargin = 5 / this.camera.getArmLength(); // px - var armAngle = this.camera.getArmRotation().horizontal; - - // draw x-grid lines - ctx.lineWidth = 1; - prettyStep = (this.defaultXStep === undefined); - step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep); - step.start(); - if (step.getCurrent() < this.xMin) { - step.next(); - } - while (!step.end()) { - var x = step.getCurrent(); + // if the hub clustering is not forces, we check if one of the edges connected + // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold + if (force == false) { + allowCluster = false; + for (j = 0; j < amountOfInitialEdges; j++) { + var edge = this.edges[edgesIdarray[j]]; + if (edge !== undefined) { + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); - if (this.showGrid) { - from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - else { - from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + if (length < minLength) { + allowCluster = true; + break; + } + } + } + } + } + } - from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + // start the clustering if allowed + if ((!force && allowCluster) || force) { + // we loop over all edges INITIALLY connected to this hub + for (j = 0; j < amountOfInitialEdges; j++) { + edge = this.edges[edgesIdarray[j]]; + // the edge can be clustered by this function in a previous loop + if (edge !== undefined) { + var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; + // we do not want hubs to merge with other hubs nor do we want to cluster itself. + if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && + (childNode.id != hubNode.id)) { + this._addToCluster(hubNode,childNode,force); + } + } + } + } } + }; - yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax; - text = this._convert3Dto2D(new Point3d(x, yText, this.zMin)); - if (Math.cos(armAngle * 2) > 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - text.y += textMargin; - } - else if (Math.sin(armAngle * 2) < 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(' ' + step.getCurrent() + ' ', text.x, text.y); - step.next(); - } - // draw y-grid lines - ctx.lineWidth = 1; - prettyStep = (this.defaultYStep === undefined); - step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep); - step.start(); - if (step.getCurrent() < this.yMin) { - step.next(); - } - while (!step.end()) { - if (this.showGrid) { - from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - else { - from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + /** + * This function adds the child node to the parent node, creating a cluster if it is not already. + * + * @param {Node} parentNode | this is the node that will house the child node + * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node + * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse + * @private + */ + exports._addToCluster = function(parentNode, childNode, force) { + // join child node in the parent node + parentNode.containedNodes[childNode.id] = childNode; - from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < childNode.dynamicEdges.length; i++) { + var edge = childNode.dynamicEdges[i]; + if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode + this._addToContainedEdges(parentNode,childNode,edge); + } + else { + this._connectEdgeToCluster(parentNode,childNode,edge); + } } + // a contained node has no dynamic edges. + childNode.dynamicEdges = []; - xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax; - text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin)); - if (Math.cos(armAngle * 2) < 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - text.y += textMargin; - } - else if (Math.sin(armAngle * 2) > 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(' ' + step.getCurrent() + ' ', text.x, text.y); + // remove circular edges from clusters + this._containCircularEdgesFromNode(parentNode,childNode); - step.next(); - } - // draw z-grid lines and axis - ctx.lineWidth = 1; - prettyStep = (this.defaultZStep === undefined); - step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep); - step.start(); - if (step.getCurrent() < this.zMin) { - step.next(); - } - xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; - yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; - while (!step.end()) { - // TODO: make z-grid lines really 3d? - from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent())); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(from.x - textMargin, from.y); - ctx.stroke(); + // remove the childNode from the global nodes object + delete this.nodes[childNode.id]; - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(step.getCurrent() + ' ', from.x - 5, from.y); + // update the properties of the child and parent + var massBefore = parentNode.mass; + childNode.clusterSession = this.clusterSession; + parentNode.mass += childNode.mass; + parentNode.clusterSize += childNode.clusterSize; + parentNode.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); - step.next(); - } - ctx.lineWidth = 1; - from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - - // draw x-axis - ctx.lineWidth = 1; - // line at yMin - xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); - xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(xMin2d.x, xMin2d.y); - ctx.lineTo(xMax2d.x, xMax2d.y); - ctx.stroke(); - // line at ymax - xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); - xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(xMin2d.x, xMin2d.y); - ctx.lineTo(xMax2d.x, xMax2d.y); - ctx.stroke(); - - // draw y-axis - ctx.lineWidth = 1; - // line at xMin - from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - // line at xMax - from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - - // draw x-label - var xLabel = this.xLabel; - if (xLabel.length > 0) { - yOffset = 0.1 / this.scale.y; - xText = (this.xMin + this.xMax) / 2; - yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset; - text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - if (Math.cos(armAngle * 2) > 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - } - else if (Math.sin(armAngle * 2) < 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; + // keep track of the clustersessions so we can open the cluster up as it has been formed. + if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { + parentNode.clusterSessions.push(this.clusterSession); } - ctx.fillStyle = this.colorAxis; - ctx.fillText(xLabel, text.x, text.y); - } - // draw y-label - var yLabel = this.yLabel; - if (yLabel.length > 0) { - xOffset = 0.1 / this.scale.x; - xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset; - yText = (this.yMin + this.yMax) / 2; - text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - if (Math.cos(armAngle * 2) < 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - } - else if (Math.sin(armAngle * 2) > 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; + // forced clusters only open from screen size and double tap + if (force == true) { + // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); + parentNode.formationScale = 0; } else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; + parentNode.formationScale = this.scale; // The latest child has been added on this scale } - ctx.fillStyle = this.colorAxis; - ctx.fillText(yLabel, text.x, text.y); - } - - // draw z-label - var zLabel = this.zLabel; - if (zLabel.length > 0) { - offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? - xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; - yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; - zText = (this.zMin + this.zMax) / 2; - text = this._convert3Dto2D(new Point3d(xText, yText, zText)); - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(zLabel, text.x - offset, text.y); - } -}; - -/** - * Calculate the color based on the given value. - * @param {Number} H Hue, a value be between 0 and 360 - * @param {Number} S Saturation, a value between 0 and 1 - * @param {Number} V Value, a value between 0 and 1 - */ -Graph3d.prototype._hsv2rgb = function(H, S, V) { - var R, G, B, C, Hi, X; - - C = V * S; - Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5 - X = C * (1 - Math.abs(((H/60) % 2) - 1)); - - switch (Hi) { - case 0: R = C; G = X; B = 0; break; - case 1: R = X; G = C; B = 0; break; - case 2: R = 0; G = C; B = X; break; - case 3: R = 0; G = X; B = C; break; - case 4: R = X; G = 0; B = C; break; - case 5: R = C; G = 0; B = X; break; - - default: R = 0; G = 0; B = 0; break; - } - - return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')'; -}; - - -/** - * Draw all datapoints as a grid - * This function can be used when the style is 'grid' - */ -Graph3d.prototype._redrawDataGrid = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - point, right, top, cross, - i, - topSideVisible, fillStyle, strokeStyle, lineWidth, - h, s, v, zAvg; - - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); - // calculate the translations and screen position of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); + // set the pop-out scale for the childnode + parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; + // nullify the movement velocity of the child, this is to avoid hectic behaviour + childNode.clearVelocity(); - // calculate the translation of the point at the bottom (needed for sorting) - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; - } + // the mass has altered, preservation of energy dictates the velocity to be updated + parentNode.updateVelocity(massBefore); - // sort the points on depth of their (x,y) position (not on z) - var sortDepth = function (a, b) { - return b.dist - a.dist; + // restart the simulation to reorganise all nodes + this.moving = true; }; - this.dataPoints.sort(sortDepth); - - if (this.style === Graph3d.STYLE.SURFACE) { - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; - cross = this.dataPoints[i].pointCross; - if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { - if (this.showGrayBottom || this.showShadow) { - // calculate the cross product of the two vectors from center - // to left and right, in order to know whether we are looking at the - // bottom or at the top side. We can also use the cross product - // for calculating light intensity - var aDiff = Point3d.subtract(cross.trans, point.trans); - var bDiff = Point3d.subtract(top.trans, right.trans); - var crossproduct = Point3d.crossProduct(aDiff, bDiff); - var len = crossproduct.length(); - // FIXME: there is a bug with determining the surface side (shadow or colored) - - topSideVisible = (crossproduct.z > 0); - } - else { - topSideVisible = true; - } - - if (topSideVisible) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - s = 1; // saturation + /** + * This function will apply the changes made to the remainingEdges during the formation of the clusters. + * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. + * It has to be called if a level is collapsed. It is called by _formClusters(). + * @private + */ + exports._updateDynamicEdges = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + node.dynamicEdgesLength = node.dynamicEdges.length; - if (this.showShadow) { - v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale - fillStyle = this._hsv2rgb(h, s, v); - strokeStyle = fillStyle; - } - else { - v = 1; - fillStyle = this._hsv2rgb(h, s, v); - strokeStyle = this.colorAxis; + // this corrects for multiple edges pointing at the same other node + var correction = 0; + if (node.dynamicEdgesLength > 1) { + for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { + var edgeToId = node.dynamicEdges[j].toId; + var edgeFromId = node.dynamicEdges[j].fromId; + for (var k = j+1; k < node.dynamicEdgesLength; k++) { + if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || + (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { + correction += 1; + } } } - else { - fillStyle = 'gray'; - strokeStyle = this.colorAxis; - } - lineWidth = 0.5; - - ctx.lineWidth = lineWidth; - ctx.fillStyle = fillStyle; - ctx.strokeStyle = strokeStyle; - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.lineTo(cross.screen.x, cross.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); } + node.dynamicEdgesLength -= correction; } - } - else { // grid style - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; - - if (point !== undefined) { - if (this.showPerspective) { - lineWidth = 2 / -point.trans.z; - } - else { - lineWidth = 2 * -(this.eye.z / this.camera.getArmLength()); - } - } + }; - if (point !== undefined && right !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - ctx.lineWidth = lineWidth; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.stroke(); - } + /** + * This adds an edge from the childNode to the contained edges of the parent node + * + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private + */ + exports._addToContainedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { + parentNode.containedEdges[childNode.id] = [] + } + // add this edge to the list + parentNode.containedEdges[childNode.id].push(edge); - if (point !== undefined && top !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + top.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + // remove the edge from the global edges object + delete this.edges[edge.id]; - ctx.lineWidth = lineWidth; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.stroke(); + // remove the edge from the parent object + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + if (parentNode.dynamicEdges[i].id == edge.id) { + parentNode.dynamicEdges.splice(i,1); + break; } } - } -}; - - -/** - * Draw all datapoints as dots. - * This function can be used when the style is 'dot' or 'dot-line' - */ -Graph3d.prototype._redrawDataDot = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - var i; - - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? - - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - - // calculate the distance from the point at the bottom to the camera - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; - } - - // order the translated points by depth - var sortDepth = function (a, b) { - return b.dist - a.dist; }; - this.dataPoints.sort(sortDepth); - // draw the datapoints as colored circles - var dotSize = this.frame.clientWidth * 0.02; // px - for (i = 0; i < this.dataPoints.length; i++) { - var point = this.dataPoints[i]; - - if (this.style === Graph3d.STYLE.DOTLINE) { - // draw a vertical line from the bottom to the graph value - //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin)); - var from = this._convert3Dto2D(point.bottom); - ctx.lineWidth = 1; - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(point.screen.x, point.screen.y); - ctx.stroke(); - } - - // calculate radius for the circle - var size; - if (this.style === Graph3d.STYLE.DOTSIZE) { - size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin); + /** + * This function connects an edge that was connected to a child node to the parent node. + * It keeps track of which nodes it has been connected to with the originalId array. + * + * @param {Node} parentNode | Node object + * @param {Node} childNode | Node object + * @param {Edge} edge | Edge object + * @private + */ + exports._connectEdgeToCluster = function(parentNode, childNode, edge) { + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); } else { - size = dotSize; - } + if (edge.toId == childNode.id) { // edge connected to other node on the "to" side + edge.originalToId.push(childNode.id); + edge.to = parentNode; + edge.toId = parentNode.id; + } + else { // edge connected to other node with the "from" side - var radius; - if (this.showPerspective) { - radius = size / -point.trans.z; - } - else { - radius = size * -(this.eye.z / this.camera.getArmLength()); - } - if (radius < 0) { - radius = 0; - } + edge.originalFromId.push(childNode.id); + edge.from = parentNode; + edge.fromId = parentNode.id; + } - var hue, color, borderColor; - if (this.style === Graph3d.STYLE.DOTCOLOR ) { - // calculate the color based on the value - hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - else if (this.style === Graph3d.STYLE.DOTSIZE) { - color = this.colorDot; - borderColor = this.colorDotBorder; - } - else { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); + this._addToReroutedEdges(parentNode,childNode,edge); } - - // draw the circle - ctx.lineWidth = 1.0; - ctx.strokeStyle = borderColor; - ctx.fillStyle = color; - ctx.beginPath(); - ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true); - ctx.fill(); - ctx.stroke(); - } -}; - -/** - * Draw all datapoints as bars. - * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' - */ -Graph3d.prototype._redrawDataBar = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - var i, j, surface, corners; - - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? - - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - - // calculate the distance from the point at the bottom to the camera - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; - } - - // order the translated points by depth - var sortDepth = function (a, b) { - return b.dist - a.dist; }; - this.dataPoints.sort(sortDepth); - - // draw the datapoints as bars - var xWidth = this.xBarWidth / 2; - var yWidth = this.yBarWidth / 2; - for (i = 0; i < this.dataPoints.length; i++) { - var point = this.dataPoints[i]; - // determine color - var hue, color, borderColor; - if (this.style === Graph3d.STYLE.BARCOLOR ) { - // calculate the color based on the value - hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - else if (this.style === Graph3d.STYLE.BARSIZE) { - color = this.colorDot; - borderColor = this.colorDotBorder; - } - else { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - // calculate size for the bar - if (this.style === Graph3d.STYLE.BARSIZE) { - xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); - yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + /** + * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain + * these edges inside of the cluster. + * + * @param parentNode + * @param childNode + * @private + */ + exports._containCircularEdgesFromNode = function(parentNode, childNode) { + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); + } } + }; - // calculate all corner points - var me = this; - var point3d = point.point; - var top = [ - {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, - {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, - {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, - {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} - ]; - var bottom = [ - {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, - {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, - {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, - {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} - ]; - - // calculate screen location of the points - top.forEach(function (obj) { - obj.screen = me._convert3Dto2D(obj.point); - }); - bottom.forEach(function (obj) { - obj.screen = me._convert3Dto2D(obj.point); - }); - - // create five sides, calculate both corner points and center points - var surfaces = [ - {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, - {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, - {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, - {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, - {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} - ]; - point.surfaces = surfaces; - // calculate the distance of each of the surface centers to the camera - for (j = 0; j < surfaces.length; j++) { - surface = surfaces[j]; - var transCenter = this._convertPointToTranslation(surface.center); - surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; - // TODO: this dept calculation doesn't work 100% of the cases due to perspective, - // but the current solution is fast/simple and works in 99.9% of all cases - // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) + /** + * This adds an edge from the childNode to the rerouted edges of the parent node + * + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private + */ + exports._addToReroutedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + // we store the edge in the rerouted edges so we can restore it when the cluster pops open + if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { + parentNode.reroutedEdges[childNode.id] = []; } + parentNode.reroutedEdges[childNode.id].push(edge); - // order the surfaces by their (translated) depth - surfaces.sort(function (a, b) { - var diff = b.dist - a.dist; - if (diff) return diff; - - // if equal depth, sort the top surface last - if (a.corners === top) return 1; - if (b.corners === top) return -1; + // this edge becomes part of the dynamicEdges of the cluster node + parentNode.dynamicEdges.push(edge); + }; - // both are equal - return 0; - }); - // draw the ordered surfaces - ctx.lineWidth = 1; - ctx.strokeStyle = borderColor; - ctx.fillStyle = color; - // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside - for (j = 2; j < surfaces.length; j++) { - surface = surfaces[j]; - corners = surface.corners; - ctx.beginPath(); - ctx.moveTo(corners[3].screen.x, corners[3].screen.y); - ctx.lineTo(corners[0].screen.x, corners[0].screen.y); - ctx.lineTo(corners[1].screen.x, corners[1].screen.y); - ctx.lineTo(corners[2].screen.x, corners[2].screen.y); - ctx.lineTo(corners[3].screen.x, corners[3].screen.y); - ctx.fill(); - ctx.stroke(); - } - } -}; + /** + * This function connects an edge that was connected to a cluster node back to the child node. + * + * @param parentNode | Node object + * @param childNode | Node object + * @private + */ + exports._connectEdgeBackToChild = function(parentNode, childNode) { + if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { + for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { + var edge = parentNode.reroutedEdges[childNode.id][i]; + if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { + edge.originalFromId.pop(); + edge.fromId = childNode.id; + edge.from = childNode; + } + else { + edge.originalToId.pop(); + edge.toId = childNode.id; + edge.to = childNode; + } -/** - * Draw a line through all datapoints. - * This function can be used when the style is 'line' - */ -Graph3d.prototype._redrawDataLine = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - point, i; + // append this edge to the list of edges connecting to the childnode + childNode.dynamicEdges.push(edge); - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + // remove the edge from the parent object + for (var j = 0; j < parentNode.dynamicEdges.length; j++) { + if (parentNode.dynamicEdges[j].id == edge.id) { + parentNode.dynamicEdges.splice(j,1); + break; + } + } + } + // remove the entry from the rerouted edges + delete parentNode.reroutedEdges[childNode.id]; + } + }; - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - } + /** + * When loops are clustered, an edge can be both in the rerouted array and the contained array. + * This function is called last to verify that all edges in dynamicEdges are in fact connected to the + * parentNode + * + * @param parentNode | Node object + * @private + */ + exports._validateEdges = function(parentNode) { + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { + parentNode.dynamicEdges.splice(i,1); + } + } + }; - // start the line - if (this.dataPoints.length > 0) { - point = this.dataPoints[0]; - ctx.lineWidth = 1; // TODO: make customizable - ctx.strokeStyle = 'blue'; // TODO: make customizable - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - } + /** + * This function released the contained edges back into the global domain and puts them back into the + * dynamic edges of both parent and child. + * + * @param {Node} parentNode | + * @param {Node} childNode | + * @private + */ + exports._releaseContainedEdges = function(parentNode, childNode) { + for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { + var edge = parentNode.containedEdges[childNode.id][i]; - // draw the datapoints as colored circles - for (i = 1; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - ctx.lineTo(point.screen.x, point.screen.y); - } + // put the edge back in the global edges object + this.edges[edge.id] = edge; - // finish the line - if (this.dataPoints.length > 0) { - ctx.stroke(); - } -}; + // put the edge back in the dynamic edges of the child and parent + childNode.dynamicEdges.push(edge); + parentNode.dynamicEdges.push(edge); + } + // remove the entry from the contained edges + delete parentNode.containedEdges[childNode.id]; -/** - * Start a moving operation inside the provided parent element - * @param {Event} event The event that occurred (required for - * retrieving the mouse position) - */ -Graph3d.prototype._onMouseDown = function(event) { - event = event || window.event; + }; - // check if mouse is still down (may be up when focus is lost for example - // in an iframe) - if (this.leftButtonDown) { - this._onMouseUp(event); - } - // only react on left mouse button down - this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); - if (!this.leftButtonDown && !this.touchDown) return; - // get mouse position (different code for IE and all other browsers) - this.startMouseX = getMouseX(event); - this.startMouseY = getMouseY(event); - this.startStart = new Date(this.start); - this.startEnd = new Date(this.end); - this.startArmRotation = this.camera.getArmRotation(); + // ------------------- UTILITY FUNCTIONS ---------------------------- // - this.frame.style.cursor = 'move'; - // add event listeners to handle moving the contents - // we store the function onmousemove and onmouseup in the graph, so we can - // remove the eventlisteners lateron in the function mouseUp() - var me = this; - this.onmousemove = function (event) {me._onMouseMove(event);}; - this.onmouseup = function (event) {me._onMouseUp(event);}; - G3DaddEventListener(document, 'mousemove', me.onmousemove); - G3DaddEventListener(document, 'mouseup', me.onmouseup); - G3DpreventDefault(event); -}; + /** + * This updates the node labels for all nodes (for debugging purposes) + */ + exports.updateLabels = function() { + var nodeId; + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.clusterSize > 1) { + node.label = "[".concat(String(node.clusterSize),"]"); + } + } + } + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.clusterSize == 1) { + if (node.originalLabel !== undefined) { + node.label = node.originalLabel; + } + else { + node.label = String(node.id); + } + } + } + } -/** - * Perform moving operating. - * This function activated from within the funcion Graph.mouseDown(). - * @param {Event} event Well, eehh, the event - */ -Graph3d.prototype._onMouseMove = function (event) { - event = event || window.event; + // /* Debug Override */ + // for (nodeId in this.nodes) { + // if (this.nodes.hasOwnProperty(nodeId)) { + // node = this.nodes[nodeId]; + // node.label = String(node.level); + // } + // } - // calculate change in mouse position - var diffX = parseFloat(getMouseX(event)) - this.startMouseX; - var diffY = parseFloat(getMouseY(event)) - this.startMouseY; + }; - var horizontalNew = this.startArmRotation.horizontal + diffX / 200; - var verticalNew = this.startArmRotation.vertical + diffY / 200; - var snapAngle = 4; // degrees - var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); + /** + * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes + * if the rest of the nodes are already a few cluster levels in. + * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not + * clustered enough to the clusterToSmallestNeighbours function. + */ + exports.normalizeClusterLevels = function() { + var maxLevel = 0; + var minLevel = 1e9; + var clusterLevel = 0; + var nodeId; - // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... - // the -0.001 is to take care that the vertical axis is always drawn at the left front corner - if (Math.abs(Math.sin(horizontalNew)) < snapValue) { - horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001; - } - if (Math.abs(Math.cos(horizontalNew)) < snapValue) { - horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001; - } - - // snap vertically to nice angles - if (Math.abs(Math.sin(verticalNew)) < snapValue) { - verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI; - } - if (Math.abs(Math.cos(verticalNew)) < snapValue) { - verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI; - } - - this.camera.setArmRotation(horizontalNew, verticalNew); - this.redraw(); - - // fire a cameraPositionChange event - var parameters = this.getCameraPosition(); - this.emit('cameraPositionChange', parameters); - - G3DpreventDefault(event); -}; - - -/** - * Stop moving operating. - * This function activated from within the funcion Graph.mouseDown(). - * @param {event} event The event - */ -Graph3d.prototype._onMouseUp = function (event) { - this.frame.style.cursor = 'auto'; - this.leftButtonDown = false; - - // remove event listeners here - G3DremoveEventListener(document, 'mousemove', this.onmousemove); - G3DremoveEventListener(document, 'mouseup', this.onmouseup); - G3DpreventDefault(event); -}; - -/** - * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point - * @param {Event} event A mouse move event - */ -Graph3d.prototype._onTooltip = function (event) { - var delay = 300; // ms - var mouseX = getMouseX(event) - getAbsoluteLeft(this.frame); - var mouseY = getMouseY(event) - getAbsoluteTop(this.frame); - - if (!this.showTooltip) { - return; - } - - if (this.tooltipTimeout) { - clearTimeout(this.tooltipTimeout); - } - - // (delayed) display of a tooltip only if no mouse button is down - if (this.leftButtonDown) { - this._hideTooltip(); - return; - } - - if (this.tooltip && this.tooltip.dataPoint) { - // tooltip is currently visible - var dataPoint = this._dataPointFromXY(mouseX, mouseY); - if (dataPoint !== this.tooltip.dataPoint) { - // datapoint changed - if (dataPoint) { - this._showTooltip(dataPoint); - } - else { - this._hideTooltip(); + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + clusterLevel = this.nodes[nodeId].clusterSessions.length; + if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} + if (minLevel > clusterLevel) {minLevel = clusterLevel;} } } - } - else { - // tooltip is currently not visible - var me = this; - this.tooltipTimeout = setTimeout(function () { - me.tooltipTimeout = null; - // show a tooltip if we have a data point - var dataPoint = me._dataPointFromXY(mouseX, mouseY); - if (dataPoint) { - me._showTooltip(dataPoint); + if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { + var amountOfNodes = this.nodeIndices.length; + var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].clusterSessions.length < targetLevel) { + this._clusterToSmallestNeighbour(this.nodes[nodeId]); + } + } } - }, delay); - } -}; - -/** - * Event handler for touchstart event on mobile devices - */ -Graph3d.prototype._onTouchStart = function(event) { - this.touchDown = true; - - var me = this; - this.ontouchmove = function (event) {me._onTouchMove(event);}; - this.ontouchend = function (event) {me._onTouchEnd(event);}; - G3DaddEventListener(document, 'touchmove', me.ontouchmove); - G3DaddEventListener(document, 'touchend', me.ontouchend); - - this._onMouseDown(event); -}; + this._updateNodeIndexList(); + this._updateDynamicEdges(); + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; + } + } + }; -/** - * Event handler for touchmove event on mobile devices - */ -Graph3d.prototype._onTouchMove = function(event) { - this._onMouseMove(event); -}; -/** - * Event handler for touchend event on mobile devices - */ -Graph3d.prototype._onTouchEnd = function(event) { - this.touchDown = false; - G3DremoveEventListener(document, 'touchmove', this.ontouchmove); - G3DremoveEventListener(document, 'touchend', this.ontouchend); + /** + * This function determines if the cluster we want to decluster is in the active area + * this means around the zoom center + * + * @param {Node} node + * @returns {boolean} + * @private + */ + exports._nodeInActiveArea = function(node) { + return ( + Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale + && + Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale + ) + }; - this._onMouseUp(event); -}; + /** + * This is an adaptation of the original repositioning function. This is called if the system is clustered initially + * It puts large clusters away from the center and randomizes the order. + * + */ + exports.repositionNodes = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + if ((node.xFixed == false || node.yFixed == false)) { + var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.mass); + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} + this._repositionBezierNodes(node); + } + } + }; -/** - * Event handler for mouse wheel event, used to zoom the graph - * Code from http://adomas.org/javascript-mouse-wheel/ - * @param {event} event The event - */ -Graph3d.prototype._onWheel = function(event) { - if (!event) /* For IE. */ - event = window.event; - - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta/120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail/3; - } - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - var oldLength = this.camera.getArmLength(); - var newLength = oldLength * (1 - delta / 10); + /** + * We determine how many connections denote an important hub. + * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) + * + * @private + */ + exports._getHubSize = function() { + var average = 0; + var averageSquared = 0; + var hubCounter = 0; + var largestHub = 0; - this.camera.setArmLength(newLength); - this.redraw(); + for (var i = 0; i < this.nodeIndices.length; i++) { - this._hideTooltip(); - } + var node = this.nodes[this.nodeIndices[i]]; + if (node.dynamicEdgesLength > largestHub) { + largestHub = node.dynamicEdgesLength; + } + average += node.dynamicEdgesLength; + averageSquared += Math.pow(node.dynamicEdgesLength,2); + hubCounter += 1; + } + average = average / hubCounter; + averageSquared = averageSquared / hubCounter; - // fire a cameraPositionChange event - var parameters = this.getCameraPosition(); - this.emit('cameraPositionChange', parameters); + var variance = averageSquared - Math.pow(average,2); - // Prevent default actions caused by mouse wheel. - // That might be ugly, but we handle scrolls somehow - // anyway, so don't bother here.. - G3DpreventDefault(event); -}; + var standardDeviation = Math.sqrt(variance); -/** - * Test whether a point lies inside given 2D triangle - * @param {Point2d} point - * @param {Point2d[]} triangle - * @return {boolean} Returns true if given point lies inside or on the edge of the triangle - * @private - */ -Graph3d.prototype._insideTriangle = function (point, triangle) { - var a = triangle[0], - b = triangle[1], - c = triangle[2]; + this.hubThreshold = Math.floor(average + 2*standardDeviation); - function sign (x) { - return x > 0 ? 1 : x < 0 ? -1 : 0; - } + // always have at least one to cluster + if (this.hubThreshold > largestHub) { + this.hubThreshold = largestHub; + } - var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); - var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); - var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); + // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); + // console.log("hubThreshold:",this.hubThreshold); + }; - // each of the three signs must be either equal to each other or zero - return (as == 0 || bs == 0 || as == bs) && - (bs == 0 || cs == 0 || bs == cs) && - (as == 0 || cs == 0 || as == cs); -}; -/** - * Find a data point close to given screen position (x, y) - * @param {Number} x - * @param {Number} y - * @return {Object | null} The closest data point or null if not close to any data point - * @private - */ -Graph3d.prototype._dataPointFromXY = function (x, y) { - var i, - distMax = 100, // px - dataPoint = null, - closestDataPoint = null, - closestDist = null, - center = new Point2d(x, y); - - if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - // the data points are ordered from far away to closest - for (i = this.dataPoints.length - 1; i >= 0; i--) { - dataPoint = this.dataPoints[i]; - var surfaces = dataPoint.surfaces; - if (surfaces) { - for (var s = surfaces.length - 1; s >= 0; s--) { - // split each surface in two triangles, and see if the center point is inside one of these - var surface = surfaces[s]; - var corners = surface.corners; - var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; - var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; - if (this._insideTriangle(center, triangle1) || - this._insideTriangle(center, triangle2)) { - // return immediately at the first hit - return dataPoint; + /** + * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. + * + * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce + * @private + */ + exports._reduceAmountOfChains = function(fraction) { + this.hubThreshold = 2; + var reduceAmount = Math.floor(this.nodeIndices.length * fraction); + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + if (reduceAmount > 0) { + this._formClusterFromHub(this.nodes[nodeId],true,true,1); + reduceAmount -= 1; } } } } - } - else { - // find the closest data point, using distance to the center of the point on 2d screen - for (i = 0; i < this.dataPoints.length; i++) { - dataPoint = this.dataPoints[i]; - var point = dataPoint.screen; - if (point) { - var distX = Math.abs(x - point.x); - var distY = Math.abs(y - point.y); - var dist = Math.sqrt(distX * distX + distY * distY); + }; - if ((closestDist === null || dist < closestDist) && dist < distMax) { - closestDist = dist; - closestDataPoint = dataPoint; + /** + * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. + * + * @private + */ + exports._getChainFraction = function() { + var chains = 0; + var total = 0; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + chains += 1; } + total += 1; } } - } - + return chains/total; + }; - return closestDataPoint; -}; -/** - * Display a tooltip for given data point - * @param {Object} dataPoint - * @private - */ -Graph3d.prototype._showTooltip = function (dataPoint) { - var content, line, dot; - - if (!this.tooltip) { - content = document.createElement('div'); - content.style.position = 'absolute'; - content.style.padding = '10px'; - content.style.border = '1px solid #4d4d4d'; - content.style.color = '#1a1a1a'; - content.style.background = 'rgba(255,255,255,0.7)'; - content.style.borderRadius = '2px'; - content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)'; - - line = document.createElement('div'); - line.style.position = 'absolute'; - line.style.height = '40px'; - line.style.width = '0'; - line.style.borderLeft = '1px solid #4d4d4d'; - - dot = document.createElement('div'); - dot.style.position = 'absolute'; - dot.style.height = '0'; - dot.style.width = '0'; - dot.style.border = '5px solid #4d4d4d'; - dot.style.borderRadius = '5px'; - - this.tooltip = { - dataPoint: null, - dom: { - content: content, - line: line, - dot: dot - } - }; - } - else { - content = this.tooltip.dom.content; - line = this.tooltip.dom.line; - dot = this.tooltip.dom.dot; - } +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { - this._hideTooltip(); + var util = __webpack_require__(1); - this.tooltip.dataPoint = dataPoint; - if (typeof this.showTooltip === 'function') { - content.innerHTML = this.showTooltip(dataPoint.point); - } - else { - content.innerHTML = '' + - '' + - '' + - '' + - '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; - } + /** + * Creation of the SectorMixin var. + * + * This contains all the functions the Network object can use to employ the sector system. + * The sector system is always used by Network, though the benefits only apply to the use of clustering. + * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. + */ - content.style.left = '0'; - content.style.top = '0'; - this.frame.appendChild(content); - this.frame.appendChild(line); - this.frame.appendChild(dot); - - // calculate sizes - var contentWidth = content.offsetWidth; - var contentHeight = content.offsetHeight; - var lineHeight = line.offsetHeight; - var dotWidth = dot.offsetWidth; - var dotHeight = dot.offsetHeight; - - var left = dataPoint.screen.x - contentWidth / 2; - left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); - - line.style.left = dataPoint.screen.x + 'px'; - line.style.top = (dataPoint.screen.y - lineHeight) + 'px'; - content.style.left = left + 'px'; - content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px'; - dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px'; - dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px'; -}; + /** + * This function is only called by the setData function of the Network object. + * This loads the global references into the active sector. This initializes the sector. + * + * @private + */ + exports._putDataInSector = function() { + this.sectors["active"][this._sector()].nodes = this.nodes; + this.sectors["active"][this._sector()].edges = this.edges; + this.sectors["active"][this._sector()].nodeIndices = this.nodeIndices; + }; -/** - * Hide the tooltip when displayed - * @private - */ -Graph3d.prototype._hideTooltip = function () { - if (this.tooltip) { - this.tooltip.dataPoint = null; - for (var prop in this.tooltip.dom) { - if (this.tooltip.dom.hasOwnProperty(prop)) { - var elem = this.tooltip.dom[prop]; - if (elem && elem.parentNode) { - elem.parentNode.removeChild(elem); - } - } + /** + * /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied (active) sector. If a type is defined, do the specific type + * + * @param {String} sectorId + * @param {String} [sectorType] | "active" or "frozen" + * @private + */ + exports._switchToSector = function(sectorId, sectorType) { + if (sectorType === undefined || sectorType == "active") { + this._switchToActiveSector(sectorId); } - } -}; + else { + this._switchToFrozenSector(sectorId); + } + }; -/** - * Add and event listener. Works for all browsers - * @param {Element} element An html element - * @param {string} action The action, for example 'click', - * without the prefix 'on' - * @param {function} listener The callback function to be executed - * @param {boolean} useCapture - */ -G3DaddEventListener = function(element, action, listener, useCapture) { - if (element.addEventListener) { - if (useCapture === undefined) - useCapture = false; + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @param sectorId + * @private + */ + exports._switchToActiveSector = function(sectorId) { + this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["active"][sectorId]["nodes"]; + this.edges = this.sectors["active"][sectorId]["edges"]; + }; - if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) { - action = 'DOMMouseScroll'; // For Firefox - } - element.addEventListener(action, listener, useCapture); - } else { - element.attachEvent('on' + action, listener); // IE browsers - } -}; + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @private + */ + exports._switchToSupportSector = function() { + this.nodeIndices = this.sectors["support"]["nodeIndices"]; + this.nodes = this.sectors["support"]["nodes"]; + this.edges = this.sectors["support"]["edges"]; + }; -/** - * Remove an event listener from an element - * @param {Element} element An html dom element - * @param {string} action The name of the event, for example 'mousedown' - * @param {function} listener The listener function - * @param {boolean} useCapture - */ -G3DremoveEventListener = function(element, action, listener, useCapture) { - if (element.removeEventListener) { - // non-IE browsers - if (useCapture === undefined) - useCapture = false; - if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) { - action = 'DOMMouseScroll'; // For Firefox - } + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied frozen sector. + * + * @param sectorId + * @private + */ + exports._switchToFrozenSector = function(sectorId) { + this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["frozen"][sectorId]["nodes"]; + this.edges = this.sectors["frozen"][sectorId]["edges"]; + }; - element.removeEventListener(action, listener, useCapture); - } else { - // IE browsers - element.detachEvent('on' + action, listener); - } -}; -/** - * Stop event propagation - */ -G3DstopPropagation = function(event) { - if (!event) - event = window.event; + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the currently active sector. + * + * @private + */ + exports._loadLatestSector = function() { + this._switchToSector(this._sector()); + }; - if (event.stopPropagation) { - event.stopPropagation(); // non-IE browsers - } - else { - event.cancelBubble = true; // IE browsers - } -}; + /** + * This function returns the currently active sector Id + * + * @returns {String} + * @private + */ + exports._sector = function() { + return this.activeSector[this.activeSector.length-1]; + }; -/** - * Cancels the event if it is cancelable, without stopping further propagation of the event. - */ -G3DpreventDefault = function (event) { - if (!event) - event = window.event; - if (event.preventDefault) { - event.preventDefault(); // non-IE browsers - } - else { - event.returnValue = false; // IE browsers - } -}; + /** + * This function returns the previously active sector Id + * + * @returns {String} + * @private + */ + exports._previousSector = function() { + if (this.activeSector.length > 1) { + return this.activeSector[this.activeSector.length-2]; + } + else { + throw new TypeError('there are not enough sectors in the this.activeSector array.'); + } + }; + /** + * We add the active sector at the end of the this.activeSector array + * This ensures it is the currently active sector returned by _sector() and it reaches the top + * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. + * + * @param newId + * @private + */ + exports._setActiveSector = function(newId) { + this.activeSector.push(newId); + }; -/** - * @prototype Point3d - * @param {Number} x - * @param {Number} y - * @param {Number} z - */ -function Point3d(x, y, z) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - this.z = z !== undefined ? z : 0; -}; -/** - * Subtract the two provided points, returns a-b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a-b - */ -Point3d.subtract = function(a, b) { - var sub = new Point3d(); - sub.x = a.x - b.x; - sub.y = a.y - b.y; - sub.z = a.z - b.z; - return sub; -}; + /** + * We remove the currently active sector id from the active sector stack. This happens when + * we reactivate the previously active sector + * + * @private + */ + exports._forgetLastSector = function() { + this.activeSector.pop(); + }; -/** - * Add the two provided points, returns a+b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a+b - */ -Point3d.add = function(a, b) { - var sum = new Point3d(); - sum.x = a.x + b.x; - sum.y = a.y + b.y; - sum.z = a.z + b.z; - return sum; -}; -/** - * Calculate the average of two 3d points - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} The average, (a+b)/2 - */ -Point3d.avg = function(a, b) { - return new Point3d( - (a.x + b.x) / 2, - (a.y + b.y) / 2, - (a.z + b.z) / 2 - ); -}; + /** + * This function creates a new active sector with the supplied newId. This newId + * is the expanding node id. + * + * @param {String} newId | Id of the new active sector + * @private + */ + exports._createNewSector = function(newId) { + // create the new sector + this.sectors["active"][newId] = {"nodes":{}, + "edges":{}, + "nodeIndices":[], + "formationScale": this.scale, + "drawingNode": undefined}; -/** - * Calculate the cross product of the two provided points, returns axb - * Documentation: http://en.wikipedia.org/wiki/Cross_product - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} cross product axb - */ -Point3d.crossProduct = function(a, b) { - var crossproduct = new Point3d(); + // create the new sector render node. This gives visual feedback that you are in a new sector. + this.sectors["active"][newId]['drawingNode'] = new Node( + {id:newId, + color: { + background: "#eaefef", + border: "495c5e" + } + },{},{},this.constants); + this.sectors["active"][newId]['drawingNode'].clusterSize = 2; + }; - crossproduct.x = a.y * b.z - a.z * b.y; - crossproduct.y = a.z * b.x - a.x * b.z; - crossproduct.z = a.x * b.y - a.y * b.x; - return crossproduct; -}; + /** + * This function removes the currently active sector. This is called when we create a new + * active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ + exports._deleteActiveSector = function(sectorId) { + delete this.sectors["active"][sectorId]; + }; -/** - * Rtrieve the length of the vector (or the distance from this point to the origin - * @return {Number} length - */ -Point3d.prototype.length = function() { - return Math.sqrt( - this.x * this.x + - this.y * this.y + - this.z * this.z - ); -}; + /** + * This function removes the currently active sector. This is called when we reactivate + * the previously active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ + exports._deleteFrozenSector = function(sectorId) { + delete this.sectors["frozen"][sectorId]; + }; -/** - * @prototype Point2d - */ -Point2d = function (x, y) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; -}; + /** + * Freezing an active sector means moving it from the "active" object to the "frozen" object. + * We copy the references, then delete the active entree. + * + * @param sectorId + * @private + */ + exports._freezeSector = function(sectorId) { + // we move the set references from the active to the frozen stack. + this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; -/** - * @class Filter - * - * @param {DataSet} data The google data table - * @param {Number} column The index of the column to be filtered - * @param {Graph} graph The graph - */ -function Filter (data, column, graph) { - this.data = data; - this.column = column; - this.graph = graph; // the parent graph + // we have moved the sector data into the frozen set, we now remove it from the active set + this._deleteActiveSector(sectorId); + }; - this.index = undefined; - this.value = undefined; - // read all distinct values and select the first one - this.values = graph.getDistinctValues(data.get(), this.column); + /** + * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" + * object to the "active" object. + * + * @param sectorId + * @private + */ + exports._activateSector = function(sectorId) { + // we move the set references from the frozen to the active stack. + this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; - // sort both numeric and string values correctly - this.values.sort(function (a, b) { - return a > b ? 1 : a < b ? -1 : 0; - }); + // we have moved the sector data into the active set, we now remove it from the frozen stack + this._deleteFrozenSector(sectorId); + }; - if (this.values.length > 0) { - this.selectValue(0); - } - // create an array with the filtered datapoints. this will be loaded afterwards - this.dataPoints = []; + /** + * This function merges the data from the currently active sector with a frozen sector. This is used + * in the process of reverting back to the previously active sector. + * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it + * upon the creation of a new active sector. + * + * @param sectorId + * @private + */ + exports._mergeThisWithFrozen = function(sectorId) { + // copy all nodes + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; + } + } - this.loaded = false; - this.onLoadCallback = undefined; + // copy all edges (if not fully clustered, else there are no edges) + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; + } + } - if (graph.animationPreload) { - this.loaded = false; - this.loadInBackground(); - } - else { - this.loaded = true; - } -}; + // merge the nodeIndices + for (var i = 0; i < this.nodeIndices.length; i++) { + this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); + } + }; -/** - * Return the label - * @return {string} label - */ -Filter.prototype.isLoaded = function() { - return this.loaded; -}; + /** + * This clusters the sector to one cluster. It was a single cluster before this process started so + * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. + * + * @private + */ + exports._collapseThisToSingleCluster = function() { + this.clusterToFit(1,false); + }; -/** - * Return the loaded progress - * @return {Number} percentage between 0 and 100 - */ -Filter.prototype.getLoadedProgress = function() { - var len = this.values.length; + /** + * We create a new active sector from the node that we want to open. + * + * @param node + * @private + */ + exports._addSector = function(node) { + // this is the currently active sector + var sector = this._sector(); - var i = 0; - while (this.dataPoints[i]) { - i++; - } + // // this should allow me to select nodes from a frozen set. + // if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { + // console.log("the node is part of the active sector"); + // } + // else { + // console.log("I dont know what the fuck happened!!"); + // } - return Math.round(i / len * 100); -}; + // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. + delete this.nodes[node.id]; + var unqiueIdentifier = util.randomUUID(); -/** - * Return the label - * @return {string} label - */ -Filter.prototype.getLabel = function() { - return this.graph.filterLabel; -}; + // we fully freeze the currently active sector + this._freezeSector(sector); + // we create a new active sector. This sector has the Id of the node to ensure uniqueness + this._createNewSector(unqiueIdentifier); -/** - * Return the columnIndex of the filter - * @return {Number} columnIndex - */ -Filter.prototype.getColumn = function() { - return this.column; -}; + // we add the active sector to the sectors array to be able to revert these steps later on + this._setActiveSector(unqiueIdentifier); -/** - * Return the currently selected value. Returns undefined if there is no selection - * @return {*} value - */ -Filter.prototype.getSelectedValue = function() { - if (this.index === undefined) - return undefined; + // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier + this._switchToSector(this._sector()); - return this.values[this.index]; -}; + // finally we add the node we removed from our previous active sector to the new active sector + this.nodes[node.id] = node; + }; -/** - * Retrieve all values of the filter - * @return {Array} values - */ -Filter.prototype.getValues = function() { - return this.values; -}; -/** - * Retrieve one value of the filter - * @param {Number} index - * @return {*} value - */ -Filter.prototype.getValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; + /** + * We close the sector that is currently open and revert back to the one before. + * If the active sector is the "default" sector, nothing happens. + * + * @private + */ + exports._collapseSector = function() { + // the currently active sector + var sector = this._sector(); - return this.values[index]; -}; + // we cannot collapse the default sector + if (sector != "default") { + if ((this.nodeIndices.length == 1) || + (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + var previousSector = this._previousSector(); + // we collapse the sector back to a single cluster + this._collapseThisToSingleCluster(); -/** - * Retrieve the (filtered) dataPoints for the currently selected filter index - * @param {Number} [index] (optional) - * @return {Array} dataPoints - */ -Filter.prototype._getDataPoints = function(index) { - if (index === undefined) - index = this.index; + // we move the remaining nodes, edges and nodeIndices to the previous sector. + // This previous sector is the one we will reactivate + this._mergeThisWithFrozen(previousSector); - if (index === undefined) - return []; + // the previously active (frozen) sector now has all the data from the currently active sector. + // we can now delete the active sector. + this._deleteActiveSector(sector); - var dataPoints; - if (this.dataPoints[index]) { - dataPoints = this.dataPoints[index]; - } - else { - var f = {}; - f.column = this.column; - f.value = this.values[index]; + // we activate the previously active (and currently frozen) sector. + this._activateSector(previousSector); - var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); - dataPoints = this.graph._getDataPoints(dataView); + // we load the references from the newly active sector into the global references + this._switchToSector(previousSector); - this.dataPoints[index] = dataPoints; - } + // we forget the previously active sector because we reverted to the one before + this._forgetLastSector(); - return dataPoints; -}; + // finally, we update the node index list. + this._updateNodeIndexList(); + // we refresh the list with calulation nodes and calculation node indices. + this._updateCalculationNodes(); + } + } + }; -/** - * Set a callback function when the filter is fully loaded. - */ -Filter.prototype.setOnLoadCallback = function(callback) { - this.onLoadCallback = callback; -}; + /** + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInAllActiveSectors = function(runFunction,argument) { + if (argument === undefined) { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + this[runFunction](); + } + } + } + else { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); + } + else { + this[runFunction](argument); + } + } + } + } + // we revert the global references back to our active sector + this._loadLatestSector(); + }; + + + /** + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInSupportSector = function(runFunction,argument) { + if (argument === undefined) { + this._switchToSupportSector(); + this[runFunction](); + } + else { + this._switchToSupportSector(); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); + } + else { + this[runFunction](argument); + } + } + // we revert the global references back to our active sector + this._loadLatestSector(); + }; + + + /** + * This runs a function in all frozen sectors. This is used in the _redraw(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInAllFrozenSectors = function(runFunction,argument) { + if (argument === undefined) { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + this[runFunction](); + } + } + } + else { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); + } + else { + this[runFunction](argument); + } + } + } + } + this._loadLatestSector(); + }; + + + /** + * This runs a function in all sectors. This is used in the _redraw(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInAllSectors = function(runFunction,argument) { + var args = Array.prototype.splice.call(arguments, 1); + if (argument === undefined) { + this._doInAllActiveSectors(runFunction); + this._doInAllFrozenSectors(runFunction); + } + else { + if (args.length > 1) { + this._doInAllActiveSectors(runFunction,args[0],args[1]); + this._doInAllFrozenSectors(runFunction,args[0],args[1]); + } + else { + this._doInAllActiveSectors(runFunction,argument); + this._doInAllFrozenSectors(runFunction,argument); + } + } + }; + + + /** + * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the + * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. + * + * @private + */ + exports._clearNodeIndexList = function() { + var sector = this._sector(); + this.sectors["active"][sector]["nodeIndices"] = []; + this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; + }; + + + /** + * Draw the encompassing sector node + * + * @param ctx + * @param sectorType + * @private + */ + exports._drawSectorNodes = function(ctx,sectorType) { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var sector in this.sectors[sectorType]) { + if (this.sectors[sectorType].hasOwnProperty(sector)) { + if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { + + this._switchToSector(sector,sectorType); + + minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.resize(ctx); + if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} + if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} + if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} + if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} + } + } + node = this.sectors[sectorType][sector]["drawingNode"]; + node.x = 0.5 * (maxX + minX); + node.y = 0.5 * (maxY + minY); + node.width = 2 * (node.x - minX); + node.height = 2 * (node.y - minY); + node.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); + node.setScale(this.scale); + node._drawCircle(ctx); + } + } + } + }; + + exports._drawAllSectorNodes = function(ctx) { + this._drawSectorNodes(ctx,"frozen"); + this._drawSectorNodes(ctx,"active"); + this._loadLatestSector(); + }; + + +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + var Node = __webpack_require__(30); + + /** + * This function can be called from the _doInAllSectors function + * + * @param object + * @param overlappingNodes + * @private + */ + exports._getNodesOverlappingWith = function(object, overlappingNodes) { + var nodes = this.nodes; + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].isOverlappingWith(object)) { + overlappingNodes.push(nodeId); + } + } + } + }; + + /** + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ + exports._getAllNodesOverlappingWith = function (object) { + var overlappingNodes = []; + this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); + return overlappingNodes; + }; + + + /** + * Return a position object in canvasspace from a single point in screenspace + * + * @param pointer + * @returns {{left: number, top: number, right: number, bottom: number}} + * @private + */ + exports._pointerToPositionObject = function(pointer) { + var x = this._XconvertDOMtoCanvas(pointer.x); + var y = this._YconvertDOMtoCanvas(pointer.y); + + return { + left: x, + top: y, + right: x, + bottom: y + }; + }; + + + /** + * Get the top node at the a specific point (like a click) + * + * @param {{x: Number, y: Number}} pointer + * @return {Node | null} node + * @private + */ + exports._getNodeAt = function (pointer) { + // we first check if this is an navigation controls element + var positionObject = this._pointerToPositionObject(pointer); + var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); + + // if there are overlapping nodes, select the last one, this is the + // one which is drawn on top of the others + if (overlappingNodes.length > 0) { + return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + } + else { + return null; + } + }; + + + /** + * retrieve all edges overlapping with given object, selector is around center + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ + exports._getEdgesOverlappingWith = function (object, overlappingEdges) { + var edges = this.edges; + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].isOverlappingWith(object)) { + overlappingEdges.push(edgeId); + } + } + } + }; + + + /** + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ + exports._getAllEdgesOverlappingWith = function (object) { + var overlappingEdges = []; + this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); + return overlappingEdges; + }; + + /** + * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call + * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. + * + * @param pointer + * @returns {null} + * @private + */ + exports._getEdgeAt = function(pointer) { + var positionObject = this._pointerToPositionObject(pointer); + var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); + + if (overlappingEdges.length > 0) { + return this.edges[overlappingEdges[overlappingEdges.length - 1]]; + } + else { + return null; + } + }; + + + /** + * Add object to the selection array. + * + * @param obj + * @private + */ + exports._addToSelection = function(obj) { + if (obj instanceof Node) { + this.selectionObj.nodes[obj.id] = obj; + } + else { + this.selectionObj.edges[obj.id] = obj; + } + }; + + /** + * Add object to the selection array. + * + * @param obj + * @private + */ + exports._addToHover = function(obj) { + if (obj instanceof Node) { + this.hoverObj.nodes[obj.id] = obj; + } + else { + this.hoverObj.edges[obj.id] = obj; + } + }; + + + /** + * Remove a single option from selection. + * + * @param {Object} obj + * @private + */ + exports._removeFromSelection = function(obj) { + if (obj instanceof Node) { + delete this.selectionObj.nodes[obj.id]; + } + else { + delete this.selectionObj.edges[obj.id]; + } + }; + + /** + * Unselect all. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._unselectAll = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + this.selectionObj.nodes[nodeId].unselect(); + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + this.selectionObj.edges[edgeId].unselect(); + } + } + + this.selectionObj = {nodes:{},edges:{}}; + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; + + /** + * Unselect all clusters. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._unselectClusters = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + this.selectionObj.nodes[nodeId].unselect(); + this._removeFromSelection(this.selectionObj.nodes[nodeId]); + } + } + } + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; + + + /** + * return the number of selected nodes + * + * @returns {number} + * @private + */ + exports._getSelectedNodeCount = function() { + var count = 0; + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; + } + } + return count; + }; + + /** + * return the selected node + * + * @returns {number} + * @private + */ + exports._getSelectedNode = function() { + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return this.selectionObj.nodes[nodeId]; + } + } + return null; + }; + + /** + * return the selected edge + * + * @returns {number} + * @private + */ + exports._getSelectedEdge = function() { + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + return this.selectionObj.edges[edgeId]; + } + } + return null; + }; + + + /** + * return the number of selected edges + * + * @returns {number} + * @private + */ + exports._getSelectedEdgeCount = function() { + var count = 0; + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; + } + } + return count; + }; + + + /** + * return the number of selected objects. + * + * @returns {number} + * @private + */ + exports._getSelectedObjectCount = function() { + var count = 0; + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; + } + } + return count; + }; + + /** + * Check if anything is selected + * + * @returns {boolean} + * @private + */ + exports._selectionIsEmpty = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return false; + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + return false; + } + } + return true; + }; + + + /** + * check if one of the selected nodes is a cluster. + * + * @returns {boolean} + * @private + */ + exports._clusterInSelection = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + return true; + } + } + } + return false; + }; + + /** + * select the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ + exports._selectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.select(); + this._addToSelection(edge); + } + }; + + /** + * select the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ + exports._hoverConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.hover = true; + this._addToHover(edge); + } + }; + + + /** + * unselect the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ + exports._unselectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.unselect(); + this._removeFromSelection(edge); + } + }; + + + + + /** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @param {Boolean} append + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._selectObject = function(object, append, doNotTrigger, highlightEdges) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + if (highlightEdges === undefined) { + highlightEdges = true; + } + + if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { + this._unselectAll(true); + } + + if (object.selected == false) { + object.select(); + this._addToSelection(object); + if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { + this._selectConnectedEdges(object); + } + } + else { + object.unselect(); + this._removeFromSelection(object); + } + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; + + + /** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @private + */ + exports._blurObject = function(object) { + if (object.hover == true) { + object.hover = false; + this.emit("blurNode",{node:object.id}); + } + }; + + /** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @private + */ + exports._hoverObject = function(object) { + if (object.hover == false) { + object.hover = true; + this._addToHover(object); + if (object instanceof Node) { + this.emit("hoverNode",{node:object.id}); + } + } + if (object instanceof Node) { + this._hoverConnectedEdges(object); + } + }; + + + /** + * handles the selection part of the touch, only for navigation controls elements; + * Touch is triggered before tap, also before hold. Hold triggers after a while. + * This is the most responsive solution + * + * @param {Object} pointer + * @private + */ + exports._handleTouch = function(pointer) { + }; + + + /** + * handles the selection part of the tap; + * + * @param {Object} pointer + * @private + */ + exports._handleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node,false); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge,false); + } + else { + this._unselectAll(); + } + } + this.emit("click", this.getSelection()); + this._redraw(); + }; + + + /** + * handles the selection part of the double tap and opens a cluster if needed + * + * @param {Object} pointer + * @private + */ + exports._handleDoubleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null && node !== undefined) { + // we reset the areaCenter here so the opening of the node will occur + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; + this.openCluster(node); + } + this.emit("doubleClick", this.getSelection()); + }; + + + /** + * Handle the onHold selection part + * + * @param pointer + * @private + */ + exports._handleOnHold = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node,true); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge,true); + } + } + this._redraw(); + }; + + + /** + * handle the onRelease event. These functions are here for the navigation controls module. + * + * @private + */ + exports._handleOnRelease = function(pointer) { + + }; + + + + /** + * + * retrieve the currently selected objects + * @return {{nodes: Array., edges: Array.}} selection + */ + exports.getSelection = function() { + var nodeIds = this.getSelectedNodes(); + var edgeIds = this.getSelectedEdges(); + return {nodes:nodeIds, edges:edgeIds}; + }; + + /** + * + * retrieve the currently selected nodes + * @return {String[]} selection An array with the ids of the + * selected nodes. + */ + exports.getSelectedNodes = function() { + var idArray = []; + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + idArray.push(nodeId); + } + } + return idArray + }; + + /** + * + * retrieve the currently selected edges + * @return {Array} selection An array with the ids of the + * selected nodes. + */ + exports.getSelectedEdges = function() { + var idArray = []; + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + idArray.push(edgeId); + } + } + return idArray; + }; + + + /** + * select zero or more nodes + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + */ + exports.setSelection = function(selection) { + var i, iMax, id; + + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; + + // first unselect any selected node + this._unselectAll(true); + + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; + + var node = this.nodes[id]; + if (!node) { + throw new RangeError('Node with id "' + id + '" not found'); + } + this._selectObject(node,true,true); + } + + console.log("setSelection is deprecated. Please use selectNodes instead.") + + this.redraw(); + }; + + + /** + * select zero or more nodes with the option to highlight edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + * @param {boolean} [highlightEdges] + */ + exports.selectNodes = function(selection, highlightEdges) { + var i, iMax, id; + + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; + + // first unselect any selected node + this._unselectAll(true); + + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; + + var node = this.nodes[id]; + if (!node) { + throw new RangeError('Node with id "' + id + '" not found'); + } + this._selectObject(node,true,true,highlightEdges); + } + this.redraw(); + }; + + + /** + * select zero or more edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + */ + exports.selectEdges = function(selection) { + var i, iMax, id; + + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; + + // first unselect any selected node + this._unselectAll(true); + + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; + + var edge = this.edges[id]; + if (!edge) { + throw new RangeError('Edge with id "' + id + '" not found'); + } + this._selectObject(edge,true,true,highlightEdges); + } + this.redraw(); + }; + + /** + * Validate the selection: remove ids of nodes which no longer exist + * @private + */ + exports._updateSelection = function () { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (!this.nodes.hasOwnProperty(nodeId)) { + delete this.selectionObj.nodes[nodeId]; + } + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + if (!this.edges.hasOwnProperty(edgeId)) { + delete this.selectionObj.edges[edgeId]; + } + } + } + }; + + +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + + /** + * clears the toolbar div element of children + * + * @private + */ + exports._clearManipulatorBar = function() { + while (this.manipulationDiv.hasChildNodes()) { + this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + } + }; + + /** + * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore + * these functions to their original functionality, we saved them in this.cachedFunctions. + * This function restores these functions to their original function. + * + * @private + */ + exports._restoreOverloadedFunctions = function() { + for (var functionName in this.cachedFunctions) { + if (this.cachedFunctions.hasOwnProperty(functionName)) { + this[functionName] = this.cachedFunctions[functionName]; + } + } + }; + + /** + * Enable or disable edit-mode. + * + * @private + */ + exports._toggleEditMode = function() { + this.editMode = !this.editMode; + var toolbar = document.getElementById("network-manipulationDiv"); + var closeDiv = document.getElementById("network-manipulation-closeDiv"); + var editModeDiv = document.getElementById("network-manipulation-editMode"); + if (this.editMode == true) { + toolbar.style.display="block"; + closeDiv.style.display="block"; + editModeDiv.style.display="none"; + closeDiv.onclick = this._toggleEditMode.bind(this); + } + else { + toolbar.style.display="none"; + closeDiv.style.display="none"; + editModeDiv.style.display="block"; + closeDiv.onclick = null; + } + this._createManipulatorBar() + }; + + /** + * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. + * + * @private + */ + exports._createManipulatorBar = function() { + // remove bound functions + if (this.boundFunction) { + this.off('select', this.boundFunction); + } + if (this.edgeBeingEdited !== undefined) { + this.edgeBeingEdited._disableControlNodes(); + this.edgeBeingEdited = undefined; + this.selectedControlNode = null; + } + + // restore overloaded functions + this._restoreOverloadedFunctions(); + + // resume calculation + this.freezeSimulation = false; + + // reset global variables + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; + + if (this.editMode == true) { + while (this.manipulationDiv.hasChildNodes()) { + this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + } + // add the icons to the manipulator div + this.manipulationDiv.innerHTML = "" + + "" + + ""+this.constants.labels['add'] +"" + + "
" + + "" + + ""+this.constants.labels['link'] +""; + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + this.manipulationDiv.innerHTML += "" + + "
" + + "" + + ""+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 += "" + + "
" + + "" + + ""+this.constants.labels['del'] +""; + } + + + // bind the icons + var addNodeButton = document.getElementById("network-manipulate-addNode"); + addNodeButton.onclick = this._createAddNodeToolbar.bind(this); + var addEdgeButton = document.getElementById("network-manipulate-connectNode"); + addEdgeButton.onclick = this._createAddEdgeToolbar.bind(this); + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + var editButton = document.getElementById("network-manipulate-editNode"); + editButton.onclick = this._editNode.bind(this); + } + else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { + var editButton = document.getElementById("network-manipulate-editEdge"); + editButton.onclick = this._createEditEdgeToolbar.bind(this); + } + if (this._selectionIsEmpty() == false) { + var deleteButton = document.getElementById("network-manipulate-delete"); + deleteButton.onclick = this._deleteSelected.bind(this); + } + var closeDiv = document.getElementById("network-manipulation-closeDiv"); + closeDiv.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 editModeButton = document.getElementById("network-manipulate-editModeButton"); + editModeButton.onclick = this._toggleEditMode.bind(this); + } + }; + + + + /** + * Create the toolbar for adding Nodes + * + * @private + */ + exports._createAddNodeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + if (this.boundFunction) { + this.off('select', this.boundFunction); + } + + // create the toolbar contents + this.manipulationDiv.innerHTML = "" + + "" + + "" + this.constants.labels['back'] + " " + + "
" + + "" + + "" + this.constants.labels['addDescription'] + ""; + + // bind the icon + var backButton = document.getElementById("network-manipulate-back"); + backButton.onclick = this._createManipulatorBar.bind(this); + + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + this.boundFunction = this._addNode.bind(this); + this.on('select', this.boundFunction); + }; + + + /** + * create the toolbar to connect nodes + * + * @private + */ + exports._createAddEdgeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + this._unselectAll(true); + this.freezeSimulation = true; + + if (this.boundFunction) { + this.off('select', this.boundFunction); + } + + this._unselectAll(); + this.forceAppendSelection = false; + this.blockConnectingEdgeSelection = true; + + this.manipulationDiv.innerHTML = "" + + "" + + "" + this.constants.labels['back'] + " " + + "
" + + "" + + "" + this.constants.labels['linkDescription'] + ""; + + // bind the icon + var backButton = document.getElementById("network-manipulate-back"); + backButton.onclick = this._createManipulatorBar.bind(this); + + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + this.boundFunction = this._handleConnect.bind(this); + this.on('select', this.boundFunction); + + // temporarily overload functions + this.cachedFunctions["_handleTouch"] = this._handleTouch; + this.cachedFunctions["_handleOnRelease"] = this._handleOnRelease; + this._handleTouch = this._handleConnect; + this._handleOnRelease = this._finishConnect; + + // redraw to show the unselect + this._redraw(); + }; + + /** + * create the toolbar to edit edges + * + * @private + */ + exports._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("network-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 + */ + exports._selectControlNode = function(pointer) { + this.edgeBeingEdited.controlNodes.from.unselect(); + this.edgeBeingEdited.controlNodes.to.unselect(); + this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y)); + if (this.selectedControlNode !== null) { + this.selectedControlNode.select(); + this.freezeSimulation = true; + } + this._redraw(); + }; + + /** + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. + * + * @private + */ + exports._controlNodeDrag = function(event) { + var pointer = this._getPointer(event.gesture.center); + if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) { + this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x); + this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y); + } + this._redraw(); + }; + + exports._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 + * to walk the user through the process. + * + * @private + */ + exports._handleConnect = function(pointer) { + if (this._getSelectedNodeCount() == 0) { + var node = this._getNodeAt(pointer); + if (node != null) { + if (node.clusterSize > 1) { + alert("Cannot create edges to a cluster.") + } + else { + this._selectObject(node,false); + // create a node the temporary line can look at + this.sectors['support']['nodes']['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); + this.sectors['support']['nodes']['targetNode'].x = node.x; + this.sectors['support']['nodes']['targetNode'].y = node.y; + this.sectors['support']['nodes']['targetViaNode'] = new Node({id:'targetViaNode'},{},{},this.constants); + this.sectors['support']['nodes']['targetViaNode'].x = node.x; + this.sectors['support']['nodes']['targetViaNode'].y = node.y; + this.sectors['support']['nodes']['targetViaNode'].parentEdgeId = "connectionEdge"; + + // create a temporary edge + this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:this.sectors['support']['nodes']['targetNode'].id}, this, this.constants); + this.edges['connectionEdge'].from = node; + this.edges['connectionEdge'].connected = true; + this.edges['connectionEdge'].smooth = true; + this.edges['connectionEdge'].selected = true; + 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(event) { + var pointer = this._getPointer(event.gesture.center); + this.sectors['support']['nodes']['targetNode'].x = this._XconvertDOMtoCanvas(pointer.x); + this.sectors['support']['nodes']['targetNode'].y = this._YconvertDOMtoCanvas(pointer.y); + this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._XconvertDOMtoCanvas(pointer.x) + this.edges['connectionEdge'].from.x); + this.sectors['support']['nodes']['targetViaNode'].y = this._YconvertDOMtoCanvas(pointer.y); + }; + + this.moving = true; + this.start(); + } + } + } + }; + + exports._finishConnect = function(pointer) { + if (this._getSelectedNodeCount() == 1) { + + // restore the drag function + this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; + delete this.cachedFunctions["_handleOnDrag"]; + + // remember the edge id + var connectFromId = this.edges['connectionEdge'].fromId; + + // remove the temporary nodes and edge + delete this.edges['connectionEdge']; + delete this.sectors['support']['nodes']['targetNode']; + delete this.sectors['support']['nodes']['targetViaNode']; + + var node = this._getNodeAt(pointer); + if (node != null) { + if (node.clusterSize > 1) { + alert("Cannot create edges to a cluster.") + } + else { + this._createEdge(connectFromId,node.id); + this._createManipulatorBar(); + } + } + this._unselectAll(); + } + }; + + + /** + * Adds a node on the specified location + */ + exports._addNode = function() { + if (this._selectionIsEmpty() && this.editMode == true) { + var positionObject = this._pointerToPositionObject(this.pointerPosition); + var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; + if (this.triggerFunctions.add) { + if (this.triggerFunctions.add.length == 2) { + var me = this; + this.triggerFunctions.add(defaultData, function(finalizedData) { + me.nodesData.add(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + alert(this.constants.labels['addError']); + this._createManipulatorBar(); + this.moving = true; + this.start(); + } + } + else { + this.nodesData.add(defaultData); + this._createManipulatorBar(); + this.moving = true; + this.start(); + } + } + }; + + + /** + * connect two nodes with a new edge. + * + * @private + */ + exports._createEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.connect) { + if (this.triggerFunctions.connect.length == 2) { + var me = this; + this.triggerFunctions.connect(defaultData, function(finalizedData) { + me.edgesData.add(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + alert(this.constants.labels["linkError"]); + this.moving = true; + this.start(); + } + } + else { + this.edgesData.add(defaultData); + this.moving = true; + this.start(); + } + } + }; + + /** + * connect two nodes with a new edge. + * + * @private + */ + exports._editEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.editEdge) { + if (this.triggerFunctions.editEdge.length == 2) { + var me = this; + this.triggerFunctions.editEdge(defaultData, function(finalizedData) { + me.edgesData.update(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + 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. + * + * @private + */ + exports._editNode = function() { + if (this.triggerFunctions.edit && this.editMode == true) { + var node = this._getSelectedNode(); + var data = {id:node.id, + label: node.label, + group: node.group, + shape: node.shape, + color: { + background:node.color.background, + border:node.color.border, + highlight: { + background:node.color.highlight.background, + border:node.color.highlight.border + } + }}; + if (this.triggerFunctions.edit.length == 2) { + var me = this; + this.triggerFunctions.edit(data, function (finalizedData) { + me.nodesData.update(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + alert(this.constants.labels["editError"]); + } + } + else { + alert(this.constants.labels["editBoundError"]); + } + }; + + + + + /** + * delete everything in the selection + * + * @private + */ + exports._deleteSelected = function() { + if (!this._selectionIsEmpty() && this.editMode == true) { + if (!this._clusterInSelection()) { + var selectedNodes = this.getSelectedNodes(); + var selectedEdges = this.getSelectedEdges(); + if (this.triggerFunctions.del) { + var me = this; + var data = {nodes: selectedNodes, edges: selectedEdges}; + if (this.triggerFunctions.del.length = 2) { + this.triggerFunctions.del(data, function (finalizedData) { + me.edgesData.remove(finalizedData.edges); + me.nodesData.remove(finalizedData.nodes); + me._unselectAll(); + me.moving = true; + me.start(); + }); + } + else { + alert(this.constants.labels["deleteError"]) + } + } + else { + this.edgesData.remove(selectedEdges); + this.nodesData.remove(selectedNodes); + this._unselectAll(); + this.moving = true; + this.start(); + } + } + else { + alert(this.constants.labels["deleteClusterError"]); + } + } + }; + + +/***/ }, +/* 47 */ +/***/ function(module, exports, __webpack_require__) { + + exports._cleanNavigation = function() { + // clean up previous navigation items + var wrapper = document.getElementById('network-navigation_wrapper'); + if (wrapper != null) { + this.containerElement.removeChild(wrapper); + } + document.onmouseup = null; + }; + + /** + * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation + * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent + * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. + * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. + * + * @private + */ + exports._loadNavigationElements = function() { + this._cleanNavigation(); + + this.navigationDivs = {}; + var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; + var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','zoomExtent']; + + this.navigationDivs['wrapper'] = document.createElement('div'); + this.navigationDivs['wrapper'].id = "network-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; i < navigationDivs.length; i++) { + this.navigationDivs[navigationDivs[i]] = document.createElement('div'); + this.navigationDivs[navigationDivs[i]].id = "network-navigation_" + navigationDivs[i]; + this.navigationDivs[navigationDivs[i]].className = "network-navigation " + navigationDivs[i]; + this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); + this.navigationDivs[navigationDivs[i]].onmousedown = this[navigationDivActions[i]].bind(this); + } + + document.onmouseup = this._stopMovement.bind(this); + }; + + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + exports._stopMovement = function() { + this._xStopMoving(); + this._yStopMoving(); + this._stopZoom(); + }; + + + /** + * stops the actions performed by page up and down etc. + * + * @param event + * @private + */ + exports._preventDefault = function(event) { + if (event !== undefined) { + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } + } + }; + + + /** + * move the screen up + * By using the increments, instead of adding a fixed number to the translation, we keep fluent and + * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently + * To avoid this behaviour, we do the translation in the start loop. + * + * @private + */ + exports._moveUp = function(event) { + this.yIncrement = this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['up'].className += " active"; + } + }; + + + /** + * move the screen down + * @private + */ + exports._moveDown = function(event) { + this.yIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['down'].className += " active"; + } + }; + + + /** + * move the screen left + * @private + */ + exports._moveLeft = function(event) { + this.xIncrement = this.constants.keyboard.speed.x; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['left'].className += " active"; + } + }; + + + /** + * move the screen right + * @private + */ + exports._moveRight = function(event) { + this.xIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['right'].className += " active"; + } + }; + + + /** + * Zoom in, using the same method as the movement. + * @private + */ + exports._zoomIn = function(event) { + this.zoomIncrement = this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['zoomIn'].className += " active"; + } + }; + + + /** + * Zoom out + * @private + */ + exports._zoomOut = function() { + this.zoomIncrement = -this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['zoomOut'].className += " active"; + } + }; + + + /** + * Stop zooming and unhighlight the zoom controls + * @private + */ + exports._stopZoom = function() { + this.zoomIncrement = 0; + if (this.navigationDivs) { + this.navigationDivs['zoomIn'].className = this.navigationDivs['zoomIn'].className.replace(" active",""); + this.navigationDivs['zoomOut'].className = this.navigationDivs['zoomOut'].className.replace(" active",""); + } + }; + + + /** + * Stop moving in the Y direction and unHighlight the up and down + * @private + */ + exports._yStopMoving = function() { + this.yIncrement = 0; + if (this.navigationDivs) { + this.navigationDivs['up'].className = this.navigationDivs['up'].className.replace(" active",""); + this.navigationDivs['down'].className = this.navigationDivs['down'].className.replace(" active",""); + } + }; + + + /** + * Stop moving in the X direction and unHighlight left and right. + * @private + */ + exports._xStopMoving = function() { + this.xIncrement = 0; + if (this.navigationDivs) { + this.navigationDivs['left'].className = this.navigationDivs['left'].className.replace(" active",""); + this.navigationDivs['right'].className = this.navigationDivs['right'].className.replace(" active",""); + } + }; + + +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { + + exports._resetLevels = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.preassignedLevel == false) { + node.level = -1; + } + } + } + }; + + /** + * This is the main function to layout the nodes in a hierarchical way. + * It checks if the node details are supplied correctly + * + * @private + */ + exports._setupHierarchicalLayout = function() { + if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { + if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "DU") { + this.constants.hierarchicalLayout.levelSeparation *= -1; + } + else { + this.constants.hierarchicalLayout.levelSeparation = Math.abs(this.constants.hierarchicalLayout.levelSeparation); + } + // get the size of the largest hubs and check if the user has defined a level for a node. + var hubsize = 0; + var node, nodeId; + var definedLevel = false; + var undefinedLevel = false; + + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level != -1) { + definedLevel = true; + } + else { + undefinedLevel = true; + } + if (hubsize < node.edges.length) { + hubsize = node.edges.length; + } + } + } + + // if the user defined some levels but not all, alert and run without hierarchical layout + if (undefinedLevel == true && definedLevel == true) { + alert("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); + this.zoomExtent(true,this.constants.clustering.enabled); + if (!this.constants.clustering.enabled) { + this.start(); + } + } + else { + // setup the system to use hierarchical method. + this._changeConstants(); + + // define levels if undefined by the users. Based on hubsize + if (undefinedLevel == true) { + this._determineLevels(hubsize); + } + // check the distribution of the nodes per level. + var distribution = this._getDistribution(); + + // place the nodes on the canvas. This also stablilizes the system. + this._placeNodesByHierarchy(distribution); + + // start the simulation. + this.start(); + } + } + }; + + + /** + * This function places the nodes on the canvas based on the hierarchial distribution. + * + * @param {Object} distribution | obtained by the function this._getDistribution() + * @private + */ + exports._placeNodesByHierarchy = function(distribution) { + var nodeId, node; + + // start placing all the level 0 nodes first. Then recursively position their branches. + for (nodeId in distribution[0].nodes) { + if (distribution[0].nodes.hasOwnProperty(nodeId)) { + node = distribution[0].nodes[nodeId]; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (node.xFixed) { + node.x = distribution[0].minPos; + node.xFixed = false; + + distribution[0].minPos += distribution[0].nodeSpacing; + } + } + else { + if (node.yFixed) { + node.y = distribution[0].minPos; + node.yFixed = false; + + distribution[0].minPos += distribution[0].nodeSpacing; + } + } + this._placeBranchNodes(node.edges,node.id,distribution,node.level); + } + } + + // stabilize the system after positioning. This function calls zoomExtent. + this._stabilize(); + }; + + + /** + * This function get the distribution of levels based on hubsize + * + * @returns {Object} + * @private + */ + exports._getDistribution = function() { + var distribution = {}; + var nodeId, node, level; + + // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. + // the fix of X is removed after the x value has been set. + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.xFixed = true; + node.yFixed = true; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + else { + node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + if (!distribution.hasOwnProperty(node.level)) { + distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; + } + distribution[node.level].amount += 1; + distribution[node.level].nodes[node.id] = node; + } + } + + // determine the largest amount of nodes of all levels + var maxCount = 0; + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + if (maxCount < distribution[level].amount) { + maxCount = distribution[level].amount; + } + } + } + + // set the initial position and spacing of each nodes accordingly + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; + distribution[level].nodeSpacing /= (distribution[level].amount + 1); + distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); + } + } + + return distribution; + }; + + + /** + * this function allocates nodes in levels based on the recursive branching from the largest hubs. + * + * @param hubsize + * @private + */ + exports._determineLevels = function(hubsize) { + var nodeId, node; + + // determine hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.edges.length == hubsize) { + node.level = 0; + } + } + } + + // branch from hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level == 0) { + this._setLevel(1,node.edges,node.id); + } + } + } + }; + + + /** + * Since hierarchical layout does not support: + * - smooth curves (based on the physics), + * - clustering (based on dynamic node counts) + * + * We disable both features so there will be no problems. + * + * @private + */ + exports._changeConstants = function() { + this.constants.clustering.enabled = false; + this.constants.physics.barnesHut.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this._loadSelectedForceSolver(); + this.constants.smoothCurves = false; + this._configureSmoothCurves(); + }; + + + /** + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. + * + * @param edges + * @param parentId + * @param distribution + * @param parentLevel + * @private + */ + exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + + // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. + var nodeMoved = false; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (childNode.xFixed && childNode.level > parentLevel) { + childNode.xFixed = false; + childNode.x = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + else { + if (childNode.yFixed && childNode.level > parentLevel) { + childNode.yFixed = false; + childNode.y = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + + if (nodeMoved == true) { + distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; + if (childNode.edges.length > 1) { + this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); + } + } + } + }; + + + /** + * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. + * + * @param level + * @param edges + * @param parentId + * @private + */ + exports._setLevel = function(level, edges, parentId) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1 || childNode.level > level) { + childNode.level = level; + if (edges.length > 1) { + this._setLevel(level+1, childNode.edges, childNode.id); + } + } + } + }; + + + /** + * Unfix nodes + * + * @private + */ + exports._restoreNodes = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.nodes[nodeId].xFixed = false; + this.nodes[nodeId].yFixed = false; + } + } + }; + + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + /*! Hammer.JS - v1.0.5 - 2013-04-07 + * http://eightmedia.github.com/hammer.js + * + * Copyright (c) 2013 Jorik Tangelder ; + * Licensed under the MIT license */ + + (function(window, undefined) { + 'use strict'; + + /** + * Hammer + * use this to create instances + * @param {HTMLElement} element + * @param {Object} options + * @returns {Hammer.Instance} + * @constructor + */ + var Hammer = function(element, options) { + return new Hammer.Instance(element, options || {}); + }; + + // default settings + Hammer.defaults = { + // add styles and attributes to the element to prevent the browser from doing + // its native behavior. this doesnt prevent the scrolling, but cancels + // the contextmenu, tap highlighting etc + // set to false to disable this + stop_browser_behavior: { + // this also triggers onselectstart=false for IE + userSelect: 'none', + // this makes the element blocking in IE10 >, you could experiment with the value + // see for more options this issue; https://github.com/EightMedia/hammer.js/issues/241 + touchAction: 'none', + touchCallout: 'none', + contentZooming: 'none', + userDrag: 'none', + tapHighlightColor: 'rgba(0,0,0,0)' + } + + // more settings are defined per gesture at gestures.js + }; + + // detect touchevents + Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; + Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); + + // dont use mouseevents on mobile devices + Hammer.MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; + Hammer.NO_MOUSEEVENTS = Hammer.HAS_TOUCHEVENTS && navigator.userAgent.match(Hammer.MOBILE_REGEX); + + // eventtypes per touchevent (start, move, end) + // are filled by Hammer.event.determineEventTypes on setup + Hammer.EVENT_TYPES = {}; + + // direction defines + Hammer.DIRECTION_DOWN = 'down'; + Hammer.DIRECTION_LEFT = 'left'; + Hammer.DIRECTION_UP = 'up'; + Hammer.DIRECTION_RIGHT = 'right'; + + // pointer type + Hammer.POINTER_MOUSE = 'mouse'; + Hammer.POINTER_TOUCH = 'touch'; + Hammer.POINTER_PEN = 'pen'; + + // touch event defines + Hammer.EVENT_START = 'start'; + Hammer.EVENT_MOVE = 'move'; + Hammer.EVENT_END = 'end'; + + // hammer document where the base events are added at + Hammer.DOCUMENT = document; + + // plugins namespace + Hammer.plugins = {}; + + // if the window events are set... + Hammer.READY = false; + + /** + * setup events to detect gestures on the document + */ + function setup() { + if(Hammer.READY) { + return; + } + + // find what eventtypes we add listeners to + Hammer.event.determineEventTypes(); + + // Register all gestures inside Hammer.gestures + for(var name in Hammer.gestures) { + if(Hammer.gestures.hasOwnProperty(name)) { + Hammer.detection.register(Hammer.gestures[name]); + } + } + + // Add touch events on the document + Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_MOVE, Hammer.detection.detect); + Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_END, Hammer.detection.detect); + + // Hammer is ready...! + Hammer.READY = true; + } + + /** + * create new hammer instance + * all methods should return the instance itself, so it is chainable. + * @param {HTMLElement} element + * @param {Object} [options={}] + * @returns {Hammer.Instance} + * @constructor + */ + Hammer.Instance = function(element, options) { + var self = this; + + // setup HammerJS window events and register all gestures + // this also sets up the default options + setup(); + + this.element = element; + + // start/stop detection option + this.enabled = true; + + // merge options + this.options = Hammer.utils.extend( + Hammer.utils.extend({}, Hammer.defaults), + options || {}); + + // add some css to the element to prevent the browser from doing its native behavoir + if(this.options.stop_browser_behavior) { + Hammer.utils.stopDefaultBrowserBehavior(this.element, this.options.stop_browser_behavior); + } + + // start detection on touchstart + Hammer.event.onTouch(element, Hammer.EVENT_START, function(ev) { + if(self.enabled) { + Hammer.detection.startDetect(self, ev); + } + }); + + // return instance + return this; + }; + + + Hammer.Instance.prototype = { + /** + * bind events to the instance + * @param {String} gesture + * @param {Function} handler + * @returns {Hammer.Instance} + */ + on: function onEvent(gesture, handler){ + var gestures = gesture.split(' '); + for(var t=0; t 0 && eventType == Hammer.EVENT_END) { + eventType = Hammer.EVENT_MOVE; + } + // no touches, force the end event + else if(!count_touches) { + eventType = Hammer.EVENT_END; + } + + // because touchend has no touches, and we often want to use these in our gestures, + // we send the last move event as our eventData in touchend + if(!count_touches && last_move_event !== null) { + ev = last_move_event; + } + // store the last move event + else { + last_move_event = ev; + } + + // trigger the handler + handler.call(Hammer.detection, self.collectEventData(element, eventType, ev)); + + // remove pointerevent from list + if(Hammer.HAS_POINTEREVENTS && eventType == Hammer.EVENT_END) { + count_touches = Hammer.PointerEvent.updatePointer(eventType, ev); + } + } + + //debug(sourceEventType +" "+ eventType); + + // on the end we reset everything + if(!count_touches) { + last_move_event = null; + enable_detect = false; + touch_triggered = false; + Hammer.PointerEvent.reset(); + } + }); + }, + + + /** + * we have different events for each device/browser + * determine what we need and set them in the Hammer.EVENT_TYPES constant + */ + determineEventTypes: function determineEventTypes() { + // determine the eventtype we want to set + var types; + + // pointerEvents magic + if(Hammer.HAS_POINTEREVENTS) { + types = Hammer.PointerEvent.getEvents(); + } + // on Android, iOS, blackberry, windows mobile we dont want any mouseevents + else if(Hammer.NO_MOUSEEVENTS) { + types = [ + 'touchstart', + 'touchmove', + 'touchend touchcancel']; + } + // for non pointer events browsers and mixed browsers, + // like chrome on windows8 touch laptop + else { + types = [ + 'touchstart mousedown', + 'touchmove mousemove', + 'touchend touchcancel mouseup']; + } + + Hammer.EVENT_TYPES[Hammer.EVENT_START] = types[0]; + Hammer.EVENT_TYPES[Hammer.EVENT_MOVE] = types[1]; + Hammer.EVENT_TYPES[Hammer.EVENT_END] = types[2]; + }, + + + /** + * create touchlist depending on the event + * @param {Object} ev + * @param {String} eventType used by the fakemultitouch plugin + */ + getTouchList: function getTouchList(ev/*, eventType*/) { + // get the fake pointerEvent touchlist + if(Hammer.HAS_POINTEREVENTS) { + return Hammer.PointerEvent.getTouchList(); + } + // get the touchlist + else if(ev.touches) { + return ev.touches; + } + // make fake touchlist from mouse position + else { + return [{ + identifier: 1, + pageX: ev.pageX, + pageY: ev.pageY, + target: ev.target + }]; + } + }, + + + /** + * collect event data for Hammer js + * @param {HTMLElement} element + * @param {String} eventType like Hammer.EVENT_MOVE + * @param {Object} eventData + */ + collectEventData: function collectEventData(element, eventType, ev) { + var touches = this.getTouchList(ev, eventType); + + // find out pointerType + var pointerType = Hammer.POINTER_TOUCH; + if(ev.type.match(/mouse/) || Hammer.PointerEvent.matchType(Hammer.POINTER_MOUSE, ev)) { + pointerType = Hammer.POINTER_MOUSE; + } + + return { + center : Hammer.utils.getCenter(touches), + timeStamp : new Date().getTime(), + target : ev.target, + touches : touches, + eventType : eventType, + pointerType : pointerType, + srcEvent : ev, + + /** + * prevent the browser default actions + * mostly used to disable scrolling of the browser + */ + preventDefault: function() { + if(this.srcEvent.preventManipulation) { + this.srcEvent.preventManipulation(); + } + + if(this.srcEvent.preventDefault) { + this.srcEvent.preventDefault(); + } + }, + + /** + * stop bubbling the event up to its parents + */ + stopPropagation: function() { + this.srcEvent.stopPropagation(); + }, + + /** + * immediately stop gesture detection + * might be useful after a swipe was detected + * @return {*} + */ + stopDetect: function() { + return Hammer.detection.stopDetect(); + } + }; + } + }; + + Hammer.PointerEvent = { + /** + * holds all pointers + * @type {Object} + */ + pointers: {}, + + /** + * get a list of pointers + * @returns {Array} touchlist + */ + getTouchList: function() { + var self = this; + var touchlist = []; + + // we can use forEach since pointerEvents only is in IE10 + Object.keys(self.pointers).sort().forEach(function(id) { + touchlist.push(self.pointers[id]); + }); + return touchlist; + }, + + /** + * update the position of a pointer + * @param {String} type Hammer.EVENT_END + * @param {Object} pointerEvent + */ + updatePointer: function(type, pointerEvent) { + if(type == Hammer.EVENT_END) { + this.pointers = {}; + } + else { + pointerEvent.identifier = pointerEvent.pointerId; + this.pointers[pointerEvent.pointerId] = pointerEvent; + } + + return Object.keys(this.pointers).length; + }, + + /** + * check if ev matches pointertype + * @param {String} pointerType Hammer.POINTER_MOUSE + * @param {PointerEvent} ev + */ + matchType: function(pointerType, ev) { + if(!ev.pointerType) { + return false; + } + + var types = {}; + types[Hammer.POINTER_MOUSE] = (ev.pointerType == ev.MSPOINTER_TYPE_MOUSE || ev.pointerType == Hammer.POINTER_MOUSE); + types[Hammer.POINTER_TOUCH] = (ev.pointerType == ev.MSPOINTER_TYPE_TOUCH || ev.pointerType == Hammer.POINTER_TOUCH); + types[Hammer.POINTER_PEN] = (ev.pointerType == ev.MSPOINTER_TYPE_PEN || ev.pointerType == Hammer.POINTER_PEN); + return types[pointerType]; + }, + + + /** + * get events + */ + getEvents: function() { + return [ + 'pointerdown MSPointerDown', + 'pointermove MSPointerMove', + 'pointerup pointercancel MSPointerUp MSPointerCancel' + ]; + }, + + /** + * reset the list + */ + reset: function() { + this.pointers = {}; + } + }; + + + Hammer.utils = { + /** + * extend method, + * also used for cloning when dest is an empty object + * @param {Object} dest + * @param {Object} src + * @parm {Boolean} merge do a merge + * @returns {Object} dest + */ + extend: function extend(dest, src, merge) { + for (var key in src) { + if(dest[key] !== undefined && merge) { + continue; + } + dest[key] = src[key]; + } + return dest; + }, + + + /** + * find if a node is in the given parent + * used for event delegation tricks + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @returns {boolean} has_parent + */ + hasParent: function(node, parent) { + while(node){ + if(node == parent) { + return true; + } + node = node.parentNode; + } + return false; + }, + + + /** + * get the center of all the touches + * @param {Array} touches + * @returns {Object} center + */ + getCenter: function getCenter(touches) { + var valuesX = [], valuesY = []; + + for(var t= 0,len=touches.length; t= y) { + return touch1.pageX - touch2.pageX > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT; + } + else { + return touch1.pageY - touch2.pageY > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN; + } + }, + + + /** + * calculate the distance between two touches + * @param {Touch} touch1 + * @param {Touch} touch2 + * @returns {Number} distance + */ + getDistance: function getDistance(touch1, touch2) { + var x = touch2.pageX - touch1.pageX, + y = touch2.pageY - touch1.pageY; + return Math.sqrt((x*x) + (y*y)); + }, + + + /** + * calculate the scale factor between two touchLists (fingers) + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @param {Array} start + * @param {Array} end + * @returns {Number} scale + */ + getScale: function getScale(start, end) { + // need two fingers... + if(start.length >= 2 && end.length >= 2) { + return this.getDistance(end[0], end[1]) / + this.getDistance(start[0], start[1]); + } + return 1; + }, + + + /** + * calculate the rotation degrees between two touchLists (fingers) + * @param {Array} start + * @param {Array} end + * @returns {Number} rotation + */ + getRotation: function getRotation(start, end) { + // need two fingers + if(start.length >= 2 && end.length >= 2) { + return this.getAngle(end[1], end[0]) - + this.getAngle(start[1], start[0]); + } + return 0; + }, + + + /** + * boolean if the direction is vertical + * @param {String} direction + * @returns {Boolean} is_vertical + */ + isVertical: function isVertical(direction) { + return (direction == Hammer.DIRECTION_UP || direction == Hammer.DIRECTION_DOWN); + }, + + + /** + * stop browser default behavior with css props + * @param {HtmlElement} element + * @param {Object} css_props + */ + stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_props) { + var prop, + vendors = ['webkit','khtml','moz','ms','o','']; + + if(!css_props || !element.style) { + return; + } + + // with css properties for modern browsers + for(var i = 0; i < vendors.length; i++) { + for(var p in css_props) { + if(css_props.hasOwnProperty(p)) { + prop = p; + + // vender prefix at the property + if(vendors[i]) { + prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1); + } + + // set the style + element.style[prop] = css_props[p]; + } + } + } + + // also the disable onselectstart + if(css_props.userSelect == 'none') { + element.onselectstart = function() { + return false; + }; + } + } + }; + + Hammer.detection = { + // contains all registred Hammer.gestures in the correct order + gestures: [], + + // data of the current Hammer.gesture detection session + current: null, + + // the previous Hammer.gesture session data + // is a full clone of the previous gesture.current object + previous: null, + + // when this becomes true, no gestures are fired + stopped: false, + + + /** + * start Hammer.gesture detection + * @param {Hammer.Instance} inst + * @param {Object} eventData + */ + startDetect: function startDetect(inst, eventData) { + // already busy with a Hammer.gesture detection on an element + if(this.current) { + return; + } + + this.stopped = false; + + this.current = { + inst : inst, // reference to HammerInstance we're working for + startEvent : Hammer.utils.extend({}, eventData), // start eventData for distances, timing etc + lastEvent : false, // last eventData + name : '' // current gesture we're in/detected, can be 'tap', 'hold' etc + }; + + this.detect(eventData); + }, + + + /** + * Hammer.gesture detection + * @param {Object} eventData + * @param {Object} eventData + */ + detect: function detect(eventData) { + if(!this.current || this.stopped) { + return; + } + + // extend event data with calculations about scale, distance etc + eventData = this.extendEventData(eventData); + + // instance options + var inst_options = this.current.inst.options; + + // call Hammer.gesture handlers + for(var g=0,len=this.gestures.length; g b.index) { + return 1; + } + return 0; + }); + + return this.gestures; + } + }; + + + Hammer.gestures = Hammer.gestures || {}; + + /** + * Custom gestures + * ============================== + * + * Gesture object + * -------------------- + * The object structure of a gesture: + * + * { name: 'mygesture', + * index: 1337, + * defaults: { + * mygesture_option: true + * } + * handler: function(type, ev, inst) { + * // trigger gesture event + * inst.trigger(this.name, ev); + * } + * } + + * @param {String} name + * this should be the name of the gesture, lowercase + * it is also being used to disable/enable the gesture per instance config. + * + * @param {Number} [index=1000] + * the index of the gesture, where it is going to be in the stack of gestures detection + * like when you build an gesture that depends on the drag gesture, it is a good + * idea to place it after the index of the drag gesture. + * + * @param {Object} [defaults={}] + * the default settings of the gesture. these are added to the instance settings, + * and can be overruled per instance. you can also add the name of the gesture, + * but this is also added by default (and set to true). + * + * @param {Function} handler + * this handles the gesture detection of your custom gesture and receives the + * following arguments: + * + * @param {Object} eventData + * event data containing the following properties: + * timeStamp {Number} time the event occurred + * target {HTMLElement} target element + * touches {Array} touches (fingers, pointers, mouse) on the screen + * pointerType {String} kind of pointer that was used. matches Hammer.POINTER_MOUSE|TOUCH + * center {Object} center position of the touches. contains pageX and pageY + * deltaTime {Number} the total time of the touches in the screen + * deltaX {Number} the delta on x axis we haved moved + * deltaY {Number} the delta on y axis we haved moved + * velocityX {Number} the velocity on the x + * velocityY {Number} the velocity on y + * angle {Number} the angle we are moving + * direction {String} the direction we are moving. matches Hammer.DIRECTION_UP|DOWN|LEFT|RIGHT + * distance {Number} the distance we haved moved + * scale {Number} scaling of the touches, needs 2 touches + * rotation {Number} rotation of the touches, needs 2 touches * + * eventType {String} matches Hammer.EVENT_START|MOVE|END + * srcEvent {Object} the source event, like TouchStart or MouseDown * + * startEvent {Object} contains the same properties as above, + * but from the first touch. this is used to calculate + * distances, deltaTime, scaling etc + * + * @param {Hammer.Instance} inst + * the instance we are doing the detection for. you can get the options from + * the inst.options object and trigger the gesture event by calling inst.trigger + * + * + * Handle gestures + * -------------------- + * inside the handler you can get/set Hammer.detection.current. This is the current + * detection session. It has the following properties + * @param {String} name + * contains the name of the gesture we have detected. it has not a real function, + * only to check in other gestures if something is detected. + * like in the drag gesture we set it to 'drag' and in the swipe gesture we can + * check if the current gesture is 'drag' by accessing Hammer.detection.current.name + * + * @readonly + * @param {Hammer.Instance} inst + * the instance we do the detection for + * + * @readonly + * @param {Object} startEvent + * contains the properties of the first gesture detection in this session. + * Used for calculations about timing, distance, etc. + * + * @readonly + * @param {Object} lastEvent + * contains all the properties of the last gesture detect in this session. + * + * after the gesture detection session has been completed (user has released the screen) + * the Hammer.detection.current object is copied into Hammer.detection.previous, + * this is usefull for gestures like doubletap, where you need to know if the + * previous gesture was a tap + * + * options that have been set by the instance can be received by calling inst.options + * + * You can trigger a gesture event by calling inst.trigger("mygesture", event). + * The first param is the name of your gesture, the second the event argument + * + * + * Register gestures + * -------------------- + * When an gesture is added to the Hammer.gestures object, it is auto registered + * at the setup of the first Hammer instance. You can also call Hammer.detection.register + * manually and pass your gesture object as a param + * + */ + + /** + * Hold + * Touch stays at the same place for x time + * @events hold + */ + Hammer.gestures.Hold = { + name: 'hold', + index: 10, + defaults: { + hold_timeout : 500, + hold_threshold : 1 + }, + timer: null, + handler: function holdGesture(ev, inst) { + switch(ev.eventType) { + case Hammer.EVENT_START: + // clear any running timers + clearTimeout(this.timer); + + // set the gesture so we can check in the timeout if it still is + Hammer.detection.current.name = this.name; + + // set timer and if after the timeout it still is hold, + // we trigger the hold event + this.timer = setTimeout(function() { + if(Hammer.detection.current.name == 'hold') { + inst.trigger('hold', ev); + } + }, inst.options.hold_timeout); + break; + + // when you move or end we clear the timer + case Hammer.EVENT_MOVE: + if(ev.distance > inst.options.hold_threshold) { + clearTimeout(this.timer); + } + break; + + case Hammer.EVENT_END: + clearTimeout(this.timer); + break; + } + } + }; + + + /** + * Tap/DoubleTap + * Quick touch at a place or double at the same place + * @events tap, doubletap + */ + Hammer.gestures.Tap = { + name: 'tap', + index: 100, + defaults: { + tap_max_touchtime : 250, + tap_max_distance : 10, + tap_always : true, + doubletap_distance : 20, + doubletap_interval : 300 + }, + handler: function tapGesture(ev, inst) { + if(ev.eventType == Hammer.EVENT_END) { + // previous gesture, for the double tap since these are two different gesture detections + var prev = Hammer.detection.previous, + did_doubletap = false; + + // when the touchtime is higher then the max touch time + // or when the moving distance is too much + if(ev.deltaTime > inst.options.tap_max_touchtime || + ev.distance > inst.options.tap_max_distance) { + return; + } + + // check if double tap + if(prev && prev.name == 'tap' && + (ev.timeStamp - prev.lastEvent.timeStamp) < inst.options.doubletap_interval && + ev.distance < inst.options.doubletap_distance) { + inst.trigger('doubletap', ev); + did_doubletap = true; + } + + // do a single tap + if(!did_doubletap || inst.options.tap_always) { + Hammer.detection.current.name = 'tap'; + inst.trigger(Hammer.detection.current.name, ev); + } + } + } + }; + + + /** + * Swipe + * triggers swipe events when the end velocity is above the threshold + * @events swipe, swipeleft, swiperight, swipeup, swipedown + */ + Hammer.gestures.Swipe = { + name: 'swipe', + index: 40, + defaults: { + // set 0 for unlimited, but this can conflict with transform + swipe_max_touches : 1, + swipe_velocity : 0.7 + }, + handler: function swipeGesture(ev, inst) { + if(ev.eventType == Hammer.EVENT_END) { + // max touches + if(inst.options.swipe_max_touches > 0 && + ev.touches.length > inst.options.swipe_max_touches) { + return; + } + + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.velocityX > inst.options.swipe_velocity || + ev.velocityY > inst.options.swipe_velocity) { + // trigger swipe events + inst.trigger(this.name, ev); + inst.trigger(this.name + ev.direction, ev); + } + } + } + }; + + + /** + * Drag + * Move with x fingers (default 1) around on the page. Blocking the scrolling when + * moving left and right is a good practice. When all the drag events are blocking + * you disable scrolling on that area. + * @events drag, drapleft, dragright, dragup, dragdown + */ + Hammer.gestures.Drag = { + name: 'drag', + index: 50, + defaults: { + drag_min_distance : 10, + // set 0 for unlimited, but this can conflict with transform + drag_max_touches : 1, + // prevent default browser behavior when dragging occurs + // be careful with it, it makes the element a blocking element + // when you are using the drag gesture, it is a good practice to set this true + drag_block_horizontal : false, + drag_block_vertical : false, + // drag_lock_to_axis keeps the drag gesture on the axis that it started on, + // It disallows vertical directions if the initial direction was horizontal, and vice versa. + drag_lock_to_axis : false, + // drag lock only kicks in when distance > drag_lock_min_distance + // This way, locking occurs only when the distance has become large enough to reliably determine the direction + drag_lock_min_distance : 25 + }, + triggered: false, + handler: function dragGesture(ev, inst) { + // current gesture isnt drag, but dragged is true + // this means an other gesture is busy. now call dragend + if(Hammer.detection.current.name != this.name && this.triggered) { + inst.trigger(this.name +'end', ev); + this.triggered = false; + return; + } + + // max touches + if(inst.options.drag_max_touches > 0 && + ev.touches.length > inst.options.drag_max_touches) { + return; + } + + switch(ev.eventType) { + case Hammer.EVENT_START: + this.triggered = false; + break; + + case Hammer.EVENT_MOVE: + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.distance < inst.options.drag_min_distance && + Hammer.detection.current.name != this.name) { + return; + } + + // we are dragging! + Hammer.detection.current.name = this.name; + + // lock drag to axis? + if(Hammer.detection.current.lastEvent.drag_locked_to_axis || (inst.options.drag_lock_to_axis && inst.options.drag_lock_min_distance<=ev.distance)) { + ev.drag_locked_to_axis = true; + } + var last_direction = Hammer.detection.current.lastEvent.direction; + if(ev.drag_locked_to_axis && last_direction !== ev.direction) { + // keep direction on the axis that the drag gesture started on + if(Hammer.utils.isVertical(last_direction)) { + ev.direction = (ev.deltaY < 0) ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN; + } + else { + ev.direction = (ev.deltaX < 0) ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT; + } + } + + // first time, trigger dragstart event + if(!this.triggered) { + inst.trigger(this.name +'start', ev); + this.triggered = true; + } + + // trigger normal event + inst.trigger(this.name, ev); + + // direction event, like dragdown + inst.trigger(this.name + ev.direction, ev); + + // block the browser events + if( (inst.options.drag_block_vertical && Hammer.utils.isVertical(ev.direction)) || + (inst.options.drag_block_horizontal && !Hammer.utils.isVertical(ev.direction))) { + ev.preventDefault(); + } + break; + + case Hammer.EVENT_END: + // trigger dragend + if(this.triggered) { + inst.trigger(this.name +'end', ev); + } + + this.triggered = false; + break; + } + } + }; + + + /** + * Transform + * User want to scale or rotate with 2 fingers + * @events transform, pinch, pinchin, pinchout, rotate + */ + Hammer.gestures.Transform = { + name: 'transform', + index: 45, + defaults: { + // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 + transform_min_scale : 0.01, + // rotation in degrees + transform_min_rotation : 1, + // prevent default browser behavior when two touches are on the screen + // but it makes the element a blocking element + // when you are using the transform gesture, it is a good practice to set this true + transform_always_block : false + }, + triggered: false, + handler: function transformGesture(ev, inst) { + // current gesture isnt drag, but dragged is true + // this means an other gesture is busy. now call dragend + if(Hammer.detection.current.name != this.name && this.triggered) { + inst.trigger(this.name +'end', ev); + this.triggered = false; + return; + } + + // atleast multitouch + if(ev.touches.length < 2) { + return; + } + + // prevent default when two fingers are on the screen + if(inst.options.transform_always_block) { + ev.preventDefault(); + } + + switch(ev.eventType) { + case Hammer.EVENT_START: + this.triggered = false; + break; + + case Hammer.EVENT_MOVE: + var scale_threshold = Math.abs(1-ev.scale); + var rotation_threshold = Math.abs(ev.rotation); + + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(scale_threshold < inst.options.transform_min_scale && + rotation_threshold < inst.options.transform_min_rotation) { + return; + } + + // we are transforming! + Hammer.detection.current.name = this.name; + + // first time, trigger dragstart event + if(!this.triggered) { + inst.trigger(this.name +'start', ev); + this.triggered = true; + } + + inst.trigger(this.name, ev); // basic transform event + + // trigger rotate event + if(rotation_threshold > inst.options.transform_min_rotation) { + inst.trigger('rotate', ev); + } + + // trigger pinch event + if(scale_threshold > inst.options.transform_min_scale) { + inst.trigger('pinch', ev); + inst.trigger('pinch'+ ((ev.scale < 1) ? 'in' : 'out'), ev); + } + break; + + case Hammer.EVENT_END: + // trigger dragend + if(this.triggered) { + inst.trigger(this.name +'end', ev); + } + + this.triggered = false; + break; + } + } + }; + + + /** + * Touch + * Called as first, tells the user has touched the screen + * @events touch + */ + Hammer.gestures.Touch = { + name: 'touch', + index: -Infinity, + defaults: { + // call preventDefault at touchstart, and makes the element blocking by + // disabling the scrolling of the page, but it improves gestures like + // transforming and dragging. + // be careful with using this, it can be very annoying for users to be stuck + // on the page + prevent_default: false, + + // disable mouse events, so only touch (or pen!) input triggers events + prevent_mouseevents: false + }, + handler: function touchGesture(ev, inst) { + if(inst.options.prevent_mouseevents && ev.pointerType == Hammer.POINTER_MOUSE) { + ev.stopDetect(); + return; + } + + if(inst.options.prevent_default) { + ev.preventDefault(); + } + + if(ev.eventType == Hammer.EVENT_START) { + inst.trigger(this.name, ev); + } + } + }; + + + /** + * Release + * Called as last, tells the user has released the screen + * @events release + */ + Hammer.gestures.Release = { + name: 'release', + index: Infinity, + handler: function releaseGesture(ev, inst) { + if(ev.eventType == Hammer.EVENT_END) { + inst.trigger(this.name, ev); + } + } + }; + + // node export + if(typeof module === 'object' && typeof module.exports === 'object'){ + module.exports = Hammer; + } + // just window export + else { + window.Hammer = Hammer; + + // requireJS module definition + if(typeof window.define === 'function' && window.define.amd) { + window.define('hammer', [], function() { + return Hammer; + }); + } + } + })(this); + +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var RepulsionMixin = __webpack_require__(52); + var HierarchialRepulsionMixin = __webpack_require__(53); + var BarnesHutMixin = __webpack_require__(54); + + /** + * Toggling barnes Hut calculation on and off. + * + * @private + */ + exports._toggleBarnesHut = function () { + this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; + this._loadSelectedForceSolver(); + this.moving = true; + this.start(); + }; + + + /** + * This loads the node force solver based on the barnes hut or repulsion algorithm + * + * @private + */ + exports._loadSelectedForceSolver = function () { + // this overloads the this._calculateNodeForces + if (this.constants.physics.barnesHut.enabled == true) { + this._clearMixin(RepulsionMixin); + this._clearMixin(HierarchialRepulsionMixin); + + this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; + this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; + this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; + this.constants.physics.damping = this.constants.physics.barnesHut.damping; + + this._loadMixin(BarnesHutMixin); + } + else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._clearMixin(BarnesHutMixin); + this._clearMixin(RepulsionMixin); + + this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; + this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; + + this._loadMixin(HierarchialRepulsionMixin); + } + else { + this._clearMixin(BarnesHutMixin); + this._clearMixin(HierarchialRepulsionMixin); + this.barnesHutTree = undefined; + + this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.repulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; + this.constants.physics.damping = this.constants.physics.repulsion.damping; + + this._loadMixin(RepulsionMixin); + } + }; + + /** + * Before calculating the forces, we check if we need to cluster to keep up performance and we check + * if there is more than one node. If it is just one node, we dont calculate anything. + * + * @private + */ + exports._initializeForceCalculation = function () { + // stop calculation if there is only one node + if (this.nodeIndices.length == 1) { + this.nodes[this.nodeIndices[0]]._setForce(0, 0); + } + else { + // if there are too many nodes on screen, we cluster without repositioning + if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { + this.clusterToFit(this.constants.clustering.reduceToNodes, false); + } + + // we now start the force calculation + this._calculateForces(); + } + }; + + + /** + * Calculate the external forces acting on the nodes + * Forces are caused by: edges, repulsing forces between nodes, gravity + * @private + */ + exports._calculateForces = function () { + // Gravity is required to keep separated groups from floating off + // the forces are reset to zero in this loop by using _setForce instead + // of _addForce + + this._calculateGravitationalForces(); + this._calculateNodeForces(); + + if (this.constants.smoothCurves == true) { + this._calculateSpringForcesWithSupport(); + } + else { + if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._calculateHierarchicalSpringForces(); + } + else { + this._calculateSpringForces(); + } + } + }; + + + /** + * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also + * handled in the calculateForces function. We then use a quadratic curve with the center node as control. + * This function joins the datanodes and invisible (called support) nodes into one object. + * We do this so we do not contaminate this.nodes with the support nodes. + * + * @private + */ + exports._updateCalculationNodes = function () { + if (this.constants.smoothCurves == true) { + this.calculationNodes = {}; + this.calculationNodeIndices = []; + + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId] = this.nodes[nodeId]; + } + } + var supportNodes = this.sectors['support']['nodes']; + for (var supportNodeId in supportNodes) { + if (supportNodes.hasOwnProperty(supportNodeId)) { + if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { + this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; + } + else { + supportNodes[supportNodeId]._setForce(0, 0); + } + } + } + + for (var idx in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(idx)) { + this.calculationNodeIndices.push(idx); + } + } + } + else { + this.calculationNodes = this.nodes; + this.calculationNodeIndices = this.nodeIndices; + } + }; + + + /** + * this function applies the central gravity effect to keep groups from floating off + * + * @private + */ + exports._calculateGravitationalForces = function () { + var dx, dy, distance, node, i; + var nodes = this.calculationNodes; + var gravity = this.constants.physics.centralGravity; + var gravityForce = 0; + + for (i = 0; i < this.calculationNodeIndices.length; i++) { + node = nodes[this.calculationNodeIndices[i]]; + node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. + // gravity does not apply when we are in a pocket sector + if (this._sector() == "default" && gravity != 0) { + dx = -node.x; + dy = -node.y; + distance = Math.sqrt(dx * dx + dy * dy); + + gravityForce = (distance == 0) ? 0 : (gravity / distance); + node.fx = dx * gravityForce; + node.fy = dy * gravityForce; + } + else { + node.fx = 0; + node.fy = 0; + } + } + }; + + + + + /** + * this function calculates the effects of the springs in the case of unsmooth curves. + * + * @private + */ + exports._calculateSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + edge.from.fx += fx; + edge.from.fy += fy; + edge.to.fx -= fx; + edge.to.fy -= fy; + } + } + } + } + }; + + + + + /** + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private + */ + exports._calculateSpringForcesWithSupport = function () { + var edgeLength, edge, edgeId, combinedClusterSize; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + if (edge.via != null) { + var node1 = edge.to; + var node2 = edge.via; + var node3 = edge.from; + + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; + + combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + + // this implies that the edges between big clusters are longer + edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; + this._calculateSpringForce(node1, node2, 0.5 * edgeLength); + this._calculateSpringForce(node2, node3, 0.5 * edgeLength); + } + } + } + } + } + }; + + + /** + * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. + * + * @param node1 + * @param node2 + * @param edgeLength + * @private + */ + exports._calculateSpringForce = function (node1, node2, edgeLength) { + var dx, dy, fx, fy, springForce, distance; + + dx = (node1.x - node2.x); + dy = (node1.y - node2.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + node1.fx += fx; + node1.fy += fy; + node2.fx -= fx; + node2.fy -= fy; + }; + + + /** + * Load the HTML for the physics config and bind it + * @private + */ + exports._loadPhysicsConfiguration = function () { + if (this.physicsConfiguration === undefined) { + this.backupConstants = {}; + util.deepExtend(this.backupConstants,this.constants); + + var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; + this.physicsConfiguration = document.createElement('div'); + this.physicsConfiguration.className = "PhysicsConfiguration"; + this.physicsConfiguration.innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Options:
' + this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); + this.optionsDiv = document.createElement("div"); + this.optionsDiv.style.fontSize = "14px"; + this.optionsDiv.style.fontFamily = "verdana"; + this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); + + var rangeElement; + rangeElement = document.getElementById('graph_BH_gc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); + rangeElement = document.getElementById('graph_BH_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_BH_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_BH_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_BH_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_R_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); + rangeElement = document.getElementById('graph_R_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_R_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_R_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_R_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_H_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); + rangeElement = document.getElementById('graph_H_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_H_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_H_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_H_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); + rangeElement = document.getElementById('graph_H_direction'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); + rangeElement = document.getElementById('graph_H_levsep'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); + rangeElement = document.getElementById('graph_H_nspac'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + var radioButton3 = document.getElementById("graph_physicsMethod3"); + radioButton2.checked = true; + if (this.constants.physics.barnesHut.enabled) { + radioButton1.checked = true; + } + if (this.constants.hierarchicalLayout.enabled) { + radioButton3.checked = true; + } + + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + var graph_repositionNodes = document.getElementById("graph_repositionNodes"); + var graph_generateOptions = document.getElementById("graph_generateOptions"); + + graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); + graph_repositionNodes.onclick = graphRepositionNodes.bind(this); + graph_generateOptions.onclick = graphGenerateOptions.bind(this); + if (this.constants.smoothCurves == true) { + graph_toggleSmooth.style.background = "#A4FF56"; + } + else { + graph_toggleSmooth.style.background = "#FF8532"; + } + + + switchConfigurations.apply(this); + + radioButton1.onchange = switchConfigurations.bind(this); + radioButton2.onchange = switchConfigurations.bind(this); + radioButton3.onchange = switchConfigurations.bind(this); + } + }; + + /** + * This overwrites the this.constants. + * + * @param constantsVariableName + * @param value + * @private + */ + exports._overWriteGraphConstants = function (constantsVariableName, value) { + var nameArray = constantsVariableName.split("_"); + if (nameArray.length == 1) { + this.constants[nameArray[0]] = value; + } + else if (nameArray.length == 2) { + this.constants[nameArray[0]][nameArray[1]] = value; + } + else if (nameArray.length == 3) { + this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; + } + }; + + + /** + * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. + */ + function graphToggleSmoothCurves () { + this.constants.smoothCurves = !this.constants.smoothCurves; + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + + this._configureSmoothCurves(false); + } + + /** + * this function is used to scramble the nodes + * + */ + function graphRepositionNodes () { + for (var nodeId in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; + this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; + } + } + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + } + else { + this.repositionNodes(); + } + this.moving = true; + this.start(); + } + + /** + * this is used to generate an options file from the playing with physics system. + */ + function graphGenerateOptions () { + var options = "No options are required, default values used."; + var optionsSpecific = []; + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + if (radioButton1.checked == true) { + if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options = "var options = {"; + options += "physics: {barnesHut: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}}' + } + if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { + if (optionsSpecific.length == 0) {options = "var options = {";} + else {options += ", "} + options += "smoothCurves: " + this.constants.smoothCurves; + } + if (options != "No options are required, default values used.") { + options += '};' + } + } + else if (radioButton2.checked == true) { + options = "var options = {"; + options += "physics: {barnesHut: {enabled: false}"; + if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += ", repulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}}' + } + if (optionsSpecific.length == 0) {options += "}"} + if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { + options += ", smoothCurves: " + this.constants.smoothCurves; + } + options += '};' + } + else { + options = "var options = {"; + if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += "physics: {hierarchicalRepulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", "; + } + } + options += '}},'; + } + options += 'hierarchicalLayout: {'; + optionsSpecific = []; + if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} + if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} + if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} + if (optionsSpecific.length != 0) { + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}' + } + else { + options += "enabled:true}"; + } + options += '};' + } + + + this.optionsDiv.innerHTML = options; + } + + /** + * this is used to switch between barnesHut, repulsion and hierarchical. + * + */ + function switchConfigurations () { + var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; + var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; + var tableId = "graph_" + radioButton + "_table"; + var table = document.getElementById(tableId); + table.style.display = "block"; + for (var i = 0; i < ids.length; i++) { + if (ids[i] != tableId) { + table = document.getElementById(ids[i]); + table.style.display = "none"; + } + } + this._restoreNodes(); + if (radioButton == "R") { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = false; + } + else if (radioButton == "H") { + if (this.constants.hierarchicalLayout.enabled == false) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + this._setupHierarchicalLayout(); + } + } + else { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = true; + } + this._loadSelectedForceSolver(); + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + this.moving = true; + this.start(); + } + + + /** + * this generates the ranges depending on the iniital values. + * + * @param id + * @param map + * @param constantsVariableName + */ + function showValueOfRange (id,map,constantsVariableName) { + var valueId = id + "_value"; + var rangeValue = document.getElementById(id).value; + + if (map instanceof Array) { + document.getElementById(valueId).value = map[parseInt(rangeValue)]; + this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); + } + else { + document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); + this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); + } + + if (constantsVariableName == "hierarchicalLayout_direction" || + constantsVariableName == "hierarchicalLayout_levelSeparation" || + constantsVariableName == "hierarchicalLayout_nodeSpacing") { + this._setupHierarchicalLayout(); + } + this.moving = true; + this.start(); + } + + +/***/ }, +/* 51 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js + //! version : 2.7.0 + //! authors : Tim Wood, Iskren Chernev, Moment.js contributors + //! license : MIT + //! momentjs.com + + (function (undefined) { + + /************************************ + Constants + ************************************/ + + var moment, + VERSION = "2.7.0", + // the global-scope this is NOT the global object in Node.js + globalScope = typeof global !== 'undefined' ? global : this, + oldGlobalMoment, + round = Math.round, + i, + + YEAR = 0, + MONTH = 1, + DATE = 2, + HOUR = 3, + MINUTE = 4, + SECOND = 5, + MILLISECOND = 6, + + // internal storage for language config files + languages = {}, + + // moment internal properties + momentProperties = { + _isAMomentObject: null, + _i : null, + _f : null, + _l : null, + _strict : null, + _tzm : null, + _isUTC : null, + _offset : null, // optional. Combine with _isUTC + _pf : null, + _lang : null // optional + }, + + // check for nodeJS + hasModule = (typeof module !== 'undefined' && module.exports), + + // ASP.NET json date format regex + aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, + aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, + + // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html + // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere + isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, + + // format tokens + formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g, + localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g, + + // parsing token regexes + parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 + parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 + parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 + parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 + parseTokenDigits = /\d+/, // nonzero number of digits + parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. + parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z + parseTokenT = /T/i, // T (ISO separator) + parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 + parseTokenOrdinal = /\d{1,2}/, + + //strict parsing regexes + parseTokenOneDigit = /\d/, // 0 - 9 + parseTokenTwoDigits = /\d\d/, // 00 - 99 + parseTokenThreeDigits = /\d{3}/, // 000 - 999 + parseTokenFourDigits = /\d{4}/, // 0000 - 9999 + parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999 + parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf + + // iso 8601 regex + // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) + isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, + + isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', + + isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], + ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], + ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], + ['GGGG-[W]WW', /\d{4}-W\d{2}/], + ['YYYY-DDD', /\d{4}-\d{3}/] + ], + + // iso time formats and regexes + isoTimes = [ + ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], + ['HH:mm', /(T| )\d\d:\d\d/], + ['HH', /(T| )\d\d/] + ], + + // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"] + parseTimezoneChunker = /([\+\-]|\d\d)/gi, + + // getter and setter names + proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), + unitMillisecondFactors = { + 'Milliseconds' : 1, + 'Seconds' : 1e3, + 'Minutes' : 6e4, + 'Hours' : 36e5, + 'Days' : 864e5, + 'Months' : 2592e6, + 'Years' : 31536e6 + }, + + unitAliases = { + ms : 'millisecond', + s : 'second', + m : 'minute', + h : 'hour', + d : 'day', + D : 'date', + w : 'week', + W : 'isoWeek', + M : 'month', + Q : 'quarter', + y : 'year', + DDD : 'dayOfYear', + e : 'weekday', + E : 'isoWeekday', + gg: 'weekYear', + GG: 'isoWeekYear' + }, + + camelFunctions = { + dayofyear : 'dayOfYear', + isoweekday : 'isoWeekday', + isoweek : 'isoWeek', + weekyear : 'weekYear', + isoweekyear : 'isoWeekYear' + }, + + // format function strings + formatFunctions = {}, + + // default relative time thresholds + relativeTimeThresholds = { + s: 45, //seconds to 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(' '), + + formatTokenFunctions = { + M : function () { + return this.month() + 1; + }, + MMM : function (format) { + return this.lang().monthsShort(this, format); + }, + MMMM : function (format) { + return this.lang().months(this, format); + }, + D : function () { + return this.date(); + }, + DDD : function () { + return this.dayOfYear(); + }, + d : function () { + return this.day(); + }, + dd : function (format) { + return this.lang().weekdaysMin(this, format); + }, + ddd : function (format) { + return this.lang().weekdaysShort(this, format); + }, + dddd : function (format) { + return this.lang().weekdays(this, format); + }, + w : function () { + return this.week(); + }, + W : function () { + return this.isoWeek(); + }, + YY : function () { + return leftZeroFill(this.year() % 100, 2); + }, + YYYY : function () { + return leftZeroFill(this.year(), 4); + }, + YYYYY : function () { + return leftZeroFill(this.year(), 5); + }, + YYYYYY : function () { + var y = this.year(), sign = y >= 0 ? '+' : '-'; + return sign + leftZeroFill(Math.abs(y), 6); + }, + gg : function () { + return leftZeroFill(this.weekYear() % 100, 2); + }, + gggg : function () { + return leftZeroFill(this.weekYear(), 4); + }, + ggggg : function () { + return leftZeroFill(this.weekYear(), 5); + }, + GG : function () { + return leftZeroFill(this.isoWeekYear() % 100, 2); + }, + GGGG : function () { + return leftZeroFill(this.isoWeekYear(), 4); + }, + GGGGG : function () { + return leftZeroFill(this.isoWeekYear(), 5); + }, + e : function () { + return this.weekday(); + }, + E : function () { + return this.isoWeekday(); + }, + a : function () { + return this.lang().meridiem(this.hours(), this.minutes(), true); + }, + A : function () { + return this.lang().meridiem(this.hours(), this.minutes(), false); + }, + H : function () { + return this.hours(); + }, + h : function () { + return this.hours() % 12 || 12; + }, + m : function () { + return this.minutes(); + }, + s : function () { + return this.seconds(); + }, + S : function () { + return toInt(this.milliseconds() / 100); + }, + SS : function () { + return leftZeroFill(toInt(this.milliseconds() / 10), 2); + }, + SSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + SSSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + Z : function () { + var a = -this.zone(), + b = "+"; + if (a < 0) { + a = -a; + b = "-"; + } + return b + leftZeroFill(toInt(a / 60), 2) + ":" + leftZeroFill(toInt(a) % 60, 2); + }, + ZZ : function () { + var a = -this.zone(), + b = "+"; + if (a < 0) { + a = -a; + b = "-"; + } + return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); + }, + z : function () { + return this.zoneAbbr(); + }, + zz : function () { + return this.zoneName(); + }, + X : function () { + return this.unix(); + }, + Q : function () { + return this.quarter(); + } + }, + + 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. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso: false + }; + } + + function deprecate(msg, fn) { + var firstTime = true; + function printMsg() { + if (moment.suppressDeprecationWarnings === false && + typeof console !== 'undefined' && console.warn) { + console.warn("Deprecation warning: " + msg); + } + } + return extend(function () { + if (firstTime) { + printMsg(); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); + } + + function padToken(func, count) { + return function (a) { + return leftZeroFill(func.call(this, a), count); + }; + } + function ordinalizeToken(func, period) { + return function (a) { + return this.lang().ordinal(func.call(this, a), period); + }; + } + + while (ordinalizeTokens.length) { + i = ordinalizeTokens.pop(); + formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); + } + while (paddedTokens.length) { + i = paddedTokens.pop(); + formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); + } + formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); + + + /************************************ + Constructors + ************************************/ + + function Language() { + + } + + // Moment prototype object + function Moment(config) { + checkOverflow(config); + extend(this, config); + } + + // Duration Constructor + function Duration(duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; + + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 36e5; // 1000 * 60 * 60 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; + + this._data = {}; + + this._bubble(); + } + + /************************************ + Helpers + ************************************/ + + + function extend(a, b) { + for (var i in b) { + if (b.hasOwnProperty(i)) { + a[i] = b[i]; + } + } + + if (b.hasOwnProperty("toString")) { + a.toString = b.toString; + } + + if (b.hasOwnProperty("valueOf")) { + a.valueOf = b.valueOf; + } + + return a; + } + + function cloneMoment(m) { + var result = {}, i; + for (i in m) { + if (m.hasOwnProperty(i) && momentProperties.hasOwnProperty(i)) { + result[i] = m[i]; + } + } + + return result; + } + + function absRound(number) { + if (number < 0) { + return Math.ceil(number); + } else { + return Math.floor(number); + } + } + + // left zero fill a number + // see http://jsperf.com/left-zero-filling for performance comparison + function leftZeroFill(number, targetLength, forceSign) { + var output = '' + Math.abs(number), + sign = number >= 0; + + while (output.length < targetLength) { + output = '0' + output; + } + return (sign ? (forceSign ? '+' : '') : '-') + output; + } + + // helper function for _.addTime and _.subtractTime + function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = duration._days, + months = duration._months; + updateOffset = updateOffset == null ? true : updateOffset; + + if (milliseconds) { + mom._d.setTime(+mom._d + milliseconds * isAdding); + } + if (days) { + rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); + } + if (months) { + rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); + } + if (updateOffset) { + moment.updateOffset(mom, days || months); + } + } + + // check if is an array + function isArray(input) { + return Object.prototype.toString.call(input) === '[object Array]'; + } + + function isDate(input) { + return Object.prototype.toString.call(input) === '[object Date]' || + input instanceof Date; + } + + // compare two arrays, return the number of differences + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; + } + } + return diffs + lengthDiff; + } + + function normalizeUnits(units) { + if (units) { + var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); + units = unitAliases[units] || camelFunctions[lowered] || lowered; + } + return units; + } + + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; + + for (prop in inputObject) { + if (inputObject.hasOwnProperty(prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } + return normalizedInput; + } -/** - * Add a value to the list with available values for this filter - * No double entries will be created. - * @param {Number} index - */ -Filter.prototype.selectValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; + function makeList(field) { + var count, setter; - this.index = index; - this.value = this.values[index]; -}; + if (field.indexOf('week') === 0) { + count = 7; + setter = 'day'; + } + else if (field.indexOf('month') === 0) { + count = 12; + setter = 'month'; + } + else { + return; + } -/** - * Load all filtered rows in the background one by one - * Start this method without providing an index! - */ -Filter.prototype.loadInBackground = function(index) { - if (index === undefined) - index = 0; + moment[field] = function (format, index) { + var i, getter, + method = moment.fn._lang[field], + results = []; - var frame = this.graph.frame; + if (typeof format === 'number') { + index = format; + format = undefined; + } - if (index < this.values.length) { - var dataPointsTemp = this._getDataPoints(index); - //this.graph.redrawInfo(); // TODO: not neat + getter = function (i) { + var m = moment().utc().set(setter, i); + return method.call(moment.fn._lang, m, format || ''); + }; - // create a progress box - if (frame.progress === undefined) { - frame.progress = document.createElement('DIV'); - frame.progress.style.position = 'absolute'; - frame.progress.style.color = 'gray'; - frame.appendChild(frame.progress); - } - var progress = this.getLoadedProgress(); - frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; - // TODO: this is no nice solution... - frame.progress.style.bottom = Graph3d.px(60); // TODO: use height of slider - frame.progress.style.left = Graph3d.px(10); + if (index != null) { + return getter(index); + } + else { + for (i = 0; i < count; i++) { + results.push(getter(i)); + } + return results; + } + }; + } - var me = this; - setTimeout(function() {me.loadInBackground(index+1);}, 10); - this.loaded = false; - } - else { - this.loaded = true; + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; - // remove the progress box - if (frame.progress !== undefined) { - frame.removeChild(frame.progress); - frame.progress = undefined; - } + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + if (coercedNumber >= 0) { + value = Math.floor(coercedNumber); + } else { + value = Math.ceil(coercedNumber); + } + } - if (this.onLoadCallback) - this.onLoadCallback(); - } -}; + return value; + } + function daysInMonth(year, month) { + return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + } + function weeksInYear(year, dow, doy) { + return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; + } -/** - * @prototype StepNumber - * The class StepNumber is an iterator for Numbers. You provide a start and end - * value, and a best step size. StepNumber itself rounds to fixed values and - * a finds the step that best fits the provided step. - * - * If prettyStep is true, the step size is chosen as close as possible to the - * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... - * - * Example usage: - * var step = new StepNumber(0, 10, 2.5, true); - * step.start(); - * while (!step.end()) { - * alert(step.getCurrent()); - * step.next(); - * } - * - * Version: 1.0 - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) - */ -StepNumber = function (start, end, step, prettyStep) { - // set default values - this._start = 0; - this._end = 0; - this._step = 1; - this.prettyStep = true; - this.precision = 5; - - this._current = 0; - this.setRange(start, end, step, prettyStep); -}; + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; + } -/** - * Set a new range: start, end and step. - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) - */ -StepNumber.prototype.setRange = function(start, end, step, prettyStep) { - this._start = start ? start : 0; - this._end = end ? end : 0; + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } - this.setStep(step, prettyStep); -}; + function checkOverflow(m) { + var overflow; + if (m._a && m._pf.overflow === -2) { + overflow = + m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : + m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : + m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR : + m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : + m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : + m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : + -1; -/** - * Set a new step size - * @param {Number} step New step size. Must be a positive value - * @param {boolean} prettyStep Optional. If true, the provided step is rounded - * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) - */ -StepNumber.prototype.setStep = function(step, prettyStep) { - if (step === undefined || step <= 0) - return; + if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } - if (prettyStep !== undefined) - this.prettyStep = prettyStep; + m._pf.overflow = overflow; + } + } - if (this.prettyStep === true) - this._step = StepNumber.calculatePrettyStep(step); - else - this._step = step; -}; + function isValid(m) { + if (m._isValid == null) { + m._isValid = !isNaN(m._d.getTime()) && + m._pf.overflow < 0 && + !m._pf.empty && + !m._pf.invalidMonth && + !m._pf.nullInput && + !m._pf.invalidFormat && + !m._pf.userInvalidated; + + if (m._strict) { + m._isValid = m._isValid && + m._pf.charsLeftOver === 0 && + m._pf.unusedTokens.length === 0; + } + } + return m._isValid; + } -/** - * Calculate a nice step size, closest to the desired step size. - * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an - * integer Number. For example 1, 2, 5, 10, 20, 50, etc... - * @param {Number} step Desired step size - * @return {Number} Nice step size - */ -StepNumber.calculatePrettyStep = function (step) { - var log10 = function (x) {return Math.log(x) / Math.LN10;}; - - // try three steps (multiple of 1, 2, or 5 - var step1 = Math.pow(10, Math.round(log10(step))), - step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), - step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); - - // choose the best step (closest to minimum step) - var prettyStep = step1; - if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; - if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; - - // for safety - if (prettyStep <= 0) { - prettyStep = 1; - } + function normalizeLanguage(key) { + return key ? key.toLowerCase().replace('_', '-') : key; + } - return prettyStep; -}; + // Return a moment from input, that is local/utc/zone equivalent to model. + function makeAs(input, model) { + return model._isUTC ? moment(input).zone(model._offset || 0) : + moment(input).local(); + } -/** - * returns the current value of the step - * @return {Number} current value - */ -StepNumber.prototype.getCurrent = function () { - return parseFloat(this._current.toPrecision(this.precision)); -}; + /************************************ + Languages + ************************************/ -/** - * returns the current step size - * @return {Number} current step size - */ -StepNumber.prototype.getStep = function () { - return this._step; -}; -/** - * Set the current value to the largest value smaller than start, which - * is a multiple of the step size - */ -StepNumber.prototype.start = function() { - this._current = this._start - this._start % this._step; -}; + extend(Language.prototype, { -/** - * Do a step, add the step size to the current value - */ -StepNumber.prototype.next = function () { - this._current += this._step; -}; + set : function (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (typeof prop === 'function') { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + }, -/** - * Returns true whether the end is reached - * @return {boolean} True if the current value has passed the end value. - */ -StepNumber.prototype.end = function () { - return (this._current > this._end); -}; + _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), + months : function (m) { + return this._months[m.month()]; + }, + _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), + monthsShort : function (m) { + return this._monthsShort[m.month()]; + }, -/** - * @constructor Slider - * - * An html slider control with start/stop/prev/next buttons - * @param {Element} container The element where the slider will be created - * @param {Object} options Available options: - * {boolean} visible If true (default) the - * slider is visible. - */ -function Slider(container, options) { - if (container === undefined) { - throw 'Error: No container element defined'; - } - this.container = container; - this.visible = (options && options.visible != undefined) ? options.visible : true; + monthsParse : function (monthName) { + var i, mom, regex; - if (this.visible) { - this.frame = document.createElement('DIV'); - //this.frame.style.backgroundColor = '#E5E5E5'; - this.frame.style.width = '100%'; - this.frame.style.position = 'relative'; - this.container.appendChild(this.frame); + if (!this._monthsParse) { + this._monthsParse = []; + } - this.frame.prev = document.createElement('INPUT'); - this.frame.prev.type = 'BUTTON'; - this.frame.prev.value = 'Prev'; - this.frame.appendChild(this.frame.prev); - - this.frame.play = document.createElement('INPUT'); - this.frame.play.type = 'BUTTON'; - this.frame.play.value = 'Play'; - this.frame.appendChild(this.frame.play); - - this.frame.next = document.createElement('INPUT'); - this.frame.next.type = 'BUTTON'; - this.frame.next.value = 'Next'; - this.frame.appendChild(this.frame.next); - - this.frame.bar = document.createElement('INPUT'); - this.frame.bar.type = 'BUTTON'; - this.frame.bar.style.position = 'absolute'; - this.frame.bar.style.border = '1px solid red'; - this.frame.bar.style.width = '100px'; - this.frame.bar.style.height = '6px'; - this.frame.bar.style.borderRadius = '2px'; - this.frame.bar.style.MozBorderRadius = '2px'; - this.frame.bar.style.border = '1px solid #7F7F7F'; - this.frame.bar.style.backgroundColor = '#E5E5E5'; - this.frame.appendChild(this.frame.bar); - - this.frame.slide = document.createElement('INPUT'); - this.frame.slide.type = 'BUTTON'; - this.frame.slide.style.margin = '0px'; - this.frame.slide.value = ' '; - this.frame.slide.style.position = 'relative'; - this.frame.slide.style.left = '-100px'; - this.frame.appendChild(this.frame.slide); - - // create events - var me = this; - this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);}; - this.frame.prev.onclick = function (event) {me.prev(event);}; - this.frame.play.onclick = function (event) {me.togglePlay(event);}; - this.frame.next.onclick = function (event) {me.next(event);}; - } + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + if (!this._monthsParse[i]) { + mom = moment.utc([2000, i]); + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (this._monthsParse[i].test(monthName)) { + return i; + } + } + }, - this.onChangeCallback = undefined; + _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), + weekdays : function (m) { + return this._weekdays[m.day()]; + }, - this.values = []; - this.index = undefined; + _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), + weekdaysShort : function (m) { + return this._weekdaysShort[m.day()]; + }, - this.playTimeout = undefined; - this.playInterval = 1000; // milliseconds - this.playLoop = true; -}; + _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), + weekdaysMin : function (m) { + return this._weekdaysMin[m.day()]; + }, -/** - * Select the previous index - */ -Slider.prototype.prev = function() { - var index = this.getIndex(); - if (index > 0) { - index--; - this.setIndex(index); - } -}; + weekdaysParse : function (weekdayName) { + var i, mom, regex; -/** - * Select the next index - */ -Slider.prototype.next = function() { - var index = this.getIndex(); - if (index < this.values.length - 1) { - index++; - this.setIndex(index); - } -}; + if (!this._weekdaysParse) { + this._weekdaysParse = []; + } -/** - * Select the next index - */ -Slider.prototype.playNext = function() { - var start = new Date(); + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + if (!this._weekdaysParse[i]) { + mom = moment([2000, 1]).day(i); + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } + }, - var index = this.getIndex(); - if (index < this.values.length - 1) { - index++; - this.setIndex(index); - } - else if (this.playLoop) { - // jump to the start - index = 0; - this.setIndex(index); - } + _longDateFormat : { + LT : "h:mm A", + L : "MM/DD/YYYY", + LL : "MMMM D YYYY", + LLL : "MMMM D YYYY LT", + LLLL : "dddd, MMMM D YYYY LT" + }, + longDateFormat : function (key) { + var output = this._longDateFormat[key]; + if (!output && this._longDateFormat[key.toUpperCase()]) { + output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + this._longDateFormat[key] = output; + } + return output; + }, - var end = new Date(); - var diff = (end - start); + isPM : function (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); + }, - // calculate how much time it to to set the index and to execute the callback - // function. - var interval = Math.max(this.playInterval - diff, 0); - // document.title = diff // TODO: cleanup + _meridiemParse : /[ap]\.?m?\.?/i, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } + }, - var me = this; - this.playTimeout = setTimeout(function() {me.playNext();}, interval); -}; + _calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + calendar : function (key, mom) { + var output = this._calendar[key]; + return typeof output === 'function' ? output.apply(mom) : output; + }, -/** - * Toggle start or stop playing - */ -Slider.prototype.togglePlay = function() { - if (this.playTimeout === undefined) { - this.play(); - } else { - this.stop(); - } -}; + _relativeTime : { + future : "in %s", + past : "%s ago", + s : "a few seconds", + m : "a minute", + mm : "%d minutes", + h : "an hour", + hh : "%d hours", + d : "a day", + dd : "%d days", + M : "a month", + MM : "%d months", + y : "a year", + yy : "%d years" + }, + relativeTime : function (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (typeof output === 'function') ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); + }, + pastFuture : function (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); + }, -/** - * Start playing - */ -Slider.prototype.play = function() { - // Test whether already playing - if (this.playTimeout) return; + ordinal : function (number) { + return this._ordinal.replace("%d", number); + }, + _ordinal : "%d", - this.playNext(); + preparse : function (string) { + return string; + }, - if (this.frame) { - this.frame.play.value = 'Stop'; - } -}; + postformat : function (string) { + return string; + }, -/** - * Stop playing - */ -Slider.prototype.stop = function() { - clearInterval(this.playTimeout); - this.playTimeout = undefined; + week : function (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; + }, - if (this.frame) { - this.frame.play.value = 'Play'; - } -}; + _week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + }, -/** - * Set a callback function which will be triggered when the value of the - * slider bar has changed. - */ -Slider.prototype.setOnChangeCallback = function(callback) { - this.onChangeCallback = callback; -}; + _invalidDate: 'Invalid date', + invalidDate: function () { + return this._invalidDate; + } + }); -/** - * Set the interval for playing the list - * @param {Number} interval The interval in milliseconds - */ -Slider.prototype.setPlayInterval = function(interval) { - this.playInterval = interval; -}; + // Loads a language definition into the `languages` cache. The function + // takes a key and optionally values. If not in the browser and no values + // are provided, it will load the language file module. As a convenience, + // this function also returns the language values. + function loadLang(key, values) { + values.abbr = key; + if (!languages[key]) { + languages[key] = new Language(); + } + languages[key].set(values); + return languages[key]; + } -/** - * Retrieve the current play interval - * @return {Number} interval The interval in milliseconds - */ -Slider.prototype.getPlayInterval = function(interval) { - return this.playInterval; -}; + // Remove a language from the `languages` cache. Mostly useful in tests. + function unloadLang(key) { + delete languages[key]; + } -/** - * Set looping on or off - * @pararm {boolean} doLoop If true, the slider will jump to the start when - * the end is passed, and will jump to the end - * when the start is passed. - */ -Slider.prototype.setPlayLoop = function(doLoop) { - this.playLoop = doLoop; -}; + // Determines which language definition to use and returns it. + // + // With no parameters, it will return the global language. If you + // pass in a language key, such as 'en', it will return the + // definition for 'en', so long as 'en' has already been loaded using + // moment.lang. + function getLangDefinition(key) { + var i = 0, j, lang, next, split, + get = function (k) { + if (!languages[k] && hasModule) { + try { + __webpack_require__(55)("./" + k); + } catch (e) { } + } + return languages[k]; + }; + + if (!key) { + return moment.fn._lang; + } + if (!isArray(key)) { + //short-circuit everything else + lang = get(key); + if (lang) { + return lang; + } + key = [key]; + } -/** - * Execute the onchange callback function - */ -Slider.prototype.onChange = function() { - if (this.onChangeCallback !== undefined) { - this.onChangeCallback(); - } -}; + //pick the language from the array + //try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + //substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + while (i < key.length) { + split = normalizeLanguage(key[i]).split('-'); + j = split.length; + next = normalizeLanguage(key[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + lang = get(split.slice(0, j).join('-')); + if (lang) { + return lang; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return moment.fn._lang; + } -/** - * redraw the slider on the correct place - */ -Slider.prototype.redraw = function() { - if (this.frame) { - // resize the bar - this.frame.bar.style.top = (this.frame.clientHeight/2 - - this.frame.bar.offsetHeight/2) + 'px'; - this.frame.bar.style.width = (this.frame.clientWidth - - this.frame.prev.clientWidth - - this.frame.play.clientWidth - - this.frame.next.clientWidth - 30) + 'px'; - - // position the slider button - var left = this.indexToLeft(this.index); - this.frame.slide.style.left = (left) + 'px'; - } -}; + /************************************ + Formatting + ************************************/ -/** - * Set the list with values for the slider - * @param {Array} values A javascript array with values (any type) - */ -Slider.prototype.setValues = function(values) { - this.values = values; + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ""); + } + return input.replace(/\\/g, ""); + } - if (this.values.length > 0) - this.setIndex(0); - else - this.index = undefined; -}; + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; -/** - * Select a value by its index - * @param {Number} index - */ -Slider.prototype.setIndex = function(index) { - if (index < this.values.length) { - this.index = index; + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } + + return function (mom) { + var output = ""; + for (i = 0; i < length; i++) { + output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; + } + return output; + }; + } + + // format date using native date object + function formatMoment(m, format) { + + if (!m.isValid()) { + return m.lang().invalidDate(); + } + + format = expandFormat(format, m.lang()); + + if (!formatFunctions[format]) { + formatFunctions[format] = makeFormatFunction(format); + } + + return formatFunctions[format](m); + } + + function expandFormat(format, lang) { + var i = 5; + + function replaceLongDateFormatTokens(input) { + return lang.longDateFormat(input) || input; + } + + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } + + return format; + } + + + /************************************ + Parsing + ************************************/ + + + // get the regex to find the next token + function getParseRegexForToken(token, config) { + var a, strict = config._strict; + switch (token) { + case 'Q': + return parseTokenOneDigit; + case 'DDDD': + return parseTokenThreeDigits; + case 'YYYY': + case 'GGGG': + case 'gggg': + return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; + case 'Y': + case 'G': + case 'g': + return parseTokenSignedNumber; + case 'YYYYYY': + case 'YYYYY': + case 'GGGGG': + case 'ggggg': + return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; + case 'S': + if (strict) { return parseTokenOneDigit; } + /* falls through */ + case 'SS': + if (strict) { return parseTokenTwoDigits; } + /* falls through */ + case 'SSS': + if (strict) { return parseTokenThreeDigits; } + /* falls through */ + case 'DDD': + return parseTokenOneToThreeDigits; + case 'MMM': + case 'MMMM': + case 'dd': + case 'ddd': + case 'dddd': + return parseTokenWord; + case 'a': + case 'A': + return getLangDefinition(config._l)._meridiemParse; + case 'X': + return parseTokenTimestampMs; + case 'Z': + case 'ZZ': + return parseTokenTimezone; + case 'T': + return parseTokenT; + case 'SSSS': + return parseTokenDigits; + case 'MM': + case 'DD': + case 'YY': + case 'GG': + case 'gg': + case 'HH': + case 'hh': + case 'mm': + case 'ss': + case 'ww': + case 'WW': + return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; + case 'M': + case 'D': + case 'd': + case 'H': + case 'h': + case 'm': + case 's': + case 'w': + case 'W': + case 'e': + case 'E': + return parseTokenOneOrTwoDigits; + case 'Do': + return parseTokenOrdinal; + default : + a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), "i")); + return a; + } + } + + function timezoneMinutesFromString(string) { + string = string || ""; + var possibleTzMatches = (string.match(parseTokenTimezone) || []), + tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], + parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], + minutes = +(parts[1] * 60) + toInt(parts[2]); + + return parts[0] === '+' ? -minutes : minutes; + } + + // function to convert string input to date + function addTimeToArrayFromToken(token, input, config) { + var a, datePartArray = config._a; + + switch (token) { + // QUARTER + case 'Q': + if (input != null) { + datePartArray[MONTH] = (toInt(input) - 1) * 3; + } + break; + // MONTH + case 'M' : // fall through to MM + case 'MM' : + if (input != null) { + datePartArray[MONTH] = toInt(input) - 1; + } + break; + case 'MMM' : // fall through to MMMM + case 'MMMM' : + a = getLangDefinition(config._l).monthsParse(input); + // if we didn't find a month name, mark the date as invalid. + if (a != null) { + datePartArray[MONTH] = a; + } else { + config._pf.invalidMonth = input; + } + break; + // DAY OF MONTH + case 'D' : // fall through to DD + case 'DD' : + if (input != null) { + datePartArray[DATE] = toInt(input); + } + break; + case 'Do' : + if (input != null) { + datePartArray[DATE] = toInt(parseInt(input, 10)); + } + break; + // DAY OF YEAR + case 'DDD' : // fall through to DDDD + case 'DDDD' : + if (input != null) { + config._dayOfYear = toInt(input); + } + + break; + // YEAR + case 'YY' : + datePartArray[YEAR] = moment.parseTwoDigitYear(input); + break; + case 'YYYY' : + case 'YYYYY' : + case 'YYYYYY' : + datePartArray[YEAR] = toInt(input); + break; + // AM / PM + case 'a' : // fall through to A + case 'A' : + config._isPm = getLangDefinition(config._l).isPM(input); + break; + // 24 HOUR + case 'H' : // fall through to hh + case 'HH' : // fall through to hh + case 'h' : // fall through to hh + case 'hh' : + datePartArray[HOUR] = toInt(input); + break; + // MINUTE + case 'm' : // fall through to mm + case 'mm' : + datePartArray[MINUTE] = toInt(input); + break; + // SECOND + case 's' : // fall through to ss + case 'ss' : + datePartArray[SECOND] = toInt(input); + break; + // MILLISECOND + case 'S' : + case 'SS' : + case 'SSS' : + case 'SSSS' : + datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); + break; + // UNIX TIMESTAMP WITH MS + case 'X': + config._d = new Date(parseFloat(input) * 1000); + break; + // TIMEZONE + case 'Z' : // fall through to ZZ + case 'ZZ' : + config._useUTC = true; + config._tzm = 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 'e': + case 'E': + token = token.substr(0, 1); + /* falls through */ + case 'gggg': + case 'GGGG': + case 'GGGGG': + token = token.substr(0, 2); + if (input) { + config._w = config._w || {}; + config._w[token] = toInt(input); + } + break; + case 'gg': + case 'GG': + config._w = config._w || {}; + config._w[token] = moment.parseTwoDigitYear(input); + } + } + + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp, 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. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function dateFromConfig(config) { + var i, date, input = [], currentDate, yearToUse; + + if (config._d) { + return; + } + + currentDate = currentDateArray(config); + + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } + + //if the day of the year is set, figure out what it is + if (config._dayOfYear) { + yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); + + if (config._dayOfYear > daysInYear(yearToUse)) { + config._pf._overflowDayOfYear = true; + } + + date = makeUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } + + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } + + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } + + 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) { + var normalizedInput; + + if (config._d) { + return; + } + + normalizedInput = normalizeObjectUnits(config._i); + config._a = [ + normalizedInput.year, + normalizedInput.month, + normalizedInput.day, + normalizedInput.hour, + normalizedInput.minute, + normalizedInput.second, + normalizedInput.millisecond + ]; + + dateFromConfig(config); + } + + function currentDateArray(config) { + var now = new Date(); + if (config._useUTC) { + return [ + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate() + ]; + } else { + return [now.getFullYear(), now.getMonth(), now.getDate()]; + } + } + + // date from string and format string + function makeDateFromStringAndFormat(config) { + + if (config._f === moment.ISO_8601) { + parseISO(config); + return; + } + + config._a = []; + config._pf.empty = true; + + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var lang = getLangDefinition(config._l), + string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + + tokens = expandFormat(config._f, lang).match(formattingTokens) || []; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + config._pf.unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + config._pf.empty = false; + } + else { + config._pf.unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + config._pf.unusedTokens.push(token); + } + } + + // add remaining unparsed input length to the string + config._pf.charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + config._pf.unusedInput.push(string); + } + + // handle am pm + if (config._isPm && config._a[HOUR] < 12) { + config._a[HOUR] += 12; + } + // if is 12 am, change hours to 0 + if (config._isPm === false && config._a[HOUR] === 12) { + config._a[HOUR] = 0; + } + + dateFromConfig(config); + checkOverflow(config); + } + + function unescapeFormat(s) { + return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + }); + } + + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function regexpEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } + + // date from string and array of format strings + function makeDateFromStringAndArray(config) { + var tempConfig, + bestMoment, + + scoreToBeat, + i, + currentScore; + + if (config._f.length === 0) { + config._pf.invalidFormat = true; + config._d = new Date(NaN); + return; + } + + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = extend({}, config); + tempConfig._pf = defaultParsingFlags(); + tempConfig._f = config._f[i]; + makeDateFromStringAndFormat(tempConfig); + + if (!isValid(tempConfig)) { + continue; + } + + // if there is any input that was not parsed add a penalty for that format + currentScore += tempConfig._pf.charsLeftOver; - this.redraw(); - this.onChange(); - } - else { - throw 'Error: index out of range'; - } -}; + //or tokens + currentScore += tempConfig._pf.unusedTokens.length * 10; -/** - * retrieve the index of the currently selected vaue - * @return {Number} index - */ -Slider.prototype.getIndex = function() { - return this.index; -}; + tempConfig._pf.score = currentScore; + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } + } -/** - * retrieve the currently selected value - * @return {*} value - */ -Slider.prototype.get = function() { - return this.values[this.index]; -}; + extend(config, bestMoment || tempConfig); + } + // date from iso format + function parseISO(config) { + var i, l, + string = config._i, + match = isoRegex.exec(string); -Slider.prototype._onMouseDown = function(event) { - // only react on left mouse button down - var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); - if (!leftButtonDown) return; + if (match) { + config._pf.iso = true; + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(string)) { + // match[5] should be "T" or undefined + config._f = isoDates[i][0] + (match[6] || " "); + break; + } + } + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(string)) { + config._f += isoTimes[i][0]; + break; + } + } + if (string.match(parseTokenTimezone)) { + config._f += "Z"; + } + makeDateFromStringAndFormat(config); + } else { + config._isValid = false; + } + } - this.startClientX = event.clientX; - this.startSlideX = parseFloat(this.frame.slide.style.left); + // date from iso format or fallback + function makeDateFromString(config) { + parseISO(config); + if (config._isValid === false) { + delete config._isValid; + moment.createFromInputFallback(config); + } + } - this.frame.style.cursor = 'move'; + function makeDateFromInput(config) { + var input = config._i, + matched = aspNetJsonRegex.exec(input); + + if (input === undefined) { + config._d = new Date(); + } else if (matched) { + config._d = new Date(+matched[1]); + } else if (typeof input === 'string') { + makeDateFromString(config); + } else if (isArray(input)) { + config._a = input.slice(0); + dateFromConfig(config); + } else if (isDate(input)) { + config._d = new Date(+input); + } else if (typeof(input) === 'object') { + dateFromObject(config); + } else if (typeof(input) === 'number') { + // from milliseconds + config._d = new Date(input); + } else { + moment.createFromInputFallback(config); + } + } - // add event listeners to handle moving the contents - // we store the function onmousemove and onmouseup in the graph, so we can - // remove the eventlisteners lateron in the function mouseUp() - var me = this; - this.onmousemove = function (event) {me._onMouseMove(event);}; - this.onmouseup = function (event) {me._onMouseUp(event);}; - G3DaddEventListener(document, 'mousemove', this.onmousemove); - G3DaddEventListener(document, 'mouseup', this.onmouseup); - G3DpreventDefault(event); -}; + function makeDate(y, m, d, h, M, s, ms) { + //can't just apply() to create a date: + //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply + var date = new Date(y, m, d, h, M, s, ms); + //the date constructor doesn't accept years < 1970 + if (y < 1970) { + date.setFullYear(y); + } + return date; + } -Slider.prototype.leftToIndex = function (left) { - var width = parseFloat(this.frame.bar.style.width) - - this.frame.slide.clientWidth - 10; - var x = left - 3; + function makeUTCDate(y) { + var date = new Date(Date.UTC.apply(null, arguments)); + if (y < 1970) { + date.setUTCFullYear(y); + } + return date; + } - var index = Math.round(x / width * (this.values.length-1)); - if (index < 0) index = 0; - if (index > this.values.length-1) index = this.values.length-1; + function parseWeekday(input, language) { + if (typeof input === 'string') { + if (!isNaN(input)) { + input = parseInt(input, 10); + } + else { + input = language.weekdaysParse(input); + if (typeof input !== 'number') { + return null; + } + } + } + return input; + } + + /************************************ + Relative Time + ************************************/ + + + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) { + return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture); + } + + function relativeTime(milliseconds, withoutSuffix, lang) { + var seconds = round(Math.abs(milliseconds) / 1000), + minutes = round(seconds / 60), + hours = round(minutes / 60), + days = round(hours / 24), + years = round(days / 365), + args = seconds < relativeTimeThresholds.s && ['s', seconds] || + minutes === 1 && ['m'] || + minutes < relativeTimeThresholds.m && ['mm', minutes] || + hours === 1 && ['h'] || + hours < relativeTimeThresholds.h && ['hh', hours] || + days === 1 && ['d'] || + days <= relativeTimeThresholds.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; + args[4] = lang; + return substituteTimeAgo.apply({}, args); + } + + + /************************************ + Week of Year + ************************************/ + + + // firstDayOfWeek 0 = sun, 6 = sat + // the day of the week that starts the week + // (usually sunday or monday) + // firstDayOfWeekOfYear 0 = sun, 6 = sat + // the first week is the week that contains the first + // of this day of the week + // (eg. ISO weeks use thursday (4)) + function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { + var end = firstDayOfWeekOfYear - firstDayOfWeek, + daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), + adjustedMoment; + + + if (daysToDayOfWeek > end) { + daysToDayOfWeek -= 7; + } - return index; -}; + if (daysToDayOfWeek < end - 7) { + daysToDayOfWeek += 7; + } -Slider.prototype.indexToLeft = function (index) { - var width = parseFloat(this.frame.bar.style.width) - - this.frame.slide.clientWidth - 10; + adjustedMoment = moment(mom).add('d', daysToDayOfWeek); + return { + week: Math.ceil(adjustedMoment.dayOfYear() / 7), + year: adjustedMoment.year() + }; + } - var x = index / (this.values.length-1) * width; - var left = x + 3; + //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { + var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; - return left; -}; + d = d === 0 ? 7 : d; + weekday = weekday != null ? weekday : firstDayOfWeek; + daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); + dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; + return { + year: dayOfYear > 0 ? year : year - 1, + dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear + }; + } + /************************************ + Top Level Functions + ************************************/ -Slider.prototype._onMouseMove = function (event) { - var diff = event.clientX - this.startClientX; - var x = this.startSlideX + diff; + function makeMoment(config) { + var input = config._i, + format = config._f; - var index = this.leftToIndex(x); + if (input === null || (format === undefined && input === '')) { + return moment.invalid({nullInput: true}); + } - this.setIndex(index); + if (typeof input === 'string') { + config._i = input = getLangDefinition().preparse(input); + } - G3DpreventDefault(); -}; + if (moment.isMoment(input)) { + config = cloneMoment(input); + config._d = new Date(+input._d); + } else if (format) { + if (isArray(format)) { + makeDateFromStringAndArray(config); + } else { + makeDateFromStringAndFormat(config); + } + } else { + makeDateFromInput(config); + } -Slider.prototype._onMouseUp = function (event) { - this.frame.style.cursor = 'auto'; + return new Moment(config); + } - // remove event listeners - G3DremoveEventListener(document, 'mousemove', this.onmousemove); - G3DremoveEventListener(document, 'mouseup', this.onmouseup); + moment = function (input, format, lang, strict) { + var c; - G3DpreventDefault(); -}; + if (typeof(lang) === "boolean") { + strict = lang; + lang = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._i = input; + c._f = format; + c._l = lang; + c._strict = strict; + c._isUTC = false; + c._pf = defaultParsingFlags(); + + return makeMoment(c); + }; + moment.suppressDeprecationWarnings = false; + moment.createFromInputFallback = deprecate( + "moment construction falls back to js Date. This is " + + "discouraged and will be removed in upcoming major " + + "release. Please refer to " + + "https://github.com/moment/moment/issues/1407 for more info.", + function (config) { + config._d = new Date(config._i); + }); -/**--------------------------------------------------------------------------**/ + // 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); + }; -/** - * Retrieve the absolute left value of a DOM element - * @param {Element} elem A dom element, for example a div - * @return {Number} left The absolute left position of this element - * in the browser page. - */ -getAbsoluteLeft = function(elem) { - var left = 0; - while( elem !== null ) { - left += elem.offsetLeft; - left -= elem.scrollLeft; - elem = elem.offsetParent; - } - return left; -}; + moment.max = function () { + var args = [].slice.call(arguments, 0); -/** - * Retrieve the absolute top value of a DOM element - * @param {Element} elem A dom element, for example a div - * @return {Number} top The absolute top position of this element - * in the browser page. - */ -getAbsoluteTop = function(elem) { - var top = 0; - while( elem !== null ) { - top += elem.offsetTop; - top -= elem.scrollTop; - elem = elem.offsetParent; - } - return top; -}; + return pickBy('isAfter', args); + }; -/** - * Get the horizontal mouse position from a mouse event - * @param {Event} event - * @return {Number} mouse x - */ -getMouseX = function(event) { - if ('clientX' in event) return event.clientX; - return event.targetTouches[0] && event.targetTouches[0].clientX || 0; -}; + // creating with utc + moment.utc = function (input, format, lang, strict) { + var c; -/** - * Get the vertical mouse position from a mouse event - * @param {Event} event - * @return {Number} mouse y - */ -getMouseY = function(event) { - if ('clientY' in event) return event.clientY; - return event.targetTouches[0] && event.targetTouches[0].clientY || 0; -}; + if (typeof(lang) === "boolean") { + strict = lang; + lang = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._useUTC = true; + c._isUTC = true; + c._l = lang; + c._i = input; + c._f = format; + c._strict = strict; + c._pf = defaultParsingFlags(); + + return makeMoment(c).utc(); + }; + // creating with unix timestamp (in seconds) + moment.unix = function (input) { + return moment(input * 1000); + }; -/** - * vis.js module exports - */ -var vis = { - moment: moment, + // duration + moment.duration = function (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + parseIso; + + if (moment.isDuration(input)) { + duration = { + ms: input._milliseconds, + d: input._days, + M: input._months + }; + } else if (typeof input === 'number') { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; + } + } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { + sign = (match[1] === "-") ? -1 : 1; + duration = { + y: 0, + d: toInt(match[DATE]) * sign, + h: toInt(match[HOUR]) * sign, + m: toInt(match[MINUTE]) * sign, + s: toInt(match[SECOND]) * sign, + ms: toInt(match[MILLISECOND]) * sign + }; + } else if (!!(match = isoDurationRegex.exec(input))) { + sign = (match[1] === "-") ? -1 : 1; + parseIso = function (inp) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + }; + duration = { + y: parseIso(match[2]), + M: parseIso(match[3]), + d: parseIso(match[4]), + h: parseIso(match[5]), + m: parseIso(match[6]), + s: parseIso(match[7]), + w: parseIso(match[8]) + }; + } - util: util, - DOMutil: DOMutil, + ret = new Duration(duration); - DataSet: DataSet, - DataView: DataView, + if (moment.isDuration(input) && input.hasOwnProperty('_lang')) { + ret._lang = input._lang; + } - Timeline: Timeline, - Graph2d: Graph2d, - timeline: { - DataStep: DataStep, - Range: Range, - stack: stack, - TimeStep: TimeStep, + return ret; + }; - components: { - items: { - Item: Item, - ItemBox: ItemBox, - ItemPoint: ItemPoint, - ItemRange: ItemRange - }, + // version number + moment.version = VERSION; - Component: Component, - CurrentTime: CurrentTime, - CustomTime: CustomTime, - DataAxis: DataAxis, - GraphGroup: GraphGroup, - Group: Group, - ItemSet: ItemSet, - Legend: Legend, - LineGraph: LineGraph, - TimeAxis: TimeAxis - } - }, - - Network: Network, - network: { - Edge: Edge, - Groups: Groups, - Images: Images, - Node: Node, - Popup: Popup - }, + // default format + moment.defaultFormat = isoFormat; - // Deprecated since v3.0.0 - Graph: function () { - throw new Error('Graph is renamed to Network. Please create a graph as new vis.Network(...)'); - }, + // constant that refers to the ISO standard + moment.ISO_8601 = function () {}; - Graph3d: Graph3d -}; + // Plugins that add properties should also add the key here (null value), + // so we can properly clone ourselves. + moment.momentProperties = momentProperties; -/** - * CommonJS module exports - */ -if (typeof exports !== 'undefined') { - exports = vis; -} -if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { - module.exports = vis; -} + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + moment.updateOffset = function () {}; -/** - * AMD module exports - */ -if (typeof(define) === 'function') { - define(function () { - return vis; - }); -} + // 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; + }; -/** - * Window exports - */ -if (typeof window !== 'undefined') { - // attach the module to the window, load as a regular javascript file - window['vis'] = vis; -} + // 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. + moment.lang = function (key, values) { + var r; + if (!key) { + return moment.fn._lang._abbr; + } + if (values) { + loadLang(normalizeLanguage(key), values); + } else if (values === null) { + unloadLang(key); + key = 'en'; + } else if (!languages[key]) { + getLangDefinition(key); + } + r = moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key); + return r._abbr; + }; + // returns language data + moment.langData = function (key) { + if (key && key._lang && key._lang._abbr) { + key = key._lang._abbr; + } + return getLangDefinition(key); + }; -},{"emitter-component":2,"hammerjs":3,"moment":4,"mousetrap":5}],2:[function(require,module,exports){ + // compare moment object + moment.isMoment = function (obj) { + return obj instanceof Moment || + (obj != null && obj.hasOwnProperty('_isAMomentObject')); + }; -/** - * Expose `Emitter`. - */ + // for typechecking Duration objects + moment.isDuration = function (obj) { + return obj instanceof Duration; + }; -module.exports = Emitter; + for (i = lists.length - 1; i >= 0; --i) { + makeList(lists[i]); + } -/** - * Initialize a new `Emitter`. - * - * @api public - */ + moment.normalizeUnits = function (units) { + return normalizeUnits(units); + }; -function Emitter(obj) { - if (obj) return mixin(obj); -}; + moment.invalid = function (flags) { + var m = moment.utc(NaN); + if (flags != null) { + extend(m._pf, flags); + } + else { + m._pf.userInvalidated = true; + } -/** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ + return m; + }; -function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; -} + moment.parseZone = function () { + return moment.apply(null, arguments).parseZone(); + }; -/** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ + moment.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; -Emitter.prototype.on = -Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks[event] = this._callbacks[event] || []) - .push(fn); - return this; -}; + /************************************ + Moment Prototype + ************************************/ -/** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ -Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; + extend(moment.fn = Moment.prototype, { - function on() { - self.off(event, on); - fn.apply(this, arguments); - } + clone : function () { + return moment(this); + }, - on.fn = fn; - this.on(event, on); - return this; -}; + valueOf : function () { + return +this._d + ((this._offset || 0) * 60000); + }, -/** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ + unix : function () { + return Math.floor(+this / 1000); + }, -Emitter.prototype.off = -Emitter.prototype.removeListener = -Emitter.prototype.removeAllListeners = -Emitter.prototype.removeEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; + toString : function () { + return this.clone().lang('en').format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ"); + }, - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } + toDate : function () { + return this._offset ? new Date(+this) : this._d; + }, - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; + toISOString : function () { + var m = moment(this).utc(); + if (0 < m.year() && m.year() <= 9999) { + return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } else { + return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + }, - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; - } + toArray : function () { + var m = this; + return [ + m.year(), + m.month(), + m.date(), + m.hours(), + m.minutes(), + m.seconds(), + m.milliseconds() + ]; + }, - // remove specific handler - var cb; - for (var i = 0; i < callbacks.length; i++) { - cb = callbacks[i]; - if (cb === fn || cb.fn === fn) { - callbacks.splice(i, 1); - break; - } - } - return this; -}; + isValid : function () { + return isValid(this); + }, -/** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ + isDSTShifted : function () { -Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks[event]; + if (this._a) { + return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; + } - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } + return false; + }, - return this; -}; + parsingFlags : function () { + return extend({}, this._pf); + }, -/** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ + invalidAt: function () { + return this._pf.overflow; + }, -Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks[event] || []; -}; + utc : function () { + return this.zone(0); + }, -/** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ + local : function () { + this.zone(0); + this._isUTC = false; + return this; + }, -Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; -}; + format : function (inputString) { + var output = formatMoment(this, inputString || moment.defaultFormat); + return this.lang().postformat(output); + }, -},{}],3:[function(require,module,exports){ -/*! Hammer.JS - v1.0.5 - 2013-04-07 - * http://eightmedia.github.com/hammer.js - * - * Copyright (c) 2013 Jorik Tangelder ; - * Licensed under the MIT license */ + add : function (input, val) { + var dur; + // switch args to support add('s', 1) and add(1, 's') + 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); + } + addOrSubtractDurationFromMoment(this, dur, 1); + return this; + }, -(function(window, undefined) { - 'use strict'; + subtract : function (input, val) { + var dur; + // switch args to support subtract('s', 1) and subtract(1, 's') + 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); + } + addOrSubtractDurationFromMoment(this, dur, -1); + return this; + }, -/** - * Hammer - * use this to create instances - * @param {HTMLElement} element - * @param {Object} options - * @returns {Hammer.Instance} - * @constructor - */ -var Hammer = function(element, options) { - return new Hammer.Instance(element, options || {}); -}; - -// default settings -Hammer.defaults = { - // add styles and attributes to the element to prevent the browser from doing - // its native behavior. this doesnt prevent the scrolling, but cancels - // the contextmenu, tap highlighting etc - // set to false to disable this - stop_browser_behavior: { - // this also triggers onselectstart=false for IE - userSelect: 'none', - // this makes the element blocking in IE10 >, you could experiment with the value - // see for more options this issue; https://github.com/EightMedia/hammer.js/issues/241 - touchAction: 'none', - touchCallout: 'none', - contentZooming: 'none', - userDrag: 'none', - tapHighlightColor: 'rgba(0,0,0,0)' - } - - // more settings are defined per gesture at gestures.js -}; - -// detect touchevents -Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; -Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); - -// dont use mouseevents on mobile devices -Hammer.MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; -Hammer.NO_MOUSEEVENTS = Hammer.HAS_TOUCHEVENTS && navigator.userAgent.match(Hammer.MOBILE_REGEX); - -// eventtypes per touchevent (start, move, end) -// are filled by Hammer.event.determineEventTypes on setup -Hammer.EVENT_TYPES = {}; - -// direction defines -Hammer.DIRECTION_DOWN = 'down'; -Hammer.DIRECTION_LEFT = 'left'; -Hammer.DIRECTION_UP = 'up'; -Hammer.DIRECTION_RIGHT = 'right'; - -// pointer type -Hammer.POINTER_MOUSE = 'mouse'; -Hammer.POINTER_TOUCH = 'touch'; -Hammer.POINTER_PEN = 'pen'; - -// touch event defines -Hammer.EVENT_START = 'start'; -Hammer.EVENT_MOVE = 'move'; -Hammer.EVENT_END = 'end'; - -// hammer document where the base events are added at -Hammer.DOCUMENT = document; - -// plugins namespace -Hammer.plugins = {}; - -// if the window events are set... -Hammer.READY = false; + diff : function (input, units, asFloat) { + var that = makeAs(input, this), + zoneDiff = (this.zone() - that.zone()) * 6e4, + diff, output; + + units = normalizeUnits(units); + + if (units === 'year' || units === 'month') { + // average number of days in the months in the given dates + diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2 + // difference in months + output = ((this.year() - that.year()) * 12) + (this.month() - that.month()); + // adjust by taking difference in days, average number of days + // and dst in the given months. + output += ((this - moment(this).startOf('month')) - + (that - moment(that).startOf('month'))) / diff; + // same as above but with zones, to negate all dst + output -= ((this.zone() - moment(this).startOf('month').zone()) - + (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff; + if (units === 'year') { + output = output / 12; + } + } else { + diff = (this - that); + output = units === 'second' ? diff / 1e3 : // 1000 + units === 'minute' ? diff / 6e4 : // 1000 * 60 + units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 + units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst + units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst + diff; + } + return asFloat ? output : absRound(output); + }, -/** - * setup events to detect gestures on the document - */ -function setup() { - if(Hammer.READY) { - return; - } + from : function (time, withoutSuffix) { + return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix); + }, - // find what eventtypes we add listeners to - Hammer.event.determineEventTypes(); + fromNow : function (withoutSuffix) { + return this.from(moment(), withoutSuffix); + }, - // Register all gestures inside Hammer.gestures - for(var name in Hammer.gestures) { - if(Hammer.gestures.hasOwnProperty(name)) { - Hammer.detection.register(Hammer.gestures[name]); - } - } + 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 now = time || moment(), + sod = makeAs(now, this).startOf('day'), + diff = this.diff(sod, 'days', true), + format = diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; + return this.format(this.lang().calendar(format, this)); + }, - // Add touch events on the document - Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_MOVE, Hammer.detection.detect); - Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_END, Hammer.detection.detect); + isLeapYear : function () { + return isLeapYear(this.year()); + }, - // Hammer is ready...! - Hammer.READY = true; -} + isDST : function () { + return (this.zone() < this.clone().month(0).zone() || + this.zone() < this.clone().month(5).zone()); + }, -/** - * create new hammer instance - * all methods should return the instance itself, so it is chainable. - * @param {HTMLElement} element - * @param {Object} [options={}] - * @returns {Hammer.Instance} - * @constructor - */ -Hammer.Instance = function(element, options) { - var self = this; + day : function (input) { + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.lang()); + return this.add({ d : input - day }); + } else { + return day; + } + }, - // setup HammerJS window events and register all gestures - // this also sets up the default options - setup(); + month : makeAccessor('Month', true), + + startOf: function (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { + case 'year': + this.month(0); + /* falls through */ + case 'quarter': + case 'month': + this.date(1); + /* falls through */ + case 'week': + case 'isoWeek': + case 'day': + this.hours(0); + /* falls through */ + case 'hour': + this.minutes(0); + /* falls through */ + case 'minute': + this.seconds(0); + /* falls through */ + case 'second': + this.milliseconds(0); + /* falls through */ + } - this.element = element; + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } else if (units === 'isoWeek') { + this.isoWeekday(1); + } - // start/stop detection option - this.enabled = true; + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } - // merge options - this.options = Hammer.utils.extend( - Hammer.utils.extend({}, Hammer.defaults), - options || {}); + return this; + }, - // add some css to the element to prevent the browser from doing its native behavoir - if(this.options.stop_browser_behavior) { - Hammer.utils.stopDefaultBrowserBehavior(this.element, this.options.stop_browser_behavior); - } + endOf: function (units) { + units = normalizeUnits(units); + return this.startOf(units).add((units === 'isoWeek' ? 'week' : units), 1).subtract('ms', 1); + }, - // start detection on touchstart - Hammer.event.onTouch(element, Hammer.EVENT_START, function(ev) { - if(self.enabled) { - Hammer.detection.startDetect(self, ev); - } - }); + isAfter: function (input, units) { + units = typeof units !== 'undefined' ? units : 'millisecond'; + return +this.clone().startOf(units) > +moment(input).startOf(units); + }, - // return instance - return this; -}; + isBefore: function (input, units) { + units = typeof units !== 'undefined' ? units : 'millisecond'; + return +this.clone().startOf(units) < +moment(input).startOf(units); + }, + isSame: function (input, units) { + units = units || 'ms'; + return +this.clone().startOf(units) === +makeAs(input, this).startOf(units); + }, -Hammer.Instance.prototype = { - /** - * bind events to the instance - * @param {String} gesture - * @param {Function} handler - * @returns {Hammer.Instance} - */ - on: function onEvent(gesture, handler){ - var gestures = gesture.split(' '); - for(var t=0; t 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 + // It is possible that 5:31:26 doesn't exist int zone +0200, so we + // adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + zone : function (input, keepTime) { + var offset = this._offset || 0; + if (input != null) { + if (typeof input === "string") { + input = timezoneMinutesFromString(input); + } + if (Math.abs(input) < 16) { + input = input * 60; + } + this._offset = input; + this._isUTC = true; + if (offset !== input) { + if (!keepTime || this._changeInProgress) { + addOrSubtractDurationFromMoment(this, + moment.duration(offset - input, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + moment.updateOffset(this, true); + this._changeInProgress = null; + } + } + } else { + return this._isUTC ? offset : this._d.getTimezoneOffset(); + } + return this; + }, + zoneAbbr : function () { + return this._isUTC ? "UTC" : ""; + }, - /** - * unbind events to the instance - * @param {String} gesture - * @param {Function} handler - * @returns {Hammer.Instance} - */ - off: function offEvent(gesture, handler){ - var gestures = gesture.split(' '); - for(var t=0; t 0 && eventType == Hammer.EVENT_END) { - eventType = Hammer.EVENT_MOVE; - } - // no touches, force the end event - else if(!count_touches) { - eventType = Hammer.EVENT_END; - } + function makeAccessor(unit, keepTime) { + return function (value) { + if (value != null) { + rawSetter(this, unit, value); + moment.updateOffset(this, keepTime); + return this; + } else { + return rawGetter(this, unit); + } + }; + } + + moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); + moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); + moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); + // Setting the hour should keep the time, because the user explicitly + // specified which hour he wants. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); + // moment.fn.month is defined separately + moment.fn.date = makeAccessor('Date', true); + moment.fn.dates = deprecate("dates accessor is deprecated. Use date instead.", makeAccessor('Date', true)); + moment.fn.year = makeAccessor('FullYear', true); + moment.fn.years = deprecate("years accessor is deprecated. Use year instead.", makeAccessor('FullYear', true)); - // because touchend has no touches, and we often want to use these in our gestures, - // we send the last move event as our eventData in touchend - if(!count_touches && last_move_event !== null) { - ev = last_move_event; - } - // store the last move event - else { - last_move_event = ev; - } + // add plural methods + moment.fn.days = moment.fn.day; + moment.fn.months = moment.fn.month; + moment.fn.weeks = moment.fn.week; + moment.fn.isoWeeks = moment.fn.isoWeek; + moment.fn.quarters = moment.fn.quarter; - // trigger the handler - handler.call(Hammer.detection, self.collectEventData(element, eventType, ev)); + // add aliased format methods + moment.fn.toJSON = moment.fn.toISOString; - // remove pointerevent from list - if(Hammer.HAS_POINTEREVENTS && eventType == Hammer.EVENT_END) { - count_touches = Hammer.PointerEvent.updatePointer(eventType, ev); - } - } + /************************************ + Duration Prototype + ************************************/ - //debug(sourceEventType +" "+ eventType); - // on the end we reset everything - if(!count_touches) { - last_move_event = null; - enable_detect = false; - touch_triggered = false; - Hammer.PointerEvent.reset(); - } - }); - }, + extend(moment.duration.fn = Duration.prototype, { + _bubble : function () { + var milliseconds = this._milliseconds, + days = this._days, + months = this._months, + data = this._data, + seconds, minutes, hours, years; - /** - * we have different events for each device/browser - * determine what we need and set them in the Hammer.EVENT_TYPES constant - */ - determineEventTypes: function determineEventTypes() { - // determine the eventtype we want to set - var types; - - // pointerEvents magic - if(Hammer.HAS_POINTEREVENTS) { - types = Hammer.PointerEvent.getEvents(); - } - // on Android, iOS, blackberry, windows mobile we dont want any mouseevents - else if(Hammer.NO_MOUSEEVENTS) { - types = [ - 'touchstart', - 'touchmove', - 'touchend touchcancel']; - } - // for non pointer events browsers and mixed browsers, - // like chrome on windows8 touch laptop - else { - types = [ - 'touchstart mousedown', - 'touchmove mousemove', - 'touchend touchcancel mouseup']; - } + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; - Hammer.EVENT_TYPES[Hammer.EVENT_START] = types[0]; - Hammer.EVENT_TYPES[Hammer.EVENT_MOVE] = types[1]; - Hammer.EVENT_TYPES[Hammer.EVENT_END] = types[2]; - }, + seconds = absRound(milliseconds / 1000); + data.seconds = seconds % 60; + minutes = absRound(seconds / 60); + data.minutes = minutes % 60; - /** - * create touchlist depending on the event - * @param {Object} ev - * @param {String} eventType used by the fakemultitouch plugin - */ - getTouchList: function getTouchList(ev/*, eventType*/) { - // get the fake pointerEvent touchlist - if(Hammer.HAS_POINTEREVENTS) { - return Hammer.PointerEvent.getTouchList(); - } - // get the touchlist - else if(ev.touches) { - return ev.touches; - } - // make fake touchlist from mouse position - else { - return [{ - identifier: 1, - pageX: ev.pageX, - pageY: ev.pageY, - target: ev.target - }]; - } - }, + hours = absRound(minutes / 60); + data.hours = hours % 24; + days += absRound(hours / 24); + data.days = days % 30; - /** - * collect event data for Hammer js - * @param {HTMLElement} element - * @param {String} eventType like Hammer.EVENT_MOVE - * @param {Object} eventData - */ - collectEventData: function collectEventData(element, eventType, ev) { - var touches = this.getTouchList(ev, eventType); + months += absRound(days / 30); + data.months = months % 12; - // find out pointerType - var pointerType = Hammer.POINTER_TOUCH; - if(ev.type.match(/mouse/) || Hammer.PointerEvent.matchType(Hammer.POINTER_MOUSE, ev)) { - pointerType = Hammer.POINTER_MOUSE; - } + years = absRound(months / 12); + data.years = years; + }, - return { - center : Hammer.utils.getCenter(touches), - timeStamp : new Date().getTime(), - target : ev.target, - touches : touches, - eventType : eventType, - pointerType : pointerType, - srcEvent : ev, + weeks : function () { + return absRound(this.days() / 7); + }, - /** - * prevent the browser default actions - * mostly used to disable scrolling of the browser - */ - preventDefault: function() { - if(this.srcEvent.preventManipulation) { - this.srcEvent.preventManipulation(); - } + valueOf : function () { + return this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6; + }, - if(this.srcEvent.preventDefault) { - this.srcEvent.preventDefault(); - } - }, + humanize : function (withSuffix) { + var difference = +this, + output = relativeTime(difference, !withSuffix, this.lang()); - /** - * stop bubbling the event up to its parents - */ - stopPropagation: function() { - this.srcEvent.stopPropagation(); - }, + if (withSuffix) { + output = this.lang().pastFuture(difference, output); + } - /** - * immediately stop gesture detection - * might be useful after a swipe was detected - * @return {*} - */ - stopDetect: function() { - return Hammer.detection.stopDetect(); - } - }; - } -}; + return this.lang().postformat(output); + }, -Hammer.PointerEvent = { - /** - * holds all pointers - * @type {Object} - */ - pointers: {}, + add : function (input, val) { + // supports only 2.0-style add(1, 's') or add(moment) + var dur = moment.duration(input, val); - /** - * get a list of pointers - * @returns {Array} touchlist - */ - getTouchList: function() { - var self = this; - var touchlist = []; + this._milliseconds += dur._milliseconds; + this._days += dur._days; + this._months += dur._months; - // we can use forEach since pointerEvents only is in IE10 - Object.keys(self.pointers).sort().forEach(function(id) { - touchlist.push(self.pointers[id]); - }); - return touchlist; - }, + this._bubble(); - /** - * update the position of a pointer - * @param {String} type Hammer.EVENT_END - * @param {Object} pointerEvent - */ - updatePointer: function(type, pointerEvent) { - if(type == Hammer.EVENT_END) { - this.pointers = {}; - } - else { - pointerEvent.identifier = pointerEvent.pointerId; - this.pointers[pointerEvent.pointerId] = pointerEvent; - } + return this; + }, - return Object.keys(this.pointers).length; - }, + subtract : function (input, val) { + var dur = moment.duration(input, val); - /** - * check if ev matches pointertype - * @param {String} pointerType Hammer.POINTER_MOUSE - * @param {PointerEvent} ev - */ - matchType: function(pointerType, ev) { - if(!ev.pointerType) { - return false; - } + this._milliseconds -= dur._milliseconds; + this._days -= dur._days; + this._months -= dur._months; - var types = {}; - types[Hammer.POINTER_MOUSE] = (ev.pointerType == ev.MSPOINTER_TYPE_MOUSE || ev.pointerType == Hammer.POINTER_MOUSE); - types[Hammer.POINTER_TOUCH] = (ev.pointerType == ev.MSPOINTER_TYPE_TOUCH || ev.pointerType == Hammer.POINTER_TOUCH); - types[Hammer.POINTER_PEN] = (ev.pointerType == ev.MSPOINTER_TYPE_PEN || ev.pointerType == Hammer.POINTER_PEN); - return types[pointerType]; - }, + this._bubble(); + return this; + }, - /** - * get events - */ - getEvents: function() { - return [ - 'pointerdown MSPointerDown', - 'pointermove MSPointerMove', - 'pointerup pointercancel MSPointerUp MSPointerCancel' - ]; - }, + get : function (units) { + units = normalizeUnits(units); + return this[units.toLowerCase() + 's'](); + }, - /** - * reset the list - */ - reset: function() { - this.pointers = {}; - } -}; + as : function (units) { + units = normalizeUnits(units); + return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's'](); + }, + lang : moment.fn.lang, + + toIsoString : function () { + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var years = Math.abs(this.years()), + months = Math.abs(this.months()), + days = Math.abs(this.days()), + hours = Math.abs(this.hours()), + minutes = Math.abs(this.minutes()), + seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); + + if (!this.asSeconds()) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } -Hammer.utils = { - /** - * extend method, - * also used for cloning when dest is an empty object - * @param {Object} dest - * @param {Object} src - * @parm {Boolean} merge do a merge - * @returns {Object} dest - */ - extend: function extend(dest, src, merge) { - for (var key in src) { - if(dest[key] !== undefined && merge) { - continue; - } - dest[key] = src[key]; - } - return dest; - }, + return (this.asSeconds() < 0 ? '-' : '') + + 'P' + + (years ? years + 'Y' : '') + + (months ? months + 'M' : '') + + (days ? days + 'D' : '') + + ((hours || minutes || seconds) ? 'T' : '') + + (hours ? hours + 'H' : '') + + (minutes ? minutes + 'M' : '') + + (seconds ? seconds + 'S' : ''); + } + }); + function makeDurationGetter(name) { + moment.duration.fn[name] = function () { + return this._data[name]; + }; + } - /** - * find if a node is in the given parent - * used for event delegation tricks - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @returns {boolean} has_parent - */ - hasParent: function(node, parent) { - while(node){ - if(node == parent) { - return true; - } - node = node.parentNode; - } - return false; - }, + function makeDurationAsGetter(name, factor) { + moment.duration.fn['as' + name] = function () { + return +this / factor; + }; + } + for (i in unitMillisecondFactors) { + if (unitMillisecondFactors.hasOwnProperty(i)) { + makeDurationAsGetter(i, unitMillisecondFactors[i]); + makeDurationGetter(i.toLowerCase()); + } + } - /** - * get the center of all the touches - * @param {Array} touches - * @returns {Object} center - */ - getCenter: function getCenter(touches) { - var valuesX = [], valuesY = []; + makeDurationAsGetter('Weeks', 6048e5); + moment.duration.fn.asMonths = function () { + return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12; + }; - for(var t= 0,len=touches.length; t= y) { - return touch1.pageX - touch2.pageX > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT; - } - else { - return touch1.pageY - touch2.pageY > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN; - } - }, + return moment; + }.call(exports, __webpack_require__, exports, module)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + makeGlobal(true); + } else { + makeGlobal(); + } + }).call(this); + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(130)(module))) +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { - /** - * calculate the distance between two touches - * @param {Touch} touch1 - * @param {Touch} touch2 - * @returns {Number} distance - */ - getDistance: function getDistance(touch1, touch2) { - var x = touch2.pageX - touch1.pageX, - y = touch2.pageY - touch1.pageY; - return Math.sqrt((x*x) + (y*y)); - }, + /** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + exports._calculateNodeForces = function () { + var dx, dy, angle, distance, fx, fy, combinedClusterSize, + repulsingForce, node1, node2, i, j; + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; - /** - * calculate the scale factor between two touchLists (fingers) - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @param {Array} start - * @param {Array} end - * @returns {Number} scale - */ - getScale: function getScale(start, end) { - // need two fingers... - if(start.length >= 2 && end.length >= 2) { - return this.getDistance(end[0], end[1]) / - this.getDistance(start[0], start[1]); - } - return 1; - }, + // approximation constants + var a_base = -2 / 3; + var b = 4 / 3; + // repulsing forces between nodes + var nodeDistance = this.constants.physics.repulsion.nodeDistance; + var minimumDistance = nodeDistance; - /** - * calculate the rotation degrees between two touchLists (fingers) - * @param {Array} start - * @param {Array} end - * @returns {Number} rotation - */ - getRotation: function getRotation(start, end) { - // need two fingers - if(start.length >= 2 && end.length >= 2) { - return this.getAngle(end[1], end[0]) - - this.getAngle(start[1], start[0]); - } - return 0; - }, + // we loop from i over all but the last entree in the array + // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j + for (i = 0; i < nodeIndices.length - 1; i++) { + node1 = nodes[nodeIndices[i]]; + for (j = i + 1; j < nodeIndices.length; j++) { + node2 = nodes[nodeIndices[j]]; + combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); - /** - * boolean if the direction is vertical - * @param {String} direction - * @returns {Boolean} is_vertical - */ - isVertical: function isVertical(direction) { - return (direction == Hammer.DIRECTION_UP || direction == Hammer.DIRECTION_DOWN); - }, + minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); + var a = a_base / minimumDistance; + if (distance < 2 * minimumDistance) { + if (distance < 0.5 * minimumDistance) { + repulsingForce = 1.0; + } + else { + repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) + } + // amplify the repulsion for clusters. + repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; + repulsingForce = repulsingForce / distance; - /** - * stop browser default behavior with css props - * @param {HtmlElement} element - * @param {Object} css_props - */ - stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_props) { - var prop, - vendors = ['webkit','khtml','moz','ms','o','']; + fx = dx * repulsingForce; + fy = dy * repulsingForce; - if(!css_props || !element.style) { - return; + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; } + } + } + }; - // with css properties for modern browsers - for(var i = 0; i < vendors.length; i++) { - for(var p in css_props) { - if(css_props.hasOwnProperty(p)) { - prop = p; - // vender prefix at the property - if(vendors[i]) { - prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1); - } +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { - // set the style - element.style[prop] = css_props[p]; - } - } - } + /** + * Calculate the forces the nodes apply on eachother based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + exports._calculateNodeForces = function () { + var dx, dy, distance, fx, fy, combinedClusterSize, + repulsingForce, node1, node2, i, j; - // also the disable onselectstart - if(css_props.userSelect == 'none') { - element.onselectstart = function() { - return false; - }; - } - } -}; + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + + // approximation constants + var b = 5; + var a_base = 0.5 * -b; + + + // repulsing forces between nodes + var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; + var minimumDistance = nodeDistance; + var a = a_base / minimumDistance; + + // we loop from i over all but the last entree in the array + // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j + for (i = 0; i < nodeIndices.length - 1; i++) { -Hammer.detection = { - // contains all registred Hammer.gestures in the correct order - gestures: [], + node1 = nodes[nodeIndices[i]]; + for (j = i + 1; j < nodeIndices.length; j++) { + node2 = nodes[nodeIndices[j]]; + if (node1.level == node2.level) { - // data of the current Hammer.gesture detection session - current: null, + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); - // the previous Hammer.gesture session data - // is a full clone of the previous gesture.current object - previous: null, - // when this becomes true, no gestures are fired - stopped: false, + if (distance < 2 * minimumDistance) { + repulsingForce = a * distance + b; + var c = 0.05; + var d = 2 * minimumDistance * 2 * c; + repulsingForce = c * Math.pow(distance,2) - d * distance + d*d/(4*c); + // normalize force with + if (distance == 0) { + distance = 0.01; + } + else { + repulsingForce = repulsingForce / distance; + } + fx = dx * repulsingForce; + fy = dy * repulsingForce; - /** - * start Hammer.gesture detection - * @param {Hammer.Instance} inst - * @param {Object} eventData - */ - startDetect: function startDetect(inst, eventData) { - // already busy with a Hammer.gesture detection on an element - if(this.current) { - return; + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; + } } + } + } + }; - this.stopped = false; - this.current = { - inst : inst, // reference to HammerInstance we're working for - startEvent : Hammer.utils.extend({}, eventData), // start eventData for distances, timing etc - lastEvent : false, // last eventData - name : '' // current gesture we're in/detected, can be 'tap', 'hold' etc - }; + /** + * this function calculates the effects of the springs in the case of unsmooth curves. + * + * @private + */ + exports._calculateHierarchicalSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); - this.detect(eventData); - }, + if (distance == 0) { + distance = 0.01; + } + distance = Math.max(0.8*edgeLength,Math.min(5*edgeLength, distance)); - /** - * Hammer.gesture detection - * @param {Object} eventData - * @param {Object} eventData - */ - detect: function detect(eventData) { - if(!this.current || this.stopped) { - return; - } + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; - // extend event data with calculations about scale, distance etc - eventData = this.extendEventData(eventData); + fx = dx * springForce; + fy = dy * springForce; - // instance options - var inst_options = this.current.inst.options; + edge.to.fx -= fx; + edge.to.fy -= fy; + edge.from.fx += fx; + edge.from.fy += fy; - // call Hammer.gesture handlers - for(var g=0,len=this.gestures.length; g edgeLength) { + factor = 25; } - } - // store as previous event event - if(this.current) { - this.current.lastEvent = eventData; + if (edge.from.level > edge.to.level) { + edge.to.fx -= factor*fx; + edge.to.fy -= factor*fy; + } + else if (edge.from.level < edge.to.level) { + edge.from.fx += factor*fx; + edge.from.fy += factor*fy; + } + } } + } + } + }; - // endevent, but not the last touch, so dont stop - if(eventData.eventType == Hammer.EVENT_END && !eventData.touches.length-1) { - this.stopDetect(); - } +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { - return eventData; - }, + /** + * This function calculates the forces the nodes apply on eachother based on a gravitational model. + * The Barnes Hut method is used to speed up this N-body simulation. + * + * @private + */ + exports._calculateNodeForces = function() { + if (this.constants.physics.barnesHut.gravitationalConstant != 0) { + var node; + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + var nodeCount = nodeIndices.length; + this._formBarnesHutTree(nodes,nodeIndices); - /** - * clear the Hammer.gesture vars - * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected - * to stop other Hammer.gestures from being fired - */ - stopDetect: function stopDetect() { - // clone current data to the store as the previous gesture - // used for the double tap gesture, since this is an other gesture detect session - this.previous = Hammer.utils.extend({}, this.current); + var barnesHutTree = this.barnesHutTree; - // reset the current - this.current = null; + // place the nodes one by one recursively + for (var i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + // starting with root is irrelevant, it never passes the BarnesHut condition + this._getForceContribution(barnesHutTree.root.children.NW,node); + this._getForceContribution(barnesHutTree.root.children.NE,node); + this._getForceContribution(barnesHutTree.root.children.SW,node); + this._getForceContribution(barnesHutTree.root.children.SE,node); + } + } + }; - // stopped! - this.stopped = true; - }, + /** + * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. + * If a region contains a single node, we check if it is not itself, then we apply the force. + * + * @param parentBranch + * @param node + * @private + */ + exports._getForceContribution = function(parentBranch,node) { + // we get no force contribution from an empty region + if (parentBranch.childrenCount > 0) { + var dx,dy,distance; - /** - * extend eventData for Hammer.gestures - * @param {Object} ev - * @returns {Object} ev - */ - extendEventData: function extendEventData(ev) { - var startEv = this.current.startEvent; - - // if the touches change, set the new touches over the startEvent touches - // this because touchevents don't have all the touches on touchstart, or the - // user must place his fingers at the EXACT same time on the screen, which is not realistic - // but, sometimes it happens that both fingers are touching at the EXACT same time - if(startEv && (ev.touches.length != startEv.touches.length || ev.touches === startEv.touches)) { - // extend 1 level deep to get the touchlist with the touch objects - startEv.touches = []; - for(var i=0,len=ev.touches.length; i 1/theta = passed + // calcSize = 1/s --> d * 1/s > 1/theta = passed + if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) { + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.1*Math.random(); + dx = distance; + } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; + } + else { + // Did not pass the condition, go into children if available + if (parentBranch.childrenCount == 4) { + this._getForceContribution(parentBranch.children.NW,node); + this._getForceContribution(parentBranch.children.NE,node); + this._getForceContribution(parentBranch.children.SW,node); + this._getForceContribution(parentBranch.children.SE,node); + } + else { // parentBranch must have only one node, if it was empty we wouldnt be here + if (parentBranch.children.data.id != node.id) { // if it is not self + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.5*Math.random(); + dx = distance; } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; + } } + } + } + }; - var delta_time = ev.timeStamp - startEv.timeStamp, - delta_x = ev.center.pageX - startEv.center.pageX, - delta_y = ev.center.pageY - startEv.center.pageY, - velocity = Hammer.utils.getVelocity(delta_time, delta_x, delta_y); - - Hammer.utils.extend(ev, { - deltaTime : delta_time, - - deltaX : delta_x, - deltaY : delta_y, - - velocityX : velocity.x, - velocityY : velocity.y, - - distance : Hammer.utils.getDistance(startEv.center, ev.center), - angle : Hammer.utils.getAngle(startEv.center, ev.center), - direction : Hammer.utils.getDirection(startEv.center, ev.center), + /** + * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * + * @param nodes + * @param nodeIndices + * @private + */ + exports._formBarnesHutTree = function(nodes,nodeIndices) { + var node; + var nodeCount = nodeIndices.length; - scale : Hammer.utils.getScale(startEv.touches, ev.touches), - rotation : Hammer.utils.getRotation(startEv.touches, ev.touches), + var minX = Number.MAX_VALUE, + minY = Number.MAX_VALUE, + maxX =-Number.MAX_VALUE, + maxY =-Number.MAX_VALUE; - startEvent : startEv - }); + // get the range of the nodes + for (var i = 0; i < nodeCount; i++) { + var x = nodes[nodeIndices[i]].x; + var y = nodes[nodeIndices[i]].y; + if (x < minX) { minX = x; } + if (x > maxX) { maxX = x; } + if (y < minY) { minY = y; } + if (y > maxY) { maxY = y; } + } + // make the range a square + var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y + if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize + else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize - return ev; - }, + var minimumTreeSize = 1e-5; + var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); + var halfRootSize = 0.5 * rootSize; + var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); - /** - * register new gesture - * @param {Object} gesture object, see gestures.js for documentation - * @returns {Array} gestures - */ - register: function register(gesture) { - // add an enable gesture options if there is no given - var options = gesture.defaults || {}; - if(options[gesture.name] === undefined) { - options[gesture.name] = true; - } + // construct the barnesHutTree + var barnesHutTree = { + root:{ + centerOfMass: {x:0, y:0}, + mass:0, + range: { + minX: centerX-halfRootSize,maxX:centerX+halfRootSize, + minY: centerY-halfRootSize,maxY:centerY+halfRootSize + }, + size: rootSize, + calcSize: 1 / rootSize, + children: { data:null}, + maxWidth: 0, + level: 0, + childrenCount: 4 + } + }; + this._splitBranch(barnesHutTree.root); - // extend Hammer default options with the Hammer.gesture options - Hammer.utils.extend(Hammer.defaults, options, true); + // place the nodes one by one recursively + for (i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + this._placeInTree(barnesHutTree.root,node); + } - // set its index - gesture.index = gesture.index || 1000; + // make global + this.barnesHutTree = barnesHutTree + }; - // add Hammer.gesture to the list - this.gestures.push(gesture); - // sort the list by index - this.gestures.sort(function(a, b) { - if (a.index < b.index) { - return -1; - } - if (a.index > b.index) { - return 1; - } - return 0; - }); + /** + * this updates the mass of a branch. this is increased by adding a node. + * + * @param parentBranch + * @param node + * @private + */ + exports._updateBranchMass = function(parentBranch, node) { + var totalMass = parentBranch.mass + node.mass; + var totalMassInv = 1/totalMass; - return this.gestures; - } -}; + parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.mass; + parentBranch.centerOfMass.x *= totalMassInv; + parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.mass; + parentBranch.centerOfMass.y *= totalMassInv; -Hammer.gestures = Hammer.gestures || {}; + parentBranch.mass = totalMass; + var biggestSize = Math.max(Math.max(node.height,node.radius),node.width); + parentBranch.maxWidth = (parentBranch.maxWidth < biggestSize) ? biggestSize : parentBranch.maxWidth; -/** - * Custom gestures - * ============================== - * - * Gesture object - * -------------------- - * The object structure of a gesture: - * - * { name: 'mygesture', - * index: 1337, - * defaults: { - * mygesture_option: true - * } - * handler: function(type, ev, inst) { - * // trigger gesture event - * inst.trigger(this.name, ev); - * } - * } - - * @param {String} name - * this should be the name of the gesture, lowercase - * it is also being used to disable/enable the gesture per instance config. - * - * @param {Number} [index=1000] - * the index of the gesture, where it is going to be in the stack of gestures detection - * like when you build an gesture that depends on the drag gesture, it is a good - * idea to place it after the index of the drag gesture. - * - * @param {Object} [defaults={}] - * the default settings of the gesture. these are added to the instance settings, - * and can be overruled per instance. you can also add the name of the gesture, - * but this is also added by default (and set to true). - * - * @param {Function} handler - * this handles the gesture detection of your custom gesture and receives the - * following arguments: - * - * @param {Object} eventData - * event data containing the following properties: - * timeStamp {Number} time the event occurred - * target {HTMLElement} target element - * touches {Array} touches (fingers, pointers, mouse) on the screen - * pointerType {String} kind of pointer that was used. matches Hammer.POINTER_MOUSE|TOUCH - * center {Object} center position of the touches. contains pageX and pageY - * deltaTime {Number} the total time of the touches in the screen - * deltaX {Number} the delta on x axis we haved moved - * deltaY {Number} the delta on y axis we haved moved - * velocityX {Number} the velocity on the x - * velocityY {Number} the velocity on y - * angle {Number} the angle we are moving - * direction {String} the direction we are moving. matches Hammer.DIRECTION_UP|DOWN|LEFT|RIGHT - * distance {Number} the distance we haved moved - * scale {Number} scaling of the touches, needs 2 touches - * rotation {Number} rotation of the touches, needs 2 touches * - * eventType {String} matches Hammer.EVENT_START|MOVE|END - * srcEvent {Object} the source event, like TouchStart or MouseDown * - * startEvent {Object} contains the same properties as above, - * but from the first touch. this is used to calculate - * distances, deltaTime, scaling etc - * - * @param {Hammer.Instance} inst - * the instance we are doing the detection for. you can get the options from - * the inst.options object and trigger the gesture event by calling inst.trigger - * - * - * Handle gestures - * -------------------- - * inside the handler you can get/set Hammer.detection.current. This is the current - * detection session. It has the following properties - * @param {String} name - * contains the name of the gesture we have detected. it has not a real function, - * only to check in other gestures if something is detected. - * like in the drag gesture we set it to 'drag' and in the swipe gesture we can - * check if the current gesture is 'drag' by accessing Hammer.detection.current.name - * - * @readonly - * @param {Hammer.Instance} inst - * the instance we do the detection for - * - * @readonly - * @param {Object} startEvent - * contains the properties of the first gesture detection in this session. - * Used for calculations about timing, distance, etc. - * - * @readonly - * @param {Object} lastEvent - * contains all the properties of the last gesture detect in this session. - * - * after the gesture detection session has been completed (user has released the screen) - * the Hammer.detection.current object is copied into Hammer.detection.previous, - * this is usefull for gestures like doubletap, where you need to know if the - * previous gesture was a tap - * - * options that have been set by the instance can be received by calling inst.options - * - * You can trigger a gesture event by calling inst.trigger("mygesture", event). - * The first param is the name of your gesture, the second the event argument - * - * - * Register gestures - * -------------------- - * When an gesture is added to the Hammer.gestures object, it is auto registered - * at the setup of the first Hammer instance. You can also call Hammer.detection.register - * manually and pass your gesture object as a param - * - */ + }; -/** - * Hold - * Touch stays at the same place for x time - * @events hold - */ -Hammer.gestures.Hold = { - name: 'hold', - index: 10, - defaults: { - hold_timeout : 500, - hold_threshold : 1 - }, - timer: null, - handler: function holdGesture(ev, inst) { - switch(ev.eventType) { - case Hammer.EVENT_START: - // clear any running timers - clearTimeout(this.timer); - - // set the gesture so we can check in the timeout if it still is - Hammer.detection.current.name = this.name; - - // set timer and if after the timeout it still is hold, - // we trigger the hold event - this.timer = setTimeout(function() { - if(Hammer.detection.current.name == 'hold') { - inst.trigger('hold', ev); - } - }, inst.options.hold_timeout); - break; - - // when you move or end we clear the timer - case Hammer.EVENT_MOVE: - if(ev.distance > inst.options.hold_threshold) { - clearTimeout(this.timer); - } - break; - case Hammer.EVENT_END: - clearTimeout(this.timer); - break; - } + /** + * determine in which branch the node will be placed. + * + * @param parentBranch + * @param node + * @param skipMassUpdate + * @private + */ + exports._placeInTree = function(parentBranch,node,skipMassUpdate) { + if (skipMassUpdate != true || skipMassUpdate === undefined) { + // update the mass of the branch. + this._updateBranchMass(parentBranch,node); } -}; - -/** - * Tap/DoubleTap - * Quick touch at a place or double at the same place - * @events tap, doubletap - */ -Hammer.gestures.Tap = { - name: 'tap', - index: 100, - defaults: { - tap_max_touchtime : 250, - tap_max_distance : 10, - tap_always : true, - doubletap_distance : 20, - doubletap_interval : 300 - }, - handler: function tapGesture(ev, inst) { - if(ev.eventType == Hammer.EVENT_END) { - // previous gesture, for the double tap since these are two different gesture detections - var prev = Hammer.detection.previous, - did_doubletap = false; - - // when the touchtime is higher then the max touch time - // or when the moving distance is too much - if(ev.deltaTime > inst.options.tap_max_touchtime || - ev.distance > inst.options.tap_max_distance) { - return; - } + if (parentBranch.children.NW.range.maxX > node.x) { // in NW or SW + if (parentBranch.children.NW.range.maxY > node.y) { // in NW + this._placeInRegion(parentBranch,node,"NW"); + } + else { // in SW + this._placeInRegion(parentBranch,node,"SW"); + } + } + else { // in NE or SE + if (parentBranch.children.NW.range.maxY > node.y) { // in NE + this._placeInRegion(parentBranch,node,"NE"); + } + else { // in SE + this._placeInRegion(parentBranch,node,"SE"); + } + } + }; - // check if double tap - if(prev && prev.name == 'tap' && - (ev.timeStamp - prev.lastEvent.timeStamp) < inst.options.doubletap_interval && - ev.distance < inst.options.doubletap_distance) { - inst.trigger('doubletap', ev); - did_doubletap = true; - } - // do a single tap - if(!did_doubletap || inst.options.tap_always) { - Hammer.detection.current.name = 'tap'; - inst.trigger(Hammer.detection.current.name, ev); - } + /** + * actually place the node in a region (or branch) + * + * @param parentBranch + * @param node + * @param region + * @private + */ + exports._placeInRegion = function(parentBranch,node,region) { + switch (parentBranch.children[region].childrenCount) { + case 0: // place node here + parentBranch.children[region].children.data = node; + parentBranch.children[region].childrenCount = 1; + this._updateBranchMass(parentBranch.children[region],node); + break; + case 1: // convert into children + // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) + // we move one node a pixel and we do not put it in the tree. + if (parentBranch.children[region].children.data.x == node.x && + parentBranch.children[region].children.data.y == node.y) { + node.x += Math.random(); + node.y += Math.random(); } - } -}; - - -/** - * Swipe - * triggers swipe events when the end velocity is above the threshold - * @events swipe, swipeleft, swiperight, swipeup, swipedown - */ -Hammer.gestures.Swipe = { - name: 'swipe', - index: 40, - defaults: { - // set 0 for unlimited, but this can conflict with transform - swipe_max_touches : 1, - swipe_velocity : 0.7 - }, - handler: function swipeGesture(ev, inst) { - if(ev.eventType == Hammer.EVENT_END) { - // max touches - if(inst.options.swipe_max_touches > 0 && - ev.touches.length > inst.options.swipe_max_touches) { - return; - } - - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.velocityX > inst.options.swipe_velocity || - ev.velocityY > inst.options.swipe_velocity) { - // trigger swipe events - inst.trigger(this.name, ev); - inst.trigger(this.name + ev.direction, ev); - } + else { + this._splitBranch(parentBranch.children[region]); + this._placeInTree(parentBranch.children[region],node); } + break; + case 4: // place in branch + this._placeInTree(parentBranch.children[region],node); + break; } -}; - - -/** - * Drag - * Move with x fingers (default 1) around on the page. Blocking the scrolling when - * moving left and right is a good practice. When all the drag events are blocking - * you disable scrolling on that area. - * @events drag, drapleft, dragright, dragup, dragdown - */ -Hammer.gestures.Drag = { - name: 'drag', - index: 50, - defaults: { - drag_min_distance : 10, - // set 0 for unlimited, but this can conflict with transform - drag_max_touches : 1, - // prevent default browser behavior when dragging occurs - // be careful with it, it makes the element a blocking element - // when you are using the drag gesture, it is a good practice to set this true - drag_block_horizontal : false, - drag_block_vertical : false, - // drag_lock_to_axis keeps the drag gesture on the axis that it started on, - // It disallows vertical directions if the initial direction was horizontal, and vice versa. - drag_lock_to_axis : false, - // drag lock only kicks in when distance > drag_lock_min_distance - // This way, locking occurs only when the distance has become large enough to reliably determine the direction - drag_lock_min_distance : 25 - }, - triggered: false, - handler: function dragGesture(ev, inst) { - // current gesture isnt drag, but dragged is true - // this means an other gesture is busy. now call dragend - if(Hammer.detection.current.name != this.name && this.triggered) { - inst.trigger(this.name +'end', ev); - this.triggered = false; - return; - } - - // max touches - if(inst.options.drag_max_touches > 0 && - ev.touches.length > inst.options.drag_max_touches) { - return; - } - - switch(ev.eventType) { - case Hammer.EVENT_START: - this.triggered = false; - break; - - case Hammer.EVENT_MOVE: - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.distance < inst.options.drag_min_distance && - Hammer.detection.current.name != this.name) { - return; - } - - // we are dragging! - Hammer.detection.current.name = this.name; - - // lock drag to axis? - if(Hammer.detection.current.lastEvent.drag_locked_to_axis || (inst.options.drag_lock_to_axis && inst.options.drag_lock_min_distance<=ev.distance)) { - ev.drag_locked_to_axis = true; - } - var last_direction = Hammer.detection.current.lastEvent.direction; - if(ev.drag_locked_to_axis && last_direction !== ev.direction) { - // keep direction on the axis that the drag gesture started on - if(Hammer.utils.isVertical(last_direction)) { - ev.direction = (ev.deltaY < 0) ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN; - } - else { - ev.direction = (ev.deltaX < 0) ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT; - } - } - - // first time, trigger dragstart event - if(!this.triggered) { - inst.trigger(this.name +'start', ev); - this.triggered = true; - } - - // trigger normal event - inst.trigger(this.name, ev); - - // direction event, like dragdown - inst.trigger(this.name + ev.direction, ev); - - // block the browser events - if( (inst.options.drag_block_vertical && Hammer.utils.isVertical(ev.direction)) || - (inst.options.drag_block_horizontal && !Hammer.utils.isVertical(ev.direction))) { - ev.preventDefault(); - } - break; + }; - case Hammer.EVENT_END: - // trigger dragend - if(this.triggered) { - inst.trigger(this.name +'end', ev); - } - this.triggered = false; - break; - } + /** + * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch + * after the split is complete. + * + * @param parentBranch + * @private + */ + exports._splitBranch = function(parentBranch) { + // if the branch is shaded with a node, replace the node in the new subset. + var containedNode = null; + if (parentBranch.childrenCount == 1) { + containedNode = parentBranch.children.data; + parentBranch.mass = 0; parentBranch.centerOfMass.x = 0; parentBranch.centerOfMass.y = 0; } -}; + parentBranch.childrenCount = 4; + parentBranch.children.data = null; + this._insertRegion(parentBranch,"NW"); + this._insertRegion(parentBranch,"NE"); + this._insertRegion(parentBranch,"SW"); + this._insertRegion(parentBranch,"SE"); + if (containedNode != null) { + this._placeInTree(parentBranch,containedNode); + } + }; -/** - * Transform - * User want to scale or rotate with 2 fingers - * @events transform, pinch, pinchin, pinchout, rotate - */ -Hammer.gestures.Transform = { - name: 'transform', - index: 45, - defaults: { - // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 - transform_min_scale : 0.01, - // rotation in degrees - transform_min_rotation : 1, - // prevent default browser behavior when two touches are on the screen - // but it makes the element a blocking element - // when you are using the transform gesture, it is a good practice to set this true - transform_always_block : false - }, - triggered: false, - handler: function transformGesture(ev, inst) { - // current gesture isnt drag, but dragged is true - // this means an other gesture is busy. now call dragend - if(Hammer.detection.current.name != this.name && this.triggered) { - inst.trigger(this.name +'end', ev); - this.triggered = false; - return; - } - // atleast multitouch - if(ev.touches.length < 2) { - return; - } + /** + * This function subdivides the region into four new segments. + * Specifically, this inserts a single new segment. + * It fills the children section of the parentBranch + * + * @param parentBranch + * @param region + * @param parentRange + * @private + */ + exports._insertRegion = function(parentBranch, region) { + var minX,maxX,minY,maxY; + var childSize = 0.5 * parentBranch.size; + switch (region) { + case "NW": + minX = parentBranch.range.minX; + maxX = parentBranch.range.minX + childSize; + minY = parentBranch.range.minY; + maxY = parentBranch.range.minY + childSize; + break; + case "NE": + minX = parentBranch.range.minX + childSize; + maxX = parentBranch.range.maxX; + minY = parentBranch.range.minY; + maxY = parentBranch.range.minY + childSize; + break; + case "SW": + minX = parentBranch.range.minX; + maxX = parentBranch.range.minX + childSize; + minY = parentBranch.range.minY + childSize; + maxY = parentBranch.range.maxY; + break; + case "SE": + minX = parentBranch.range.minX + childSize; + maxX = parentBranch.range.maxX; + minY = parentBranch.range.minY + childSize; + maxY = parentBranch.range.maxY; + break; + } - // prevent default when two fingers are on the screen - if(inst.options.transform_always_block) { - ev.preventDefault(); - } - switch(ev.eventType) { - case Hammer.EVENT_START: - this.triggered = false; - break; + parentBranch.children[region] = { + centerOfMass:{x:0,y:0}, + mass:0, + range:{minX:minX,maxX:maxX,minY:minY,maxY:maxY}, + size: 0.5 * parentBranch.size, + calcSize: 2 * parentBranch.calcSize, + children: {data:null}, + maxWidth: 0, + level: parentBranch.level+1, + childrenCount: 0 + }; + }; - case Hammer.EVENT_MOVE: - var scale_threshold = Math.abs(1-ev.scale); - var rotation_threshold = Math.abs(ev.rotation); - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(scale_threshold < inst.options.transform_min_scale && - rotation_threshold < inst.options.transform_min_rotation) { - return; - } + /** + * This function is for debugging purposed, it draws the tree. + * + * @param ctx + * @param color + * @private + */ + exports._drawTree = function(ctx,color) { + if (this.barnesHutTree !== undefined) { - // we are transforming! - Hammer.detection.current.name = this.name; + ctx.lineWidth = 1; - // first time, trigger dragstart event - if(!this.triggered) { - inst.trigger(this.name +'start', ev); - this.triggered = true; - } + this._drawBranch(this.barnesHutTree.root,ctx,color); + } + }; - inst.trigger(this.name, ev); // basic transform event - // trigger rotate event - if(rotation_threshold > inst.options.transform_min_rotation) { - inst.trigger('rotate', ev); - } + /** + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private + */ + exports._drawBranch = function(branch,ctx,color) { + if (color === undefined) { + color = "#FF0000"; + } - // trigger pinch event - if(scale_threshold > inst.options.transform_min_scale) { - inst.trigger('pinch', ev); - inst.trigger('pinch'+ ((ev.scale < 1) ? 'in' : 'out'), ev); - } - break; + if (branch.childrenCount == 4) { + this._drawBranch(branch.children.NW,ctx); + this._drawBranch(branch.children.NE,ctx); + this._drawBranch(branch.children.SE,ctx); + this._drawBranch(branch.children.SW,ctx); + } + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.moveTo(branch.range.minX,branch.range.minY); + ctx.lineTo(branch.range.maxX,branch.range.minY); + ctx.stroke(); - case Hammer.EVENT_END: - // trigger dragend - if(this.triggered) { - inst.trigger(this.name +'end', ev); - } + ctx.beginPath(); + ctx.moveTo(branch.range.maxX,branch.range.minY); + ctx.lineTo(branch.range.maxX,branch.range.maxY); + ctx.stroke(); - this.triggered = false; - break; - } - } -}; + ctx.beginPath(); + ctx.moveTo(branch.range.maxX,branch.range.maxY); + ctx.lineTo(branch.range.minX,branch.range.maxY); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(branch.range.minX,branch.range.maxY); + ctx.lineTo(branch.range.minX,branch.range.minY); + ctx.stroke(); -/** - * Touch - * Called as first, tells the user has touched the screen - * @events touch - */ -Hammer.gestures.Touch = { - name: 'touch', - index: -Infinity, - defaults: { - // call preventDefault at touchstart, and makes the element blocking by - // disabling the scrolling of the page, but it improves gestures like - // transforming and dragging. - // be careful with using this, it can be very annoying for users to be stuck - // on the page - prevent_default: false, - - // disable mouse events, so only touch (or pen!) input triggers events - prevent_mouseevents: false - }, - handler: function touchGesture(ev, inst) { - if(inst.options.prevent_mouseevents && ev.pointerType == Hammer.POINTER_MOUSE) { - ev.stopDetect(); - return; - } + /* + if (branch.mass > 0) { + ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); + ctx.stroke(); + } + */ + }; - if(inst.options.prevent_default) { - ev.preventDefault(); - } - if(ev.eventType == Hammer.EVENT_START) { - inst.trigger(this.name, ev); - } - } -}; +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + var map = { + "./ar": 58, + "./ar-ma": 56, + "./ar-ma.js": 56, + "./ar-sa": 57, + "./ar-sa.js": 57, + "./ar.js": 58, + "./az": 59, + "./az.js": 59, + "./bg": 60, + "./bg.js": 60, + "./bn": 61, + "./bn.js": 61, + "./br": 62, + "./br.js": 62, + "./bs": 63, + "./bs.js": 63, + "./ca": 64, + "./ca.js": 64, + "./cs": 65, + "./cs.js": 65, + "./cv": 66, + "./cv.js": 66, + "./cy": 67, + "./cy.js": 67, + "./da": 68, + "./da.js": 68, + "./de": 70, + "./de-at": 69, + "./de-at.js": 69, + "./de.js": 70, + "./el": 71, + "./el.js": 71, + "./en-au": 72, + "./en-au.js": 72, + "./en-ca": 73, + "./en-ca.js": 73, + "./en-gb": 74, + "./en-gb.js": 74, + "./eo": 75, + "./eo.js": 75, + "./es": 76, + "./es.js": 76, + "./et": 77, + "./et.js": 77, + "./eu": 78, + "./eu.js": 78, + "./fa": 79, + "./fa.js": 79, + "./fi": 80, + "./fi.js": 80, + "./fo": 81, + "./fo.js": 81, + "./fr": 83, + "./fr-ca": 82, + "./fr-ca.js": 82, + "./fr.js": 83, + "./gl": 84, + "./gl.js": 84, + "./he": 85, + "./he.js": 85, + "./hi": 86, + "./hi.js": 86, + "./hr": 87, + "./hr.js": 87, + "./hu": 88, + "./hu.js": 88, + "./hy-am": 89, + "./hy-am.js": 89, + "./id": 90, + "./id.js": 90, + "./is": 91, + "./is.js": 91, + "./it": 92, + "./it.js": 92, + "./ja": 93, + "./ja.js": 93, + "./ka": 94, + "./ka.js": 94, + "./km": 95, + "./km.js": 95, + "./ko": 96, + "./ko.js": 96, + "./lb": 97, + "./lb.js": 97, + "./lt": 98, + "./lt.js": 98, + "./lv": 99, + "./lv.js": 99, + "./mk": 100, + "./mk.js": 100, + "./ml": 101, + "./ml.js": 101, + "./mr": 102, + "./mr.js": 102, + "./ms-my": 103, + "./ms-my.js": 103, + "./nb": 104, + "./nb.js": 104, + "./ne": 105, + "./ne.js": 105, + "./nl": 106, + "./nl.js": 106, + "./nn": 107, + "./nn.js": 107, + "./pl": 108, + "./pl.js": 108, + "./pt": 110, + "./pt-br": 109, + "./pt-br.js": 109, + "./pt.js": 110, + "./ro": 111, + "./ro.js": 111, + "./ru": 112, + "./ru.js": 112, + "./sk": 113, + "./sk.js": 113, + "./sl": 114, + "./sl.js": 114, + "./sq": 115, + "./sq.js": 115, + "./sr": 117, + "./sr-cyrl": 116, + "./sr-cyrl.js": 116, + "./sr.js": 117, + "./sv": 118, + "./sv.js": 118, + "./ta": 119, + "./ta.js": 119, + "./th": 120, + "./th.js": 120, + "./tl-ph": 121, + "./tl-ph.js": 121, + "./tr": 122, + "./tr.js": 122, + "./tzm": 124, + "./tzm-latn": 123, + "./tzm-latn.js": 123, + "./tzm.js": 124, + "./uk": 125, + "./uk.js": 125, + "./uz": 126, + "./uz.js": 126, + "./vi": 127, + "./vi.js": 127, + "./zh-cn": 128, + "./zh-cn.js": 128, + "./zh-tw": 129, + "./zh-tw.js": 129 + }; + function webpackContext(req) { + return __webpack_require__(webpackContextResolve(req)); + }; + function webpackContextResolve(req) { + return map[req] || (function() { throw new Error("Cannot find module '" + req + "'.") }()); + }; + webpackContext.keys = function webpackContextKeys() { + return Object.keys(map); + }; + webpackContext.resolve = webpackContextResolve; + module.exports = webpackContext; -/** - * Release - * Called as last, tells the user has released the screen - * @events release - */ -Hammer.gestures.Release = { - name: 'release', - index: Infinity, - handler: function releaseGesture(ev, inst) { - if(ev.eventType == Hammer.EVENT_END) { - inst.trigger(this.name, ev); - } - } -}; - -// node export -if(typeof module === 'object' && typeof module.exports === 'object'){ - module.exports = Hammer; -} -// just window export -else { - window.Hammer = Hammer; - - // requireJS module definition - if(typeof window.define === 'function' && window.define.amd) { - window.define('hammer', [], function() { - return Hammer; - }); - } -} -})(this); -},{}],4:[function(require,module,exports){ -var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};//! moment.js -//! version : 2.7.0 -//! authors : Tim Wood, Iskren Chernev, Moment.js contributors -//! license : MIT -//! momentjs.com - -(function (undefined) { - - /************************************ - Constants - ************************************/ - - var moment, - VERSION = "2.7.0", - // the global-scope this is NOT the global object in Node.js - globalScope = typeof global !== 'undefined' ? global : this, - oldGlobalMoment, - round = Math.round, - i, +/***/ }, +/* 56 */ +/***/ function(module, exports, __webpack_require__) { - YEAR = 0, - MONTH = 1, - DATE = 2, - HOUR = 3, - MINUTE = 4, - SECOND = 5, - MILLISECOND = 6, - - // internal storage for language config files - languages = {}, - - // moment internal properties - momentProperties = { - _isAMomentObject: null, - _i : null, - _f : null, - _l : null, - _strict : null, - _tzm : null, - _isUTC : null, - _offset : null, // optional. Combine with _isUTC - _pf : null, - _lang : null // optional - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Moroccan Arabic (ar-ma) + // author : ElFadili Yassine : https://github.com/ElFadiliY + // author : Abdel Said : https://github.com/abdelsaid - // check for nodeJS - hasModule = (typeof module !== 'undefined' && module.exports), - - // ASP.NET json date format regex - aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, - aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, - - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, - - // format tokens - formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g, - localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g, - - // parsing token regexes - parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 - parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 - parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 - parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 - parseTokenDigits = /\d+/, // nonzero number of digits - parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. - parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z - parseTokenT = /T/i, // T (ISO separator) - parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 - parseTokenOrdinal = /\d{1,2}/, - - //strict parsing regexes - parseTokenOneDigit = /\d/, // 0 - 9 - parseTokenTwoDigits = /\d\d/, // 00 - 99 - parseTokenThreeDigits = /\d{3}/, // 000 - 999 - parseTokenFourDigits = /\d{4}/, // 0000 - 9999 - parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999 - parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf - - // iso 8601 regex - // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) - isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, - - isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', - - isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], - ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], - ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], - ['GGGG-[W]WW', /\d{4}-W\d{2}/], - ['YYYY-DDD', /\d{4}-\d{3}/] - ], - - // iso time formats and regexes - isoTimes = [ - ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], - ['HH:mm', /(T| )\d\d:\d\d/], - ['HH', /(T| )\d\d/] - ], - - // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"] - parseTimezoneChunker = /([\+\-]|\d\d)/gi, - - // getter and setter names - proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), - unitMillisecondFactors = { - 'Milliseconds' : 1, - 'Seconds' : 1e3, - 'Minutes' : 6e4, - 'Hours' : 36e5, - 'Days' : 864e5, - 'Months' : 2592e6, - 'Years' : 31536e6 - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('ar-ma', { + months : "يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"), + monthsShort : "يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"), + weekdays : "الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"), + weekdaysShort : "احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"), + weekdaysMin : "ح_ن_ث_ر_خ_ج_س".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay: "[اليوم على الساعة] LT", + nextDay: '[غدا على الساعة] LT', + nextWeek: 'dddd [على الساعة] LT', + lastDay: '[أمس على الساعة] LT', + lastWeek: 'dddd [على الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : "في %s", + past : "منذ %s", + s : "ثوان", + m : "دقيقة", + mm : "%d دقائق", + h : "ساعة", + hh : "%d ساعات", + d : "يوم", + dd : "%d أيام", + M : "شهر", + MM : "%d أشهر", + y : "سنة", + yy : "%d سنوات" + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - unitAliases = { - ms : 'millisecond', - s : 'second', - m : 'minute', - h : 'hour', - d : 'day', - D : 'date', - w : 'week', - W : 'isoWeek', - M : 'month', - Q : 'quarter', - y : 'year', - DDD : 'dayOfYear', - e : 'weekday', - E : 'isoWeekday', - gg: 'weekYear', - GG: 'isoWeekYear' - }, - camelFunctions = { - dayofyear : 'dayOfYear', - isoweekday : 'isoWeekday', - isoweek : 'isoWeek', - weekyear : 'weekYear', - isoweekyear : 'isoWeekYear' - }, +/***/ }, +/* 57 */ +/***/ function(module, exports, __webpack_require__) { - // 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 - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Arabic Saudi Arabia (ar-sa) + // author : Suhail Alkowaileet : https://github.com/xsoh - // tokens to ordinalize and pad - ordinalizeTokens = 'DDD w W M D d'.split(' '), - paddedTokens = 'M D H h m s w W'.split(' '), + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var symbolMap = { + '1': '١', + '2': '٢', + '3': '٣', + '4': '٤', + '5': '٥', + '6': '٦', + '7': '٧', + '8': '٨', + '9': '٩', + '0': '٠' + }, numberMap = { + '١': '1', + '٢': '2', + '٣': '3', + '٤': '4', + '٥': '5', + '٦': '6', + '٧': '7', + '٨': '8', + '٩': '9', + '٠': '0' + }; - formatTokenFunctions = { - M : function () { - return this.month() + 1; - }, - MMM : function (format) { - return this.lang().monthsShort(this, format); - }, - MMMM : function (format) { - return this.lang().months(this, format); - }, - D : function () { - return this.date(); - }, - DDD : function () { - return this.dayOfYear(); - }, - d : function () { - return this.day(); - }, - dd : function (format) { - return this.lang().weekdaysMin(this, format); - }, - ddd : function (format) { - return this.lang().weekdaysShort(this, format); - }, - dddd : function (format) { - return this.lang().weekdays(this, format); - }, - w : function () { - return this.week(); - }, - W : function () { - return this.isoWeek(); - }, - YY : function () { - return leftZeroFill(this.year() % 100, 2); - }, - YYYY : function () { - return leftZeroFill(this.year(), 4); - }, - YYYYY : function () { - return leftZeroFill(this.year(), 5); - }, - YYYYYY : function () { - var y = this.year(), sign = y >= 0 ? '+' : '-'; - return sign + leftZeroFill(Math.abs(y), 6); - }, - gg : function () { - return leftZeroFill(this.weekYear() % 100, 2); - }, - gggg : function () { - return leftZeroFill(this.weekYear(), 4); - }, - ggggg : function () { - return leftZeroFill(this.weekYear(), 5); - }, - GG : function () { - return leftZeroFill(this.isoWeekYear() % 100, 2); - }, - GGGG : function () { - return leftZeroFill(this.isoWeekYear(), 4); - }, - GGGGG : function () { - return leftZeroFill(this.isoWeekYear(), 5); - }, - e : function () { - return this.weekday(); - }, - E : function () { - return this.isoWeekday(); - }, - a : function () { - return this.lang().meridiem(this.hours(), this.minutes(), true); - }, - A : function () { - return this.lang().meridiem(this.hours(), this.minutes(), false); - }, - H : function () { - return this.hours(); - }, - h : function () { - return this.hours() % 12 || 12; - }, - m : function () { - return this.minutes(); - }, - s : function () { - return this.seconds(); - }, - S : function () { - return toInt(this.milliseconds() / 100); - }, - SS : function () { - return leftZeroFill(toInt(this.milliseconds() / 10), 2); - }, - SSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - SSSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - Z : function () { - var a = -this.zone(), - b = "+"; - if (a < 0) { - a = -a; - b = "-"; - } - return b + leftZeroFill(toInt(a / 60), 2) + ":" + leftZeroFill(toInt(a) % 60, 2); - }, - ZZ : function () { - var a = -this.zone(), - b = "+"; - if (a < 0) { - a = -a; - b = "-"; - } - return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); - }, - z : function () { - return this.zoneAbbr(); - }, - zz : function () { - return this.zoneName(); - }, - X : function () { - return this.unix(); - }, - Q : function () { - return this.quarter(); - } - }, + return moment.lang('ar-sa', { + months : "يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"), + monthsShort : "يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"), + weekdays : "الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"), + weekdaysShort : "أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"), + weekdaysMin : "ح_ن_ث_ر_خ_ج_س".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return "ص"; + } else { + return "م"; + } + }, + calendar : { + sameDay: "[اليوم على الساعة] LT", + nextDay: '[غدا على الساعة] LT', + nextWeek: 'dddd [على الساعة] LT', + lastDay: '[أمس على الساعة] LT', + lastWeek: 'dddd [على الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : "في %s", + past : "منذ %s", + s : "ثوان", + m : "دقيقة", + mm : "%d دقائق", + h : "ساعة", + hh : "%d ساعات", + d : "يوم", + dd : "%d أيام", + M : "شهر", + MM : "%d أشهر", + y : "سنة", + yy : "%d سنوات" + }, + preparse: function (string) { + return string.replace(/[۰-۹]/g, function (match) { + return numberMap[match]; + }).replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }).replace(/,/g, '،'); + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - 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. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso: false - }; - } - function deprecate(msg, fn) { - var firstTime = true; - function printMsg() { - if (moment.suppressDeprecationWarnings === false && - typeof console !== 'undefined' && console.warn) { - console.warn("Deprecation warning: " + msg); - } - } - return extend(function () { - if (firstTime) { - printMsg(); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { - function padToken(func, count) { - return function (a) { - return leftZeroFill(func.call(this, a), count); - }; - } - function ordinalizeToken(func, period) { - return function (a) { - return this.lang().ordinal(func.call(this, a), period); - }; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Arabic (ar) + // author : Abdel Said : https://github.com/abdelsaid + // changes in months, weekdays : Ahmed Elkhatib - while (ordinalizeTokens.length) { - i = ordinalizeTokens.pop(); - formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); - } - while (paddedTokens.length) { - i = paddedTokens.pop(); - formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); - } - formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var symbolMap = { + '1': '١', + '2': '٢', + '3': '٣', + '4': '٤', + '5': '٥', + '6': '٦', + '7': '٧', + '8': '٨', + '9': '٩', + '0': '٠' + }, numberMap = { + '١': '1', + '٢': '2', + '٣': '3', + '٤': '4', + '٥': '5', + '٦': '6', + '٧': '7', + '٨': '8', + '٩': '9', + '٠': '0' + }; + return moment.lang('ar', { + months : "يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"), + monthsShort : "يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"), + weekdays : "الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"), + weekdaysShort : "أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"), + weekdaysMin : "ح_ن_ث_ر_خ_ج_س".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return "ص"; + } else { + return "م"; + } + }, + calendar : { + sameDay: "[اليوم على الساعة] LT", + nextDay: '[غدا على الساعة] LT', + nextWeek: 'dddd [على الساعة] LT', + lastDay: '[أمس على الساعة] LT', + lastWeek: 'dddd [على الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : "في %s", + past : "منذ %s", + s : "ثوان", + m : "دقيقة", + mm : "%d دقائق", + h : "ساعة", + hh : "%d ساعات", + d : "يوم", + dd : "%d أيام", + M : "شهر", + MM : "%d أشهر", + y : "سنة", + yy : "%d سنوات" + }, + preparse: function (string) { + return string.replace(/[۰-۹]/g, function (match) { + return numberMap[match]; + }).replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }).replace(/,/g, '،'); + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - /************************************ - Constructors - ************************************/ - function Language() { +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : azerbaijani (az) + // author : topchiyev : https://github.com/topchiyev - // Moment prototype object - function Moment(config) { - checkOverflow(config); - extend(this, config); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { - // Duration Constructor - function Duration(duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; + var suffixes = { + 1: "-inci", + 5: "-inci", + 8: "-inci", + 70: "-inci", + 80: "-inci", - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 36e5; // 1000 * 60 * 60 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; + 2: "-nci", + 7: "-nci", + 20: "-nci", + 50: "-nci", - this._data = {}; + 3: "-üncü", + 4: "-üncü", + 100: "-üncü", - this._bubble(); - } + 6: "-ncı", - /************************************ - Helpers - ************************************/ + 9: "-uncu", + 10: "-uncu", + 30: "-uncu", + 60: "-ıncı", + 90: "-ıncı" + }; + return moment.lang('az', { + months : "yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"), + monthsShort : "yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"), + weekdays : "Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə".split("_"), + weekdaysShort : "Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən".split("_"), + weekdaysMin : "Bz_BE_ÇA_Çə_CA_Cü_Şə".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay : '[bugün saat] LT', + nextDay : '[sabah saat] LT', + nextWeek : '[gələn həftə] dddd [saat] LT', + lastDay : '[dünən] LT', + lastWeek : '[keçən həftə] dddd [saat] LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s sonra", + past : "%s əvvəl", + s : "birneçə saniyyə", + m : "bir dəqiqə", + mm : "%d dəqiqə", + h : "bir saat", + hh : "%d saat", + d : "bir gün", + dd : "%d gün", + M : "bir ay", + MM : "%d ay", + y : "bir il", + yy : "%d il" + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return "gecə"; + } else if (hour < 12) { + return "səhər"; + } else if (hour < 17) { + return "gündüz"; + } else { + return "axşam"; + } + }, + ordinal : function (number) { + if (number === 0) { // special case for zero + return number + "-ıncı"; + } + var a = number % 10, + b = number % 100 - a, + c = number >= 100 ? 100 : null; - function extend(a, b) { - for (var i in b) { - if (b.hasOwnProperty(i)) { - a[i] = b[i]; - } - } + return number + (suffixes[a] || suffixes[b] || suffixes[c]); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - if (b.hasOwnProperty("toString")) { - a.toString = b.toString; - } - if (b.hasOwnProperty("valueOf")) { - a.valueOf = b.valueOf; - } +/***/ }, +/* 60 */ +/***/ function(module, exports, __webpack_require__) { - return a; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : bulgarian (bg) + // author : Krasen Borisov : https://github.com/kraz - function cloneMoment(m) { - var result = {}, i; - for (i in m) { - if (m.hasOwnProperty(i) && momentProperties.hasOwnProperty(i)) { - result[i] = m[i]; - } - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('bg', { + months : "януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"), + monthsShort : "янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"), + weekdays : "неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"), + weekdaysShort : "нед_пон_вто_сря_чет_пет_съб".split("_"), + weekdaysMin : "нд_пн_вт_ср_чт_пт_сб".split("_"), + longDateFormat : { + LT : "H:mm", + L : "D.MM.YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay : '[Днес в] LT', + nextDay : '[Утре в] LT', + nextWeek : 'dddd [в] LT', + lastDay : '[Вчера в] LT', + lastWeek : function () { + switch (this.day()) { + case 0: + case 3: + case 6: + return '[В изминалата] dddd [в] LT'; + case 1: + case 2: + case 4: + case 5: + return '[В изминалия] dddd [в] LT'; + } + }, + sameElse : 'L' + }, + relativeTime : { + future : "след %s", + past : "преди %s", + s : "няколко секунди", + m : "минута", + mm : "%d минути", + h : "час", + hh : "%d часа", + d : "ден", + dd : "%d дни", + M : "месец", + MM : "%d месеца", + y : "година", + yy : "%d години" + }, + ordinal : function (number) { + var lastDigit = number % 10, + last2Digits = number % 100; + if (number === 0) { + return number + '-ев'; + } else if (last2Digits === 0) { + return number + '-ен'; + } else if (last2Digits > 10 && last2Digits < 20) { + return number + '-ти'; + } else if (lastDigit === 1) { + return number + '-ви'; + } else if (lastDigit === 2) { + return number + '-ри'; + } else if (lastDigit === 7 || lastDigit === 8) { + return number + '-ми'; + } else { + return number + '-ти'; + } + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - return result; - } - function absRound(number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); - } - } +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { - // left zero fill a number - // see http://jsperf.com/left-zero-filling for performance comparison - function leftZeroFill(number, targetLength, forceSign) { - var output = '' + Math.abs(number), - sign = number >= 0; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Bengali (bn) + // author : Kaushik Gandhi : https://github.com/kaushikgandhi - while (output.length < targetLength) { - output = '0' + output; - } - return (sign ? (forceSign ? '+' : '') : '-') + output; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var symbolMap = { + '1': '১', + '2': '২', + '3': '৩', + '4': '৪', + '5': '৫', + '6': '৬', + '7': '৭', + '8': '৮', + '9': '৯', + '0': '০' + }, + numberMap = { + '১': '1', + '২': '2', + '৩': '3', + '৪': '4', + '৫': '5', + '৬': '6', + '৭': '7', + '৮': '8', + '৯': '9', + '০': '0' + }; - // helper function for _.addTime and _.subtractTime - function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = duration._days, - months = duration._months; - updateOffset = updateOffset == null ? true : updateOffset; + return moment.lang('bn', { + months : 'জানুয়ারী_ফেবুয়ারী_মার্চ_এপ্রিল_মে_জুন_জুলাই_অগাস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split("_"), + monthsShort : 'জানু_ফেব_মার্চ_এপর_মে_জুন_জুল_অগ_সেপ্ট_অক্টো_নভ_ডিসেম্'.split("_"), + weekdays : 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পত্তিবার_শুক্রুবার_শনিবার'.split("_"), + weekdaysShort : 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পত্তি_শুক্রু_শনি'.split("_"), + weekdaysMin : 'রব_সম_মঙ্গ_বু_ব্রিহ_শু_শনি'.split("_"), + longDateFormat : { + LT : "A h:mm সময়", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY, LT", + LLLL : "dddd, D MMMM YYYY, LT" + }, + calendar : { + sameDay : '[আজ] LT', + nextDay : '[আগামীকাল] LT', + nextWeek : 'dddd, LT', + lastDay : '[গতকাল] LT', + lastWeek : '[গত] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s পরে", + past : "%s আগে", + s : "কএক সেকেন্ড", + m : "এক মিনিট", + mm : "%d মিনিট", + h : "এক ঘন্টা", + hh : "%d ঘন্টা", + d : "এক দিন", + dd : "%d দিন", + M : "এক মাস", + MM : "%d মাস", + y : "এক বছর", + yy : "%d বছর" + }, + preparse: function (string) { + return string.replace(/[১২৩৪৫৬৭৮৯০]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + //Bengali is a vast language its spoken + //in different forms in various parts of the world. + //I have just generalized with most common one used + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return "রাত"; + } else if (hour < 10) { + return "শকাল"; + } else if (hour < 17) { + return "দুপুর"; + } else if (hour < 20) { + return "বিকেল"; + } else { + return "রাত"; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - if (milliseconds) { - mom._d.setTime(+mom._d + milliseconds * isAdding); - } - if (days) { - rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); - } - if (months) { - rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); - } - if (updateOffset) { - moment.updateOffset(mom, days || months); - } - } - // check if is an array - function isArray(input) { - return Object.prototype.toString.call(input) === '[object Array]'; - } +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { - function isDate(input) { - return Object.prototype.toString.call(input) === '[object Date]' || - input instanceof Date; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : breton (br) + // author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function relativeTimeWithMutation(number, withoutSuffix, key) { + var format = { + 'mm': "munutenn", + 'MM': "miz", + 'dd': "devezh" + }; + return number + ' ' + mutation(format[key], number); + } + + function specialMutationForYears(number) { + switch (lastNumber(number)) { + case 1: + case 3: + case 4: + case 5: + case 9: + return number + ' bloaz'; + default: + return number + ' vloaz'; + } + } - function normalizeUnits(units) { - if (units) { - var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); - units = unitAliases[units] || camelFunctions[lowered] || lowered; - } - return units; - } + function lastNumber(number) { + if (number > 9) { + return lastNumber(number % 10); + } + return number; + } - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; + function mutation(text, number) { + if (number === 2) { + return softMutation(text); + } + return text; + } - for (prop in inputObject) { - if (inputObject.hasOwnProperty(prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } + function softMutation(text) { + var mutationTable = { + 'm': 'v', + 'b': 'v', + 'd': 'z' + }; + if (mutationTable[text.charAt(0)] === undefined) { + return text; + } + return mutationTable[text.charAt(0)] + text.substring(1); + } + + return moment.lang('br', { + months : "Genver_C'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"), + monthsShort : "Gen_C'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"), + weekdays : "Sul_Lun_Meurzh_Merc'her_Yaou_Gwener_Sadorn".split("_"), + weekdaysShort : "Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"), + weekdaysMin : "Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"), + longDateFormat : { + LT : "h[e]mm A", + L : "DD/MM/YYYY", + LL : "D [a viz] MMMM YYYY", + LLL : "D [a viz] MMMM YYYY LT", + LLLL : "dddd, D [a viz] MMMM YYYY LT" + }, + calendar : { + sameDay : '[Hiziv da] LT', + nextDay : '[Warc\'hoazh da] LT', + nextWeek : 'dddd [da] LT', + lastDay : '[Dec\'h da] LT', + lastWeek : 'dddd [paset da] LT', + sameElse : 'L' + }, + relativeTime : { + future : "a-benn %s", + past : "%s 'zo", + s : "un nebeud segondennoù", + m : "ur vunutenn", + mm : relativeTimeWithMutation, + h : "un eur", + hh : "%d eur", + d : "un devezh", + dd : relativeTimeWithMutation, + M : "ur miz", + MM : relativeTimeWithMutation, + y : "ur bloaz", + yy : specialMutationForYears + }, + ordinal : function (number) { + var output = (number === 1) ? 'añ' : 'vet'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - return normalizedInput; - } - function makeList(field) { - var count, setter; +/***/ }, +/* 63 */ +/***/ function(module, exports, __webpack_require__) { - if (field.indexOf('week') === 0) { - count = 7; - setter = 'day'; - } - else if (field.indexOf('month') === 0) { - count = 12; - setter = 'month'; - } - else { - return; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : bosnian (bs) + // author : Nedim Cholich : https://github.com/frontyard + // based on (hr) translation by Bojan Marković - moment[field] = function (format, index) { - var i, getter, - method = moment.fn._lang[field], - results = []; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + + function translate(number, withoutSuffix, key) { + var result = number + " "; + switch (key) { + case 'm': + return withoutSuffix ? 'jedna minuta' : 'jedne minute'; + case 'mm': + if (number === 1) { + result += 'minuta'; + } else if (number === 2 || number === 3 || number === 4) { + result += 'minute'; + } else { + result += 'minuta'; + } + return result; + case 'h': + return withoutSuffix ? 'jedan sat' : 'jednog sata'; + case 'hh': + if (number === 1) { + result += 'sat'; + } else if (number === 2 || number === 3 || number === 4) { + result += 'sata'; + } else { + result += 'sati'; + } + return result; + case 'dd': + if (number === 1) { + result += 'dan'; + } else { + result += 'dana'; + } + return result; + case 'MM': + if (number === 1) { + result += 'mjesec'; + } else if (number === 2 || number === 3 || number === 4) { + result += 'mjeseca'; + } else { + result += 'mjeseci'; + } + return result; + case 'yy': + if (number === 1) { + result += 'godina'; + } else if (number === 2 || number === 3 || number === 4) { + result += 'godine'; + } else { + result += 'godina'; + } + return result; + } + } - if (typeof format === 'number') { - index = format; - format = undefined; - } + return moment.lang('bs', { + months : "januar_februar_mart_april_maj_juni_juli_avgust_septembar_oktobar_novembar_decembar".split("_"), + monthsShort : "jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"), + weekdays : "nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"), + weekdaysShort : "ned._pon._uto._sri._čet._pet._sub.".split("_"), + weekdaysMin : "ne_po_ut_sr_če_pe_su".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD. MM. YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd, D. MMMM YYYY LT" + }, + calendar : { + sameDay : '[danas u] LT', + nextDay : '[sutra u] LT', + + nextWeek : function () { + switch (this.day()) { + case 0: + return '[u] [nedjelju] [u] LT'; + case 3: + return '[u] [srijedu] [u] LT'; + case 6: + return '[u] [subotu] [u] LT'; + case 1: + case 2: + case 4: + case 5: + return '[u] dddd [u] LT'; + } + }, + lastDay : '[jučer u] LT', + lastWeek : function () { + switch (this.day()) { + case 0: + case 3: + return '[prošlu] dddd [u] LT'; + case 6: + return '[prošle] [subote] [u] LT'; + case 1: + case 2: + case 4: + case 5: + return '[prošli] dddd [u] LT'; + } + }, + sameElse : 'L' + }, + relativeTime : { + future : "za %s", + past : "prije %s", + s : "par sekundi", + m : translate, + mm : translate, + h : translate, + hh : translate, + d : "dan", + dd : translate, + M : "mjesec", + MM : translate, + y : "godinu", + yy : translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - getter = function (i) { - var m = moment().utc().set(setter, i); - return method.call(moment.fn._lang, m, format || ''); - }; - if (index != null) { - return getter(index); - } - else { - for (i = 0; i < count; i++) { - results.push(getter(i)); - } - return results; - } - }; - } +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : catalan (ca) + // author : Juan G. Hurtado : https://github.com/juanghurtado - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - if (coercedNumber >= 0) { - value = Math.floor(coercedNumber); - } else { - value = Math.ceil(coercedNumber); - } - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('ca', { + months : "gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"), + monthsShort : "gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.".split("_"), + weekdays : "diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"), + weekdaysShort : "dg._dl._dt._dc._dj._dv._ds.".split("_"), + weekdaysMin : "Dg_Dl_Dt_Dc_Dj_Dv_Ds".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay : function () { + return '[avui a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; + }, + nextDay : function () { + return '[demà a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; + }, + nextWeek : function () { + return 'dddd [a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; + }, + lastDay : function () { + return '[ahir a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; + }, + lastWeek : function () { + return '[el] dddd [passat a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; + }, + sameElse : 'L' + }, + relativeTime : { + future : "en %s", + past : "fa %s", + s : "uns segons", + m : "un minut", + mm : "%d minuts", + h : "una hora", + hh : "%d hores", + d : "un dia", + dd : "%d dies", + M : "un mes", + MM : "%d mesos", + y : "un any", + yy : "%d anys" + }, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - return value; - } - function daysInMonth(year, month) { - return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); - } +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { - function weeksInYear(year, dow, doy) { - return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : czech (cs) + // author : petrbela : https://github.com/petrbela - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var months = "leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_"), + monthsShort = "led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_"); + + function plural(n) { + return (n > 1) && (n < 5) && (~~(n / 10) !== 1); + } + + function translate(number, withoutSuffix, key, isFuture) { + var result = number + " "; + switch (key) { + case 's': // a few seconds / in a few seconds / a few seconds ago + return (withoutSuffix || isFuture) ? 'pár sekund' : 'pár sekundami'; + case 'm': // a minute / in a minute / a minute ago + return withoutSuffix ? 'minuta' : (isFuture ? 'minutu' : 'minutou'); + case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'minuty' : 'minut'); + } else { + return result + 'minutami'; + } + break; + case 'h': // an hour / in an hour / an hour ago + return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou'); + case 'hh': // 9 hours / in 9 hours / 9 hours ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'hodiny' : 'hodin'); + } else { + return result + 'hodinami'; + } + break; + case 'd': // a day / in a day / a day ago + return (withoutSuffix || isFuture) ? 'den' : 'dnem'; + case 'dd': // 9 days / in 9 days / 9 days ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'dny' : 'dní'); + } else { + return result + 'dny'; + } + break; + case 'M': // a month / in a month / a month ago + return (withoutSuffix || isFuture) ? 'měsíc' : 'měsícem'; + case 'MM': // 9 months / in 9 months / 9 months ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'měsíce' : 'měsíců'); + } else { + return result + 'měsíci'; + } + break; + case 'y': // a year / in a year / a year ago + return (withoutSuffix || isFuture) ? 'rok' : 'rokem'; + case 'yy': // 9 years / in 9 years / 9 years ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'roky' : 'let'); + } else { + return result + 'lety'; + } + break; + } + } - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; - } + return moment.lang('cs', { + months : months, + monthsShort : monthsShort, + monthsParse : (function (months, monthsShort) { + var i, _monthsParse = []; + for (i = 0; i < 12; i++) { + // use custom parser to solve problem with July (červenec) + _monthsParse[i] = new RegExp('^' + months[i] + '$|^' + monthsShort[i] + '$', 'i'); + } + return _monthsParse; + }(months, monthsShort)), + weekdays : "neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"), + weekdaysShort : "ne_po_út_st_čt_pá_so".split("_"), + weekdaysMin : "ne_po_út_st_čt_pá_so".split("_"), + longDateFormat : { + LT: "H.mm", + L : "DD. MM. YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd D. MMMM YYYY LT" + }, + calendar : { + sameDay: "[dnes v] LT", + nextDay: '[zítra v] LT', + nextWeek: function () { + switch (this.day()) { + case 0: + return '[v neděli v] LT'; + case 1: + case 2: + return '[v] dddd [v] LT'; + case 3: + return '[ve středu v] LT'; + case 4: + return '[ve čtvrtek v] LT'; + case 5: + return '[v pátek v] LT'; + case 6: + return '[v sobotu v] LT'; + } + }, + lastDay: '[včera v] LT', + lastWeek: function () { + switch (this.day()) { + case 0: + return '[minulou neděli v] LT'; + case 1: + case 2: + return '[minulé] dddd [v] LT'; + case 3: + return '[minulou středu v] LT'; + case 4: + case 5: + return '[minulý] dddd [v] LT'; + case 6: + return '[minulou sobotu v] LT'; + } + }, + sameElse: "L" + }, + relativeTime : { + future : "za %s", + past : "před %s", + s : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - function checkOverflow(m) { - var overflow; - if (m._a && m._pf.overflow === -2) { - overflow = - m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : - m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : - m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR : - m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : - m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : - m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : - -1; - if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { - m._pf.overflow = overflow; - } - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : chuvash (cv) + // author : Anatoly Mironov : https://github.com/mirontoli - function isValid(m) { - if (m._isValid == null) { - m._isValid = !isNaN(m._d.getTime()) && - m._pf.overflow < 0 && - !m._pf.empty && - !m._pf.invalidMonth && - !m._pf.nullInput && - !m._pf.invalidFormat && - !m._pf.userInvalidated; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('cv', { + months : "кăрлач_нарăс_пуш_ака_май_çĕртме_утă_çурла_авăн_юпа_чӳк_раштав".split("_"), + monthsShort : "кăр_нар_пуш_ака_май_çĕр_утă_çур_ав_юпа_чӳк_раш".split("_"), + weekdays : "вырсарникун_тунтикун_ытларикун_юнкун_кĕçнерникун_эрнекун_шăматкун".split("_"), + weekdaysShort : "выр_тун_ытл_юн_кĕç_эрн_шăм".split("_"), + weekdaysMin : "вр_тн_ыт_юн_кç_эр_шм".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD-MM-YYYY", + LL : "YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ]", + LLL : "YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ], LT", + LLLL : "dddd, YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ], LT" + }, + calendar : { + sameDay: '[Паян] LT [сехетре]', + nextDay: '[Ыран] LT [сехетре]', + lastDay: '[Ĕнер] LT [сехетре]', + nextWeek: '[Çитес] dddd LT [сехетре]', + lastWeek: '[Иртнĕ] dddd LT [сехетре]', + sameElse: 'L' + }, + relativeTime : { + future : function (output) { + var affix = /сехет$/i.exec(output) ? "рен" : /çул$/i.exec(output) ? "тан" : "ран"; + return output + affix; + }, + past : "%s каялла", + s : "пĕр-ик çеккунт", + m : "пĕр минут", + mm : "%d минут", + h : "пĕр сехет", + hh : "%d сехет", + d : "пĕр кун", + dd : "%d кун", + M : "пĕр уйăх", + MM : "%d уйăх", + y : "пĕр çул", + yy : "%d çул" + }, + ordinal : '%d-мĕш', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - if (m._strict) { - m._isValid = m._isValid && - m._pf.charsLeftOver === 0 && - m._pf.unusedTokens.length === 0; - } - } - return m._isValid; - } - function normalizeLanguage(key) { - return key ? key.toLowerCase().replace('_', '-') : key; - } +/***/ }, +/* 67 */ +/***/ function(module, exports, __webpack_require__) { - // Return a moment from input, that is local/utc/zone equivalent to model. - function makeAs(input, model) { - return model._isUTC ? moment(input).zone(model._offset || 0) : - moment(input).local(); - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Welsh (cy) + // author : Robert Allen - /************************************ - Languages - ************************************/ + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang("cy", { + months: "Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"), + monthsShort: "Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"), + weekdays: "Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"), + weekdaysShort: "Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"), + weekdaysMin: "Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"), + // time formats are the same as en-gb + longDateFormat: { + LT: "HH:mm", + L: "DD/MM/YYYY", + LL: "D MMMM YYYY", + LLL: "D MMMM YYYY LT", + LLLL: "dddd, D MMMM YYYY LT" + }, + calendar: { + sameDay: '[Heddiw am] LT', + nextDay: '[Yfory am] LT', + nextWeek: 'dddd [am] LT', + lastDay: '[Ddoe am] LT', + lastWeek: 'dddd [diwethaf am] LT', + sameElse: 'L' + }, + relativeTime: { + future: "mewn %s", + past: "%s yn ôl", + s: "ychydig eiliadau", + m: "munud", + mm: "%d munud", + h: "awr", + hh: "%d awr", + d: "diwrnod", + dd: "%d diwrnod", + M: "mis", + MM: "%d mis", + y: "blwyddyn", + yy: "%d flynedd" + }, + // traditional ordinal numbers above 31 are not commonly used in colloquial Welsh + ordinal: function (number) { + var b = number, + output = '', + lookup = [ + '', 'af', 'il', 'ydd', 'ydd', 'ed', 'ed', 'ed', 'fed', 'fed', 'fed', // 1af to 10fed + 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'fed' // 11eg to 20fed + ]; + + if (b > 20) { + if (b === 40 || b === 50 || b === 60 || b === 80 || b === 100) { + output = 'fed'; // not 30ain, 70ain or 90ain + } else { + output = 'ain'; + } + } else if (b > 0) { + output = lookup[b]; + } + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - extend(Language.prototype, { - set : function (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (typeof prop === 'function') { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - }, +/***/ }, +/* 68 */ +/***/ function(module, exports, __webpack_require__) { - _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), - months : function (m) { - return this._months[m.month()]; - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : danish (da) + // author : Ulrik Nielsen : https://github.com/mrbase - _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), - monthsShort : function (m) { - return this._monthsShort[m.month()]; - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('da', { + months : "januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"), + monthsShort : "jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"), + weekdays : "søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"), + weekdaysShort : "søn_man_tir_ons_tor_fre_lør".split("_"), + weekdaysMin : "sø_ma_ti_on_to_fr_lø".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd [d.] D. MMMM YYYY LT" + }, + calendar : { + sameDay : '[I dag kl.] LT', + nextDay : '[I morgen kl.] LT', + nextWeek : 'dddd [kl.] LT', + lastDay : '[I går kl.] LT', + lastWeek : '[sidste] dddd [kl] LT', + sameElse : 'L' + }, + relativeTime : { + future : "om %s", + past : "%s siden", + s : "få sekunder", + m : "et minut", + mm : "%d minutter", + h : "en time", + hh : "%d timer", + d : "en dag", + dd : "%d dage", + M : "en måned", + MM : "%d måneder", + y : "et år", + yy : "%d år" + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - monthsParse : function (monthName) { - var i, mom, regex; - if (!this._monthsParse) { - this._monthsParse = []; - } +/***/ }, +/* 69 */ +/***/ function(module, exports, __webpack_require__) { - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - if (!this._monthsParse[i]) { - mom = moment.utc([2000, i]); - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (this._monthsParse[i].test(monthName)) { - return i; - } - } - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : austrian german (de-at) + // author : lluchs : https://github.com/lluchs + // author: Menelion Elensúle: https://github.com/Oire + // author : Martin Groller : https://github.com/MadMG - _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), - weekdays : function (m) { - return this._weekdays[m.day()]; - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 'm': ['eine Minute', 'einer Minute'], + 'h': ['eine Stunde', 'einer Stunde'], + 'd': ['ein Tag', 'einem Tag'], + 'dd': [number + ' Tage', number + ' Tagen'], + 'M': ['ein Monat', 'einem Monat'], + 'MM': [number + ' Monate', number + ' Monaten'], + 'y': ['ein Jahr', 'einem Jahr'], + 'yy': [number + ' Jahre', number + ' Jahren'] + }; + return withoutSuffix ? format[key][0] : format[key][1]; + } + + return moment.lang('de-at', { + months : "Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"), + monthsShort : "Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"), + weekdays : "Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"), + weekdaysShort : "So._Mo._Di._Mi._Do._Fr._Sa.".split("_"), + weekdaysMin : "So_Mo_Di_Mi_Do_Fr_Sa".split("_"), + longDateFormat : { + LT: "HH:mm [Uhr]", + L : "DD.MM.YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd, D. MMMM YYYY LT" + }, + calendar : { + sameDay: "[Heute um] LT", + sameElse: "L", + nextDay: '[Morgen um] LT', + nextWeek: 'dddd [um] LT', + lastDay: '[Gestern um] LT', + lastWeek: '[letzten] dddd [um] LT' + }, + relativeTime : { + future : "in %s", + past : "vor %s", + s : "ein paar Sekunden", + m : processRelativeTime, + mm : "%d Minuten", + h : processRelativeTime, + hh : "%d Stunden", + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), - weekdaysShort : function (m) { - return this._weekdaysShort[m.day()]; - }, - _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), - weekdaysMin : function (m) { - return this._weekdaysMin[m.day()]; - }, +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { - weekdaysParse : function (weekdayName) { - var i, mom, regex; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : german (de) + // author : lluchs : https://github.com/lluchs + // author: Menelion Elensúle: https://github.com/Oire - if (!this._weekdaysParse) { - this._weekdaysParse = []; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 'm': ['eine Minute', 'einer Minute'], + 'h': ['eine Stunde', 'einer Stunde'], + 'd': ['ein Tag', 'einem Tag'], + 'dd': [number + ' Tage', number + ' Tagen'], + 'M': ['ein Monat', 'einem Monat'], + 'MM': [number + ' Monate', number + ' Monaten'], + 'y': ['ein Jahr', 'einem Jahr'], + 'yy': [number + ' Jahre', number + ' Jahren'] + }; + return withoutSuffix ? format[key][0] : format[key][1]; + } + + return moment.lang('de', { + months : "Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"), + monthsShort : "Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"), + weekdays : "Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"), + weekdaysShort : "So._Mo._Di._Mi._Do._Fr._Sa.".split("_"), + weekdaysMin : "So_Mo_Di_Mi_Do_Fr_Sa".split("_"), + longDateFormat : { + LT: "HH:mm [Uhr]", + L : "DD.MM.YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd, D. MMMM YYYY LT" + }, + calendar : { + sameDay: "[Heute um] LT", + sameElse: "L", + nextDay: '[Morgen um] LT', + nextWeek: 'dddd [um] LT', + lastDay: '[Gestern um] LT', + lastWeek: '[letzten] dddd [um] LT' + }, + relativeTime : { + future : "in %s", + past : "vor %s", + s : "ein paar Sekunden", + m : processRelativeTime, + mm : "%d Minuten", + h : processRelativeTime, + hh : "%d Stunden", + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - if (!this._weekdaysParse[i]) { - mom = moment([2000, 1]).day(i); - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - }, - _longDateFormat : { - LT : "h:mm A", - L : "MM/DD/YYYY", - LL : "MMMM D YYYY", - LLL : "MMMM D YYYY LT", - LLLL : "dddd, MMMM D YYYY LT" - }, - longDateFormat : function (key) { - var output = this._longDateFormat[key]; - if (!output && this._longDateFormat[key.toUpperCase()]) { - output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - this._longDateFormat[key] = output; - } - return output; - }, +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { - isPM : function (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : modern greek (el) + // author : Aggelos Karalias : https://github.com/mehiel - _meridiemParse : /[ap]\.?m?\.?/i, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('el', { + monthsNominativeEl : "Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"), + monthsGenitiveEl : "Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"), + months : function (momentToFormat, format) { + if (/D/.test(format.substring(0, format.indexOf("MMMM")))) { // if there is a day number before 'MMMM' + return this._monthsGenitiveEl[momentToFormat.month()]; + } else { + return this._monthsNominativeEl[momentToFormat.month()]; + } + }, + monthsShort : "Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"), + weekdays : "Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"), + weekdaysShort : "Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"), + weekdaysMin : "Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"), + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'μμ' : 'ΜΜ'; + } else { + return isLower ? 'πμ' : 'ΠΜ'; + } + }, + longDateFormat : { + LT : "h:mm A", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendarEl : { + sameDay : '[Σήμερα {}] LT', + nextDay : '[Αύριο {}] LT', + nextWeek : 'dddd [{}] LT', + lastDay : '[Χθες {}] LT', + lastWeek : function() { + switch (this.day()) { + case 6: + return '[το προηγούμενο] dddd [{}] LT'; + default: + return '[την προηγούμενη] dddd [{}] LT'; + } + }, + sameElse : 'L' + }, + calendar : function (key, mom) { + var output = this._calendarEl[key], + hours = mom && mom.hours(); - _calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - calendar : function (key, mom) { - var output = this._calendar[key]; - return typeof output === 'function' ? output.apply(mom) : output; - }, + if (typeof output === 'function') { + output = output.apply(mom); + } - _relativeTime : { - future : "in %s", - past : "%s ago", - s : "a few seconds", - m : "a minute", - mm : "%d minutes", - h : "an hour", - hh : "%d hours", - d : "a day", - dd : "%d days", - M : "a month", - MM : "%d months", - y : "a year", - yy : "%d years" - }, - relativeTime : function (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (typeof output === 'function') ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - }, - pastFuture : function (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); - }, + return output.replace("{}", (hours % 12 === 1 ? "στη" : "στις")); + }, + relativeTime : { + future : "σε %s", + past : "%s πριν", + s : "δευτερόλεπτα", + m : "ένα λεπτό", + mm : "%d λεπτά", + h : "μία ώρα", + hh : "%d ώρες", + d : "μία μέρα", + dd : "%d μέρες", + M : "ένας μήνας", + MM : "%d μήνες", + y : "ένας χρόνος", + yy : "%d χρόνια" + }, + ordinal : function (number) { + return number + 'η'; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4st is the first week of the year. + } + }); + })); - ordinal : function (number) { - return this._ordinal.replace("%d", number); - }, - _ordinal : "%d", - preparse : function (string) { - return string; - }, +/***/ }, +/* 72 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : australian english (en-au) + + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('en-au', { + months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), + monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), + weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), + weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), + weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), + longDateFormat : { + LT : "h:mm A", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + relativeTime : { + future : "in %s", + past : "%s ago", + s : "a few seconds", + m : "a minute", + mm : "%d minutes", + h : "an hour", + hh : "%d hours", + d : "a day", + dd : "%d days", + M : "a month", + MM : "%d months", + y : "a year", + yy : "%d years" + }, + ordinal : function (number) { + var b = number % 10, + output = (~~ (number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - postformat : function (string) { - return string; - }, - week : function (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - }, +/***/ }, +/* 73 */ +/***/ function(module, exports, __webpack_require__) { - _week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : canadian english (en-ca) + // author : Jonathan Abourbih : https://github.com/jonbca - _invalidDate: 'Invalid date', - invalidDate: function () { - return this._invalidDate; - } - }); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('en-ca', { + months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), + monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), + weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), + weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), + weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), + longDateFormat : { + LT : "h:mm A", + L : "YYYY-MM-DD", + LL : "D MMMM, YYYY", + LLL : "D MMMM, YYYY LT", + LLLL : "dddd, D MMMM, YYYY LT" + }, + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + relativeTime : { + future : "in %s", + past : "%s ago", + s : "a few seconds", + m : "a minute", + mm : "%d minutes", + h : "an hour", + hh : "%d hours", + d : "a day", + dd : "%d days", + M : "a month", + MM : "%d months", + y : "a year", + yy : "%d years" + }, + ordinal : function (number) { + var b = number % 10, + output = (~~ (number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } + }); + })); - // Loads a language definition into the `languages` cache. The function - // takes a key and optionally values. If not in the browser and no values - // are provided, it will load the language file module. As a convenience, - // this function also returns the language values. - function loadLang(key, values) { - values.abbr = key; - if (!languages[key]) { - languages[key] = new Language(); - } - languages[key].set(values); - return languages[key]; - } - - // Remove a language from the `languages` cache. Mostly useful in tests. - function unloadLang(key) { - delete languages[key]; - } - - // Determines which language definition to use and returns it. - // - // With no parameters, it will return the global language. If you - // pass in a language key, such as 'en', it will return the - // definition for 'en', so long as 'en' has already been loaded using - // moment.lang. - function getLangDefinition(key) { - var i = 0, j, lang, next, split, - get = function (k) { - if (!languages[k] && hasModule) { - try { - require('./lang/' + k); - } catch (e) { } - } - return languages[k]; - }; - if (!key) { - return moment.fn._lang; - } +/***/ }, +/* 74 */ +/***/ function(module, exports, __webpack_require__) { - if (!isArray(key)) { - //short-circuit everything else - lang = get(key); - if (lang) { - return lang; - } - key = [key]; - } - - //pick the language from the array - //try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - //substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - while (i < key.length) { - split = normalizeLanguage(key[i]).split('-'); - j = split.length; - next = normalizeLanguage(key[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - lang = get(split.slice(0, j).join('-')); - if (lang) { - return lang; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return moment.fn._lang; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : great britain english (en-gb) + // author : Chris Gedrim : https://github.com/chrisgedrim - /************************************ - Formatting - ************************************/ + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('en-gb', { + months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), + monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), + weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), + weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), + weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + relativeTime : { + future : "in %s", + past : "%s ago", + s : "a few seconds", + m : "a minute", + mm : "%d minutes", + h : "an hour", + hh : "%d hours", + d : "a day", + dd : "%d days", + M : "a month", + MM : "%d months", + y : "a year", + yy : "%d years" + }, + ordinal : function (number) { + var b = number % 10, + output = (~~ (number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ""); - } - return input.replace(/\\/g, ""); - } +/***/ }, +/* 75 */ +/***/ function(module, exports, __webpack_require__) { - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : esperanto (eo) + // author : Colin Dean : https://github.com/colindean + // komento: Mi estas malcerta se mi korekte traktis akuzativojn en tiu traduko. + // Se ne, bonvolu korekti kaj avizi min por ke mi povas lerni! - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('eo', { + months : "januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro".split("_"), + monthsShort : "jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec".split("_"), + weekdays : "Dimanĉo_Lundo_Mardo_Merkredo_Ĵaŭdo_Vendredo_Sabato".split("_"), + weekdaysShort : "Dim_Lun_Mard_Merk_Ĵaŭ_Ven_Sab".split("_"), + weekdaysMin : "Di_Lu_Ma_Me_Ĵa_Ve_Sa".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "YYYY-MM-DD", + LL : "D[-an de] MMMM, YYYY", + LLL : "D[-an de] MMMM, YYYY LT", + LLLL : "dddd, [la] D[-an de] MMMM, YYYY LT" + }, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'p.t.m.' : 'P.T.M.'; + } else { + return isLower ? 'a.t.m.' : 'A.T.M.'; + } + }, + calendar : { + sameDay : '[Hodiaŭ je] LT', + nextDay : '[Morgaŭ je] LT', + nextWeek : 'dddd [je] LT', + lastDay : '[Hieraŭ je] LT', + lastWeek : '[pasinta] dddd [je] LT', + sameElse : 'L' + }, + relativeTime : { + future : "je %s", + past : "antaŭ %s", + s : "sekundoj", + m : "minuto", + mm : "%d minutoj", + h : "horo", + hh : "%d horoj", + d : "tago",//ne 'diurno', ĉar estas uzita por proksimumo + dd : "%d tagoj", + M : "monato", + MM : "%d monatoj", + y : "jaro", + yy : "%d jaroj" + }, + ordinal : "%da", + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - return function (mom) { - var output = ""; - for (i = 0; i < length; i++) { - output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; - } - return output; - }; - } - // format date using native date object - function formatMoment(m, format) { - - if (!m.isValid()) { - return m.lang().invalidDate(); - } - - format = expandFormat(format, m.lang()); - - if (!formatFunctions[format]) { - formatFunctions[format] = makeFormatFunction(format); - } - - return formatFunctions[format](m); - } - - function expandFormat(format, lang) { - var i = 5; - - function replaceLongDateFormatTokens(input) { - return lang.longDateFormat(input) || input; - } - - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; - } - - return format; - } - - - /************************************ - Parsing - ************************************/ - - - // get the regex to find the next token - function getParseRegexForToken(token, config) { - var a, strict = config._strict; - switch (token) { - case 'Q': - return parseTokenOneDigit; - case 'DDDD': - return parseTokenThreeDigits; - case 'YYYY': - case 'GGGG': - case 'gggg': - return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; - case 'Y': - case 'G': - case 'g': - return parseTokenSignedNumber; - case 'YYYYYY': - case 'YYYYY': - case 'GGGGG': - case 'ggggg': - return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; - case 'S': - if (strict) { return parseTokenOneDigit; } - /* falls through */ - case 'SS': - if (strict) { return parseTokenTwoDigits; } - /* falls through */ - case 'SSS': - if (strict) { return parseTokenThreeDigits; } - /* falls through */ - case 'DDD': - return parseTokenOneToThreeDigits; - case 'MMM': - case 'MMMM': - case 'dd': - case 'ddd': - case 'dddd': - return parseTokenWord; - case 'a': - case 'A': - return getLangDefinition(config._l)._meridiemParse; - case 'X': - return parseTokenTimestampMs; - case 'Z': - case 'ZZ': - return parseTokenTimezone; - case 'T': - return parseTokenT; - case 'SSSS': - return parseTokenDigits; - case 'MM': - case 'DD': - case 'YY': - case 'GG': - case 'gg': - case 'HH': - case 'hh': - case 'mm': - case 'ss': - case 'ww': - case 'WW': - return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; - case 'M': - case 'D': - case 'd': - case 'H': - case 'h': - case 'm': - case 's': - case 'w': - case 'W': - case 'e': - case 'E': - return parseTokenOneOrTwoDigits; - case 'Do': - return parseTokenOrdinal; - default : - a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), "i")); - return a; - } - } - - function timezoneMinutesFromString(string) { - string = string || ""; - var possibleTzMatches = (string.match(parseTokenTimezone) || []), - tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], - parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], - minutes = +(parts[1] * 60) + toInt(parts[2]); - - return parts[0] === '+' ? -minutes : minutes; - } - - // function to convert string input to date - function addTimeToArrayFromToken(token, input, config) { - var a, datePartArray = config._a; - - switch (token) { - // QUARTER - case 'Q': - if (input != null) { - datePartArray[MONTH] = (toInt(input) - 1) * 3; - } - break; - // MONTH - case 'M' : // fall through to MM - case 'MM' : - if (input != null) { - datePartArray[MONTH] = toInt(input) - 1; - } - break; - case 'MMM' : // fall through to MMMM - case 'MMMM' : - a = getLangDefinition(config._l).monthsParse(input); - // if we didn't find a month name, mark the date as invalid. - if (a != null) { - datePartArray[MONTH] = a; - } else { - config._pf.invalidMonth = input; - } - break; - // DAY OF MONTH - case 'D' : // fall through to DD - case 'DD' : - if (input != null) { - datePartArray[DATE] = toInt(input); - } - break; - case 'Do' : - if (input != null) { - datePartArray[DATE] = toInt(parseInt(input, 10)); - } - break; - // DAY OF YEAR - case 'DDD' : // fall through to DDDD - case 'DDDD' : - if (input != null) { - config._dayOfYear = toInt(input); - } +/***/ }, +/* 76 */ +/***/ function(module, exports, __webpack_require__) { - break; - // YEAR - case 'YY' : - datePartArray[YEAR] = moment.parseTwoDigitYear(input); - break; - case 'YYYY' : - case 'YYYYY' : - case 'YYYYYY' : - datePartArray[YEAR] = toInt(input); - break; - // AM / PM - case 'a' : // fall through to A - case 'A' : - config._isPm = getLangDefinition(config._l).isPM(input); - break; - // 24 HOUR - case 'H' : // fall through to hh - case 'HH' : // fall through to hh - case 'h' : // fall through to hh - case 'hh' : - datePartArray[HOUR] = toInt(input); - break; - // MINUTE - case 'm' : // fall through to mm - case 'mm' : - datePartArray[MINUTE] = toInt(input); - break; - // SECOND - case 's' : // fall through to ss - case 'ss' : - datePartArray[SECOND] = toInt(input); - break; - // MILLISECOND - case 'S' : - case 'SS' : - case 'SSS' : - case 'SSSS' : - datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); - break; - // UNIX TIMESTAMP WITH MS - case 'X': - config._d = new Date(parseFloat(input) * 1000); - break; - // TIMEZONE - case 'Z' : // fall through to ZZ - case 'ZZ' : - config._useUTC = true; - config._tzm = 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 'e': - case 'E': - token = token.substr(0, 1); - /* falls through */ - case 'gggg': - case 'GGGG': - case 'GGGGG': - token = token.substr(0, 2); - if (input) { - config._w = config._w || {}; - config._w[token] = toInt(input); - } - break; - case 'gg': - case 'GG': - config._w = config._w || {}; - config._w[token] = moment.parseTwoDigitYear(input); - } - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : spanish (es) + // author : Julio Napurí : https://github.com/julionc - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp, lang; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var monthsShortDot = "ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"), + monthsShort = "ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"); + + return moment.lang('es', { + months : "enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"), + monthsShort : function (m, format) { + if (/-MMM-/.test(format)) { + return monthsShort[m.month()]; + } else { + return monthsShortDot[m.month()]; + } + }, + weekdays : "domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"), + weekdaysShort : "dom._lun._mar._mié._jue._vie._sáb.".split("_"), + weekdaysMin : "Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD/MM/YYYY", + LL : "D [de] MMMM [del] YYYY", + LLL : "D [de] MMMM [del] YYYY LT", + LLLL : "dddd, D [de] MMMM [del] YYYY LT" + }, + calendar : { + sameDay : function () { + return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + nextDay : function () { + return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + nextWeek : function () { + return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + lastDay : function () { + return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + lastWeek : function () { + return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + sameElse : 'L' + }, + relativeTime : { + future : "en %s", + past : "hace %s", + s : "unos segundos", + m : "un minuto", + mm : "%d minutos", + h : "una hora", + hh : "%d horas", + d : "un día", + dd : "%d días", + M : "un mes", + MM : "%d meses", + y : "un año", + yy : "%d años" + }, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - 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); +/***/ }, +/* 77 */ +/***/ function(module, exports, __webpack_require__) { - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : estonian (et) + // author : Henry Kehlmann : https://github.com/madhenry + // improvements : Illimar Tambek : https://github.com/ragulka - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function dateFromConfig(config) { - var i, date, input = [], currentDate, yearToUse; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 's' : ['mõne sekundi', 'mõni sekund', 'paar sekundit'], + 'm' : ['ühe minuti', 'üks minut'], + 'mm': [number + ' minuti', number + ' minutit'], + 'h' : ['ühe tunni', 'tund aega', 'üks tund'], + 'hh': [number + ' tunni', number + ' tundi'], + 'd' : ['ühe päeva', 'üks päev'], + 'M' : ['kuu aja', 'kuu aega', 'üks kuu'], + 'MM': [number + ' kuu', number + ' kuud'], + 'y' : ['ühe aasta', 'aasta', 'üks aasta'], + 'yy': [number + ' aasta', number + ' aastat'] + }; + if (withoutSuffix) { + return format[key][2] ? format[key][2] : format[key][1]; + } + return isFuture ? format[key][0] : format[key][1]; + } + + return moment.lang('et', { + months : "jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"), + monthsShort : "jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"), + weekdays : "pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev".split("_"), + weekdaysShort : "P_E_T_K_N_R_L".split("_"), + weekdaysMin : "P_E_T_K_N_R_L".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD.MM.YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd, D. MMMM YYYY LT" + }, + calendar : { + sameDay : '[Täna,] LT', + nextDay : '[Homme,] LT', + nextWeek : '[Järgmine] dddd LT', + lastDay : '[Eile,] LT', + lastWeek : '[Eelmine] dddd LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s pärast", + past : "%s tagasi", + s : processRelativeTime, + m : processRelativeTime, + mm : processRelativeTime, + h : processRelativeTime, + hh : processRelativeTime, + d : processRelativeTime, + dd : '%d päeva', + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - if (config._d) { - return; - } - currentDate = currentDateArray(config); +/***/ }, +/* 78 */ +/***/ function(module, exports, __webpack_require__) { - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : euskara (eu) + // author : Eneko Illarramendi : https://github.com/eillarra - //if the day of the year is set, figure out what it is - if (config._dayOfYear) { - yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('eu', { + months : "urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"), + monthsShort : "urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"), + weekdays : "igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"), + weekdaysShort : "ig._al._ar._az._og._ol._lr.".split("_"), + weekdaysMin : "ig_al_ar_az_og_ol_lr".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "YYYY-MM-DD", + LL : "YYYY[ko] MMMM[ren] D[a]", + LLL : "YYYY[ko] MMMM[ren] D[a] LT", + LLLL : "dddd, YYYY[ko] MMMM[ren] D[a] LT", + l : "YYYY-M-D", + ll : "YYYY[ko] MMM D[a]", + lll : "YYYY[ko] MMM D[a] LT", + llll : "ddd, YYYY[ko] MMM D[a] LT" + }, + calendar : { + sameDay : '[gaur] LT[etan]', + nextDay : '[bihar] LT[etan]', + nextWeek : 'dddd LT[etan]', + lastDay : '[atzo] LT[etan]', + lastWeek : '[aurreko] dddd LT[etan]', + sameElse : 'L' + }, + relativeTime : { + future : "%s barru", + past : "duela %s", + s : "segundo batzuk", + m : "minutu bat", + mm : "%d minutu", + h : "ordu bat", + hh : "%d ordu", + d : "egun bat", + dd : "%d egun", + M : "hilabete bat", + MM : "%d hilabete", + y : "urte bat", + yy : "%d urte" + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - if (config._dayOfYear > daysInYear(yearToUse)) { - config._pf._overflowDayOfYear = true; - } - date = makeUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } +/***/ }, +/* 79 */ +/***/ function(module, exports, __webpack_require__) { - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Persian Language + // author : Ebrahim Byagowi : https://github.com/ebraminio - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var symbolMap = { + '1': '۱', + '2': '۲', + '3': '۳', + '4': '۴', + '5': '۵', + '6': '۶', + '7': '۷', + '8': '۸', + '9': '۹', + '0': '۰' + }, numberMap = { + '۱': '1', + '۲': '2', + '۳': '3', + '۴': '4', + '۵': '5', + '۶': '6', + '۷': '7', + '۸': '8', + '۹': '9', + '۰': '0' + }; - 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); - } - } + return moment.lang('fa', { + months : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'), + monthsShort : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'), + weekdays : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'), + weekdaysShort : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'), + weekdaysMin : 'ی_د_س_چ_پ_ج_ش'.split('_'), + longDateFormat : { + LT : 'HH:mm', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY LT', + LLLL : 'dddd, D MMMM YYYY LT' + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return "قبل از ظهر"; + } else { + return "بعد از ظهر"; + } + }, + calendar : { + sameDay : '[امروز ساعت] LT', + nextDay : '[فردا ساعت] LT', + nextWeek : 'dddd [ساعت] LT', + lastDay : '[دیروز ساعت] LT', + lastWeek : 'dddd [پیش] [ساعت] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'در %s', + past : '%s پیش', + s : 'چندین ثانیه', + m : 'یک دقیقه', + mm : '%d دقیقه', + h : 'یک ساعت', + hh : '%d ساعت', + d : 'یک روز', + dd : '%d روز', + M : 'یک ماه', + MM : '%d ماه', + y : 'یک سال', + yy : '%d سال' + }, + preparse: function (string) { + return string.replace(/[۰-۹]/g, function (match) { + return numberMap[match]; + }).replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }).replace(/,/g, '،'); + }, + ordinal : '%dم', + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - function dateFromObject(config) { - var normalizedInput; - if (config._d) { - return; - } +/***/ }, +/* 80 */ +/***/ function(module, exports, __webpack_require__) { - normalizedInput = normalizeObjectUnits(config._i); - config._a = [ - normalizedInput.year, - normalizedInput.month, - normalizedInput.day, - normalizedInput.hour, - normalizedInput.minute, - normalizedInput.second, - normalizedInput.millisecond - ]; - - dateFromConfig(config); - } - - function currentDateArray(config) { - var now = new Date(); - if (config._useUTC) { - return [ - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate() - ]; - } else { - return [now.getFullYear(), now.getMonth(), now.getDate()]; - } - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : finnish (fi) + // author : Tarmo Aidantausta : https://github.com/bleadof - // date from string and format string - function makeDateFromStringAndFormat(config) { + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' '), + numbersFuture = ['nolla', 'yhden', 'kahden', 'kolmen', 'neljän', 'viiden', 'kuuden', + numbersPast[7], numbersPast[8], numbersPast[9]]; + + function translate(number, withoutSuffix, key, isFuture) { + var result = ""; + switch (key) { + case 's': + return isFuture ? 'muutaman sekunnin' : 'muutama sekunti'; + case 'm': + return isFuture ? 'minuutin' : 'minuutti'; + case 'mm': + result = isFuture ? 'minuutin' : 'minuuttia'; + break; + case 'h': + return isFuture ? 'tunnin' : 'tunti'; + case 'hh': + result = isFuture ? 'tunnin' : 'tuntia'; + break; + case 'd': + return isFuture ? 'päivän' : 'päivä'; + case 'dd': + result = isFuture ? 'päivän' : 'päivää'; + break; + case 'M': + return isFuture ? 'kuukauden' : 'kuukausi'; + case 'MM': + result = isFuture ? 'kuukauden' : 'kuukautta'; + break; + case 'y': + return isFuture ? 'vuoden' : 'vuosi'; + case 'yy': + result = isFuture ? 'vuoden' : 'vuotta'; + break; + } + result = verbalNumber(number, isFuture) + " " + result; + return result; + } + + function verbalNumber(number, isFuture) { + return number < 10 ? (isFuture ? numbersFuture[number] : numbersPast[number]) : number; + } + + return moment.lang('fi', { + months : "tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"), + monthsShort : "tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"), + weekdays : "sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"), + weekdaysShort : "su_ma_ti_ke_to_pe_la".split("_"), + weekdaysMin : "su_ma_ti_ke_to_pe_la".split("_"), + longDateFormat : { + LT : "HH.mm", + L : "DD.MM.YYYY", + LL : "Do MMMM[ta] YYYY", + LLL : "Do MMMM[ta] YYYY, [klo] LT", + LLLL : "dddd, Do MMMM[ta] YYYY, [klo] LT", + l : "D.M.YYYY", + ll : "Do MMM YYYY", + lll : "Do MMM YYYY, [klo] LT", + llll : "ddd, Do MMM YYYY, [klo] LT" + }, + calendar : { + sameDay : '[tänään] [klo] LT', + nextDay : '[huomenna] [klo] LT', + nextWeek : 'dddd [klo] LT', + lastDay : '[eilen] [klo] LT', + lastWeek : '[viime] dddd[na] [klo] LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s päästä", + past : "%s sitten", + s : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + ordinal : "%d.", + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - if (config._f === moment.ISO_8601) { - parseISO(config); - return; - } - config._a = []; - config._pf.empty = true; +/***/ }, +/* 81 */ +/***/ function(module, exports, __webpack_require__) { - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var lang = getLangDefinition(config._l), - string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : faroese (fo) + // author : Ragnar Johannesen : https://github.com/ragnar123 - tokens = expandFormat(config._f, lang).match(formattingTokens) || []; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('fo', { + months : "januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember".split("_"), + monthsShort : "jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"), + weekdays : "sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur".split("_"), + weekdaysShort : "sun_mán_týs_mik_hós_frí_ley".split("_"), + weekdaysMin : "su_má_tý_mi_hó_fr_le".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D. MMMM, YYYY LT" + }, + calendar : { + sameDay : '[Í dag kl.] LT', + nextDay : '[Í morgin kl.] LT', + nextWeek : 'dddd [kl.] LT', + lastDay : '[Í gjár kl.] LT', + lastWeek : '[síðstu] dddd [kl] LT', + sameElse : 'L' + }, + relativeTime : { + future : "um %s", + past : "%s síðani", + s : "fá sekund", + m : "ein minutt", + mm : "%d minuttir", + h : "ein tími", + hh : "%d tímar", + d : "ein dagur", + dd : "%d dagar", + M : "ein mánaði", + MM : "%d mánaðir", + y : "eitt ár", + yy : "%d ár" + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - config._pf.unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - config._pf.empty = false; - } - else { - config._pf.unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - config._pf.unusedTokens.push(token); - } - } - // add remaining unparsed input length to the string - config._pf.charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - config._pf.unusedInput.push(string); - } +/***/ }, +/* 82 */ +/***/ function(module, exports, __webpack_require__) { - // handle am pm - if (config._isPm && config._a[HOUR] < 12) { - config._a[HOUR] += 12; - } - // if is 12 am, change hours to 0 - if (config._isPm === false && config._a[HOUR] === 12) { - config._a[HOUR] = 0; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : canadian french (fr-ca) + // author : Jonathan Abourbih : https://github.com/jonbca - dateFromConfig(config); - checkOverflow(config); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('fr-ca', { + months : "janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"), + monthsShort : "janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"), + weekdays : "dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"), + weekdaysShort : "dim._lun._mar._mer._jeu._ven._sam.".split("_"), + weekdaysMin : "Di_Lu_Ma_Me_Je_Ve_Sa".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "YYYY-MM-DD", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay: "[Aujourd'hui à] LT", + nextDay: '[Demain à] LT', + nextWeek: 'dddd [à] LT', + lastDay: '[Hier à] LT', + lastWeek: 'dddd [dernier à] LT', + sameElse: 'L' + }, + relativeTime : { + future : "dans %s", + past : "il y a %s", + s : "quelques secondes", + m : "une minute", + mm : "%d minutes", + h : "une heure", + hh : "%d heures", + d : "un jour", + dd : "%d jours", + M : "un mois", + MM : "%d mois", + y : "un an", + yy : "%d ans" + }, + ordinal : function (number) { + return number + (number === 1 ? 'er' : ''); + } + }); + })); - function unescapeFormat(s) { - return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - }); - } - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function regexpEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } +/***/ }, +/* 83 */ +/***/ function(module, exports, __webpack_require__) { - // date from string and array of format strings - function makeDateFromStringAndArray(config) { - var tempConfig, - bestMoment, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : french (fr) + // author : John Fischer : https://github.com/jfroffice - scoreToBeat, - i, - currentScore; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('fr', { + months : "janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"), + monthsShort : "janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"), + weekdays : "dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"), + weekdaysShort : "dim._lun._mar._mer._jeu._ven._sam.".split("_"), + weekdaysMin : "Di_Lu_Ma_Me_Je_Ve_Sa".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay: "[Aujourd'hui à] LT", + nextDay: '[Demain à] LT', + nextWeek: 'dddd [à] LT', + lastDay: '[Hier à] LT', + lastWeek: 'dddd [dernier à] LT', + sameElse: 'L' + }, + relativeTime : { + future : "dans %s", + past : "il y a %s", + s : "quelques secondes", + m : "une minute", + mm : "%d minutes", + h : "une heure", + hh : "%d heures", + d : "un jour", + dd : "%d jours", + M : "un mois", + MM : "%d mois", + y : "un an", + yy : "%d ans" + }, + ordinal : function (number) { + return number + (number === 1 ? 'er' : ''); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - if (config._f.length === 0) { - config._pf.invalidFormat = true; - config._d = new Date(NaN); - return; - } - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = extend({}, config); - tempConfig._pf = defaultParsingFlags(); - tempConfig._f = config._f[i]; - makeDateFromStringAndFormat(tempConfig); +/***/ }, +/* 84 */ +/***/ function(module, exports, __webpack_require__) { - if (!isValid(tempConfig)) { - continue; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : galician (gl) + // author : Juan G. Hurtado : https://github.com/juanghurtado - // if there is any input that was not parsed add a penalty for that format - currentScore += tempConfig._pf.charsLeftOver; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('gl', { + months : "Xaneiro_Febreiro_Marzo_Abril_Maio_Xuño_Xullo_Agosto_Setembro_Outubro_Novembro_Decembro".split("_"), + monthsShort : "Xan._Feb._Mar._Abr._Mai._Xuñ._Xul._Ago._Set._Out._Nov._Dec.".split("_"), + weekdays : "Domingo_Luns_Martes_Mércores_Xoves_Venres_Sábado".split("_"), + weekdaysShort : "Dom._Lun._Mar._Mér._Xov._Ven._Sáb.".split("_"), + weekdaysMin : "Do_Lu_Ma_Mé_Xo_Ve_Sá".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay : function () { + return '[hoxe ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT'; + }, + nextDay : function () { + return '[mañá ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT'; + }, + nextWeek : function () { + return 'dddd [' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT'; + }, + lastDay : function () { + return '[onte ' + ((this.hours() !== 1) ? 'á' : 'a') + '] LT'; + }, + lastWeek : function () { + return '[o] dddd [pasado ' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT'; + }, + sameElse : 'L' + }, + relativeTime : { + future : function (str) { + if (str === "uns segundos") { + return "nuns segundos"; + } + return "en " + str; + }, + past : "hai %s", + s : "uns segundos", + m : "un minuto", + mm : "%d minutos", + h : "unha hora", + hh : "%d horas", + d : "un día", + dd : "%d días", + M : "un mes", + MM : "%d meses", + y : "un ano", + yy : "%d anos" + }, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - //or tokens - currentScore += tempConfig._pf.unusedTokens.length * 10; - tempConfig._pf.score = currentScore; +/***/ }, +/* 85 */ +/***/ function(module, exports, __webpack_require__) { - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Hebrew (he) + // author : Tomer Cohen : https://github.com/tomer + // author : Moshe Simantov : https://github.com/DevelopmentIL + // author : Tal Ater : https://github.com/TalAter - extend(config, bestMoment || tempConfig); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('he', { + months : "ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר".split("_"), + monthsShort : "ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳".split("_"), + weekdays : "ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת".split("_"), + weekdaysShort : "א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"), + weekdaysMin : "א_ב_ג_ד_ה_ו_ש".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D [ב]MMMM YYYY", + LLL : "D [ב]MMMM YYYY LT", + LLLL : "dddd, D [ב]MMMM YYYY LT", + l : "D/M/YYYY", + ll : "D MMM YYYY", + lll : "D MMM YYYY LT", + llll : "ddd, D MMM YYYY LT" + }, + calendar : { + sameDay : '[היום ב־]LT', + nextDay : '[מחר ב־]LT', + nextWeek : 'dddd [בשעה] LT', + lastDay : '[אתמול ב־]LT', + lastWeek : '[ביום] dddd [האחרון בשעה] LT', + sameElse : 'L' + }, + relativeTime : { + future : "בעוד %s", + past : "לפני %s", + s : "מספר שניות", + m : "דקה", + mm : "%d דקות", + h : "שעה", + hh : function (number) { + if (number === 2) { + return "שעתיים"; + } + return number + " שעות"; + }, + d : "יום", + dd : function (number) { + if (number === 2) { + return "יומיים"; + } + return number + " ימים"; + }, + M : "חודש", + MM : function (number) { + if (number === 2) { + return "חודשיים"; + } + return number + " חודשים"; + }, + y : "שנה", + yy : function (number) { + if (number === 2) { + return "שנתיים"; + } + return number + " שנים"; + } + } + }); + })); - // date from iso format - function parseISO(config) { - var i, l, - string = config._i, - match = isoRegex.exec(string); - if (match) { - config._pf.iso = true; - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(string)) { - // match[5] should be "T" or undefined - config._f = isoDates[i][0] + (match[6] || " "); - break; - } - } - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(string)) { - config._f += isoTimes[i][0]; - break; - } - } - if (string.match(parseTokenTimezone)) { - config._f += "Z"; - } - makeDateFromStringAndFormat(config); - } else { - config._isValid = false; - } - } - - // date from iso format or fallback - function makeDateFromString(config) { - parseISO(config); - if (config._isValid === false) { - delete config._isValid; - moment.createFromInputFallback(config); - } - } - - function makeDateFromInput(config) { - var input = config._i, - matched = aspNetJsonRegex.exec(input); - - if (input === undefined) { - config._d = new Date(); - } else if (matched) { - config._d = new Date(+matched[1]); - } else if (typeof input === 'string') { - makeDateFromString(config); - } else if (isArray(input)) { - config._a = input.slice(0); - dateFromConfig(config); - } else if (isDate(input)) { - config._d = new Date(+input); - } else if (typeof(input) === 'object') { - dateFromObject(config); - } else if (typeof(input) === 'number') { - // from milliseconds - config._d = new Date(input); - } else { - moment.createFromInputFallback(config); - } - } +/***/ }, +/* 86 */ +/***/ function(module, exports, __webpack_require__) { - function makeDate(y, m, d, h, M, s, ms) { - //can't just apply() to create a date: - //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var date = new Date(y, m, d, h, M, s, ms); + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : hindi (hi) + // author : Mayank Singhal : https://github.com/mayanksinghal - //the date constructor doesn't accept years < 1970 - if (y < 1970) { - date.setFullYear(y); - } - return date; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var symbolMap = { + '1': '१', + '2': '२', + '3': '३', + '4': '४', + '5': '५', + '6': '६', + '7': '७', + '8': '८', + '9': '९', + '0': '०' + }, + numberMap = { + '१': '1', + '२': '2', + '३': '3', + '४': '4', + '५': '5', + '६': '6', + '७': '7', + '८': '8', + '९': '9', + '०': '0' + }; - function makeUTCDate(y) { - var date = new Date(Date.UTC.apply(null, arguments)); - if (y < 1970) { - date.setUTCFullYear(y); - } - return date; - } + return moment.lang('hi', { + months : 'जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर'.split("_"), + monthsShort : 'जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.'.split("_"), + weekdays : 'रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split("_"), + weekdaysShort : 'रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि'.split("_"), + weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split("_"), + longDateFormat : { + LT : "A h:mm बजे", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY, LT", + LLLL : "dddd, D MMMM YYYY, LT" + }, + calendar : { + sameDay : '[आज] LT', + nextDay : '[कल] LT', + nextWeek : 'dddd, LT', + lastDay : '[कल] LT', + lastWeek : '[पिछले] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s में", + past : "%s पहले", + s : "कुछ ही क्षण", + m : "एक मिनट", + mm : "%d मिनट", + h : "एक घंटा", + hh : "%d घंटे", + d : "एक दिन", + dd : "%d दिन", + M : "एक महीने", + MM : "%d महीने", + y : "एक वर्ष", + yy : "%d वर्ष" + }, + preparse: function (string) { + return string.replace(/[१२३४५६७८९०]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + // Hindi notation for meridiems are quite fuzzy in practice. While there exists + // a rigid notion of a 'Pahar' it is not used as rigidly in modern Hindi. + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return "रात"; + } else if (hour < 10) { + return "सुबह"; + } else if (hour < 17) { + return "दोपहर"; + } else if (hour < 20) { + return "शाम"; + } else { + return "रात"; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - function parseWeekday(input, language) { - if (typeof input === 'string') { - if (!isNaN(input)) { - input = parseInt(input, 10); - } - else { - input = language.weekdaysParse(input); - if (typeof input !== 'number') { - return null; - } - } - } - return input; - } - /************************************ - Relative Time - ************************************/ +/***/ }, +/* 87 */ +/***/ function(module, exports, __webpack_require__) { + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : hrvatski (hr) + // author : Bojan Marković : https://github.com/bmarkovic - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) { - return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } + // based on (sl) translation by Robert Sedovšek - function relativeTime(milliseconds, withoutSuffix, lang) { - var seconds = round(Math.abs(milliseconds) / 1000), - minutes = round(seconds / 60), - hours = round(minutes / 60), - days = round(hours / 24), - years = round(days / 365), - args = seconds < relativeTimeThresholds.s && ['s', seconds] || - minutes === 1 && ['m'] || - minutes < relativeTimeThresholds.m && ['mm', minutes] || - hours === 1 && ['h'] || - hours < relativeTimeThresholds.h && ['hh', hours] || - days === 1 && ['d'] || - days <= relativeTimeThresholds.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; - args[4] = lang; - return substituteTimeAgo.apply({}, args); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + + function translate(number, withoutSuffix, key) { + var result = number + " "; + switch (key) { + case 'm': + return withoutSuffix ? 'jedna minuta' : 'jedne minute'; + case 'mm': + if (number === 1) { + result += 'minuta'; + } else if (number === 2 || number === 3 || number === 4) { + result += 'minute'; + } else { + result += 'minuta'; + } + return result; + case 'h': + return withoutSuffix ? 'jedan sat' : 'jednog sata'; + case 'hh': + if (number === 1) { + result += 'sat'; + } else if (number === 2 || number === 3 || number === 4) { + result += 'sata'; + } else { + result += 'sati'; + } + return result; + case 'dd': + if (number === 1) { + result += 'dan'; + } else { + result += 'dana'; + } + return result; + case 'MM': + if (number === 1) { + result += 'mjesec'; + } else if (number === 2 || number === 3 || number === 4) { + result += 'mjeseca'; + } else { + result += 'mjeseci'; + } + return result; + case 'yy': + if (number === 1) { + result += 'godina'; + } else if (number === 2 || number === 3 || number === 4) { + result += 'godine'; + } else { + result += 'godina'; + } + return result; + } + } + return moment.lang('hr', { + months : "sječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_"), + monthsShort : "sje._vel._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"), + weekdays : "nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"), + weekdaysShort : "ned._pon._uto._sri._čet._pet._sub.".split("_"), + weekdaysMin : "ne_po_ut_sr_če_pe_su".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD. MM. YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd, D. MMMM YYYY LT" + }, + calendar : { + sameDay : '[danas u] LT', + nextDay : '[sutra u] LT', + + nextWeek : function () { + switch (this.day()) { + case 0: + return '[u] [nedjelju] [u] LT'; + case 3: + return '[u] [srijedu] [u] LT'; + case 6: + return '[u] [subotu] [u] LT'; + case 1: + case 2: + case 4: + case 5: + return '[u] dddd [u] LT'; + } + }, + lastDay : '[jučer u] LT', + lastWeek : function () { + switch (this.day()) { + case 0: + case 3: + return '[prošlu] dddd [u] LT'; + case 6: + return '[prošle] [subote] [u] LT'; + case 1: + case 2: + case 4: + case 5: + return '[prošli] dddd [u] LT'; + } + }, + sameElse : 'L' + }, + relativeTime : { + future : "za %s", + past : "prije %s", + s : "par sekundi", + m : translate, + mm : translate, + h : translate, + hh : translate, + d : "dan", + dd : translate, + M : "mjesec", + MM : translate, + y : "godinu", + yy : translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - /************************************ - Week of Year - ************************************/ +/***/ }, +/* 88 */ +/***/ function(module, exports, __webpack_require__) { - // firstDayOfWeek 0 = sun, 6 = sat - // the day of the week that starts the week - // (usually sunday or monday) - // firstDayOfWeekOfYear 0 = sun, 6 = sat - // the first week is the week that contains the first - // of this day of the week - // (eg. ISO weeks use thursday (4)) - function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { - var end = firstDayOfWeekOfYear - firstDayOfWeek, - daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), - adjustedMoment; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : hungarian (hu) + // author : Adam Brunner : https://github.com/adambrunner + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var weekEndings = 'vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton'.split(' '); + + function translate(number, withoutSuffix, key, isFuture) { + var num = number, + suffix; + + switch (key) { + case 's': + return (isFuture || withoutSuffix) ? 'néhány másodperc' : 'néhány másodperce'; + case 'm': + return 'egy' + (isFuture || withoutSuffix ? ' perc' : ' perce'); + case 'mm': + return num + (isFuture || withoutSuffix ? ' perc' : ' perce'); + case 'h': + return 'egy' + (isFuture || withoutSuffix ? ' óra' : ' órája'); + case 'hh': + return num + (isFuture || withoutSuffix ? ' óra' : ' órája'); + case 'd': + return 'egy' + (isFuture || withoutSuffix ? ' nap' : ' napja'); + case 'dd': + return num + (isFuture || withoutSuffix ? ' nap' : ' napja'); + case 'M': + return 'egy' + (isFuture || withoutSuffix ? ' hónap' : ' hónapja'); + case 'MM': + return num + (isFuture || withoutSuffix ? ' hónap' : ' hónapja'); + case 'y': + return 'egy' + (isFuture || withoutSuffix ? ' év' : ' éve'); + case 'yy': + return num + (isFuture || withoutSuffix ? ' év' : ' éve'); + } - if (daysToDayOfWeek > end) { - daysToDayOfWeek -= 7; - } + return ''; + } - if (daysToDayOfWeek < end - 7) { - daysToDayOfWeek += 7; - } + function week(isFuture) { + return (isFuture ? '' : '[múlt] ') + '[' + weekEndings[this.day()] + '] LT[-kor]'; + } - adjustedMoment = moment(mom).add('d', daysToDayOfWeek); - return { - week: Math.ceil(adjustedMoment.dayOfYear() / 7), - year: adjustedMoment.year() - }; - } + return moment.lang('hu', { + months : "január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"), + monthsShort : "jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"), + weekdays : "vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"), + weekdaysShort : "vas_hét_kedd_sze_csüt_pén_szo".split("_"), + weekdaysMin : "v_h_k_sze_cs_p_szo".split("_"), + longDateFormat : { + LT : "H:mm", + L : "YYYY.MM.DD.", + LL : "YYYY. MMMM D.", + LLL : "YYYY. MMMM D., LT", + LLLL : "YYYY. MMMM D., dddd LT" + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 12) { + return isLower === true ? 'de' : 'DE'; + } else { + return isLower === true ? 'du' : 'DU'; + } + }, + calendar : { + sameDay : '[ma] LT[-kor]', + nextDay : '[holnap] LT[-kor]', + nextWeek : function () { + return week.call(this, true); + }, + lastDay : '[tegnap] LT[-kor]', + lastWeek : function () { + return week.call(this, false); + }, + sameElse : 'L' + }, + relativeTime : { + future : "%s múlva", + past : "%s", + s : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { - var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; - d = d === 0 ? 7 : d; - weekday = weekday != null ? weekday : firstDayOfWeek; - daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); - dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; +/***/ }, +/* 89 */ +/***/ function(module, exports, __webpack_require__) { - return { - year: dayOfYear > 0 ? year : year - 1, - dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear - }; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Armenian (hy-am) + // author : Armendarabyan : https://github.com/armendarabyan - /************************************ - Top Level Functions - ************************************/ + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { - function makeMoment(config) { - var input = config._i, - format = config._f; + function monthsCaseReplace(m, format) { + var months = { + 'nominative': 'հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր'.split('_'), + 'accusative': 'հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի'.split('_') + }, - if (input === null || (format === undefined && input === '')) { - return moment.invalid({nullInput: true}); - } + nounCase = (/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/).test(format) ? + 'accusative' : + 'nominative'; - if (typeof input === 'string') { - config._i = input = getLangDefinition().preparse(input); - } + return months[nounCase][m.month()]; + } - if (moment.isMoment(input)) { - config = cloneMoment(input); + function monthsShortCaseReplace(m, format) { + var monthsShort = 'հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ'.split('_'); - config._d = new Date(+input._d); - } else if (format) { - if (isArray(format)) { - makeDateFromStringAndArray(config); - } else { - makeDateFromStringAndFormat(config); - } - } else { - makeDateFromInput(config); - } + return monthsShort[m.month()]; + } - return new Moment(config); - } + function weekdaysCaseReplace(m, format) { + var weekdays = 'կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ'.split('_'); - moment = function (input, format, lang, strict) { - var c; + return weekdays[m.day()]; + } - if (typeof(lang) === "boolean") { - strict = lang; - lang = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._i = input; - c._f = format; - c._l = lang; - c._strict = strict; - c._isUTC = false; - c._pf = defaultParsingFlags(); + return moment.lang('hy-am', { + months : monthsCaseReplace, + monthsShort : monthsShortCaseReplace, + weekdays : weekdaysCaseReplace, + weekdaysShort : "կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"), + weekdaysMin : "կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY թ.", + LLL : "D MMMM YYYY թ., LT", + LLLL : "dddd, D MMMM YYYY թ., LT" + }, + calendar : { + sameDay: '[այսօր] LT', + nextDay: '[վաղը] LT', + lastDay: '[երեկ] LT', + nextWeek: function () { + return 'dddd [օրը ժամը] LT'; + }, + lastWeek: function () { + return '[անցած] dddd [օրը ժամը] LT'; + }, + sameElse: 'L' + }, + relativeTime : { + future : "%s հետո", + past : "%s առաջ", + s : "մի քանի վայրկյան", + m : "րոպե", + mm : "%d րոպե", + h : "ժամ", + hh : "%d ժամ", + d : "օր", + dd : "%d օր", + M : "ամիս", + MM : "%d ամիս", + y : "տարի", + yy : "%d տարի" + }, - return makeMoment(c); - }; + meridiem : function (hour) { + if (hour < 4) { + return "գիշերվա"; + } else if (hour < 12) { + return "առավոտվա"; + } else if (hour < 17) { + return "ցերեկվա"; + } else { + return "երեկոյան"; + } + }, - moment.suppressDeprecationWarnings = false; + ordinal: function (number, period) { + switch (period) { + case 'DDD': + case 'w': + case 'W': + case 'DDDo': + if (number === 1) { + return number + '-ին'; + } + return number + '-րդ'; + default: + return number; + } + }, - moment.createFromInputFallback = deprecate( - "moment construction falls back to js Date. This is " + - "discouraged and will be removed in upcoming major " + - "release. Please refer to " + - "https://github.com/moment/moment/issues/1407 for more info.", - function (config) { - config._d = new Date(config._i); - }); + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - // 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); +/***/ }, +/* 90 */ +/***/ function(module, exports, __webpack_require__) { - return pickBy('isBefore', args); - }; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Bahasa Indonesia (id) + // author : Mohammad Satrio Utomo : https://github.com/tyok + // reference: http://id.wikisource.org/wiki/Pedoman_Umum_Ejaan_Bahasa_Indonesia_yang_Disempurnakan - moment.max = function () { - var args = [].slice.call(arguments, 0); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('id', { + months : "Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"), + monthsShort : "Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"), + weekdays : "Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"), + weekdaysShort : "Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"), + weekdaysMin : "Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"), + longDateFormat : { + LT : "HH.mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY [pukul] LT", + LLLL : "dddd, D MMMM YYYY [pukul] LT" + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 11) { + return 'pagi'; + } else if (hours < 15) { + return 'siang'; + } else if (hours < 19) { + return 'sore'; + } else { + return 'malam'; + } + }, + calendar : { + sameDay : '[Hari ini pukul] LT', + nextDay : '[Besok pukul] LT', + nextWeek : 'dddd [pukul] LT', + lastDay : '[Kemarin pukul] LT', + lastWeek : 'dddd [lalu pukul] LT', + sameElse : 'L' + }, + relativeTime : { + future : "dalam %s", + past : "%s yang lalu", + s : "beberapa detik", + m : "semenit", + mm : "%d menit", + h : "sejam", + hh : "%d jam", + d : "sehari", + dd : "%d hari", + M : "sebulan", + MM : "%d bulan", + y : "setahun", + yy : "%d tahun" + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - return pickBy('isAfter', args); - }; - // creating with utc - moment.utc = function (input, format, lang, strict) { - var c; - - if (typeof(lang) === "boolean") { - strict = lang; - lang = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._useUTC = true; - c._isUTC = true; - c._l = lang; - c._i = input; - c._f = format; - c._strict = strict; - c._pf = defaultParsingFlags(); - - return makeMoment(c).utc(); - }; +/***/ }, +/* 91 */ +/***/ function(module, exports, __webpack_require__) { - // creating with unix timestamp (in seconds) - moment.unix = function (input) { - return moment(input * 1000); - }; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : icelandic (is) + // author : Hinrik Örn Sigurðsson : https://github.com/hinrik - // duration - moment.duration = function (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - parseIso; - - if (moment.isDuration(input)) { - duration = { - ms: input._milliseconds, - d: input._days, - M: input._months - }; - } else if (typeof input === 'number') { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { - sign = (match[1] === "-") ? -1 : 1; - duration = { - y: 0, - d: toInt(match[DATE]) * sign, - h: toInt(match[HOUR]) * sign, - m: toInt(match[MINUTE]) * sign, - s: toInt(match[SECOND]) * sign, - ms: toInt(match[MILLISECOND]) * sign - }; - } else if (!!(match = isoDurationRegex.exec(input))) { - sign = (match[1] === "-") ? -1 : 1; - parseIso = function (inp) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - }; - duration = { - y: parseIso(match[2]), - M: parseIso(match[3]), - d: parseIso(match[4]), - h: parseIso(match[5]), - m: parseIso(match[6]), - s: parseIso(match[7]), - w: parseIso(match[8]) - }; - } - - ret = new Duration(duration); - - if (moment.isDuration(input) && input.hasOwnProperty('_lang')) { - ret._lang = input._lang; - } - - return ret; - }; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function plural(n) { + if (n % 100 === 11) { + return true; + } else if (n % 10 === 1) { + return false; + } + return true; + } - // version number - moment.version = VERSION; + function translate(number, withoutSuffix, key, isFuture) { + var result = number + " "; + switch (key) { + case 's': + return withoutSuffix || isFuture ? 'nokkrar sekúndur' : 'nokkrum sekúndum'; + case 'm': + return withoutSuffix ? 'mínúta' : 'mínútu'; + case 'mm': + if (plural(number)) { + return result + (withoutSuffix || isFuture ? 'mínútur' : 'mínútum'); + } else if (withoutSuffix) { + return result + 'mínúta'; + } + return result + 'mínútu'; + case 'hh': + if (plural(number)) { + return result + (withoutSuffix || isFuture ? 'klukkustundir' : 'klukkustundum'); + } + return result + 'klukkustund'; + case 'd': + if (withoutSuffix) { + return 'dagur'; + } + return isFuture ? 'dag' : 'degi'; + case 'dd': + if (plural(number)) { + if (withoutSuffix) { + return result + 'dagar'; + } + return result + (isFuture ? 'daga' : 'dögum'); + } else if (withoutSuffix) { + return result + 'dagur'; + } + return result + (isFuture ? 'dag' : 'degi'); + case 'M': + if (withoutSuffix) { + return 'mánuður'; + } + return isFuture ? 'mánuð' : 'mánuði'; + case 'MM': + if (plural(number)) { + if (withoutSuffix) { + return result + 'mánuðir'; + } + return result + (isFuture ? 'mánuði' : 'mánuðum'); + } else if (withoutSuffix) { + return result + 'mánuður'; + } + return result + (isFuture ? 'mánuð' : 'mánuði'); + case 'y': + return withoutSuffix || isFuture ? 'ár' : 'ári'; + case 'yy': + if (plural(number)) { + return result + (withoutSuffix || isFuture ? 'ár' : 'árum'); + } + return result + (withoutSuffix || isFuture ? 'ár' : 'ári'); + } + } - // default format - moment.defaultFormat = isoFormat; + return moment.lang('is', { + months : "janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember".split("_"), + monthsShort : "jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des".split("_"), + weekdays : "sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"), + weekdaysShort : "sun_mán_þri_mið_fim_fös_lau".split("_"), + weekdaysMin : "Su_Má_Þr_Mi_Fi_Fö_La".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD/MM/YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY [kl.] LT", + LLLL : "dddd, D. MMMM YYYY [kl.] LT" + }, + calendar : { + sameDay : '[í dag kl.] LT', + nextDay : '[á morgun kl.] LT', + nextWeek : 'dddd [kl.] LT', + lastDay : '[í gær kl.] LT', + lastWeek : '[síðasta] dddd [kl.] LT', + sameElse : 'L' + }, + relativeTime : { + future : "eftir %s", + past : "fyrir %s síðan", + s : translate, + m : translate, + mm : translate, + h : "klukkustund", + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - // 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; +/***/ }, +/* 92 */ +/***/ function(module, exports, __webpack_require__) { - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - moment.updateOffset = function () {}; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : italian (it) + // author : Lorenzo : https://github.com/aliem + // author: Mattia Larentis: https://github.com/nostalgiaz - // 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; - }; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('it', { + months : "gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"), + monthsShort : "gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"), + weekdays : "Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"), + weekdaysShort : "Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"), + weekdaysMin : "D_L_Ma_Me_G_V_S".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay: '[Oggi alle] LT', + nextDay: '[Domani alle] LT', + nextWeek: 'dddd [alle] LT', + lastDay: '[Ieri alle] LT', + lastWeek: '[lo scorso] dddd [alle] LT', + sameElse: 'L' + }, + relativeTime : { + future : function (s) { + return ((/^[0-9].+$/).test(s) ? "tra" : "in") + " " + s; + }, + past : "%s fa", + s : "alcuni secondi", + m : "un minuto", + mm : "%d minuti", + h : "un'ora", + hh : "%d ore", + d : "un giorno", + dd : "%d giorni", + M : "un mese", + MM : "%d mesi", + y : "un anno", + yy : "%d anni" + }, + ordinal: '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - // 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. - moment.lang = function (key, values) { - var r; - if (!key) { - return moment.fn._lang._abbr; - } - if (values) { - loadLang(normalizeLanguage(key), values); - } else if (values === null) { - unloadLang(key); - key = 'en'; - } else if (!languages[key]) { - getLangDefinition(key); - } - r = moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key); - return r._abbr; - }; - // returns language data - moment.langData = function (key) { - if (key && key._lang && key._lang._abbr) { - key = key._lang._abbr; - } - return getLangDefinition(key); - }; +/***/ }, +/* 93 */ +/***/ function(module, exports, __webpack_require__) { - // compare moment object - moment.isMoment = function (obj) { - return obj instanceof Moment || - (obj != null && obj.hasOwnProperty('_isAMomentObject')); - }; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : japanese (ja) + // author : LI Long : https://github.com/baryon - // for typechecking Duration objects - moment.isDuration = function (obj) { - return obj instanceof Duration; - }; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('ja', { + months : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"), + monthsShort : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"), + weekdays : "日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"), + weekdaysShort : "日_月_火_水_木_金_土".split("_"), + weekdaysMin : "日_月_火_水_木_金_土".split("_"), + longDateFormat : { + LT : "Ah時m分", + L : "YYYY/MM/DD", + LL : "YYYY年M月D日", + LLL : "YYYY年M月D日LT", + LLLL : "YYYY年M月D日LT dddd" + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return "午前"; + } else { + return "午後"; + } + }, + calendar : { + sameDay : '[今日] LT', + nextDay : '[明日] LT', + nextWeek : '[来週]dddd LT', + lastDay : '[昨日] LT', + lastWeek : '[前週]dddd LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s後", + past : "%s前", + s : "数秒", + m : "1分", + mm : "%d分", + h : "1時間", + hh : "%d時間", + d : "1日", + dd : "%d日", + M : "1ヶ月", + MM : "%dヶ月", + y : "1年", + yy : "%d年" + } + }); + })); - for (i = lists.length - 1; i >= 0; --i) { - makeList(lists[i]); - } - moment.normalizeUnits = function (units) { - return normalizeUnits(units); - }; +/***/ }, +/* 94 */ +/***/ function(module, exports, __webpack_require__) { - moment.invalid = function (flags) { - var m = moment.utc(NaN); - if (flags != null) { - extend(m._pf, flags); - } - else { - m._pf.userInvalidated = true; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Georgian (ka) + // author : Irakli Janiashvili : https://github.com/irakli-janiashvili - return m; - }; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { - moment.parseZone = function () { - return moment.apply(null, arguments).parseZone(); - }; + function monthsCaseReplace(m, format) { + var months = { + 'nominative': 'იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი'.split('_'), + 'accusative': 'იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს'.split('_') + }, - moment.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; + nounCase = (/D[oD] *MMMM?/).test(format) ? + 'accusative' : + 'nominative'; - /************************************ - Moment Prototype - ************************************/ + return months[nounCase][m.month()]; + } + function weekdaysCaseReplace(m, format) { + var weekdays = { + 'nominative': 'კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი'.split('_'), + 'accusative': 'კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს'.split('_') + }, - extend(moment.fn = Moment.prototype, { + nounCase = (/(წინა|შემდეგ)/).test(format) ? + 'accusative' : + 'nominative'; + + return weekdays[nounCase][m.day()]; + } + + return moment.lang('ka', { + months : monthsCaseReplace, + monthsShort : "იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ".split("_"), + weekdays : weekdaysCaseReplace, + weekdaysShort : "კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ".split("_"), + weekdaysMin : "კვ_ორ_სა_ოთ_ხუ_პა_შა".split("_"), + longDateFormat : { + LT : "h:mm A", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay : '[დღეს] LT[-ზე]', + nextDay : '[ხვალ] LT[-ზე]', + lastDay : '[გუშინ] LT[-ზე]', + nextWeek : '[შემდეგ] dddd LT[-ზე]', + lastWeek : '[წინა] dddd LT-ზე', + sameElse : 'L' + }, + relativeTime : { + future : function (s) { + return (/(წამი|წუთი|საათი|წელი)/).test(s) ? + s.replace(/ი$/, "ში") : + s + "ში"; + }, + past : function (s) { + if ((/(წამი|წუთი|საათი|დღე|თვე)/).test(s)) { + return s.replace(/(ი|ე)$/, "ის წინ"); + } + if ((/წელი/).test(s)) { + return s.replace(/წელი$/, "წლის წინ"); + } + }, + s : "რამდენიმე წამი", + m : "წუთი", + mm : "%d წუთი", + h : "საათი", + hh : "%d საათი", + d : "დღე", + dd : "%d დღე", + M : "თვე", + MM : "%d თვე", + y : "წელი", + yy : "%d წელი" + }, + ordinal : function (number) { + if (number === 0) { + return number; + } - clone : function () { - return moment(this); - }, + if (number === 1) { + return number + "-ლი"; + } - valueOf : function () { - return +this._d + ((this._offset || 0) * 60000); - }, + if ((number < 20) || (number <= 100 && (number % 20 === 0)) || (number % 100 === 0)) { + return "მე-" + number; + } - unix : function () { - return Math.floor(+this / 1000); - }, + return number + "-ე"; + }, + week : { + dow : 1, + doy : 7 + } + }); + })); - toString : function () { - return this.clone().lang('en').format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ"); - }, - toDate : function () { - return this._offset ? new Date(+this) : this._d; - }, +/***/ }, +/* 95 */ +/***/ function(module, exports, __webpack_require__) { - toISOString : function () { - var m = moment(this).utc(); - if (0 < m.year() && m.year() <= 9999) { - return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } else { - return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : khmer (km) + // author : Kruy Vanna : https://github.com/kruyvanna - toArray : function () { - var m = this; - return [ - m.year(), - m.month(), - m.date(), - m.hours(), - m.minutes(), - m.seconds(), - m.milliseconds() - ]; - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('km', { + months: "មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"), + monthsShort: "មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"), + weekdays: "អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"), + weekdaysShort: "អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"), + weekdaysMin: "អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"), + longDateFormat: { + LT: "HH:mm", + L: "DD/MM/YYYY", + LL: "D MMMM YYYY", + LLL: "D MMMM YYYY LT", + LLLL: "dddd, D MMMM YYYY LT" + }, + calendar: { + sameDay: '[ថ្ងៃនៈ ម៉ោង] LT', + nextDay: '[ស្អែក ម៉ោង] LT', + nextWeek: 'dddd [ម៉ោង] LT', + lastDay: '[ម្សិលមិញ ម៉ោង] LT', + lastWeek: 'dddd [សប្តាហ៍មុន] [ម៉ោង] LT', + sameElse: 'L' + }, + relativeTime: { + future: "%sទៀត", + past: "%sមុន", + s: "ប៉ុន្មានវិនាទី", + m: "មួយនាទី", + mm: "%d នាទី", + h: "មួយម៉ោង", + hh: "%d ម៉ោង", + d: "មួយថ្ងៃ", + dd: "%d ថ្ងៃ", + M: "មួយខែ", + MM: "%d ខែ", + y: "មួយឆ្នាំ", + yy: "%d ឆ្នាំ" + }, + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); + + +/***/ }, +/* 96 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : korean (ko) + // + // authors + // + // - Kyungwook, Park : https://github.com/kyungw00k + // - Jeeeyul Lee + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('ko', { + months : "1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"), + monthsShort : "1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"), + weekdays : "일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"), + weekdaysShort : "일_월_화_수_목_금_토".split("_"), + weekdaysMin : "일_월_화_수_목_금_토".split("_"), + longDateFormat : { + LT : "A h시 mm분", + L : "YYYY.MM.DD", + LL : "YYYY년 MMMM D일", + LLL : "YYYY년 MMMM D일 LT", + LLLL : "YYYY년 MMMM D일 dddd LT" + }, + meridiem : function (hour, minute, isUpper) { + return hour < 12 ? '오전' : '오후'; + }, + calendar : { + sameDay : '오늘 LT', + nextDay : '내일 LT', + nextWeek : 'dddd LT', + lastDay : '어제 LT', + lastWeek : '지난주 dddd LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s 후", + past : "%s 전", + s : "몇초", + ss : "%d초", + m : "일분", + mm : "%d분", + h : "한시간", + hh : "%d시간", + d : "하루", + dd : "%d일", + M : "한달", + MM : "%d달", + y : "일년", + yy : "%d년" + }, + ordinal : '%d일', + meridiemParse : /(오전|오후)/, + isPM : function (token) { + return token === "오후"; + } + }); + })); - isValid : function () { - return isValid(this); - }, - isDSTShifted : function () { +/***/ }, +/* 97 */ +/***/ function(module, exports, __webpack_require__) { - if (this._a) { - return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Luxembourgish (lb) + // author : mweimerskirch : https://github.com/mweimerskirch - return false; - }, + // Note: Luxembourgish has a very particular phonological rule ("Eifeler Regel") that causes the + // deletion of the final "n" in certain contexts. That's what the "eifelerRegelAppliesToWeekday" + // and "eifelerRegelAppliesToNumber" methods are meant for - parsingFlags : function () { - return extend({}, this._pf); - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 'm': ['eng Minutt', 'enger Minutt'], + 'h': ['eng Stonn', 'enger Stonn'], + 'd': ['een Dag', 'engem Dag'], + 'dd': [number + ' Deeg', number + ' Deeg'], + 'M': ['ee Mount', 'engem Mount'], + 'MM': [number + ' Méint', number + ' Méint'], + 'y': ['ee Joer', 'engem Joer'], + 'yy': [number + ' Joer', number + ' Joer'] + }; + return withoutSuffix ? format[key][0] : format[key][1]; + } - invalidAt: function () { - return this._pf.overflow; - }, + function processFutureTime(string) { + var number = string.substr(0, string.indexOf(' ')); + if (eifelerRegelAppliesToNumber(number)) { + return "a " + string; + } + return "an " + string; + } - utc : function () { - return this.zone(0); - }, + function processPastTime(string) { + var number = string.substr(0, string.indexOf(' ')); + if (eifelerRegelAppliesToNumber(number)) { + return "viru " + string; + } + return "virun " + string; + } - local : function () { - this.zone(0); - this._isUTC = false; - return this; - }, + function processLastWeek(string1) { + var weekday = this.format('d'); + if (eifelerRegelAppliesToWeekday(weekday)) { + return '[Leschte] dddd [um] LT'; + } + return '[Leschten] dddd [um] LT'; + } - format : function (inputString) { - var output = formatMoment(this, inputString || moment.defaultFormat); - return this.lang().postformat(output); - }, + /** + * Returns true if the word before the given week day loses the "-n" ending. + * e.g. "Leschten Dënschdeg" but "Leschte Méindeg" + * + * @param weekday {integer} + * @returns {boolean} + */ + function eifelerRegelAppliesToWeekday(weekday) { + weekday = parseInt(weekday, 10); + switch (weekday) { + case 0: // Sonndeg + case 1: // Méindeg + case 3: // Mëttwoch + case 5: // Freideg + case 6: // Samschdeg + return true; + default: // 2 Dënschdeg, 4 Donneschdeg + return false; + } + } - add : function (input, val) { - var dur; - // switch args to support add('s', 1) and add(1, 's') - 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); - } - addOrSubtractDurationFromMoment(this, dur, 1); - return this; - }, + /** + * Returns true if the word before the given number loses the "-n" ending. + * e.g. "an 10 Deeg" but "a 5 Deeg" + * + * @param number {integer} + * @returns {boolean} + */ + function eifelerRegelAppliesToNumber(number) { + number = parseInt(number, 10); + if (isNaN(number)) { + return false; + } + if (number < 0) { + // Negative Number --> always true + return true; + } else if (number < 10) { + // Only 1 digit + if (4 <= number && number <= 7) { + return true; + } + return false; + } else if (number < 100) { + // 2 digits + var lastDigit = number % 10, firstDigit = number / 10; + if (lastDigit === 0) { + return eifelerRegelAppliesToNumber(firstDigit); + } + return eifelerRegelAppliesToNumber(lastDigit); + } else if (number < 10000) { + // 3 or 4 digits --> recursively check first digit + while (number >= 10) { + number = number / 10; + } + return eifelerRegelAppliesToNumber(number); + } else { + // Anything larger than 4 digits: recursively check first n-3 digits + number = number / 1000; + return eifelerRegelAppliesToNumber(number); + } + } - subtract : function (input, val) { - var dur; - // switch args to support subtract('s', 1) and subtract(1, 's') - 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); - } - addOrSubtractDurationFromMoment(this, dur, -1); - return this; - }, + return moment.lang('lb', { + months: "Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"), + monthsShort: "Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"), + weekdays: "Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg".split("_"), + weekdaysShort: "So._Mé._Dë._Më._Do._Fr._Sa.".split("_"), + weekdaysMin: "So_Mé_Dë_Më_Do_Fr_Sa".split("_"), + longDateFormat: { + LT: "H:mm [Auer]", + L: "DD.MM.YYYY", + LL: "D. MMMM YYYY", + LLL: "D. MMMM YYYY LT", + LLLL: "dddd, D. MMMM YYYY LT" + }, + calendar: { + sameDay: "[Haut um] LT", + sameElse: "L", + nextDay: '[Muer um] LT', + nextWeek: 'dddd [um] LT', + lastDay: '[Gëschter um] LT', + lastWeek: processLastWeek + }, + relativeTime: { + future: processFutureTime, + past: processPastTime, + s: "e puer Sekonnen", + m: processRelativeTime, + mm: "%d Minutten", + h: processRelativeTime, + hh: "%d Stonnen", + d: processRelativeTime, + dd: processRelativeTime, + M: processRelativeTime, + MM: processRelativeTime, + y: processRelativeTime, + yy: processRelativeTime + }, + ordinal: '%d.', + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - diff : function (input, units, asFloat) { - var that = makeAs(input, this), - zoneDiff = (this.zone() - that.zone()) * 6e4, - diff, output; - - units = normalizeUnits(units); - - if (units === 'year' || units === 'month') { - // average number of days in the months in the given dates - diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2 - // difference in months - output = ((this.year() - that.year()) * 12) + (this.month() - that.month()); - // adjust by taking difference in days, average number of days - // and dst in the given months. - output += ((this - moment(this).startOf('month')) - - (that - moment(that).startOf('month'))) / diff; - // same as above but with zones, to negate all dst - output -= ((this.zone() - moment(this).startOf('month').zone()) - - (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff; - if (units === 'year') { - output = output / 12; - } - } else { - diff = (this - that); - output = units === 'second' ? diff / 1e3 : // 1000 - units === 'minute' ? diff / 6e4 : // 1000 * 60 - units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 - units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst - units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst - diff; - } - return asFloat ? output : absRound(output); - }, - from : function (time, withoutSuffix) { - return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix); - }, +/***/ }, +/* 98 */ +/***/ function(module, exports, __webpack_require__) { - fromNow : function (withoutSuffix) { - return this.from(moment(), withoutSuffix); - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Lithuanian (lt) + // author : Mindaugas Mozūras : https://github.com/mmozuras - 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 now = time || moment(), - sod = makeAs(now, this).startOf('day'), - diff = this.diff(sod, 'days', true), - format = diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - return this.format(this.lang().calendar(format, this)); - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var units = { + "m" : "minutė_minutės_minutę", + "mm": "minutės_minučių_minutes", + "h" : "valanda_valandos_valandą", + "hh": "valandos_valandų_valandas", + "d" : "diena_dienos_dieną", + "dd": "dienos_dienų_dienas", + "M" : "mėnuo_mėnesio_mėnesį", + "MM": "mėnesiai_mėnesių_mėnesius", + "y" : "metai_metų_metus", + "yy": "metai_metų_metus" + }, + weekDays = "sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_"); - isLeapYear : function () { - return isLeapYear(this.year()); - }, + function translateSeconds(number, withoutSuffix, key, isFuture) { + if (withoutSuffix) { + return "kelios sekundės"; + } else { + return isFuture ? "kelių sekundžių" : "kelias sekundes"; + } + } - isDST : function () { - return (this.zone() < this.clone().month(0).zone() || - this.zone() < this.clone().month(5).zone()); - }, + function translateSingular(number, withoutSuffix, key, isFuture) { + return withoutSuffix ? forms(key)[0] : (isFuture ? forms(key)[1] : forms(key)[2]); + } - day : function (input) { - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.lang()); - return this.add({ d : input - day }); - } else { - return day; - } - }, + function special(number) { + return number % 10 === 0 || (number > 10 && number < 20); + } - month : makeAccessor('Month', true), - - startOf: function (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { - case 'year': - this.month(0); - /* falls through */ - case 'quarter': - case 'month': - this.date(1); - /* falls through */ - case 'week': - case 'isoWeek': - case 'day': - this.hours(0); - /* falls through */ - case 'hour': - this.minutes(0); - /* falls through */ - case 'minute': - this.seconds(0); - /* falls through */ - case 'second': - this.milliseconds(0); - /* falls through */ - } + function forms(key) { + return units[key].split("_"); + } - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } else if (units === 'isoWeek') { - this.isoWeekday(1); - } + function translate(number, withoutSuffix, key, isFuture) { + var result = number + " "; + if (number === 1) { + return result + translateSingular(number, withoutSuffix, key[0], isFuture); + } else if (withoutSuffix) { + return result + (special(number) ? forms(key)[1] : forms(key)[0]); + } else { + if (isFuture) { + return result + forms(key)[1]; + } else { + return result + (special(number) ? forms(key)[1] : forms(key)[2]); + } + } + } - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); - } + function relativeWeekDay(moment, format) { + var nominative = format.indexOf('dddd HH:mm') === -1, + weekDay = weekDays[moment.day()]; + + return nominative ? weekDay : weekDay.substring(0, weekDay.length - 2) + "į"; + } + + return moment.lang("lt", { + months : "sausio_vasario_kovo_balandžio_gegužės_biržėlio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"), + monthsShort : "sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"), + weekdays : relativeWeekDay, + weekdaysShort : "Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"), + weekdaysMin : "S_P_A_T_K_Pn_Š".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "YYYY-MM-DD", + LL : "YYYY [m.] MMMM D [d.]", + LLL : "YYYY [m.] MMMM D [d.], LT [val.]", + LLLL : "YYYY [m.] MMMM D [d.], dddd, LT [val.]", + l : "YYYY-MM-DD", + ll : "YYYY [m.] MMMM D [d.]", + lll : "YYYY [m.] MMMM D [d.], LT [val.]", + llll : "YYYY [m.] MMMM D [d.], ddd, LT [val.]" + }, + calendar : { + sameDay : "[Šiandien] LT", + nextDay : "[Rytoj] LT", + nextWeek : "dddd LT", + lastDay : "[Vakar] LT", + lastWeek : "[Praėjusį] dddd LT", + sameElse : "L" + }, + relativeTime : { + future : "po %s", + past : "prieš %s", + s : translateSeconds, + m : translateSingular, + mm : translate, + h : translateSingular, + hh : translate, + d : translateSingular, + dd : translate, + M : translateSingular, + MM : translate, + y : translateSingular, + yy : translate + }, + ordinal : function (number) { + return number + '-oji'; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - return this; - }, - endOf: function (units) { - units = normalizeUnits(units); - return this.startOf(units).add((units === 'isoWeek' ? 'week' : units), 1).subtract('ms', 1); - }, +/***/ }, +/* 99 */ +/***/ function(module, exports, __webpack_require__) { - isAfter: function (input, units) { - units = typeof units !== 'undefined' ? units : 'millisecond'; - return +this.clone().startOf(units) > +moment(input).startOf(units); - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : latvian (lv) + // author : Kristaps Karlsons : https://github.com/skakri - isBefore: function (input, units) { - units = typeof units !== 'undefined' ? units : 'millisecond'; - return +this.clone().startOf(units) < +moment(input).startOf(units); - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var units = { + 'mm': 'minūti_minūtes_minūte_minūtes', + 'hh': 'stundu_stundas_stunda_stundas', + 'dd': 'dienu_dienas_diena_dienas', + 'MM': 'mēnesi_mēnešus_mēnesis_mēneši', + 'yy': 'gadu_gadus_gads_gadi' + }; - isSame: function (input, units) { - units = units || 'ms'; - return +this.clone().startOf(units) === +makeAs(input, this).startOf(units); - }, + function format(word, number, withoutSuffix) { + var forms = word.split('_'); + if (withoutSuffix) { + return number % 10 === 1 && number !== 11 ? forms[2] : forms[3]; + } else { + return number % 10 === 1 && number !== 11 ? forms[0] : forms[1]; + } + } - 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; - } - ), + function relativeTimeWithPlural(number, withoutSuffix, key) { + return number + ' ' + format(units[key], number, withoutSuffix); + } - // keepTime = true means only change the timezone, without affecting - // the local hour. So 5:31:26 +0300 --[zone(2, true)]--> 5:31:26 +0200 - // It is possible that 5:31:26 doesn't exist int zone +0200, so we - // adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - zone : function (input, keepTime) { - var offset = this._offset || 0; - if (input != null) { - if (typeof input === "string") { - input = timezoneMinutesFromString(input); - } - if (Math.abs(input) < 16) { - input = input * 60; - } - this._offset = input; - this._isUTC = true; - if (offset !== input) { - if (!keepTime || this._changeInProgress) { - addOrSubtractDurationFromMoment(this, - moment.duration(offset - input, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - moment.updateOffset(this, true); - this._changeInProgress = null; - } - } - } else { - return this._isUTC ? offset : this._d.getTimezoneOffset(); - } - return this; - }, + return moment.lang('lv', { + months : "janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"), + monthsShort : "jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"), + weekdays : "svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"), + weekdaysShort : "Sv_P_O_T_C_Pk_S".split("_"), + weekdaysMin : "Sv_P_O_T_C_Pk_S".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "YYYY. [gada] D. MMMM", + LLL : "YYYY. [gada] D. MMMM, LT", + LLLL : "YYYY. [gada] D. MMMM, dddd, LT" + }, + calendar : { + sameDay : '[Šodien pulksten] LT', + nextDay : '[Rīt pulksten] LT', + nextWeek : 'dddd [pulksten] LT', + lastDay : '[Vakar pulksten] LT', + lastWeek : '[Pagājušā] dddd [pulksten] LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s vēlāk", + past : "%s agrāk", + s : "dažas sekundes", + m : "minūti", + mm : relativeTimeWithPlural, + h : "stundu", + hh : relativeTimeWithPlural, + d : "dienu", + dd : relativeTimeWithPlural, + M : "mēnesi", + MM : relativeTimeWithPlural, + y : "gadu", + yy : relativeTimeWithPlural + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - zoneAbbr : function () { - return this._isUTC ? "UTC" : ""; - }, - zoneName : function () { - return this._isUTC ? "Coordinated Universal Time" : ""; - }, +/***/ }, +/* 100 */ +/***/ function(module, exports, __webpack_require__) { - parseZone : function () { - if (this._tzm) { - this.zone(this._tzm); - } else if (typeof this._i === 'string') { - this.zone(this._i); - } - return this; - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : macedonian (mk) + // author : Borislav Mickov : https://github.com/B0k0 - hasAlignedHourOffset : function (input) { - if (!input) { - input = 0; - } - else { - input = moment(input).zone(); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('mk', { + months : "јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември".split("_"), + monthsShort : "јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек".split("_"), + weekdays : "недела_понеделник_вторник_среда_четврток_петок_сабота".split("_"), + weekdaysShort : "нед_пон_вто_сре_чет_пет_саб".split("_"), + weekdaysMin : "нe_пo_вт_ср_че_пе_сa".split("_"), + longDateFormat : { + LT : "H:mm", + L : "D.MM.YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay : '[Денес во] LT', + nextDay : '[Утре во] LT', + nextWeek : 'dddd [во] LT', + lastDay : '[Вчера во] LT', + lastWeek : function () { + switch (this.day()) { + case 0: + case 3: + case 6: + return '[Во изминатата] dddd [во] LT'; + case 1: + case 2: + case 4: + case 5: + return '[Во изминатиот] dddd [во] LT'; + } + }, + sameElse : 'L' + }, + relativeTime : { + future : "после %s", + past : "пред %s", + s : "неколку секунди", + m : "минута", + mm : "%d минути", + h : "час", + hh : "%d часа", + d : "ден", + dd : "%d дена", + M : "месец", + MM : "%d месеци", + y : "година", + yy : "%d години" + }, + ordinal : function (number) { + var lastDigit = number % 10, + last2Digits = number % 100; + if (number === 0) { + return number + '-ев'; + } else if (last2Digits === 0) { + return number + '-ен'; + } else if (last2Digits > 10 && last2Digits < 20) { + return number + '-ти'; + } else if (lastDigit === 1) { + return number + '-ви'; + } else if (lastDigit === 2) { + return number + '-ри'; + } else if (lastDigit === 7 || lastDigit === 8) { + return number + '-ми'; + } else { + return number + '-ти'; + } + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - return (this.zone() - input) % 60 === 0; - }, - daysInMonth : function () { - return daysInMonth(this.year(), this.month()); - }, +/***/ }, +/* 101 */ +/***/ function(module, exports, __webpack_require__) { - dayOfYear : function (input) { - var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add("d", (input - dayOfYear)); - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : malayalam (ml) + // author : Floyd Pink : https://github.com/floydpink - quarter : function (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('ml', { + months : 'ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ'.split("_"), + monthsShort : 'ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.'.split("_"), + weekdays : 'ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച'.split("_"), + weekdaysShort : 'ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി'.split("_"), + weekdaysMin : 'ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ'.split("_"), + longDateFormat : { + LT : "A h:mm -നു", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY, LT", + LLLL : "dddd, D MMMM YYYY, LT" + }, + calendar : { + sameDay : '[ഇന്ന്] LT', + nextDay : '[നാളെ] LT', + nextWeek : 'dddd, LT', + lastDay : '[ഇന്നലെ] LT', + lastWeek : '[കഴിഞ്ഞ] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s കഴിഞ്ഞ്", + past : "%s മുൻപ്", + s : "അൽപ നിമിഷങ്ങൾ", + m : "ഒരു മിനിറ്റ്", + mm : "%d മിനിറ്റ്", + h : "ഒരു മണിക്കൂർ", + hh : "%d മണിക്കൂർ", + d : "ഒരു ദിവസം", + dd : "%d ദിവസം", + M : "ഒരു മാസം", + MM : "%d മാസം", + y : "ഒരു വർഷം", + yy : "%d വർഷം" + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return "രാത്രി"; + } else if (hour < 12) { + return "രാവിലെ"; + } else if (hour < 17) { + return "ഉച്ച കഴിഞ്ഞ്"; + } else if (hour < 20) { + return "വൈകുന്നേരം"; + } else { + return "രാത്രി"; + } + } + }); + })); - weekYear : function (input) { - var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year; - return input == null ? year : this.add("y", (input - year)); - }, - isoWeekYear : function (input) { - var year = weekOfYear(this, 1, 4).year; - return input == null ? year : this.add("y", (input - year)); - }, +/***/ }, +/* 102 */ +/***/ function(module, exports, __webpack_require__) { - week : function (input) { - var week = this.lang().week(this); - return input == null ? week : this.add("d", (input - week) * 7); - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Marathi (mr) + // author : Harshad Kale : https://github.com/kalehv - isoWeek : function (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add("d", (input - week) * 7); - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var symbolMap = { + '1': '१', + '2': '२', + '3': '३', + '4': '४', + '5': '५', + '6': '६', + '7': '७', + '8': '८', + '9': '९', + '0': '०' + }, + numberMap = { + '१': '1', + '२': '2', + '३': '3', + '४': '4', + '५': '5', + '६': '6', + '७': '7', + '८': '8', + '९': '9', + '०': '0' + }; - weekday : function (input) { - var weekday = (this.day() + 7 - this.lang()._week.dow) % 7; - return input == null ? weekday : this.add("d", input - weekday); - }, + return moment.lang('mr', { + months : 'जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर'.split("_"), + monthsShort: 'जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.'.split("_"), + weekdays : 'रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split("_"), + weekdaysShort : 'रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि'.split("_"), + weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split("_"), + longDateFormat : { + LT : "A h:mm वाजता", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY, LT", + LLLL : "dddd, D MMMM YYYY, LT" + }, + calendar : { + sameDay : '[आज] LT', + nextDay : '[उद्या] LT', + nextWeek : 'dddd, LT', + lastDay : '[काल] LT', + lastWeek: '[मागील] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s नंतर", + past : "%s पूर्वी", + s : "सेकंद", + m: "एक मिनिट", + mm: "%d मिनिटे", + h : "एक तास", + hh : "%d तास", + d : "एक दिवस", + dd : "%d दिवस", + M : "एक महिना", + MM : "%d महिने", + y : "एक वर्ष", + yy : "%d वर्षे" + }, + preparse: function (string) { + return string.replace(/[१२३४५६७८९०]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + meridiem: function (hour, minute, isLower) + { + if (hour < 4) { + return "रात्री"; + } else if (hour < 10) { + return "सकाळी"; + } else if (hour < 17) { + return "दुपारी"; + } else if (hour < 20) { + return "सायंकाळी"; + } else { + return "रात्री"; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - isoWeekday : function (input) { - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); - }, - isoWeeksInYear : function () { - return weeksInYear(this.year(), 1, 4); - }, +/***/ }, +/* 103 */ +/***/ function(module, exports, __webpack_require__) { - weeksInYear : function () { - var weekInfo = this._lang._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Bahasa Malaysia (ms-MY) + // author : Weldan Jamili : https://github.com/weldan - get : function (units) { - units = normalizeUnits(units); - return this[units](); - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('ms-my', { + months : "Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"), + monthsShort : "Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"), + weekdays : "Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"), + weekdaysShort : "Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"), + weekdaysMin : "Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"), + longDateFormat : { + LT : "HH.mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY [pukul] LT", + LLLL : "dddd, D MMMM YYYY [pukul] LT" + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 11) { + return 'pagi'; + } else if (hours < 15) { + return 'tengahari'; + } else if (hours < 19) { + return 'petang'; + } else { + return 'malam'; + } + }, + calendar : { + sameDay : '[Hari ini pukul] LT', + nextDay : '[Esok pukul] LT', + nextWeek : 'dddd [pukul] LT', + lastDay : '[Kelmarin pukul] LT', + lastWeek : 'dddd [lepas pukul] LT', + sameElse : 'L' + }, + relativeTime : { + future : "dalam %s", + past : "%s yang lepas", + s : "beberapa saat", + m : "seminit", + mm : "%d minit", + h : "sejam", + hh : "%d jam", + d : "sehari", + dd : "%d hari", + M : "sebulan", + MM : "%d bulan", + y : "setahun", + yy : "%d tahun" + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - set : function (units, value) { - units = normalizeUnits(units); - if (typeof this[units] === 'function') { - this[units](value); - } - return this; - }, - // If passed a language key, it will set the language for this - // instance. Otherwise, it will return the language configuration - // variables for this instance. - lang : function (key) { - if (key === undefined) { - return this._lang; - } else { - this._lang = getLangDefinition(key); - return this; - } - } - }); +/***/ }, +/* 104 */ +/***/ function(module, exports, __webpack_require__) { - function rawMonthSetter(mom, value) { - var dayOfMonth; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : norwegian bokmål (nb) + // authors : Espen Hovlandsdal : https://github.com/rexxars + // Sigurd Gartmann : https://github.com/sigurdga - // TODO: Move this out of here! - if (typeof value === 'string') { - value = mom.lang().monthsParse(value); - // TODO: Another silent failure? - if (typeof value !== 'number') { - return mom; - } - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('nb', { + months : "januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"), + monthsShort : "jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.".split("_"), + weekdays : "søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"), + weekdaysShort : "sø._ma._ti._on._to._fr._lø.".split("_"), + weekdaysMin : "sø_ma_ti_on_to_fr_lø".split("_"), + longDateFormat : { + LT : "H.mm", + L : "DD.MM.YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY [kl.] LT", + LLLL : "dddd D. MMMM YYYY [kl.] LT" + }, + calendar : { + sameDay: '[i dag kl.] LT', + nextDay: '[i morgen kl.] LT', + nextWeek: 'dddd [kl.] LT', + lastDay: '[i går kl.] LT', + lastWeek: '[forrige] dddd [kl.] LT', + sameElse: 'L' + }, + relativeTime : { + future : "om %s", + past : "for %s siden", + s : "noen sekunder", + m : "ett minutt", + mm : "%d minutter", + h : "en time", + hh : "%d timer", + d : "en dag", + dd : "%d dager", + M : "en måned", + MM : "%d måneder", + y : "ett år", + yy : "%d år" + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - dayOfMonth = Math.min(mom.date(), - daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; - } - function rawGetter(mom, unit) { - return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); - } +/***/ }, +/* 105 */ +/***/ function(module, exports, __webpack_require__) { - function rawSetter(mom, unit, value) { - if (unit === 'Month') { - return rawMonthSetter(mom, value); - } else { - return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : nepali/nepalese + // author : suvash : https://github.com/suvash - function makeAccessor(unit, keepTime) { - return function (value) { - if (value != null) { - rawSetter(this, unit, value); - moment.updateOffset(this, keepTime); - return this; - } else { - return rawGetter(this, unit); - } - }; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var symbolMap = { + '1': '१', + '2': '२', + '3': '३', + '4': '४', + '5': '५', + '6': '६', + '7': '७', + '8': '८', + '9': '९', + '0': '०' + }, + numberMap = { + '१': '1', + '२': '2', + '३': '3', + '४': '4', + '५': '5', + '६': '6', + '७': '7', + '८': '8', + '९': '9', + '०': '0' + }; - moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); - moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); - moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); - // Setting the hour should keep the time, because the user explicitly - // specified which hour he wants. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); - // moment.fn.month is defined separately - moment.fn.date = makeAccessor('Date', true); - moment.fn.dates = deprecate("dates accessor is deprecated. Use date instead.", makeAccessor('Date', true)); - moment.fn.year = makeAccessor('FullYear', true); - moment.fn.years = deprecate("years accessor is deprecated. Use year instead.", makeAccessor('FullYear', true)); + return moment.lang('ne', { + months : 'जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर'.split("_"), + monthsShort : 'जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.'.split("_"), + weekdays : 'आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार'.split("_"), + weekdaysShort : 'आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.'.split("_"), + weekdaysMin : 'आइ._सो._मङ्_बु._बि._शु._श.'.split("_"), + longDateFormat : { + LT : "Aको h:mm बजे", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY, LT", + LLLL : "dddd, D MMMM YYYY, LT" + }, + preparse: function (string) { + return string.replace(/[१२३४५६७८९०]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + meridiem : function (hour, minute, isLower) { + if (hour < 3) { + return "राती"; + } else if (hour < 10) { + return "बिहान"; + } else if (hour < 15) { + return "दिउँसो"; + } else if (hour < 18) { + return "बेलुका"; + } else if (hour < 20) { + return "साँझ"; + } else { + return "राती"; + } + }, + calendar : { + sameDay : '[आज] LT', + nextDay : '[भोली] LT', + nextWeek : '[आउँदो] dddd[,] LT', + lastDay : '[हिजो] LT', + lastWeek : '[गएको] dddd[,] LT', + sameElse : 'L' + }, + relativeTime : { + future : "%sमा", + past : "%s अगाडी", + s : "केही समय", + m : "एक मिनेट", + mm : "%d मिनेट", + h : "एक घण्टा", + hh : "%d घण्टा", + d : "एक दिन", + dd : "%d दिन", + M : "एक महिना", + MM : "%d महिना", + y : "एक बर्ष", + yy : "%d बर्ष" + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - // add plural methods - moment.fn.days = moment.fn.day; - moment.fn.months = moment.fn.month; - moment.fn.weeks = moment.fn.week; - moment.fn.isoWeeks = moment.fn.isoWeek; - moment.fn.quarters = moment.fn.quarter; - // add aliased format methods - moment.fn.toJSON = moment.fn.toISOString; +/***/ }, +/* 106 */ +/***/ function(module, exports, __webpack_require__) { - /************************************ - Duration Prototype - ************************************/ + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : dutch (nl) + // author : Joris Röling : https://github.com/jjupiter + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var monthsShortWithDots = "jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"), + monthsShortWithoutDots = "jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"); + + return moment.lang('nl', { + months : "januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"), + monthsShort : function (m, format) { + if (/-MMM-/.test(format)) { + return monthsShortWithoutDots[m.month()]; + } else { + return monthsShortWithDots[m.month()]; + } + }, + weekdays : "zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"), + weekdaysShort : "zo._ma._di._wo._do._vr._za.".split("_"), + weekdaysMin : "Zo_Ma_Di_Wo_Do_Vr_Za".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD-MM-YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay: '[vandaag om] LT', + nextDay: '[morgen om] LT', + nextWeek: 'dddd [om] LT', + lastDay: '[gisteren om] LT', + lastWeek: '[afgelopen] dddd [om] LT', + sameElse: 'L' + }, + relativeTime : { + future : "over %s", + past : "%s geleden", + s : "een paar seconden", + m : "één minuut", + mm : "%d minuten", + h : "één uur", + hh : "%d uur", + d : "één dag", + dd : "%d dagen", + M : "één maand", + MM : "%d maanden", + y : "één jaar", + yy : "%d jaar" + }, + ordinal : function (number) { + return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - extend(moment.duration.fn = Duration.prototype, { - _bubble : function () { - var milliseconds = this._milliseconds, - days = this._days, - months = this._months, - data = this._data, - seconds, minutes, hours, years; +/***/ }, +/* 107 */ +/***/ function(module, exports, __webpack_require__) { - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : norwegian nynorsk (nn) + // author : https://github.com/mechuwind - seconds = absRound(milliseconds / 1000); - data.seconds = seconds % 60; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('nn', { + months : "januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"), + monthsShort : "jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"), + weekdays : "sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"), + weekdaysShort : "sun_mån_tys_ons_tor_fre_lau".split("_"), + weekdaysMin : "su_må_ty_on_to_fr_lø".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay: '[I dag klokka] LT', + nextDay: '[I morgon klokka] LT', + nextWeek: 'dddd [klokka] LT', + lastDay: '[I går klokka] LT', + lastWeek: '[Føregåande] dddd [klokka] LT', + sameElse: 'L' + }, + relativeTime : { + future : "om %s", + past : "for %s sidan", + s : "nokre sekund", + m : "eit minutt", + mm : "%d minutt", + h : "ein time", + hh : "%d timar", + d : "ein dag", + dd : "%d dagar", + M : "ein månad", + MM : "%d månader", + y : "eit år", + yy : "%d år" + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - minutes = absRound(seconds / 60); - data.minutes = minutes % 60; - hours = absRound(minutes / 60); - data.hours = hours % 24; +/***/ }, +/* 108 */ +/***/ function(module, exports, __webpack_require__) { - days += absRound(hours / 24); - data.days = days % 30; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : polish (pl) + // author : Rafal Hirsz : https://github.com/evoL - months += absRound(days / 30); - data.months = months % 12; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var monthsNominative = "styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"), + monthsSubjective = "stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_"); + + function plural(n) { + return (n % 10 < 5) && (n % 10 > 1) && ((~~(n / 10) % 10) !== 1); + } + + function translate(number, withoutSuffix, key) { + var result = number + " "; + switch (key) { + case 'm': + return withoutSuffix ? 'minuta' : 'minutę'; + case 'mm': + return result + (plural(number) ? 'minuty' : 'minut'); + case 'h': + return withoutSuffix ? 'godzina' : 'godzinę'; + case 'hh': + return result + (plural(number) ? 'godziny' : 'godzin'); + case 'MM': + return result + (plural(number) ? 'miesiące' : 'miesięcy'); + case 'yy': + return result + (plural(number) ? 'lata' : 'lat'); + } + } - years = absRound(months / 12); - data.years = years; - }, + return moment.lang('pl', { + months : function (momentToFormat, format) { + if (/D MMMM/.test(format)) { + return monthsSubjective[momentToFormat.month()]; + } else { + return monthsNominative[momentToFormat.month()]; + } + }, + monthsShort : "sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"), + weekdays : "niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"), + weekdaysShort : "nie_pon_wt_śr_czw_pt_sb".split("_"), + weekdaysMin : "N_Pn_Wt_Śr_Cz_Pt_So".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay: '[Dziś o] LT', + nextDay: '[Jutro o] LT', + nextWeek: '[W] dddd [o] LT', + lastDay: '[Wczoraj o] LT', + lastWeek: function () { + switch (this.day()) { + case 0: + return '[W zeszłą niedzielę o] LT'; + case 3: + return '[W zeszłą środę o] LT'; + case 6: + return '[W zeszłą sobotę o] LT'; + default: + return '[W zeszły] dddd [o] LT'; + } + }, + sameElse: 'L' + }, + relativeTime : { + future : "za %s", + past : "%s temu", + s : "kilka sekund", + m : translate, + mm : translate, + h : translate, + hh : translate, + d : "1 dzień", + dd : '%d dni', + M : "miesiąc", + MM : translate, + y : "rok", + yy : translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - weeks : function () { - return absRound(this.days() / 7); - }, - valueOf : function () { - return this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6; - }, +/***/ }, +/* 109 */ +/***/ function(module, exports, __webpack_require__) { - humanize : function (withSuffix) { - var difference = +this, - output = relativeTime(difference, !withSuffix, this.lang()); + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : brazilian portuguese (pt-br) + // author : Caio Ribeiro Pereira : https://github.com/caio-ribeiro-pereira - if (withSuffix) { - output = this.lang().pastFuture(difference, output); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('pt-br', { + months : "janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"), + monthsShort : "jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"), + weekdays : "domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"), + weekdaysShort : "dom_seg_ter_qua_qui_sex_sáb".split("_"), + weekdaysMin : "dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D [de] MMMM [de] YYYY", + LLL : "D [de] MMMM [de] YYYY [às] LT", + LLLL : "dddd, D [de] MMMM [de] YYYY [às] LT" + }, + calendar : { + sameDay: '[Hoje às] LT', + nextDay: '[Amanhã às] LT', + nextWeek: 'dddd [às] LT', + lastDay: '[Ontem às] LT', + lastWeek: function () { + return (this.day() === 0 || this.day() === 6) ? + '[Último] dddd [às] LT' : // Saturday + Sunday + '[Última] dddd [às] LT'; // Monday - Friday + }, + sameElse: 'L' + }, + relativeTime : { + future : "em %s", + past : "%s atrás", + s : "segundos", + m : "um minuto", + mm : "%d minutos", + h : "uma hora", + hh : "%d horas", + d : "um dia", + dd : "%d dias", + M : "um mês", + MM : "%d meses", + y : "um ano", + yy : "%d anos" + }, + ordinal : '%dº' + }); + })); - return this.lang().postformat(output); - }, - add : function (input, val) { - // supports only 2.0-style add(1, 's') or add(moment) - var dur = moment.duration(input, val); +/***/ }, +/* 110 */ +/***/ function(module, exports, __webpack_require__) { - this._milliseconds += dur._milliseconds; - this._days += dur._days; - this._months += dur._months; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : portuguese (pt) + // author : Jefferson : https://github.com/jalex79 - this._bubble(); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('pt', { + months : "janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"), + monthsShort : "jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"), + weekdays : "domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"), + weekdaysShort : "dom_seg_ter_qua_qui_sex_sáb".split("_"), + weekdaysMin : "dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D [de] MMMM [de] YYYY", + LLL : "D [de] MMMM [de] YYYY LT", + LLLL : "dddd, D [de] MMMM [de] YYYY LT" + }, + calendar : { + sameDay: '[Hoje às] LT', + nextDay: '[Amanhã às] LT', + nextWeek: 'dddd [às] LT', + lastDay: '[Ontem às] LT', + lastWeek: function () { + return (this.day() === 0 || this.day() === 6) ? + '[Último] dddd [às] LT' : // Saturday + Sunday + '[Última] dddd [às] LT'; // Monday - Friday + }, + sameElse: 'L' + }, + relativeTime : { + future : "em %s", + past : "há %s", + s : "segundos", + m : "um minuto", + mm : "%d minutos", + h : "uma hora", + hh : "%d horas", + d : "um dia", + dd : "%d dias", + M : "um mês", + MM : "%d meses", + y : "um ano", + yy : "%d anos" + }, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - return this; - }, - subtract : function (input, val) { - var dur = moment.duration(input, val); +/***/ }, +/* 111 */ +/***/ function(module, exports, __webpack_require__) { - this._milliseconds -= dur._milliseconds; - this._days -= dur._days; - this._months -= dur._months; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : romanian (ro) + // author : Vlad Gurdiga : https://github.com/gurdiga + // author : Valentin Agachi : https://github.com/avaly - this._bubble(); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': 'minute', + 'hh': 'ore', + 'dd': 'zile', + 'MM': 'luni', + 'yy': 'ani' + }, + separator = ' '; + if (number % 100 >= 20 || (number >= 100 && number % 100 === 0)) { + separator = ' de '; + } - return this; - }, + return number + separator + format[key]; + } + + return moment.lang('ro', { + months : "ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"), + monthsShort : "ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"), + weekdays : "duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"), + weekdaysShort : "Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"), + weekdaysMin : "Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY H:mm", + LLLL : "dddd, D MMMM YYYY H:mm" + }, + calendar : { + sameDay: "[azi la] LT", + nextDay: '[mâine la] LT', + nextWeek: 'dddd [la] LT', + lastDay: '[ieri la] LT', + lastWeek: '[fosta] dddd [la] LT', + sameElse: 'L' + }, + relativeTime : { + future : "peste %s", + past : "%s în urmă", + s : "câteva secunde", + m : "un minut", + mm : relativeTimeWithPlural, + h : "o oră", + hh : relativeTimeWithPlural, + d : "o zi", + dd : relativeTimeWithPlural, + M : "o lună", + MM : relativeTimeWithPlural, + y : "un an", + yy : relativeTimeWithPlural + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - get : function (units) { - units = normalizeUnits(units); - return this[units.toLowerCase() + 's'](); - }, - as : function (units) { - units = normalizeUnits(units); - return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's'](); - }, +/***/ }, +/* 112 */ +/***/ function(module, exports, __webpack_require__) { - lang : moment.fn.lang, - - toIsoString : function () { - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var years = Math.abs(this.years()), - months = Math.abs(this.months()), - days = Math.abs(this.days()), - hours = Math.abs(this.hours()), - minutes = Math.abs(this.minutes()), - seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); - - if (!this.asSeconds()) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : russian (ru) + // author : Viktorminator : https://github.com/Viktorminator + // Author : Menelion Elensúle : https://github.com/Oire - return (this.asSeconds() < 0 ? '-' : '') + - 'P' + - (years ? years + 'Y' : '') + - (months ? months + 'M' : '') + - (days ? days + 'D' : '') + - ((hours || minutes || seconds) ? 'T' : '') + - (hours ? hours + 'H' : '') + - (minutes ? minutes + 'M' : '') + - (seconds ? seconds + 'S' : ''); - } - }); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function plural(word, num) { + var forms = word.split('_'); + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); + } - function makeDurationGetter(name) { - moment.duration.fn[name] = function () { - return this._data[name]; - }; - } + function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут', + 'hh': 'час_часа_часов', + 'dd': 'день_дня_дней', + 'MM': 'месяц_месяца_месяцев', + 'yy': 'год_года_лет' + }; + if (key === 'm') { + return withoutSuffix ? 'минута' : 'минуту'; + } + else { + return number + ' ' + plural(format[key], +number); + } + } - function makeDurationAsGetter(name, factor) { - moment.duration.fn['as' + name] = function () { - return +this / factor; - }; - } + function monthsCaseReplace(m, format) { + var months = { + 'nominative': 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'), + 'accusative': 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_') + }, - for (i in unitMillisecondFactors) { - if (unitMillisecondFactors.hasOwnProperty(i)) { - makeDurationAsGetter(i, unitMillisecondFactors[i]); - makeDurationGetter(i.toLowerCase()); - } - } + nounCase = (/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/).test(format) ? + 'accusative' : + 'nominative'; - makeDurationAsGetter('Weeks', 6048e5); - moment.duration.fn.asMonths = function () { - return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12; - }; + return months[nounCase][m.month()]; + } + function monthsShortCaseReplace(m, format) { + var monthsShort = { + 'nominative': 'янв_фев_мар_апр_май_июнь_июль_авг_сен_окт_ноя_дек'.split('_'), + 'accusative': 'янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек'.split('_') + }, - /************************************ - Default Lang - ************************************/ + nounCase = (/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/).test(format) ? + 'accusative' : + 'nominative'; + return monthsShort[nounCase][m.month()]; + } - // Set default language, other languages will inherit from English. - moment.lang('en', { - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); + function weekdaysCaseReplace(m, format) { + var weekdays = { + 'nominative': 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'), + 'accusative': 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_') + }, - /* EMBED_LANGUAGES */ + nounCase = (/\[ ?[Вв] ?(?:прошлую|следующую)? ?\] ?dddd/).test(format) ? + 'accusative' : + 'nominative'; + + return weekdays[nounCase][m.day()]; + } + + return moment.lang('ru', { + months : monthsCaseReplace, + monthsShort : monthsShortCaseReplace, + weekdays : weekdaysCaseReplace, + weekdaysShort : "вс_пн_вт_ср_чт_пт_сб".split("_"), + weekdaysMin : "вс_пн_вт_ср_чт_пт_сб".split("_"), + monthsParse : [/^янв/i, /^фев/i, /^мар/i, /^апр/i, /^ма[й|я]/i, /^июн/i, /^июл/i, /^авг/i, /^сен/i, /^окт/i, /^ноя/i, /^дек/i], + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY г.", + LLL : "D MMMM YYYY г., LT", + LLLL : "dddd, D MMMM YYYY г., LT" + }, + calendar : { + sameDay: '[Сегодня в] LT', + nextDay: '[Завтра в] LT', + lastDay: '[Вчера в] LT', + nextWeek: function () { + return this.day() === 2 ? '[Во] dddd [в] LT' : '[В] dddd [в] LT'; + }, + lastWeek: function () { + switch (this.day()) { + case 0: + return '[В прошлое] dddd [в] LT'; + case 1: + case 2: + case 4: + return '[В прошлый] dddd [в] LT'; + case 3: + case 5: + case 6: + return '[В прошлую] dddd [в] LT'; + } + }, + sameElse: 'L' + }, + relativeTime : { + future : "через %s", + past : "%s назад", + s : "несколько секунд", + m : relativeTimeWithPlural, + mm : relativeTimeWithPlural, + h : "час", + hh : relativeTimeWithPlural, + d : "день", + dd : relativeTimeWithPlural, + M : "месяц", + MM : relativeTimeWithPlural, + y : "год", + yy : relativeTimeWithPlural + }, - /************************************ - Exposing Moment - ************************************/ + meridiemParse: /ночи|утра|дня|вечера/i, + isPM : function (input) { + return /^(дня|вечера)$/.test(input); + }, - function makeGlobal(shouldDeprecate) { - /*global ender:false */ - if (typeof ender !== 'undefined') { - return; - } - oldGlobalMoment = globalScope.moment; - if (shouldDeprecate) { - globalScope.moment = deprecate( - "Accessing Moment through the global scope is " + - "deprecated, and will be removed in an upcoming " + - "release.", - moment); - } else { - globalScope.moment = moment; - } - } + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return "ночи"; + } else if (hour < 12) { + return "утра"; + } else if (hour < 17) { + return "дня"; + } else { + return "вечера"; + } + }, - // CommonJS module is defined - if (hasModule) { - module.exports = moment; - } else if (typeof define === "function" && define.amd) { - define("moment", function (require, exports, module) { - if (module.config && module.config() && module.config().noGlobal === true) { - // release the global variable - globalScope.moment = oldGlobalMoment; - } + ordinal: function (number, period) { + switch (period) { + case 'M': + case 'd': + case 'DDD': + return number + '-й'; + case 'D': + return number + '-го'; + case 'w': + case 'W': + return number + '-я'; + default: + return number; + } + }, - return moment; - }); - makeGlobal(true); - } else { - makeGlobal(); - } -}).call(this); + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); -},{}],5:[function(require,module,exports){ -/** - * Copyright 2012 Craig Campbell - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Mousetrap is a simple keyboard shortcut library for Javascript with - * no external dependencies - * - * @version 1.1.2 - * @url craig.is/killing/mice - */ - /** - * mapping of special keycodes to their corresponding keys - * - * everything in this dictionary cannot use keypress events - * so it has to be here to map to the correct keycodes for - * keyup/keydown events - * - * @type {Object} - */ - var _MAP = { - 8: 'backspace', - 9: 'tab', - 13: 'enter', - 16: 'shift', - 17: 'ctrl', - 18: 'alt', - 20: 'capslock', - 27: 'esc', - 32: 'space', - 33: 'pageup', - 34: 'pagedown', - 35: 'end', - 36: 'home', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 45: 'ins', - 46: 'del', - 91: 'meta', - 93: 'meta', - 224: 'meta' - }, +/***/ }, +/* 113 */ +/***/ function(module, exports, __webpack_require__) { - /** - * mapping for special characters so they can support - * - * this dictionary is only used incase you want to bind a - * keyup or keydown event to one of these keys - * - * @type {Object} - */ - _KEYCODE_MAP = { - 106: '*', - 107: '+', - 109: '-', - 110: '.', - 111 : '/', - 186: ';', - 187: '=', - 188: ',', - 189: '-', - 190: '.', - 191: '/', - 192: '`', - 219: '[', - 220: '\\', - 221: ']', - 222: '\'' - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : slovak (sk) + // author : Martin Minka : https://github.com/k2s + // based on work of petrbela : https://github.com/petrbela - /** - * this is a mapping of keys that require shift on a US keypad - * back to the non shift equivelents - * - * this is so you can use keyup events with these keys - * - * note that this will only work reliably on US keyboards - * - * @type {Object} - */ - _SHIFT_MAP = { - '~': '`', - '!': '1', - '@': '2', - '#': '3', - '$': '4', - '%': '5', - '^': '6', - '&': '7', - '*': '8', - '(': '9', - ')': '0', - '_': '-', - '+': '=', - ':': ';', - '\"': '\'', - '<': ',', - '>': '.', - '?': '/', - '|': '\\' - }, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + var months = "január_február_marec_apríl_máj_jún_júl_august_september_október_november_december".split("_"), + monthsShort = "jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_"); + + function plural(n) { + return (n > 1) && (n < 5); + } + + function translate(number, withoutSuffix, key, isFuture) { + var result = number + " "; + switch (key) { + case 's': // a few seconds / in a few seconds / a few seconds ago + return (withoutSuffix || isFuture) ? 'pár sekúnd' : 'pár sekundami'; + case 'm': // a minute / in a minute / a minute ago + return withoutSuffix ? 'minúta' : (isFuture ? 'minútu' : 'minútou'); + case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'minúty' : 'minút'); + } else { + return result + 'minútami'; + } + break; + case 'h': // an hour / in an hour / an hour ago + return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou'); + case 'hh': // 9 hours / in 9 hours / 9 hours ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'hodiny' : 'hodín'); + } else { + return result + 'hodinami'; + } + break; + case 'd': // a day / in a day / a day ago + return (withoutSuffix || isFuture) ? 'deň' : 'dňom'; + case 'dd': // 9 days / in 9 days / 9 days ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'dni' : 'dní'); + } else { + return result + 'dňami'; + } + break; + case 'M': // a month / in a month / a month ago + return (withoutSuffix || isFuture) ? 'mesiac' : 'mesiacom'; + case 'MM': // 9 months / in 9 months / 9 months ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'mesiace' : 'mesiacov'); + } else { + return result + 'mesiacmi'; + } + break; + case 'y': // a year / in a year / a year ago + return (withoutSuffix || isFuture) ? 'rok' : 'rokom'; + case 'yy': // 9 years / in 9 years / 9 years ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'roky' : 'rokov'); + } else { + return result + 'rokmi'; + } + break; + } + } - /** - * this is a list of special strings you can use to map - * to modifier keys when you specify your keyboard shortcuts - * - * @type {Object} - */ - _SPECIAL_ALIASES = { - 'option': 'alt', - 'command': 'meta', - 'return': 'enter', - 'escape': 'esc' - }, + return moment.lang('sk', { + months : months, + monthsShort : monthsShort, + monthsParse : (function (months, monthsShort) { + var i, _monthsParse = []; + for (i = 0; i < 12; i++) { + // use custom parser to solve problem with July (červenec) + _monthsParse[i] = new RegExp('^' + months[i] + '$|^' + monthsShort[i] + '$', 'i'); + } + return _monthsParse; + }(months, monthsShort)), + weekdays : "nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"), + weekdaysShort : "ne_po_ut_st_št_pi_so".split("_"), + weekdaysMin : "ne_po_ut_st_št_pi_so".split("_"), + longDateFormat : { + LT: "H:mm", + L : "DD.MM.YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd D. MMMM YYYY LT" + }, + calendar : { + sameDay: "[dnes o] LT", + nextDay: '[zajtra o] LT', + nextWeek: function () { + switch (this.day()) { + case 0: + return '[v nedeľu o] LT'; + case 1: + case 2: + return '[v] dddd [o] LT'; + case 3: + return '[v stredu o] LT'; + case 4: + return '[vo štvrtok o] LT'; + case 5: + return '[v piatok o] LT'; + case 6: + return '[v sobotu o] LT'; + } + }, + lastDay: '[včera o] LT', + lastWeek: function () { + switch (this.day()) { + case 0: + return '[minulú nedeľu o] LT'; + case 1: + case 2: + return '[minulý] dddd [o] LT'; + case 3: + return '[minulú stredu o] LT'; + case 4: + case 5: + return '[minulý] dddd [o] LT'; + case 6: + return '[minulú sobotu o] LT'; + } + }, + sameElse: "L" + }, + relativeTime : { + future : "za %s", + past : "pred %s", + s : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - /** - * variable to store the flipped version of _MAP from above - * needed to check if we should use keypress or not when no action - * is specified - * - * @type {Object|undefined} - */ - _REVERSE_MAP, - /** - * a list of all the callbacks setup via Mousetrap.bind() - * - * @type {Object} - */ - _callbacks = {}, +/***/ }, +/* 114 */ +/***/ function(module, exports, __webpack_require__) { - /** - * direct map of string combinations to callbacks used for trigger() - * - * @type {Object} - */ - _direct_map = {}, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : slovenian (sl) + // author : Robert Sedovšek : https://github.com/sedovsek - /** - * keeps track of what level each sequence is at since multiple - * sequences can start out with the same sequence - * - * @type {Object} - */ - _sequence_levels = {}, + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function translate(number, withoutSuffix, key) { + var result = number + " "; + switch (key) { + case 'm': + return withoutSuffix ? 'ena minuta' : 'eno minuto'; + case 'mm': + if (number === 1) { + result += 'minuta'; + } else if (number === 2) { + result += 'minuti'; + } else if (number === 3 || number === 4) { + result += 'minute'; + } else { + result += 'minut'; + } + return result; + case 'h': + return withoutSuffix ? 'ena ura' : 'eno uro'; + case 'hh': + if (number === 1) { + result += 'ura'; + } else if (number === 2) { + result += 'uri'; + } else if (number === 3 || number === 4) { + result += 'ure'; + } else { + result += 'ur'; + } + return result; + case 'dd': + if (number === 1) { + result += 'dan'; + } else { + result += 'dni'; + } + return result; + case 'MM': + if (number === 1) { + result += 'mesec'; + } else if (number === 2) { + result += 'meseca'; + } else if (number === 3 || number === 4) { + result += 'mesece'; + } else { + result += 'mesecev'; + } + return result; + case 'yy': + if (number === 1) { + result += 'leto'; + } else if (number === 2) { + result += 'leti'; + } else if (number === 3 || number === 4) { + result += 'leta'; + } else { + result += 'let'; + } + return result; + } + } - /** - * variable to store the setTimeout call - * - * @type {null|number} - */ - _reset_timer, + return moment.lang('sl', { + months : "januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"), + monthsShort : "jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"), + weekdays : "nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"), + weekdaysShort : "ned._pon._tor._sre._čet._pet._sob.".split("_"), + weekdaysMin : "ne_po_to_sr_če_pe_so".split("_"), + longDateFormat : { + LT : "H:mm", + L : "DD. MM. YYYY", + LL : "D. MMMM YYYY", + LLL : "D. MMMM YYYY LT", + LLLL : "dddd, D. MMMM YYYY LT" + }, + calendar : { + sameDay : '[danes ob] LT', + nextDay : '[jutri ob] LT', + + nextWeek : function () { + switch (this.day()) { + case 0: + return '[v] [nedeljo] [ob] LT'; + case 3: + return '[v] [sredo] [ob] LT'; + case 6: + return '[v] [soboto] [ob] LT'; + case 1: + case 2: + case 4: + case 5: + return '[v] dddd [ob] LT'; + } + }, + lastDay : '[včeraj ob] LT', + lastWeek : function () { + switch (this.day()) { + case 0: + case 3: + case 6: + return '[prejšnja] dddd [ob] LT'; + case 1: + case 2: + case 4: + case 5: + return '[prejšnji] dddd [ob] LT'; + } + }, + sameElse : 'L' + }, + relativeTime : { + future : "čez %s", + past : "%s nazaj", + s : "nekaj sekund", + m : translate, + mm : translate, + h : translate, + hh : translate, + d : "en dan", + dd : translate, + M : "en mesec", + MM : translate, + y : "eno leto", + yy : translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - /** - * temporary state where we will ignore the next keyup - * - * @type {boolean|string} - */ - _ignore_next_keyup = false, - /** - * are we currently inside of a sequence? - * type of action ("keyup" or "keydown" or "keypress") or false - * - * @type {boolean|string} - */ - _inside_sequence = false; +/***/ }, +/* 115 */ +/***/ function(module, exports, __webpack_require__) { - /** - * loop through the f keys, f1 to f19 and add them to the map - * programatically - */ - for (var i = 1; i < 20; ++i) { - _MAP[111 + i] = 'f' + i; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Albanian (sq) + // author : Flakërim Ismani : https://github.com/flakerimi + // author: Menelion Elensúle: https://github.com/Oire (tests) + // author : Oerd Cukalla : https://github.com/oerd (fixes) - /** - * loop through to map numbers on the numeric keypad - */ - for (i = 0; i <= 9; ++i) { - _MAP[i + 96] = i; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('sq', { + months : "Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor".split("_"), + monthsShort : "Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj".split("_"), + weekdays : "E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë".split("_"), + weekdaysShort : "Die_Hën_Mar_Mër_Enj_Pre_Sht".split("_"), + weekdaysMin : "D_H_Ma_Më_E_P_Sh".split("_"), + meridiem : function (hours, minutes, isLower) { + return hours < 12 ? 'PD' : 'MD'; + }, + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay : '[Sot në] LT', + nextDay : '[Nesër në] LT', + nextWeek : 'dddd [në] LT', + lastDay : '[Dje në] LT', + lastWeek : 'dddd [e kaluar në] LT', + sameElse : 'L' + }, + relativeTime : { + future : "në %s", + past : "%s më parë", + s : "disa sekonda", + m : "një minutë", + mm : "%d minuta", + h : "një orë", + hh : "%d orë", + d : "një ditë", + dd : "%d ditë", + M : "një muaj", + MM : "%d muaj", + y : "një vit", + yy : "%d vite" + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - /** - * cross browser add event method - * - * @param {Element|HTMLDocument} object - * @param {string} type - * @param {Function} callback - * @returns void - */ - function _addEvent(object, type, callback) { - if (object.addEventListener) { - return object.addEventListener(type, callback, false); - } - object.attachEvent('on' + type, callback); - } +/***/ }, +/* 116 */ +/***/ function(module, exports, __webpack_require__) { - /** - * takes the event and returns the key character - * - * @param {Event} e - * @return {string} - */ - function _characterFromEvent(e) { + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Serbian-cyrillic (sr-cyrl) + // author : Milan Janačković : https://github.com/milan-j - // for keypress events we should return the character as is - if (e.type == 'keypress') { - return String.fromCharCode(e.which); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + + var translator = { + words: { //Different grammatical cases + m: ['један минут', 'једне минуте'], + mm: ['минут', 'минуте', 'минута'], + h: ['један сат', 'једног сата'], + hh: ['сат', 'сата', 'сати'], + dd: ['дан', 'дана', 'дана'], + MM: ['месец', 'месеца', 'месеци'], + yy: ['година', 'године', 'година'] + }, + correctGrammaticalCase: function (number, wordKey) { + return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); + }, + translate: function (number, withoutSuffix, key) { + var wordKey = translator.words[key]; + if (key.length === 1) { + return withoutSuffix ? wordKey[0] : wordKey[1]; + } else { + return number + ' ' + translator.correctGrammaticalCase(number, wordKey); + } + } + }; - // for non keypress events the special maps are needed - if (_MAP[e.which]) { - return _MAP[e.which]; - } + return moment.lang('sr-cyrl', { + months: ['јануар', 'фебруар', 'март', 'април', 'мај', 'јун', 'јул', 'август', 'септембар', 'октобар', 'новембар', 'децембар'], + monthsShort: ['јан.', 'феб.', 'мар.', 'апр.', 'мај', 'јун', 'јул', 'авг.', 'сеп.', 'окт.', 'нов.', 'дец.'], + weekdays: ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'], + weekdaysShort: ['нед.', 'пон.', 'уто.', 'сре.', 'чет.', 'пет.', 'суб.'], + weekdaysMin: ['не', 'по', 'ут', 'ср', 'че', 'пе', 'су'], + longDateFormat: { + LT: "H:mm", + L: "DD. MM. YYYY", + LL: "D. MMMM YYYY", + LLL: "D. MMMM YYYY LT", + LLLL: "dddd, D. MMMM YYYY LT" + }, + calendar: { + sameDay: '[данас у] LT', + nextDay: '[сутра у] LT', + + nextWeek: function () { + switch (this.day()) { + case 0: + return '[у] [недељу] [у] LT'; + case 3: + return '[у] [среду] [у] LT'; + case 6: + return '[у] [суботу] [у] LT'; + case 1: + case 2: + case 4: + case 5: + return '[у] dddd [у] LT'; + } + }, + lastDay : '[јуче у] LT', + lastWeek : function () { + var lastWeekDays = [ + '[прошле] [недеље] [у] LT', + '[прошлог] [понедељка] [у] LT', + '[прошлог] [уторка] [у] LT', + '[прошле] [среде] [у] LT', + '[прошлог] [четвртка] [у] LT', + '[прошлог] [петка] [у] LT', + '[прошле] [суботе] [у] LT' + ]; + return lastWeekDays[this.day()]; + }, + sameElse : 'L' + }, + relativeTime : { + future : "за %s", + past : "пре %s", + s : "неколико секунди", + m : translator.translate, + mm : translator.translate, + h : translator.translate, + hh : translator.translate, + d : "дан", + dd : translator.translate, + M : "месец", + MM : translator.translate, + y : "годину", + yy : translator.translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - if (_KEYCODE_MAP[e.which]) { - return _KEYCODE_MAP[e.which]; - } - // if it is not in the special map - return String.fromCharCode(e.which).toLowerCase(); - } +/***/ }, +/* 117 */ +/***/ function(module, exports, __webpack_require__) { - /** - * should we stop this event before firing off callbacks - * - * @param {Event} e - * @return {boolean} - */ - function _stop(e) { - var element = e.target || e.srcElement, - tag_name = element.tagName; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Serbian-latin (sr) + // author : Milan Janačković : https://github.com/milan-j - // if the element has the class "mousetrap" then no need to stop - if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) { - return false; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + + var translator = { + words: { //Different grammatical cases + m: ['jedan minut', 'jedne minute'], + mm: ['minut', 'minute', 'minuta'], + h: ['jedan sat', 'jednog sata'], + hh: ['sat', 'sata', 'sati'], + dd: ['dan', 'dana', 'dana'], + MM: ['mesec', 'meseca', 'meseci'], + yy: ['godina', 'godine', 'godina'] + }, + correctGrammaticalCase: function (number, wordKey) { + return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); + }, + translate: function (number, withoutSuffix, key) { + var wordKey = translator.words[key]; + if (key.length === 1) { + return withoutSuffix ? wordKey[0] : wordKey[1]; + } else { + return number + ' ' + translator.correctGrammaticalCase(number, wordKey); + } + } + }; - // stop for input, select, and textarea - return tag_name == 'INPUT' || tag_name == 'SELECT' || tag_name == 'TEXTAREA' || (element.contentEditable && element.contentEditable == 'true'); - } + return moment.lang('sr', { + months: ['januar', 'februar', 'mart', 'april', 'maj', 'jun', 'jul', 'avgust', 'septembar', 'oktobar', 'novembar', 'decembar'], + monthsShort: ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun', 'jul', 'avg.', 'sep.', 'okt.', 'nov.', 'dec.'], + weekdays: ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak', 'petak', 'subota'], + weekdaysShort: ['ned.', 'pon.', 'uto.', 'sre.', 'čet.', 'pet.', 'sub.'], + weekdaysMin: ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'], + longDateFormat: { + LT: "H:mm", + L: "DD. MM. YYYY", + LL: "D. MMMM YYYY", + LLL: "D. MMMM YYYY LT", + LLLL: "dddd, D. MMMM YYYY LT" + }, + calendar: { + sameDay: '[danas u] LT', + nextDay: '[sutra u] LT', + + nextWeek: function () { + switch (this.day()) { + case 0: + return '[u] [nedelju] [u] LT'; + case 3: + return '[u] [sredu] [u] LT'; + case 6: + return '[u] [subotu] [u] LT'; + case 1: + case 2: + case 4: + case 5: + return '[u] dddd [u] LT'; + } + }, + lastDay : '[juče u] LT', + lastWeek : function () { + var lastWeekDays = [ + '[prošle] [nedelje] [u] LT', + '[prošlog] [ponedeljka] [u] LT', + '[prošlog] [utorka] [u] LT', + '[prošle] [srede] [u] LT', + '[prošlog] [četvrtka] [u] LT', + '[prošlog] [petka] [u] LT', + '[prošle] [subote] [u] LT' + ]; + return lastWeekDays[this.day()]; + }, + sameElse : 'L' + }, + relativeTime : { + future : "za %s", + past : "pre %s", + s : "nekoliko sekundi", + m : translator.translate, + mm : translator.translate, + h : translator.translate, + hh : translator.translate, + d : "dan", + dd : translator.translate, + M : "mesec", + MM : translator.translate, + y : "godinu", + yy : translator.translate + }, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - /** - * checks if two arrays are equal - * - * @param {Array} modifiers1 - * @param {Array} modifiers2 - * @returns {boolean} - */ - function _modifiersMatch(modifiers1, modifiers2) { - return modifiers1.sort().join(',') === modifiers2.sort().join(','); - } - /** - * resets all sequence counters except for the ones passed in - * - * @param {Object} do_not_reset - * @returns void - */ - function _resetSequences(do_not_reset) { - do_not_reset = do_not_reset || {}; +/***/ }, +/* 118 */ +/***/ function(module, exports, __webpack_require__) { - var active_sequences = false, - key; + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : swedish (sv) + // author : Jens Alm : https://github.com/ulmus - for (key in _sequence_levels) { - if (do_not_reset[key]) { - active_sequences = true; - continue; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('sv', { + months : "januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"), + monthsShort : "jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"), + weekdays : "söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"), + weekdaysShort : "sön_mån_tis_ons_tor_fre_lör".split("_"), + weekdaysMin : "sö_må_ti_on_to_fr_lö".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "YYYY-MM-DD", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay: '[Idag] LT', + nextDay: '[Imorgon] LT', + lastDay: '[Igår] LT', + nextWeek: 'dddd LT', + lastWeek: '[Förra] dddd[en] LT', + sameElse: 'L' + }, + relativeTime : { + future : "om %s", + past : "för %s sedan", + s : "några sekunder", + m : "en minut", + mm : "%d minuter", + h : "en timme", + hh : "%d timmar", + d : "en dag", + dd : "%d dagar", + M : "en månad", + MM : "%d månader", + y : "ett år", + yy : "%d år" + }, + ordinal : function (number) { + var b = number % 10, + output = (~~ (number % 100 / 10) === 1) ? 'e' : + (b === 1) ? 'a' : + (b === 2) ? 'a' : + (b === 3) ? 'e' : 'e'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } - _sequence_levels[key] = 0; - } + }); + })); - if (!active_sequences) { - _inside_sequence = false; - } - } - /** - * finds all callbacks that match based on the keycode, modifiers, - * and action - * - * @param {string} character - * @param {Array} modifiers - * @param {string} action - * @param {boolean=} remove - should we remove any matches - * @param {string=} combination - * @returns {Array} - */ - function _getMatches(character, modifiers, action, remove, combination) { - var i, - callback, - matches = []; +/***/ }, +/* 119 */ +/***/ function(module, exports, __webpack_require__) { - // if there are no events related to this keycode - if (!_callbacks[character]) { - return []; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : tamil (ta) + // author : Arjunkumar Krishnamoorthy : https://github.com/tk120404 - // if a modifier key is coming up on its own we should allow it - if (action == 'keyup' && _isModifier(character)) { - modifiers = [character]; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + /*var symbolMap = { + '1': '௧', + '2': '௨', + '3': '௩', + '4': '௪', + '5': '௫', + '6': '௬', + '7': '௭', + '8': '௮', + '9': '௯', + '0': '௦' + }, + numberMap = { + '௧': '1', + '௨': '2', + '௩': '3', + '௪': '4', + '௫': '5', + '௬': '6', + '௭': '7', + '௮': '8', + '௯': '9', + '௦': '0' + }; */ + + return moment.lang('ta', { + months : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split("_"), + monthsShort : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split("_"), + weekdays : 'ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை'.split("_"), + weekdaysShort : 'ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி'.split("_"), + weekdaysMin : 'ஞா_தி_செ_பு_வி_வெ_ச'.split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY, LT", + LLLL : "dddd, D MMMM YYYY, LT" + }, + calendar : { + sameDay : '[இன்று] LT', + nextDay : '[நாளை] LT', + nextWeek : 'dddd, LT', + lastDay : '[நேற்று] LT', + lastWeek : '[கடந்த வாரம்] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s இல்", + past : "%s முன்", + s : "ஒரு சில விநாடிகள்", + m : "ஒரு நிமிடம்", + mm : "%d நிமிடங்கள்", + h : "ஒரு மணி நேரம்", + hh : "%d மணி நேரம்", + d : "ஒரு நாள்", + dd : "%d நாட்கள்", + M : "ஒரு மாதம்", + MM : "%d மாதங்கள்", + y : "ஒரு வருடம்", + yy : "%d ஆண்டுகள்" + }, + /* preparse: function (string) { + return string.replace(/[௧௨௩௪௫௬௭௮௯௦]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + },*/ + ordinal : function (number) { + return number + 'வது'; + }, - // loop through all callbacks for the key that was pressed - // and see if any of them match - for (i = 0; i < _callbacks[character].length; ++i) { - callback = _callbacks[character][i]; - // if this is a sequence but it is not at the right level - // then move onto the next match - if (callback.seq && _sequence_levels[callback.seq] != callback.level) { - continue; + // refer http://ta.wikipedia.org/s/1er1 + + meridiem : function (hour, minute, isLower) { + if (hour >= 6 && hour <= 10) { + return " காலை"; + } else if (hour >= 10 && hour <= 14) { + return " நண்பகல்"; + } else if (hour >= 14 && hour <= 18) { + return " எற்பாடு"; + } else if (hour >= 18 && hour <= 20) { + return " மாலை"; + } else if (hour >= 20 && hour <= 24) { + return " இரவு"; + } else if (hour >= 0 && hour <= 6) { + return " வைகறை"; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. } + }); + })); - // if the action we are looking for doesn't match the action we got - // then we should keep going - if (action != callback.action) { - continue; - } - // if this is a keypress event that means that we need to only - // look at the character, otherwise check the modifiers as - // well - if (action == 'keypress' || _modifiersMatch(modifiers, callback.modifiers)) { +/***/ }, +/* 120 */ +/***/ function(module, exports, __webpack_require__) { - // remove is used so if you change your mind and call bind a - // second time with a new function the first one is overwritten - if (remove && callback.combo == combination) { - _callbacks[character].splice(i, 1); - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : thai (th) + // author : Kridsada Thanabulpong : https://github.com/sirn - matches.push(callback); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('th', { + months : "มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"), + monthsShort : "มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"), + weekdays : "อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"), + weekdaysShort : "อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"), // yes, three characters difference + weekdaysMin : "อา._จ._อ._พ._พฤ._ศ._ส.".split("_"), + longDateFormat : { + LT : "H นาฬิกา m นาที", + L : "YYYY/MM/DD", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY เวลา LT", + LLLL : "วันddddที่ D MMMM YYYY เวลา LT" + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return "ก่อนเที่ยง"; + } else { + return "หลังเที่ยง"; + } + }, + calendar : { + sameDay : '[วันนี้ เวลา] LT', + nextDay : '[พรุ่งนี้ เวลา] LT', + nextWeek : 'dddd[หน้า เวลา] LT', + lastDay : '[เมื่อวานนี้ เวลา] LT', + lastWeek : '[วัน]dddd[ที่แล้ว เวลา] LT', + sameElse : 'L' + }, + relativeTime : { + future : "อีก %s", + past : "%sที่แล้ว", + s : "ไม่กี่วินาที", + m : "1 นาที", + mm : "%d นาที", + h : "1 ชั่วโมง", + hh : "%d ชั่วโมง", + d : "1 วัน", + dd : "%d วัน", + M : "1 เดือน", + MM : "%d เดือน", + y : "1 ปี", + yy : "%d ปี" } - } + }); + })); - return matches; - } - /** - * takes a key event and figures out what the modifiers are - * - * @param {Event} e - * @returns {Array} - */ - function _eventModifiers(e) { - var modifiers = []; +/***/ }, +/* 121 */ +/***/ function(module, exports, __webpack_require__) { - if (e.shiftKey) { - modifiers.push('shift'); - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Tagalog/Filipino (tl-ph) + // author : Dan Hagman - if (e.altKey) { - modifiers.push('alt'); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('tl-ph', { + months : "Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"), + monthsShort : "Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"), + weekdays : "Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"), + weekdaysShort : "Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"), + weekdaysMin : "Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "MM/D/YYYY", + LL : "MMMM D, YYYY", + LLL : "MMMM D, YYYY LT", + LLLL : "dddd, MMMM DD, YYYY LT" + }, + calendar : { + sameDay: "[Ngayon sa] LT", + nextDay: '[Bukas sa] LT', + nextWeek: 'dddd [sa] LT', + lastDay: '[Kahapon sa] LT', + lastWeek: 'dddd [huling linggo] LT', + sameElse: 'L' + }, + relativeTime : { + future : "sa loob ng %s", + past : "%s ang nakalipas", + s : "ilang segundo", + m : "isang minuto", + mm : "%d minuto", + h : "isang oras", + hh : "%d oras", + d : "isang araw", + dd : "%d araw", + M : "isang buwan", + MM : "%d buwan", + y : "isang taon", + yy : "%d taon" + }, + ordinal : function (number) { + return number; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - if (e.ctrlKey) { - modifiers.push('ctrl'); - } - if (e.metaKey) { - modifiers.push('meta'); - } +/***/ }, +/* 122 */ +/***/ function(module, exports, __webpack_require__) { - return modifiers; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : turkish (tr) + // authors : Erhan Gundogan : https://github.com/erhangundogan, + // Burak Yiğit Kaya: https://github.com/BYK - /** - * actually calls the callback function - * - * if your callback function returns false this will use the jquery - * convention - prevent default and stop propogation on the event - * - * @param {Function} callback - * @param {Event} e - * @returns void - */ - function _fireCallback(callback, e) { - if (callback(e) === false) { - if (e.preventDefault) { - e.preventDefault(); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { - if (e.stopPropagation) { - e.stopPropagation(); - } + var suffixes = { + 1: "'inci", + 5: "'inci", + 8: "'inci", + 70: "'inci", + 80: "'inci", - e.returnValue = false; - e.cancelBubble = true; - } - } + 2: "'nci", + 7: "'nci", + 20: "'nci", + 50: "'nci", - /** - * handles a character key event - * - * @param {string} character - * @param {Event} e - * @returns void - */ - function _handleCharacter(character, e) { + 3: "'üncü", + 4: "'üncü", + 100: "'üncü", - // if this event should not happen stop here - if (_stop(e)) { - return; - } + 6: "'ncı", - var callbacks = _getMatches(character, _eventModifiers(e), e.type), - i, - do_not_reset = {}, - processed_sequence_callback = false; + 9: "'uncu", + 10: "'uncu", + 30: "'uncu", - // loop through matching callbacks for this key event - for (i = 0; i < callbacks.length; ++i) { + 60: "'ıncı", + 90: "'ıncı" + }; - // fire for all sequence callbacks - // this is because if for example you have multiple sequences - // bound such as "g i" and "g t" they both need to fire the - // callback for matching g cause otherwise you can only ever - // match the first one - if (callbacks[i].seq) { - processed_sequence_callback = true; + return moment.lang('tr', { + months : "Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"), + monthsShort : "Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"), + weekdays : "Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"), + weekdaysShort : "Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"), + weekdaysMin : "Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd, D MMMM YYYY LT" + }, + calendar : { + sameDay : '[bugün saat] LT', + nextDay : '[yarın saat] LT', + nextWeek : '[haftaya] dddd [saat] LT', + lastDay : '[dün] LT', + lastWeek : '[geçen hafta] dddd [saat] LT', + sameElse : 'L' + }, + relativeTime : { + future : "%s sonra", + past : "%s önce", + s : "birkaç saniye", + m : "bir dakika", + mm : "%d dakika", + h : "bir saat", + hh : "%d saat", + d : "bir gün", + dd : "%d gün", + M : "bir ay", + MM : "%d ay", + y : "bir yıl", + yy : "%d yıl" + }, + ordinal : function (number) { + if (number === 0) { // special case for zero + return number + "'ıncı"; + } + var a = number % 10, + b = number % 100 - a, + c = number >= 100 ? 100 : null; - // keep a list of which sequences were matches for later - do_not_reset[callbacks[i].seq] = 1; - _fireCallback(callbacks[i].callback, e); - continue; + return number + (suffixes[a] || suffixes[b] || suffixes[c]); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. } + }); + })); - // if there were no sequence matches but we are still here - // that means this is a regular match so we should fire that - if (!processed_sequence_callback && !_inside_sequence) { - _fireCallback(callbacks[i].callback, e); - } - } - // if you are inside of a sequence and the key you are pressing - // is not a modifier key then we should reset all sequences - // that were not matched by this key event - if (e.type == _inside_sequence && !_isModifier(character)) { - _resetSequences(do_not_reset); - } - } +/***/ }, +/* 123 */ +/***/ function(module, exports, __webpack_require__) { - /** - * handles a keydown event - * - * @param {Event} e - * @returns void - */ - function _handleKey(e) { + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Morocco Central Atlas Tamaziɣt in Latin (tzm-latn) + // author : Abdel Said : https://github.com/abdelsaid - // normalize e.which for key events - // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion - e.which = typeof e.which == "number" ? e.which : e.keyCode; + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('tzm-latn', { + months : "innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"), + monthsShort : "innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"), + weekdays : "asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"), + weekdaysShort : "asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"), + weekdaysMin : "asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay: "[asdkh g] LT", + nextDay: '[aska g] LT', + nextWeek: 'dddd [g] LT', + lastDay: '[assant g] LT', + lastWeek: 'dddd [g] LT', + sameElse: 'L' + }, + relativeTime : { + future : "dadkh s yan %s", + past : "yan %s", + s : "imik", + m : "minuḍ", + mm : "%d minuḍ", + h : "saɛa", + hh : "%d tassaɛin", + d : "ass", + dd : "%d ossan", + M : "ayowr", + MM : "%d iyyirn", + y : "asgas", + yy : "%d isgasn" + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - var character = _characterFromEvent(e); - // no character found then stop - if (!character) { - return; - } +/***/ }, +/* 124 */ +/***/ function(module, exports, __webpack_require__) { - if (e.type == 'keyup' && _ignore_next_keyup == character) { - _ignore_next_keyup = false; - return; - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : Morocco Central Atlas Tamaziɣt (tzm) + // author : Abdel Said : https://github.com/abdelsaid - _handleCharacter(character, e); - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('tzm', { + months : "ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"), + monthsShort : "ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"), + weekdays : "ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"), + weekdaysShort : "ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"), + weekdaysMin : "ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "dddd D MMMM YYYY LT" + }, + calendar : { + sameDay: "[ⴰⵙⴷⵅ ⴴ] LT", + nextDay: '[ⴰⵙⴽⴰ ⴴ] LT', + nextWeek: 'dddd [ⴴ] LT', + lastDay: '[ⴰⵚⴰⵏⵜ ⴴ] LT', + lastWeek: 'dddd [ⴴ] LT', + sameElse: 'L' + }, + relativeTime : { + future : "ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s", + past : "ⵢⴰⵏ %s", + s : "ⵉⵎⵉⴽ", + m : "ⵎⵉⵏⵓⴺ", + mm : "%d ⵎⵉⵏⵓⴺ", + h : "ⵙⴰⵄⴰ", + hh : "%d ⵜⴰⵙⵙⴰⵄⵉⵏ", + d : "ⴰⵙⵙ", + dd : "%d oⵙⵙⴰⵏ", + M : "ⴰⵢoⵓⵔ", + MM : "%d ⵉⵢⵢⵉⵔⵏ", + y : "ⴰⵙⴳⴰⵙ", + yy : "%d ⵉⵙⴳⴰⵙⵏ" + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - /** - * determines if the keycode specified is a modifier key or not - * - * @param {string} key - * @returns {boolean} - */ - function _isModifier(key) { - return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta'; - } - /** - * called to set a 1 second timeout on the specified sequence - * - * this is so after each key press in the sequence you have 1 second - * to press the next key before you have to start over - * - * @returns void - */ - function _resetSequenceTimer() { - clearTimeout(_reset_timer); - _reset_timer = setTimeout(_resetSequences, 1000); - } +/***/ }, +/* 125 */ +/***/ function(module, exports, __webpack_require__) { - /** - * reverses the map lookup so that we can look for specific keys - * to see what can and can't use keypress - * - * @return {Object} - */ - function _getReverseMap() { - if (!_REVERSE_MAP) { - _REVERSE_MAP = {}; - for (var key in _MAP) { + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : ukrainian (uk) + // author : zemlanin : https://github.com/zemlanin + // Author : Menelion Elensúle : https://github.com/Oire - // pull out the numeric keypad from here cause keypress should - // be able to detect the keys from the character - if (key > 95 && key < 112) { - continue; - } + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + function plural(word, num) { + var forms = word.split('_'); + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); + } - if (_MAP.hasOwnProperty(key)) { - _REVERSE_MAP[_MAP[key]] = key; - } + function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': 'хвилина_хвилини_хвилин', + 'hh': 'година_години_годин', + 'dd': 'день_дні_днів', + 'MM': 'місяць_місяці_місяців', + 'yy': 'рік_роки_років' + }; + if (key === 'm') { + return withoutSuffix ? 'хвилина' : 'хвилину'; + } + else if (key === 'h') { + return withoutSuffix ? 'година' : 'годину'; + } + else { + return number + ' ' + plural(format[key], +number); } } - return _REVERSE_MAP; - } - /** - * picks the best action based on the key combination - * - * @param {string} key - character for key - * @param {Array} modifiers - * @param {string=} action passed in - */ - function _pickBestAction(key, modifiers, action) { + function monthsCaseReplace(m, format) { + var months = { + 'nominative': 'січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень'.split('_'), + 'accusative': 'січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня'.split('_') + }, - // if no action was picked in we should try to pick the one - // that we think would work best for this key - if (!action) { - action = _getReverseMap()[key] ? 'keydown' : 'keypress'; - } + nounCase = (/D[oD]? *MMMM?/).test(format) ? + 'accusative' : + 'nominative'; - // modifier keys don't work as expected with keypress, - // switch to keydown - if (action == 'keypress' && modifiers.length) { - action = 'keydown'; + return months[nounCase][m.month()]; } - return action; - } + function weekdaysCaseReplace(m, format) { + var weekdays = { + 'nominative': 'неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота'.split('_'), + 'accusative': 'неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу'.split('_'), + 'genitive': 'неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи'.split('_') + }, - /** - * binds a key sequence to an event - * - * @param {string} combo - combo specified in bind call - * @param {Array} keys - * @param {Function} callback - * @param {string=} action - * @returns void - */ - function _bindSequence(combo, keys, callback, action) { + nounCase = (/(\[[ВвУу]\]) ?dddd/).test(format) ? + 'accusative' : + ((/\[?(?:минулої|наступної)? ?\] ?dddd/).test(format) ? + 'genitive' : + 'nominative'); - // start off by adding a sequence level record for this combination - // and setting the level to 0 - _sequence_levels[combo] = 0; + return weekdays[nounCase][m.day()]; + } - // if there is no action pick the best one for the first key - // in the sequence - if (!action) { - action = _pickBestAction(keys[0], []); + function processHoursFunction(str) { + return function () { + return str + 'о' + (this.hours() === 11 ? 'б' : '') + '] LT'; + }; } - /** - * callback to increase the sequence level for this sequence and reset - * all other sequences that were active - * - * @param {Event} e - * @returns void - */ - var _increaseSequence = function(e) { - _inside_sequence = action; - ++_sequence_levels[combo]; - _resetSequenceTimer(); - }, - - /** - * wraps the specified callback inside of another function in order - * to reset all sequence counters as soon as this sequence is done - * - * @param {Event} e - * @returns void - */ - _callbackAndReset = function(e) { - _fireCallback(callback, e); - - // we should ignore the next key up if the action is key down - // or keypress. this is so if you finish a sequence and - // release the key the final key will not trigger a keyup - if (action !== 'keyup') { - _ignore_next_keyup = _characterFromEvent(e); - } + return moment.lang('uk', { + months : monthsCaseReplace, + monthsShort : "січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"), + weekdays : weekdaysCaseReplace, + weekdaysShort : "нд_пн_вт_ср_чт_пт_сб".split("_"), + weekdaysMin : "нд_пн_вт_ср_чт_пт_сб".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY р.", + LLL : "D MMMM YYYY р., LT", + LLLL : "dddd, D MMMM YYYY р., LT" + }, + calendar : { + sameDay: processHoursFunction('[Сьогодні '), + nextDay: processHoursFunction('[Завтра '), + lastDay: processHoursFunction('[Вчора '), + nextWeek: processHoursFunction('[У] dddd ['), + lastWeek: function () { + switch (this.day()) { + case 0: + case 3: + case 5: + case 6: + return processHoursFunction('[Минулої] dddd [').call(this); + case 1: + case 2: + case 4: + return processHoursFunction('[Минулого] dddd [').call(this); + } + }, + sameElse: 'L' + }, + relativeTime : { + future : "за %s", + past : "%s тому", + s : "декілька секунд", + m : relativeTimeWithPlural, + mm : relativeTimeWithPlural, + h : "годину", + hh : relativeTimeWithPlural, + d : "день", + dd : relativeTimeWithPlural, + M : "місяць", + MM : relativeTimeWithPlural, + y : "рік", + yy : relativeTimeWithPlural + }, - // weird race condition if a sequence ends with the key - // another sequence begins with - setTimeout(_resetSequences, 10); + // M. E.: those two are virtually unused but a user might want to implement them for his/her website for some reason + + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return "ночі"; + } else if (hour < 12) { + return "ранку"; + } else if (hour < 17) { + return "дня"; + } else { + return "вечора"; + } }, - i; - // loop through keys one at a time and bind the appropriate callback - // function. for any key leading up to the final one it should - // increase the sequence. after the final, it should reset all sequences - for (i = 0; i < keys.length; ++i) { - _bindSingle(keys[i], i < keys.length - 1 ? _increaseSequence : _callbackAndReset, action, combo, i); - } - } + ordinal: function (number, period) { + switch (period) { + case 'M': + case 'd': + case 'DDD': + case 'w': + case 'W': + return number + '-й'; + case 'D': + return number + '-го'; + default: + return number; + } + }, - /** - * binds a single keyboard combination - * - * @param {string} combination - * @param {Function} callback - * @param {string=} action - * @param {string=} sequence_name - name of sequence if part of sequence - * @param {number=} level - what part of the sequence the command is - * @returns void - */ - function _bindSingle(combination, callback, action, sequence_name, level) { + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } + }); + })); - // make sure multiple spaces in a row become a single space - combination = combination.replace(/\s+/g, ' '); - var sequence = combination.split(' '), - i, - key, - keys, - modifiers = []; +/***/ }, +/* 126 */ +/***/ function(module, exports, __webpack_require__) { - // if this pattern is a sequence of keys then run through this method - // to reprocess each pattern one key at a time - if (sequence.length > 1) { - return _bindSequence(combination, sequence, callback, action); - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : uzbek + // author : Sardor Muminov : https://github.com/muminoff - // take the keys from this pattern and figure out what the actual - // pattern is all about - keys = combination === '+' ? ['+'] : combination.split('+'); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('uz', { + months : "январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"), + monthsShort : "янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"), + weekdays : "Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба".split("_"), + weekdaysShort : "Якш_Душ_Сеш_Чор_Пай_Жум_Шан".split("_"), + weekdaysMin : "Як_Ду_Се_Чо_Па_Жу_Ша".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM YYYY", + LLL : "D MMMM YYYY LT", + LLLL : "D MMMM YYYY, dddd LT" + }, + calendar : { + sameDay : '[Бугун соат] LT [да]', + nextDay : '[Эртага] LT [да]', + nextWeek : 'dddd [куни соат] LT [да]', + lastDay : '[Кеча соат] LT [да]', + lastWeek : '[Утган] dddd [куни соат] LT [да]', + sameElse : 'L' + }, + relativeTime : { + future : "Якин %s ичида", + past : "Бир неча %s олдин", + s : "фурсат", + m : "бир дакика", + mm : "%d дакика", + h : "бир соат", + hh : "%d соат", + d : "бир кун", + dd : "%d кун", + M : "бир ой", + MM : "%d ой", + y : "бир йил", + yy : "%d йил" + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 4th is the first week of the year. + } + }); + })); - for (i = 0; i < keys.length; ++i) { - key = keys[i]; - // normalize key names - if (_SPECIAL_ALIASES[key]) { - key = _SPECIAL_ALIASES[key]; - } +/***/ }, +/* 127 */ +/***/ function(module, exports, __webpack_require__) { - // if this is not a keypress event then we should - // be smart about using shift keys - // this will only work for US keyboards however - if (action && action != 'keypress' && _SHIFT_MAP[key]) { - key = _SHIFT_MAP[key]; - modifiers.push('shift'); - } + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : vietnamese (vi) + // author : Bang Nguyen : https://github.com/bangnk - // if this key is a modifier then add it to the list of modifiers - if (_isModifier(key)) { - modifiers.push(key); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('vi', { + months : "tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"), + monthsShort : "Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12".split("_"), + weekdays : "chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"), + weekdaysShort : "CN_T2_T3_T4_T5_T6_T7".split("_"), + weekdaysMin : "CN_T2_T3_T4_T5_T6_T7".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD/MM/YYYY", + LL : "D MMMM [năm] YYYY", + LLL : "D MMMM [năm] YYYY LT", + LLLL : "dddd, D MMMM [năm] YYYY LT", + l : "DD/M/YYYY", + ll : "D MMM YYYY", + lll : "D MMM YYYY LT", + llll : "ddd, D MMM YYYY LT" + }, + calendar : { + sameDay: "[Hôm nay lúc] LT", + nextDay: '[Ngày mai lúc] LT', + nextWeek: 'dddd [tuần tới lúc] LT', + lastDay: '[Hôm qua lúc] LT', + lastWeek: 'dddd [tuần rồi lúc] LT', + sameElse: 'L' + }, + relativeTime : { + future : "%s tới", + past : "%s trước", + s : "vài giây", + m : "một phút", + mm : "%d phút", + h : "một giờ", + hh : "%d giờ", + d : "một ngày", + dd : "%d ngày", + M : "một tháng", + MM : "%d tháng", + y : "một năm", + yy : "%d năm" + }, + ordinal : function (number) { + return number; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } - } + }); + })); - // depending on what the key combination is - // we will try to pick the best event for it - action = _pickBestAction(key, modifiers, action); - // make sure to initialize array if this is the first time - // a callback is added for this key - if (!_callbacks[key]) { - _callbacks[key] = []; - } +/***/ }, +/* 128 */ +/***/ function(module, exports, __webpack_require__) { - // remove an existing match if there is one - _getMatches(key, modifiers, action, !sequence_name, combination); + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : chinese + // author : suupic : https://github.com/suupic + // author : Zeno Zeng : https://github.com/zenozeng - // add this call back to the array - // if it is a sequence put it at the beginning - // if not put it at the end - // - // this is important because the way these are processed expects - // the sequence ones to come first - _callbacks[key][sequence_name ? 'unshift' : 'push']({ - callback: callback, - modifiers: modifiers, - action: action, - seq: sequence_name, - level: level, - combo: combination + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('zh-cn', { + months : "一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"), + monthsShort : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"), + weekdays : "星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"), + weekdaysShort : "周日_周一_周二_周三_周四_周五_周六".split("_"), + weekdaysMin : "日_一_二_三_四_五_六".split("_"), + longDateFormat : { + LT : "Ah点mm", + L : "YYYY-MM-DD", + LL : "YYYY年MMMD日", + LLL : "YYYY年MMMD日LT", + LLLL : "YYYY年MMMD日ddddLT", + l : "YYYY-MM-DD", + ll : "YYYY年MMMD日", + lll : "YYYY年MMMD日LT", + llll : "YYYY年MMMD日ddddLT" + }, + meridiem : function (hour, minute, isLower) { + var hm = hour * 100 + minute; + if (hm < 600) { + return "凌晨"; + } else if (hm < 900) { + return "早上"; + } else if (hm < 1130) { + return "上午"; + } else if (hm < 1230) { + return "中午"; + } else if (hm < 1800) { + return "下午"; + } else { + return "晚上"; + } + }, + calendar : { + sameDay : function () { + return this.minutes() === 0 ? "[今天]Ah[点整]" : "[今天]LT"; + }, + nextDay : function () { + return this.minutes() === 0 ? "[明天]Ah[点整]" : "[明天]LT"; + }, + lastDay : function () { + return this.minutes() === 0 ? "[昨天]Ah[点整]" : "[昨天]LT"; + }, + nextWeek : function () { + var startOfWeek, prefix; + startOfWeek = moment().startOf('week'); + prefix = this.unix() - startOfWeek.unix() >= 7 * 24 * 3600 ? '[下]' : '[本]'; + return this.minutes() === 0 ? prefix + "dddAh点整" : prefix + "dddAh点mm"; + }, + lastWeek : function () { + var startOfWeek, prefix; + startOfWeek = moment().startOf('week'); + prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]'; + return this.minutes() === 0 ? prefix + "dddAh点整" : prefix + "dddAh点mm"; + }, + sameElse : 'LL' + }, + ordinal : function (number, period) { + switch (period) { + case "d": + case "D": + case "DDD": + return number + "日"; + case "M": + return number + "月"; + case "w": + case "W": + return number + "周"; + default: + return number; + } + }, + relativeTime : { + future : "%s内", + past : "%s前", + s : "几秒", + m : "1分钟", + mm : "%d分钟", + h : "1小时", + hh : "%d小时", + d : "1天", + dd : "%d天", + M : "1个月", + MM : "%d个月", + y : "1年", + yy : "%d年" + }, + week : { + // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } }); - } - - /** - * binds multiple combinations to the same callback - * - * @param {Array} combinations - * @param {Function} callback - * @param {string|undefined} action - * @returns void - */ - function _bindMultiple(combinations, callback, action) { - for (var i = 0; i < combinations.length; ++i) { - _bindSingle(combinations[i], callback, action); - } - } + })); - // start! - _addEvent(document, 'keypress', _handleKey); - _addEvent(document, 'keydown', _handleKey); - _addEvent(document, 'keyup', _handleKey); - var mousetrap = { +/***/ }, +/* 129 */ +/***/ function(module, exports, __webpack_require__) { - /** - * binds an event to mousetrap - * - * can be a single key, a combination of keys separated with +, - * a comma separated list of keys, an array of keys, or - * a sequence of keys separated by spaces - * - * be sure to list the modifier keys first to make sure that the - * correct key ends up getting bound (the last key in the pattern) - * - * @param {string|Array} keys - * @param {Function} callback - * @param {string=} action - 'keypress', 'keydown', or 'keyup' - * @returns void - */ - bind: function(keys, callback, action) { - _bindMultiple(keys instanceof Array ? keys : [keys], callback, action); - _direct_map[keys + ':' + action] = callback; - return this; - }, + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// moment.js language configuration + // language : traditional chinese (zh-tw) + // author : Ben : https://github.com/ben-lin - /** - * unbinds an event to mousetrap - * - * the unbinding sets the callback function of the specified key combo - * to an empty function and deletes the corresponding key in the - * _direct_map dict. - * - * the keycombo+action has to be exactly the same as - * it was defined in the bind method - * - * TODO: actually remove this from the _callbacks dictionary instead - * of binding an empty function - * - * @param {string|Array} keys - * @param {string} action - * @returns void - */ - unbind: function(keys, action) { - if (_direct_map[keys + ':' + action]) { - delete _direct_map[keys + ':' + action]; - this.bind(keys, function() {}, action); + (function (factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(51)], __WEBPACK_AMD_DEFINE_RESULT__ = (factory.apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // AMD + } else if (typeof exports === 'object') { + module.exports = factory(require('../moment')); // Node + } else { + factory(window.moment); // Browser global + } + }(function (moment) { + return moment.lang('zh-tw', { + months : "一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"), + monthsShort : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"), + weekdays : "星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"), + weekdaysShort : "週日_週一_週二_週三_週四_週五_週六".split("_"), + weekdaysMin : "日_一_二_三_四_五_六".split("_"), + longDateFormat : { + LT : "Ah點mm", + L : "YYYY年MMMD日", + LL : "YYYY年MMMD日", + LLL : "YYYY年MMMD日LT", + LLLL : "YYYY年MMMD日ddddLT", + l : "YYYY年MMMD日", + ll : "YYYY年MMMD日", + lll : "YYYY年MMMD日LT", + llll : "YYYY年MMMD日ddddLT" + }, + meridiem : function (hour, minute, isLower) { + var hm = hour * 100 + minute; + if (hm < 900) { + return "早上"; + } else if (hm < 1130) { + return "上午"; + } else if (hm < 1230) { + return "中午"; + } else if (hm < 1800) { + return "下午"; + } else { + return "晚上"; + } + }, + calendar : { + sameDay : '[今天]LT', + nextDay : '[明天]LT', + nextWeek : '[下]ddddLT', + lastDay : '[昨天]LT', + lastWeek : '[上]ddddLT', + sameElse : 'L' + }, + ordinal : function (number, period) { + switch (period) { + case "d" : + case "D" : + case "DDD" : + return number + "日"; + case "M" : + return number + "月"; + case "w" : + case "W" : + return number + "週"; + default : + return number; + } + }, + relativeTime : { + future : "%s內", + past : "%s前", + s : "幾秒", + m : "一分鐘", + mm : "%d分鐘", + h : "一小時", + hh : "%d小時", + d : "一天", + dd : "%d天", + M : "一個月", + MM : "%d個月", + y : "一年", + yy : "%d年" } - return this; - }, + }); + })); - /** - * triggers an event that has already been bound - * - * @param {string} keys - * @param {string=} action - * @returns void - */ - trigger: function(keys, action) { - _direct_map[keys + ':' + action](); - return this; - }, - /** - * resets the library back to its initial state. this is useful - * if you want to clear out the current keyboard shortcuts and bind - * new ones - for example if you switch to another page - * - * @returns void - */ - reset: function() { - _callbacks = {}; - _direct_map = {}; - return this; - } - }; +/***/ }, +/* 130 */ +/***/ function(module, exports, __webpack_require__) { -module.exports = mousetrap; + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } -},{}]},{},[1]) -(1) -}); \ No newline at end of file +/***/ } +/******/ ]) +}) diff --git a/dist/vis.map b/dist/vis.map new file mode 100644 index 00000000..ada69a2b --- /dev/null +++ b/dist/vis.map @@ -0,0 +1 @@ +{"version":3,"file":"vis.map","sources":["./dist/vis.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","util","DOMutil","DataSet","DataView","Graph3d","Timeline","Graph2d","timeline","DataStep","Range","stack","TimeStep","components","items","Item","ItemBox","ItemPoint","ItemRange","Component","CurrentTime","CustomTime","DataAxis","GraphGroup","Group","ItemSet","Legend","LineGraph","TimeAxis","Network","network","Edge","Groups","Images","Node","Popup","dotparser","Graph","Error","Hammer","moment","isNumber","object","Number","isString","String","isDate","Date","match","ASPDateRegex","exec","isNaN","parse","isDataTable","google","visualization","DataTable","randomUUID","S4","Math","floor","random","toString","extend","a","i","len","arguments","length","other","prop","hasOwnProperty","selectiveExtend","props","Array","isArray","selectiveDeepExtend","b","TypeError","constructor","Object","undefined","deepExtend","equalArray","convert","type","Boolean","valueOf","isMoment","toDate","getType","toISOString","value","getAbsoluteLeft","elem","doc","document","documentElement","body","left","offsetLeft","e","offsetParent","scrollLeft","getAbsoluteTop","top","offsetTop","scrollTop","getPageY","event","pageY","clientY","targetTouches","clientTop","getPageX","pageX","clientX","clientLeft","addClassName","className","classes","split","indexOf","push","join","removeClassName","index","splice","forEach","callback","toArray","array","updateProperty","key","addEventListener","element","action","listener","useCapture","navigator","userAgent","attachEvent","removeEventListener","detachEvent","getTarget","window","target","srcElement","nodeType","parentNode","fakeGesture","eventType","gesture","collectEventData","center","option","asBoolean","defaultValue","asNumber","asString","asSize","asElement","GiveDec","Hex","Value","eval","GiveHex","Dec","parseColor","color","isValidHex","hsv","hexToHSV","lighterColorHSV","h","s","v","min","darkerColorHSV","darkerColorHex","HSVToHex","lighterColorHex","background","border","highlight","hover","hexToRGB","hex","replace","toUpperCase","substring","d","f","r","g","RGBToHex","red","green","blue","RGBToHSV","minRGB","maxRGB","max","hue","saturation","HSVToRGB","q","t","rgb","isOk","test","selectiveBridgeObject","fields","referenceObject","objectTo","create","bridgeObject","mergeOptions","mergeTarget","options","enabled","binarySearch","orderedItems","range","field","field2","newGuess","interval","end","start","found","low","high","guess","binarySearchGeneric","sidePreference","prevValue","nextValue","prepareElements","JSONcontainer","elementType","redundant","used","cleanupElements","removeChild","getSVGElement","svgContainer","shift","createElementNS","appendChild","getDOMElement","DOMContainer","createElement","drawPoint","x","y","group","point","drawPoints","style","setAttributeNS","size","drawBar","width","height","rect","data","_options","_data","_fieldId","fieldId","_type","_subscribers","add","prototype","on","subscribers","subscribe","off","filter","unsubscribe","_trigger","params","senderId","concat","subscriber","addedIds","me","_addItem","columns","_getColumnNames","row","rows","getNumberOfRows","item","col","cols","getValue","update","updatedIds","addOrUpdate","_updateItem","get","ids","firstType","returnType","itemId","_getItem","order","_sort","_filterFields","_appendRow","getIds","getDataSet","map","mappedItems","filteredItem","name","sort","av","bv","remove","removedId","removedIds","_remove","clear","keys","maxField","itemField","minField","distinct","values","fieldType","count","exists","types","raw","converted","JSON","stringify","dataTable","getNumberOfColumns","getColumnId","getColumnLabel","addRow","setValue","_ids","_onEvent","apply","setData","viewOptions","getArguments","defaultFilter","dataSet","added","updated","removed","container","SyntaxError","containerElement","margin","defaultXCenter","defaultYCenter","xLabel","yLabel","zLabel","filterLabel","legendLabel","STYLE","DOT","showPerspective","showGrid","keepAspectRatio","showShadow","showGrayBottom","showTooltip","verticalRatio","animationInterval","animationPreload","camera","Camera","eye","Point3d","dataPoints","colX","colY","colZ","colValue","colFilter","xMin","xStep","xMax","yMin","yStep","yMax","zMin","zStep","zMax","valueMin","valueMax","xBarWidth","yBarWidth","colorAxis","colorGrid","colorDot","colorDotBorder","setOptions","Slider","visible","frame","position","prev","play","next","bar","borderRadius","MozBorderRadius","backgroundColor","slide","onmousedown","_onMouseDown","onclick","togglePlay","onChangeCallback","playTimeout","playInterval","playLoop","Emitter","Point2d","Filter","StepNumber","armLocation","armRotation","horizontal","vertical","armLength","cameraLocation","cameraRotation","PI","calculateCameraOrientation","setArmLocation","z","setArmRotation","getArmRotation","rot","setArmLength","getArmLength","getCameraLocation","getCameraRotation","sin","cos","_setScale","scale","xCenter","yCenter","zCenter","_convert3Dto2D","point3d","translation","_convertPointToTranslation","_convertTranslationToScreen","ax","ay","az","cx","cy","cz","sinTx","cosTx","sinTy","cosTy","sinTz","cosTz","dx","dy","dz","bx","by","ex","ey","ez","xcenter","canvas","clientWidth","ycenter","_setBackgroundColor","fill","stroke","strokeWidth","borderColor","borderWidth","borderStyle","BAR","BARCOLOR","BARSIZE","DOTLINE","DOTCOLOR","DOTSIZE","GRID","LINE","SURFACE","_getStyleNumber","styleName","_determineColumnIndexes","counter","column","getDistinctValues","distinctValues","getColumnRange","minMax","_dataInitialize","rawData","_onChange","dataFilter","setOnLoadCallback","redraw","withBars","defaultXBarWidth","dataX","defaultYBarWidth","dataY","xRange","defaultXMin","defaultXMax","defaultXStep","yRange","defaultYMin","defaultYMax","defaultYStep","zRange","defaultZMin","defaultZMax","defaultZStep","valueRange","defaultValueMin","defaultValueMax","_getDataPoints","sortNumber","obj","dataMatrix","xIndex","yIndex","trans","screen","bottom","pointRight","pointTop","pointCross","hasChildNodes","firstChild","overflow","noCanvas","fontWeight","padding","innerHTML","ontouchstart","_onTouchStart","onmousewheel","_onWheel","ontooltip","_onTooltip","G3DaddEventListener","onkeydown","setSize","_resizeCanvas","clientHeight","animationStart","slider","animationStop","stop","_resizeCenter","charAt","parseFloat","setCameraPosition","pos","distance","getCameraPosition","_readData","_redrawFilter","animationAutoStart","cameraPosition","styleNumber","tooltip","showAnimationControls","_redrawSlider","_redrawClear","_redrawAxis","_redrawDataGrid","_redrawDataLine","_redrawDataBar","_redrawDataDot","_redrawInfo","_redrawLegend","ctx","getContext","clearRect","widthMin","widthMax","dotSize","right","lineWidth","font","ymin","ymax","_hsv2rgb","strokeStyle","beginPath","moveTo","lineTo","strokeRect","fillStyle","closePath","gridLineLen","step","getCurrent","textAlign","textBaseline","fillText","label","setValues","setPlayInterval","onchange","getIndex","selectValue","setOnChangeCallback","lineStyle","getLabel","getSelectedValue","from","to","prettyStep","text","xText","yText","zText","offset","xOffset","yOffset","xMin2d","xMax2d","gridLenX","gridLenY","textMargin","armAngle","H","S","V","R","G","B","C","Hi","X","abs","parseInt","cross","topSideVisible","zAvg","transBottom","dist","sortDepth","aDiff","subtract","bDiff","crossproduct","crossProduct","radius","arc","j","surface","corners","xWidth","yWidth","surfaces","avg","transCenter","diff","leftButtonDown","_onMouseUp","which","button","touchDown","startMouseX","getMouseX","startMouseY","getMouseY","startStart","startEnd","startArmRotation","cursor","onmousemove","_onMouseMove","onmouseup","G3DpreventDefault","diffX","diffY","horizontalNew","verticalNew","snapAngle","snapValue","round","parameters","emit","G3DremoveEventListener","delay","mouseX","mouseY","tooltipTimeout","clearTimeout","_hideTooltip","dataPoint","_dataPointFromXY","_showTooltip","setTimeout","ontouchmove","_onTouchMove","ontouchend","_onTouchEnd","delta","wheelDelta","detail","oldLength","newLength","_insideTriangle","triangle","sign","as","bs","cs","distMax","closestDataPoint","closestDist","triangle1","triangle2","distX","distY","sqrt","content","line","dot","dom","boxShadow","borderLeft","contentWidth","offsetWidth","contentHeight","offsetHeight","lineHeight","dotWidth","dotHeight","G3DstopPropagation","stopPropagation","cancelBubble","preventDefault","returnValue","setIndex","playNext","clearInterval","getPlayInterval","setPlayLoop","doLoop","onChange","indexToLeft","startClientX","startSlideX","leftToIndex","defaultOptions","autoResize","orientation","maxHeight","minHeight","_create","domProps","emitter","bind","snap","toScreen","_toScreen","toGlobalScreen","_toGlobalScreen","toTime","_toTime","toGlobalTime","_toGlobalTime","timeAxis","currentTime","customTime","itemSet","itemsData","groupsData","setItems","backgroundVertical","backgroundHorizontal","centerContainer","leftContainer","rightContainer","shadowTop","shadowBottom","shadowTopLeft","shadowBottomLeft","shadowTopRight","shadowBottomRight","_onTouch","_onPinch","_onDragStart","_onDrag","hammer","prevent_default","listeners","events","args","slice","scrollTopMin","touch","destroy","_stopAutoResize","component","_initAutoResize","setCustomTime","time","getCustomTime","newDataSet","initialLoad","fit","setWindow","setGroups","groups","what","dataRange","getItemRange","setRange","dataset","minItem","maxStartItem","maxEndItem","setSelection","getSelection","getWindow","getRange","resized","borderRootHeight","borderRootWidth","autoHeight","containerHeight","centerWidth","_updateScrollTop","visibilityTop","visibilityBottom","visibility","repaint","conversion","_startAutoResize","_onResize","lastWidth","lastHeight","watchTimer","setInterval","allowDragging","initialScrollTop","deltaY","oldScrollTop","_getScrollTop","newScrollTop","_setScrollTop","linegraph","backgroundHorizontalContainer","minimumStep","forcedStepSize","current","autoScale","stepIndex","marginStart","marginEnd","majorSteps","minorSteps","_start","_end","setMinimumStep","setFirst","safeSize","minimumStepValue","orderOfMagnitude","log","LN10","minorStepIdx","magnitudefactor","pow","solutionFound","stepSize","first","niceStart","niceEnd","roundToMinor","marginRange","rounded","hasNext","previous","toPrecision","isMajor","now","hours","minutes","seconds","milliseconds","clone","direction","moveable","zoomable","zoomMin","zoomMax","_onDragEnd","_onHold","_onMouseWheel","validateDirection","getPointer","vis","changed","_applyRange","newStart","newEnd","deltaX","diffRange","pointer","pointerDate","_pointerToDate","zoom","touches","initDate","move","orderByStart","orderByEnd","aTime","bTime","force","iMax","axis","collidingItem","jj","collision","nostack","SCALE","DAY","MILLISECOND","SECOND","MINUTE","HOUR","WEEKDAY","MONTH","YEAR","setFullYear","getFullYear","setMonth","setDate","setHours","setMinutes","setSeconds","setMilliseconds","getMilliseconds","getSeconds","getMinutes","getHours","getDate","getMonth","setScale","newScale","newStep","setAutoScale","enable","stepYear","stepMonth","stepDay","stepHour","stepMinute","stepSecond","stepMillisecond","date","year","getLabelMinor","format","getLabelMajor","_isResized","_previousWidth","_previousHeight","showCurrentTime","parent","title","currentTimeTimer","showCustomTime","eventParams","drag","dragging","svg","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","linegraphSVG","DOMelements","lines","labels","conversionFactor","minWidth","stepPixels","stepPixelsForced","lineOffset","master","svgElements","amountOfGroups","addGroup","graphOptions","updateGroup","removeGroup","hide","show","lineContainer","display","_redrawGroupIcons","iconHeight","iconOffset","groupId","drawIcon","changeCalled","_calculateCharSize","minorLabelHeight","minorCharHeight","majorLabelHeight","majorCharHeight","minorLineWidth","minorLineHeight","majorLineWidth","majorLineHeight","_redrawLabels","amountOfSteps","stepDifference","valueAtZero","marginStartPos","maxLabelSize","_redrawLabel","_redrawLine","characterHeight","largestWidth","majorCharWidth","minorCharWidth","convertValue","invertedValue","convertedValue","textMinor","createTextNode","measureCharMinor","textMajor","measureCharMajor","groupsUsingDefaultStyles","usingDefaultStyle","zeroPosition","setZeroPosition","catmullRom","parametrization","alpha","SVGcontainer","path","fillPath","fillHeight","outline","shaded","barWidth","bar1Height","bar2Height","visibleItems","byStart","byEnd","inner","foreground","marker","Element","getLabelWidth","restack","_updateVisibleItems","markerHeight","lastMarkerHeight","dirty","displayed","ii","repositionY","labelSet","setParent","_checkIfVisible","removeFromDataSet","removeItem","_constructByEndArray","endArray","initialPosByStart","newVisibleItems","initialPosByEnd","_checkIfInvisible","isVisible","repositionX","align","groupOrder","selectable","editable","updateTime","onAdd","onUpdate","onMove","onRemove","itemOptions","itemListeners","_onAdd","_onUpdate","_onRemove","groupListeners","_onAddGroups","_onUpdateGroups","_onRemoveGroups","groupIds","selection","stackDirty","touchParams","UNGROUPED","box","_updateUngrouped","_onSelectItem","_onMultiSelectItem","_onAddItem","addCallback","fn","Function","markDirty","unselect","select","_deselect","_orderGroups","visibleInterval","zoomed","lastVisibleInterval","firstGroup","_firstGroup","firstMargin","nonFirstMargin","groupMargin","groupResized","firstGroupIndex","firstGroupId","ungrouped","getLabelSet","oldItemsData","getItems","_order","getGroups","itemData","_removeItem","groupData","groupOptions","oldGroupId","oldGroup","itemFromTarget","selected","dragLeftItem","dragRightItem","itemProps","groupFromTarget","changes","ctrlKey","srcEvent","shiftKey","oldSelection","newSelection","xAbs","newItem","itemSetFromTarget","side","iconSize","iconSpacing","textArea","drawLegendIcons","getComputedStyle","paddingTop","yAxisOrientation","defaultGroup","sampling","graphHeight","barChart","dataAxis","legend","lastStart","rangePerPixelInv","_updateGraph","yAxisLeft","yAxisRight","legendLeft","legendRight","_updateAllGroupData","_updateGroup","groupsContent","ungroupedCounter","preprocessedGroup","preprocessedGroupData","processedGroupData","groupRanges","minDate","maxDate","_preprocessData","_updateYAxis","_convertYvalues","_drawLineGraph","_drawBarGraph","minVal","maxVal","yAxisLeftUsed","yAxisRightUsed","minLeft","minRight","maxLeft","maxRight","_toggleAxisVisiblity","drawIcons","axisUsed","coreDistance","_drawPoints","svgHeight","_catmullRom","_linear","dFill","datapoints","xValue","yValue","extractedData","increment","amountOfPoints","xDistance","pointsPerPixel","ceil","_catmullRomUniform","p0","p1","p2","p3","bp1","bp2","normalization","d1","d2","d3","A","N","M","d3powA","d2powA","d3pow2A","d2pow2A","d1pow2A","d1powA","majorLines","majorTexts","minorLines","minorTexts","lineTop","parentChanged","foregroundNextSibling","nextSibling","backgroundNextSibling","_repaintLabels","insertBefore","xFirstMajorLabel","cur","_repaintMinorText","_repaintMajorText","_repaintMajorLine","_repaintMinorLine","leftTime","leftText","widthText","arr","pop","childNodes","nodeValue","_repaintDeleteButton","anchor","deleteButton","itemSetHeight","marginLeft","baseClassName","_repaintDragLeft","_repaintDragRight","contentLeft","parentWidth","boxWidth","dragLeft","dragRight","_initializeMixinLoaders","renderRefreshRate","renderTimestep","renderTime","maxPhysicsTicksPerRender","physicsDiscreteStepsize","stabilize","initializing","triggerFunctions","edit","editEdge","connect","del","constants","nodes","radiusMin","radiusMax","shape","image","fixed","fontColor","fontSize","fontFace","level","highlightColor","edges","widthSelectionMultiplier","hoverWidth","fontFill","arrowScaleFactor","dash","gap","altLength","configurePhysics","physics","barnesHut","theta","gravitationalConstant","centralGravity","springLength","springConstant","damping","repulsion","nodeDistance","hierarchicalRepulsion","clustering","initialMaxNodes","clusterThreshold","reduceToNodes","chainThreshold","clusterEdgeThreshold","sectorThreshold","screenSizeThreshold","fontSizeMultiplier","maxFontSize","forceAmplification","distanceAmplification","edgeGrowth","nodeScaling","maxNodeSizeIncrements","activeAreaBoxSize","clusterLevelDifference","navigation","keyboard","speed","dataManipulation","initiallyVisible","hierarchicalLayout","levelSeparation","nodeSpacing","freezeForStabilization","smoothCurves","maxVelocity","minVelocity","stabilizationIterations","link","editNode","back","addDescription","linkDescription","editEdgeDescription","addError","linkError","editError","editBoundError","deleteError","deleteClusterError","dragNetwork","dragNodes","hoverObj","images","setOnloadCallback","_redraw","xIncrement","yIncrement","zoomIncrement","_loadPhysicsSystem","_loadSectorSystem","_loadClusterSystem","_loadSelectionSystem","_loadHierarchySystem","_setTranslation","freezeSimulation","cachedFunctions","calculationNodes","calculationNodeIndices","nodeIndices","canvasTopLeft","canvasBottomRight","pointerPosition","areaCenter","previousScale","nodesData","edgesData","nodesListeners","_addNodes","_updateNodes","_removeNodes","edgesListeners","_addEdges","_updateEdges","_removeEdges","moving","timer","_setupHierarchicalLayout","zoomExtent","startWithClustering","mousetrap","MixinLoader","_getScriptPath","scripts","getElementsByTagName","src","_getRange","node","minY","maxY","minX","maxX","nodeId","_findCenter","_centerNetwork","initialZoom","disableStart","zoomLevel","numberOfNodes","factor","yDistance","xZoomLevel","yZoomLevel","_updateNodeIndexList","_clearNodeIndexList","idx","dotData","DOTToGraph","_setNodes","_setEdges","_putDataInSector","_stabilize","dragGraph","onEdit","onEditEdge","onConnect","onDelete","editMode","groupname","_loadNavigationControls","_loadManipulationSystem","_configureSmoothCurves","_createKeyBinds","pinch","_onTap","_onDoubleTap","_onRelease","_onMouseMoveTitle","reset","_moveUp","_yStopMoving","_moveDown","_moveLeft","_xStopMoving","_moveRight","_zoomIn","_stopZoom","_zoomOut","_createManipulatorBar","_deleteSelected","_getPointer","pinched","_getScale","_handleTouch","_handleDragStart","_getNodeAt","_getTranslation","isSelected","_selectObject","objectId","selectionObj","xFixed","yFixed","_handleOnDrag","_XconvertDOMtoCanvas","_XconvertCanvasToDOM","_YconvertDOMtoCanvas","_YconvertCanvasToDOM","_handleTap","_handleDoubleTap","_handleOnHold","_handleOnRelease","_zoom","scaleOld","scaleFrac","tx","ty","updateClustersDefault","popupObj","_checkHidePopup","checkShow","_checkShowPopup","popupTimer","edgeId","_getEdgeAt","_hoverObject","_blurObject","lastPopupNode","getTitle","isOverlappingWith","edge","connected","popup","setPosition","setText","manipulationDiv","navigationDivs","oldNodesData","_updateSelection","angle","_resetLevels","_updateCalculationNodes","_reconnectEdges","_updateValueRange","updateLabels","setProperties","properties","oldEdgesData","oldEdge","disconnect","showInternalIds","_createBezierNodes","via","sectors","setValueRange","w","save","translate","_doInAllSectors","restore","offsetX","offsetY","canvasToDOM","DOMtoCanvas","_drawNodes","alwaysShow","setScaleAndPos","inArea","draw","sMax","_drawEdges","_drawControlNodes","_freezeDefinedNodes","_physicsTick","_restoreFrozenNodes","iterations","fixedData","_isMoving","vmin","isMoving","_discreteStepNodes","nodesPresent","discreteStepLimited","discreteStep","vminCorrected","_doInAllActiveSectors","_doInSupportSector","_animationStep","_handleNavigation","calculationTime","maxSteps","timeRequired","requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","msRequestAnimationFrame","ua","toLowerCase","requiresTimeout","toggleFreeze","smooth","mass","internalMultiplier","parentEdgeId","positionBezierNode","mixin","storePosition","dataArray","allowedToMoveX","allowedToMoveY","focusOnNode","nodePosition","requiredScale","canvasCenter","distanceFromCenter","console","fromId","toId","widthSelected","customLength","originalFromId","originalToId","widthFixed","lengthFixed","controlNodesEnabled","controlNodes","positions","connectedNode","_drawLine","_drawArrow","_drawArrowCenter","_drawDashLine","attachEdge","detachEdge","xFrom","yFrom","xTo","yTo","xObj","yObj","_getDistanceToEdge","_getLineWidth","_line","midpointX","midpointY","_pointOnLine","_label","resize","_circle","_pointOnCircle","networkScaleInv","quadraticCurveTo","measureText","fillRect","mozDash","setLineDash","pattern","lineDashOffset","mozDashOffset","lineCap","dashedLine","percentage","atan2","arrow","edgeSegmentLength","fromBorderDist","distanceToBorder","fromBorderPoint","toBorderDist","toBorderPoint","x1","y1","x2","y2","x3","y3","minDistance","px","py","something","u","nodeIdFrom","nodeIdTo","getControlNodePositions","_enableControlNodes","_disableControlNodes","_getSelectedControlNode","fromDistance","toDistance","_restoreControlNodes","defaultIndex","DEFAULT","load","url","img","Image","onload","imagelist","grouplist","dynamicEdges","reroutedEdges","fontDrawThreshold","horizontalAlignLeft","verticalAlignTop","baseRadiusValue","radiusFixed","preassignedLevel","fx","fy","vx","vy","minForce","resetCluster","dynamicEdgesLength","clusterSession","clusterSizeWidthFactor","clusterSizeHeightFactor","clusterSizeRadiusFactor","growthIndicator","networkScale","formationScale","clusterSize","containedNodes","containedEdges","clusterSessions","originalLabel","triggerFunction","groupObj","imageObj","_drawDatabase","_resizeDatabase","_drawBox","_resizeBox","_drawCircle","_resizeCircle","_drawEllipse","_resizeEllipse","_drawImage","_resizeImage","_drawText","_resizeText","_drawDot","_resizeShape","_drawSquare","_drawTriangle","_drawTriangleDown","_drawStar","_reset","clearSizeCache","_setForce","_addForce","isFixed","getDistance","globalAlpha","drawImage","textSize","getTextSize","clusterLineWidth","selectionLineWidth","roundRect","database","diameter","circle","defaultSize","ellipse","_drawShape","radiusMultiplier","baseline","labelUnderNode","lineCount","yLine","inView","clearVelocity","updateVelocity","massBeforeClustering","energyBefore","styleAttr","fontFamily","WebkitBorderRadius","whiteSpace","maxWidth","parseDOT","parseGraph","nextPreview","isAlphaNumeric","regexAlphaNumeric","merge","o","addNode","graph","graphs","attr","addEdge","createEdge","getToken","tokenType","TOKENTYPE","NULL","token","isComment","DELIMITER","c2","DELIMITERS","IDENTIFIER","newSyntaxError","UNKNOWN","chop","strict","parseStatements","parseStatement","subgraph","parseSubgraph","parseEdge","parseAttributeStatement","parseNodeStatement","subgraphs","parseAttributeList","message","maxLength","substr","forEach2","array1","array2","elem1","elem2","convertEdge","dotEdge","graphEdge","graphData","dotNode","graphNode","subEdge","{","}","[","]",";","=",",","->","--","sub","sum","onLoadCallback","loadInBackground","isLoaded","getLoadedProgress","getColumn","getValues","dataView","progress","_step","precision","_current","setStep","calculatePrettyStep","log10","step1","step2","step5","getStep","CanvasRenderingContext2D","square","s2","ir","triangleDown","star","n","r2d","kappa","ox","oy","xe","ye","xm","ym","bezierCurveTo","wEllipse","hEllipse","ymb","yeb","xt","yt","xi","yi","xl","yl","xr","yr","dashArray","dashLength","dashCount","slope","distRemaining","dashIndex","PhysicsMixin","ClusterMixin","SectorsMixin","SelectionMixin","ManipulationMixin","NavigationMixin","HierarchicalLayoutMixin","_loadMixin","sourceVariable","mixinFunction","_clearMixin","_loadSelectedForceSolver","_loadPhysicsConfiguration","hubThreshold","activeSector","drawingNode","blockConnectingEdgeSelection","forceAppendSelection","editModeDiv","closeDiv","_cleanNavigation","_loadNavigationElements","_callbacks","once","self","removeListener","removeAllListeners","callbacks","cb","hasListeners","_addEvent","_characterFromEvent","fromCharCode","_MAP","_KEYCODE_MAP","_stop","tag_name","tagName","contentEditable","_modifiersMatch","modifiers1","modifiers2","_resetSequences","do_not_reset","active_sequences","_sequence_levels","_inside_sequence","_getMatches","character","modifiers","combination","matches","_isModifier","seq","combo","_eventModifiers","altKey","metaKey","_fireCallback","_handleCharacter","processed_sequence_callback","_handleKey","keyCode","_ignore_next_keyup","_resetSequenceTimer","_reset_timer","_getReverseMap","_REVERSE_MAP","_pickBestAction","_bindSequence","_increaseSequence","_callbackAndReset","_bindSingle","sequence_name","sequence","_SPECIAL_ALIASES","_SHIFT_MAP","_bindMultiple","combinations",8,9,13,16,17,18,20,27,32,33,34,35,36,37,38,39,40,45,46,91,93,224,106,107,109,110,111,186,187,188,189,190,191,192,219,220,221,222,"~","!","@","#","$","%","^","&","*","(",")","_","+",":","\"","<",">","?","|","command","return","escape","_direct_map","unbind","trigger","clusterToFit","maxNumberOfNodes","reposition","maxLevels","forceAggregateHubs","normalizeClusterLevels","increaseClusterLevel","repositionNodes","openCluster","isMovingBeforeClustering","_nodeInActiveArea","_sector","_addSector","decreaseClusterLevel","_expandClusterNode","_updateDynamicEdges","updateClusters","zoomDirection","recursive","doNotStart","amountOfNodes","_collapseSector","_formClusters","_openClusters","_openClustersBySize","_aggregateHubs","handleChains","chainPercentage","_getChainFraction","_reduceAmountOfChains","_getHubSize","_formClustersByHub","openAll","containedNodeId","childNode","_expelChildFromParent","_unselectAll","_releaseContainedEdges","_connectEdgeBackToChild","_validateEdges","othersPresent","childNodeId","_repositionBezierNodes","_formClustersByZoom","_forceClustersByZoom","minLength","_addToCluster","_clusterToSmallestNeighbour","smallestNeighbour","smallestNeighbourNode","neighbour","onlyEqual","_formClusterFromHub","hubNode","absorptionSizeOffset","allowCluster","edgesIdarray","amountOfInitialEdges","_addToContainedEdges","_connectEdgeToCluster","_containCircularEdgesFromNode","massBefore","correction","edgeToId","edgeFromId","k","_addToReroutedEdges","maxLevel","minLevel","clusterLevel","targetLevel","average","averageSquared","hubCounter","largestHub","variance","standardDeviation","fraction","reduceAmount","chains","total","_switchToSector","sectorId","sectorType","_switchToActiveSector","_switchToFrozenSector","_switchToSupportSector","_loadLatestSector","_previousSector","_setActiveSector","newId","_forgetLastSector","_createNewSector","_deleteActiveSector","_deleteFrozenSector","_freezeSector","_activateSector","_mergeThisWithFrozen","_collapseThisToSingleCluster","sector","unqiueIdentifier","previousSector","runFunction","argument","_doInAllFrozenSectors","_drawSectorNodes","_drawAllSectorNodes","_getNodesOverlappingWith","overlappingNodes","_getAllNodesOverlappingWith","_pointerToPositionObject","positionObject","_getEdgesOverlappingWith","overlappingEdges","_getAllEdgesOverlappingWith","_addToSelection","_addToHover","_removeFromSelection","doNotTrigger","_unselectClusters","_getSelectedNodeCount","_getSelectedNode","_getSelectedEdge","_getSelectedEdgeCount","_getSelectedObjectCount","_selectionIsEmpty","_clusterInSelection","_selectConnectedEdges","_hoverConnectedEdges","_unselectConnectedEdges","append","highlightEdges","nodeIds","getSelectedNodes","edgeIds","getSelectedEdges","idArray","RangeError","selectNodes","selectEdges","_clearManipulatorBar","_restoreOverloadedFunctions","functionName","_toggleEditMode","toolbar","getElementById","boundFunction","edgeBeingEdited","selectedControlNode","addNodeButton","_createAddNodeToolbar","addEdgeButton","_createAddEdgeToolbar","editButton","_editNode","_createEditEdgeToolbar","editModeButton","backButton","_addNode","_handleConnect","_finishConnect","_selectControlNode","_controlNodeDrag","_releaseControlNode","newNode","_editEdge","alert","connectFromId","_createEdge","defaultData","finalizedData","sourceNodeId","targetNodeId","selectedNodes","selectedEdges","wrapper","navigationDivActions","_stopMovement","_preventDefault","hubsize","definedLevel","undefinedLevel","_changeConstants","_determineLevels","distribution","_getDistribution","_placeNodesByHierarchy","minPos","_placeBranchNodes","amount","maxCount","_setLevel","parentId","parentLevel","nodeMoved","_restoreNodes","setup","READY","determineEventTypes","gestures","detection","register","onTouch","DOCUMENT","EVENT_MOVE","detect","EVENT_END","Instance","defaults","stop_browser_behavior","userSelect","touchAction","touchCallout","contentZooming","userDrag","tapHighlightColor","HAS_POINTEREVENTS","pointerEnabled","msPointerEnabled","HAS_TOUCHEVENTS","MOBILE_REGEX","NO_MOUSEEVENTS","EVENT_TYPES","DIRECTION_DOWN","DIRECTION_LEFT","DIRECTION_UP","DIRECTION_RIGHT","POINTER_MOUSE","POINTER_TOUCH","POINTER_PEN","EVENT_START","plugins","utils","stopDefaultBrowserBehavior","ev","startDetect","handler","eventData","createEvent","initEvent","hasParent","dispatchEvent","state","last_move_event","enable_detect","touch_triggered","bindDom","sourceEventType","count_touches","PointerEvent","updatePointer","getEvents","getTouchList","identifier","pointerType","matchType","getCenter","timeStamp","getTime","preventManipulation","stopDetect","pointers","touchlist","pointerEvent","pointerId","MSPOINTER_TYPE_MOUSE","MSPOINTER_TYPE_TOUCH","MSPOINTER_TYPE_PEN","dest","valuesX","valuesY","getVelocity","delta_time","delta_x","delta_y","getAngle","touch1","touch2","getDirection","getScale","getRotation","isVertical","css_props","vendors","onselectstart","stopped","inst","startEvent","lastEvent","extendEventData","inst_options","startEv","velocity","deltaTime","velocityX","velocityY","rotation","Hold","hold_timeout","hold_threshold","Tap","tap_max_touchtime","tap_max_distance","tap_always","doubletap_distance","doubletap_interval","did_doubletap","Swipe","swipe_max_touches","swipe_velocity","Drag","drag_min_distance","drag_max_touches","drag_block_horizontal","drag_block_vertical","drag_lock_to_axis","drag_lock_min_distance","triggered","drag_locked_to_axis","last_direction","Transform","transform_min_scale","transform_min_rotation","transform_always_block","scale_threshold","rotation_threshold","Touch","Infinity","prevent_mouseevents","Release","graphToggleSmoothCurves","graph_toggleSmooth","graphRepositionNodes","graphGenerateOptions","optionsSpecific","radioButton1","radioButton2","checked","backupConstants","optionsDiv","switchConfigurations","radioButton","querySelector","tableId","table","showValueOfRange","constantsVariableName","valueId","rangeValue","_overWriteGraphConstants","RepulsionMixin","HierarchialRepulsionMixin","BarnesHutMixin","_toggleBarnesHut","barnesHutTree","_initializeForceCalculation","_calculateForces","_calculateGravitationalForces","_calculateNodeForces","_calculateSpringForcesWithSupport","_calculateHierarchicalSpringForces","_calculateSpringForces","supportNodes","supportNodeId","gravity","gravityForce","edgeLength","springForce","combinedClusterSize","node1","node2","node3","_calculateSpringForce","physicsConfiguration","hierarchicalLayoutDirections","parentElement","rangeElement","radioButton3","graph_repositionNodes","graph_generateOptions","nameArray","__WEBPACK_AMD_DEFINE_RESULT__","global","dfl","defaultParsingFlags","empty","unusedTokens","unusedInput","charsLeftOver","nullInput","invalidMonth","invalidFormat","userInvalidated","iso","deprecate","msg","printMsg","suppressDeprecationWarnings","warn","firstTime","padToken","func","leftZeroFill","ordinalizeToken","period","lang","ordinal","Language","Moment","config","checkOverflow","Duration","duration","normalizedInput","normalizeObjectUnits","years","quarters","quarter","months","month","weeks","week","days","day","hour","minute","second","millisecond","_milliseconds","_days","_months","_bubble","cloneMoment","result","momentProperties","absRound","number","targetLength","forceSign","output","addOrSubtractDurationFromMoment","mom","isAdding","updateOffset","_d","setTime","rawSetter","rawGetter","rawMonthSetter","input","compareArrays","dontConvert","lengthDiff","diffs","toInt","normalizeUnits","units","lowered","unitAliases","camelFunctions","inputObject","normalizedProp","makeList","setter","getter","method","_lang","results","utc","set","argumentForCoercion","coercedNumber","isFinite","daysInMonth","UTC","getUTCDate","weeksInYear","dow","doy","weekOfYear","daysInYear","isLeapYear","_a","_pf","DATE","_overflowDayOfYear","isValid","_isValid","_strict","normalizeLanguage","makeAs","model","_isUTC","zone","_offset","local","loadLang","abbr","languages","unloadLang","getLangDefinition","hasModule","removeFormattingTokens","makeFormatFunction","formattingTokens","formatTokenFunctions","formatMoment","expandFormat","formatFunctions","invalidDate","replaceLongDateFormatTokens","longDateFormat","localFormattingTokens","lastIndex","getParseRegexForToken","parseTokenOneDigit","parseTokenThreeDigits","parseTokenFourDigits","parseTokenOneToFourDigits","parseTokenSignedNumber","parseTokenSixDigits","parseTokenOneToSixDigits","parseTokenTwoDigits","parseTokenOneToThreeDigits","parseTokenWord","_l","_meridiemParse","parseTokenTimestampMs","parseTokenTimezone","parseTokenT","parseTokenDigits","parseTokenOneOrTwoDigits","parseTokenOrdinal","RegExp","regexpEscape","unescapeFormat","timezoneMinutesFromString","string","possibleTzMatches","tzChunk","parts","parseTimezoneChunker","addTimeToArrayFromToken","datePartArray","monthsParse","_dayOfYear","parseTwoDigitYear","_isPm","isPM","_useUTC","_tzm","weekdaysParse","_w","invalidWeekday","dayOfYearFromWeekInfo","weekYear","weekday","temp","GG","W","E","_week","gg","dayOfYearFromWeeks","dayOfYear","dateFromConfig","currentDate","yearToUse","currentDateArray","makeUTCDate","getUTCMonth","makeDate","setUTCMinutes","getUTCMinutes","dateFromObject","_i","getUTCFullYear","makeDateFromStringAndFormat","_f","ISO_8601","parseISO","parsedInput","tokens","skipped","stringLength","totalParsedInputLength","matched","p4","makeDateFromStringAndArray","tempConfig","bestMoment","scoreToBeat","currentScore","NaN","score","l","isoRegex","isoDates","isoTimes","makeDateFromString","createFromInputFallback","makeDateFromInput","aspNetJsonRegex","ms","setUTCFullYear","parseWeekday","language","substituteTimeAgo","withoutSuffix","isFuture","relativeTime","relativeTimeThresholds","dd","dm","firstDayOfWeek","firstDayOfWeekOfYear","adjustedMoment","daysToDayOfWeek","daysToAdd","getUTCDay","makeMoment","invalid","preparse","pickBy","moments","res","dayOfMonth","unit","makeAccessor","keepTime","makeDurationGetter","makeDurationAsGetter","makeGlobal","shouldDeprecate","ender","oldGlobalMoment","globalScope","VERSION","_isAMomentObject","aspNetTimeSpanJsonRegex","isoDurationRegex","isoFormat","unitMillisecondFactors","Milliseconds","Seconds","Minutes","Hours","Days","Months","Years","D","Q","DDD","dayofyear","isoweekday","isoweek","weekyear","isoweekyear","ordinalizeTokens","paddedTokens","MMM","monthsShort","MMMM","weekdaysMin","ddd","weekdaysShort","dddd","weekdays","isoWeek","YY","YYYY","YYYYY","YYYYYY","gggg","ggggg","isoWeekYear","GGGG","GGGGG","isoWeekday","meridiem","SS","SSS","SSSS","Z","ZZ","zoneAbbr","zz","zoneName","unix","lists","DDDD","_monthsShort","monthName","regex","_monthsParse","_weekdays","_weekdaysShort","_weekdaysMin","weekdayName","_weekdaysParse","_longDateFormat","LT","L","LL","LLL","LLLL","val","isLower","_calendar","sameDay","nextDay","nextWeek","lastDay","lastWeek","sameElse","calendar","_relativeTime","future","past","mm","hh","MM","yy","pastFuture","_ordinal","postformat","_invalidDate","ret","parseIso","isDuration","inp","version","defaultFormat","relativeTimeThreshold","threshold","limit","_abbr","langData","flags","parseZone","isDSTShifted","parsingFlags","invalidAt","inputString","dur","asFloat","that","zoneDiff","startOf","humanize","fromNow","sod","isDST","getDay","endOf","isAfter","isBefore","isSame","getTimezoneOffset","_changeInProgress","hasAlignedHourOffset","isoWeeksInYear","weekInfo","dates","isoWeeks","toJSON","withSuffix","difference","toIsoString","asSeconds","asMonths","require","noGlobal","repulsingForce","a_base","minimumDistance","nodeCount","_formBarnesHutTree","_getForceContribution","children","NW","NE","SW","SE","parentBranch","childrenCount","centerOfMass","calcSize","MAX_VALUE","sizeDiff","minimumTreeSize","rootSize","halfRootSize","centerX","centerY","_splitBranch","_placeInTree","_updateBranchMass","totalMass","totalMassInv","biggestSize","skipMassUpdate","_placeInRegion","region","containedNode","_insertRegion","childSize","_drawTree","_drawBranch","branch","webpackContext","req","webpackContextResolve","./ar","./ar-ma","./ar-ma.js","./ar-sa","./ar-sa.js","./ar.js","./az","./az.js","./bg","./bg.js","./bn","./bn.js","./br","./br.js","./bs","./bs.js","./ca","./ca.js","./cs","./cs.js","./cv","./cv.js","./cy","./cy.js","./da","./da.js","./de","./de-at","./de-at.js","./de.js","./el","./el.js","./en-au","./en-au.js","./en-ca","./en-ca.js","./en-gb","./en-gb.js","./eo","./eo.js","./es","./es.js","./et","./et.js","./eu","./eu.js","./fa","./fa.js","./fi","./fi.js","./fo","./fo.js","./fr","./fr-ca","./fr-ca.js","./fr.js","./gl","./gl.js","./he","./he.js","./hi","./hi.js","./hr","./hr.js","./hu","./hu.js","./hy-am","./hy-am.js","./id","./id.js","./is","./is.js","./it","./it.js","./ja","./ja.js","./ka","./ka.js","./km","./km.js","./ko","./ko.js","./lb","./lb.js","./lt","./lt.js","./lv","./lv.js","./mk","./mk.js","./ml","./ml.js","./mr","./mr.js","./ms-my","./ms-my.js","./nb","./nb.js","./ne","./ne.js","./nl","./nl.js","./nn","./nn.js","./pl","./pl.js","./pt","./pt-br","./pt-br.js","./pt.js","./ro","./ro.js","./ru","./ru.js","./sk","./sk.js","./sl","./sl.js","./sq","./sq.js","./sr","./sr-cyrl","./sr-cyrl.js","./sr.js","./sv","./sv.js","./ta","./ta.js","./th","./th.js","./tl-ph","./tl-ph.js","./tr","./tr.js","./tzm","./tzm-latn","./tzm-latn.js","./tzm.js","./uk","./uk.js","./uz","./uz.js","./vi","./vi.js","./zh-cn","./zh-cn.js","./zh-tw","./zh-tw.js","resolve","__WEBPACK_AMD_DEFINE_ARRAY__","symbolMap","1","2","3","4","5","6","7","0","numberMap","١","٢","٣","٤","٥","٦","٧","٨","٩","٠","suffixes",70,80,50,100,10,30,60,90,"lastDigit","last2Digits","১","২","৩","৪","৫","৬","৭","৮","৯","০","relativeTimeWithMutation","mutation","specialMutationForYears","lastNumber","softMutation","mutationTable","plural","affix","lookup","processRelativeTime","monthsNominativeEl","monthsGenitiveEl","momentToFormat","_monthsGenitiveEl","_monthsNominativeEl","calendarEl","_calendarEl","monthsShortDot","ll","lll","llll","۱","۲","۳","۴","۵","۶","۷","۸","۹","۰","verbalNumber","numbersFuture","numbersPast","str","१","२","३","४","५","६","७","८","९","०","num","weekEndings","monthsCaseReplace","nominative","accusative","nounCase","monthsShortCaseReplace","weekdaysCaseReplace","ss","meridiemParse","processFutureTime","eifelerRegelAppliesToNumber","processPastTime","processLastWeek","eifelerRegelAppliesToWeekday","firstDigit","translateSeconds","translateSingular","forms","special","relativeWeekDay","weekDay","weekDays","word","relativeTimeWithPlural","monthsShortWithDots","monthsShortWithoutDots","monthsNominative","monthsSubjective","separator","translator","words","correctGrammaticalCase","wordKey","lastWeekDays","genitive","processHoursFunction","hm","startOfWeek","prefix","webpackPolyfill","paths"],"mappings":"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"} \ No newline at end of file diff --git a/dist/vis.min.css b/dist/vis.min.css old mode 100644 new mode 100755 diff --git a/dist/vis.min.js b/dist/vis.min.js index f1a4ebb0..3a3d02a6 100644 --- a/dist/vis.min.js +++ b/dist/vis.min.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 3.0.1-SNAPSHOT - * @date 2014-07-07 + * @date 2014-07-08 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -22,15 +22,19 @@ * 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 o(r,a){if(!i[r]){if(!e[r]){var h="function"==typeof require&&require;if(!a&&h)return h(r,!0);if(n)return n(r,!0);throw new Error("Cannot find module '"+r+"'")}var d=i[r]={exports:{}};e[r][0].call(d.exports,function(t){var i=e[r][1][t];return o(i?i:t)},d,d.exports,t,e,i,s)}return i[r].exports}for(var n="function"==typeof require&&require,r=0;re?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,o;if(null==this)throw new TypeError(" this is null or not defined");var n=Object(this),r=n.length>>>0;if("function"!=typeof t)throw new TypeError(t+" is not a function");for(e&&(i=e),s=new Array(r),o=0;r>o;){var a,h;o in n&&(a=n[o],h=t.call(i,a,o,n),s[o]=h),o++}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=[],o=arguments[1],n=0;i>n;n++)if(n in e){var r=e[n];t.call(o,r,n,e)&&s.push(r)}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(o){if("object"!=typeof o&&"function"!=typeof o||null===o)throw new TypeError("Object.keys called on non-object");var n=[];for(var r in o)t.call(o,r)&&n.push(r);if(e)for(var a=0;s>a;a++)t.call(o,i[a])&&n.push(i[a]);return n}}()),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(){},o=function(){return i.apply(this instanceof s&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return s.prototype=this.prototype,o.prototype=new s,o}),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(){},o=function(){return i.apply(this instanceof s&&t?this:t,e.concat(Array.prototype.slice.call(arguments))) -};return s.prototype=this.prototype,o.prototype=new s,o});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 o in s)s.hasOwnProperty(o)&&(t[o]=s[o])}return t},util.selectiveExtend=function(t,e){if(!Array.isArray(t))throw new Error("Array with property names expected as first argument");for(var i=2;ii;i++)if(t[i]!=e[i])return!1;return!0},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('Unknown 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,o=t.offsetParent;null!=o&&o!=i&&o!=e;)s+=o.offsetLeft,s-=o.scrollLeft,o=o.offsetParent;return s},util.getAbsoluteTop=function(t){for(var e=document.documentElement,i=document.body,s=t.offsetTop,o=t.offsetParent;null!=o&&o!=i&&o!=e;)s+=o.offsetTop,s-=o.scrollTop,o=o.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(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)},o={h:i.h,s:Math.min(1,1.25*i.v),v:.6*i.v},n=util.HSVToHex(o.h,o.h,o.v),r=util.HSVToHex(s.h,s.s,s.v);e={background:t,border:n,highlight:{background:r,border:n},hover:{background:r,border:n}}}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)),o=util.GiveDec(t.substring(3,4)),n=util.GiveDec(t.substring(4,5)),r=util.GiveDec(t.substring(5,6)),a=16*e+i,h=16*s+o,i=16*n+r;return{r:a,g:h,b:i}},util.RGBToHex=function(t,e,i){var s=util.GiveHex(Math.floor(t/16)),o=util.GiveHex(t%16),n=util.GiveHex(Math.floor(e/16)),r=util.GiveHex(e%16),a=util.GiveHex(Math.floor(i/16)),h=util.GiveHex(i%16),d=s+o+n+r+a+h;return"#"+d},util.RGBToHSV=function(t,e,i){t/=255,e/=255,i/=255;var s=Math.min(t,Math.min(e,i)),o=Math.max(t,Math.max(e,i));if(s==o)return{h:0,s:0,v:s};var n=t==s?e-i:i==s?t-e:i-t,r=t==s?3:i==s?1:5,a=60*(r-n/(o-s))/360,h=(o-s)/o,d=o;return{h:a,s:h,v:d}},util.HSVToRGB=function(t,e,i){var s,o,n,r=Math.floor(6*t),a=6*t-r,h=i*(1-e),d=i*(1-a*e),l=i*(1-(1-a)*e);switch(r%6){case 0:s=i,o=l,n=h;break;case 1:s=d,o=i,n=h;break;case 2:s=h,o=i,n=l;break;case 3:s=h,o=d,n=i;break;case 4:s=l,o=h,n=i;break;case 5:s=i,o=h,n=d}return{r:Math.floor(255*s),g:Math.floor(255*o),b:Math.floor(255*n)}},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.selectiveBridgeObject=function(t,e){if("object"==typeof e){for(var i=Object.create(e),s=0;se.start-a&&ne.start-a&&nn&&r>e||e>r&&a>e?(d=!0,r!=e&&("before"==s?e>n&&r>e&&(p=Math.max(0,p-1)):e>r&&a>e&&(p=Math.min(h.length-1,p+1)))):(e>r?l=Math.floor(.5*(c+l)):c=Math.floor(.5*(c+l)),o=Math.floor(.5*(c+l)),p==o?(p=-2,d=!0):p=o);return p};var DOMutil={};DOMutil.prepareElements=function(t){for(var e in t)t.hasOwnProperty(e)&&(t[e].redundant=t[e].used,t[e].used=[])},DOMutil.cleanupElements=function(t){for(var e in t)if(t.hasOwnProperty(e)&&t[e].redundant){for(var i=0;i0?(s=e[t].redundant[0],e[t].redundant.shift()):(s=document.createElementNS("http://www.w3.org/2000/svg",t),i.appendChild(s)):(s=document.createElementNS("http://www.w3.org/2000/svg",t),e[t]={used:[],redundant:[]},i.appendChild(s)),e[t].used.push(s),s},DOMutil.getDOMElement=function(t,e,i){var s;return e.hasOwnProperty(t)?e[t].redundant.length>0?(s=e[t].redundant[0],e[t].redundant.shift()):(s=document.createElement(t),i.appendChild(s)):(s=document.createElement(t),e[t]={used:[],redundant:[]},i.appendChild(s)),e[t].used.push(s),s},DOMutil.drawPoint=function(t,e,i,s,o){var n;return"circle"==i.options.drawPoints.style?(n=DOMutil.getSVGElement("circle",s,o),n.setAttributeNS(null,"cx",t),n.setAttributeNS(null,"cy",e),n.setAttributeNS(null,"r",.5*i.options.drawPoints.size),n.setAttributeNS(null,"class",i.className+" point")):(n=DOMutil.getSVGElement("rect",s,o),n.setAttributeNS(null,"x",t-.5*i.options.drawPoints.size),n.setAttributeNS(null,"y",e-.5*i.options.drawPoints.size),n.setAttributeNS(null,"width",i.options.drawPoints.size),n.setAttributeNS(null,"height",i.options.drawPoints.size),n.setAttributeNS(null,"class",i.className+" point")),n},DOMutil.drawBar=function(t,e,i,s,o,n,r){var a=DOMutil.getSVGElement("rect",n,r);a.setAttributeNS(null,"x",t-.5*i),a.setAttributeNS(null,"y",e),a.setAttributeNS(null,"width",i),a.setAttributeNS(null,"height",s),a.setAttributeNS(null,"class",o)},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 o=0;on;n++)i=o._addItem(t[n]),s.push(i);else if(util.isDataTable(t))for(var a=this._getColumnNames(t),h=0,d=t.getNumberOfRows();d>h;h++){for(var l={},c=0,p=a.length;p>c;c++){var u=a[c];l[u]=t.getValue(h,c)}i=o._addItem(l),s.push(i)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");i=o._addItem(t),s.push(i)}return s.length&&this._trigger("add",{items:s},e),s},DataSet.prototype.update=function(t,e){var i=[],s=[],o=this,n=o._fieldId,r=function(t){var e=t[n];o._data[e]?(e=o._updateItem(t),s.push(e)):(e=o._addItem(t),i.push(e))};if(Array.isArray(t))for(var a=0,h=t.length;h>a;a++)r(t[a]);else if(util.isDataTable(t))for(var d=this._getColumnNames(t),l=0,c=t.getNumberOfRows();c>l;l++){for(var p={},u=0,m=d.length;m>u;u++){var g=d[u];p[g]=t.getValue(l,u)}r(p)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");r(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,o=this,n=util.getType(arguments[0]);"String"==n||"Number"==n?(t=arguments[0],i=arguments[1],s=arguments[2]):"Array"==n?(e=arguments[0],i=arguments[1],s=arguments[2]):(i=arguments[0],s=arguments[1]);var r;if(i&&i.returnType){if(r="DataTable"==i.returnType?"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";var a,h,d,l,c=i&&i.type||this._options.type,p=i&&i.filter,u=[];if(void 0!=t)a=o._getItem(t,c),p&&!p(a)&&(a=null);else if(void 0!=e)for(d=0,l=e.length;l>d;d++)a=o._getItem(e[d],c),(!p||p(a))&&u.push(a);else for(h in this._data)this._data.hasOwnProperty(h)&&(a=o._getItem(h,c),(!p||p(a))&&u.push(a));if(i&&i.order&&void 0==t&&this._sort(u,i.order),i&&i.fields){var m=i.fields;if(void 0!=t)a=this._filterFields(a,m);else for(d=0,l=u.length;l>d;d++)u[d]=this._filterFields(u[d],m)}if("DataTable"==r){var g=this._getColumnNames(s);if(void 0!=t)o._appendRow(s,g,a);else for(d=0,l=u.length;l>d;d++)o._appendRow(s,g,u[d]);return s}if(void 0!=t)return a;if(s){for(d=0,l=u.length;l>d;d++)s.push(u[d]);return s}return u},DataSet.prototype.getIds=function(t){var e,i,s,o,n,r=this._data,a=t&&t.filter,h=t&&t.order,d=t&&t.type||this._options.type,l=[];if(a)if(h){n=[];for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&n.push(o));for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&l.push(o[this._fieldId]));else if(h){n=[];for(s in r)r.hasOwnProperty(s)&&n.push(r[s]);for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=r[s],l.push(o[this._fieldId]));return l},DataSet.prototype.getDataSet=function(){return this},DataSet.prototype.forEach=function(t,e){var i,s,o=e&&e.filter,n=e&&e.type||this._options.type,r=this._data;if(e&&e.order)for(var a=this.get(e),h=0,d=a.length;d>h;h++)i=a[h],s=i[this._fieldId],t(i,s);else for(s in r)r.hasOwnProperty(s)&&(i=this._getItem(s,n),(!o||o(i))&&t(i,s))},DataSet.prototype.map=function(t,e){var i,s=e&&e.filter,o=e&&e.type||this._options.type,n=[],r=this._data;for(var a in r)r.hasOwnProperty(a)&&(i=this._getItem(a,o),(!s||s(i))&&n.push(t(i,a)));return e&&e.order&&this._sort(n,e.order),n},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],o=e[i];return s>o?1:o>s?-1:0})}else{if("function"!=typeof e)throw new TypeError("Order must be a function or a string");t.sort(e)}},DataSet.prototype.remove=function(t,e){var i,s,o,n=[];if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)o=this._remove(t[i]),null!=o&&n.push(o);else o=this._remove(t),null!=o&&n.push(o);return n.length&&this._trigger("remove",{items:n},e),n},DataSet.prototype._remove=function(t){if(util.isNumber(t)||util.isString(t)){if(this._data[t])return delete this._data[t],t}else if(t instanceof Object){var e=t[this._fieldId];if(e&&this._data[e])return delete this._data[e],e}return null},DataSet.prototype.clear=function(t){var e=Object.keys(this._data);return this._data={},this._trigger("remove",{items:e},t),e},DataSet.prototype.max=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||r>s)&&(i=n,s=r)}return i},DataSet.prototype.min=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||s>r)&&(i=n,s=r)}return i},DataSet.prototype.distinct=function(t){var e,i=this._data,s=[],o=this._options.type&&this._options.type[t]||null,n=0;for(var r in i)if(i.hasOwnProperty(r)){var a=i[r],h=a[t],d=!1;for(e=0;n>e;e++)if(s[e]==h){d=!0;break}d||void 0===h||(s[n]=h,n++)}if(o)for(e=0;ei;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},DataSet.prototype._appendRow=function(t,e,i){for(var s=t.addRow(),o=0,n=e.length;n>o;o++){var r=e[o];t.setValue(s,o,i[r])}},DataView.prototype.setData=function(t){var e,i,s;if(this._data){this._data.unsubscribe&&this._data.unsubscribe("*",this.listener),e=[];for(var o in this._ids)this._ids.hasOwnProperty(o)&&e.push(o);this._ids={},this._trigger("remove",{items:e})}if(this._data=t,this._data){for(this._fieldId=this._options.fieldId||this._data&&this._data.options&&this._data.options.fieldId||"id",e=this._data.getIds({filter:this._options&&this._options.filter}),i=0,s=e.length;s>i;i++)o=e[i],this._ids[o]=!0;this._trigger("add",{items:e}),this._data.on&&this._data.on("*",this.listener)}},DataView.prototype.get=function(){var t,e,i,s=this,o=util.getType(arguments[0]);"String"==o||"Number"==o||"Array"==o?(t=arguments[0],e=arguments[1],i=arguments[2]):(e=arguments[0],i=arguments[1]);var n=util.extend({},this._options,e);this._options.filter&&e&&e.filter&&(n.filter=function(t){return s._options.filter(t)&&e.filter(t)});var r=[];return void 0!=t&&r.push(t),r.push(n),r.push(i),this._data&&this._data.get.apply(this._data,r)},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.getDataSet=function(){for(var t=this;t instanceof DataView;)t=t._data;return t||null},DataView.prototype._onEvent=function(t,e,i){var s,o,n,r,a=e&&e.items,h=this._data,d=[],l=[],c=[];if(a&&h){switch(t){case"add":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r&&(this._ids[n]=!0,d.push(n));break;case"update":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r?this._ids[n]?l.push(n):(this._ids[n]=!0,d.push(n)):this._ids[n]&&(delete this._ids[n],c.push(n));break;case"remove":for(s=0,o=a.length;o>s;s++)n=a[s],this._ids[n]&&(delete this._ids[n],c.push(n))}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,GraphGroup.prototype.setItems=function(t){null!=t?(this.itemsData=t,1==this.options.sort&&this.itemsData.sort(function(t,e){return t.x-e.x})):this.itemsData=[]},GraphGroup.prototype.setZeroPosition=function(t){this.zeroPosition=t},GraphGroup.prototype.setOptions=function(t){if(void 0!==t){var e=["sampling","style","sort","yAxisOrientation","barChart"];util.selectiveDeepExtend(e,this.options,t),util.mergeOptions(this.options,t,"catmullRom"),util.mergeOptions(this.options,t,"drawPoints"),util.mergeOptions(this.options,t,"shaded"),t.catmullRom&&"object"==typeof t.catmullRom&&t.catmullRom.parametrization&&("uniform"==t.catmullRom.parametrization?this.options.catmullRom.alpha=0:"chordal"==t.catmullRom.parametrization?this.options.catmullRom.alpha=1:(this.options.catmullRom.parametrization="centripetal",this.options.catmullRom.alpha=.5))}},GraphGroup.prototype.update=function(t){this.group=t,this.content=t.content||"graph",this.className=t.className||this.className||"graphGroup"+this.groupsUsingDefaultStyles[0]%10,this.setOptions(t.options)},GraphGroup.prototype.drawIcon=function(t,e,i,s,o,n){var r,a,h=.5*n,d=DOMutil.getSVGElement("rect",i,s);if(d.setAttributeNS(null,"x",t),d.setAttributeNS(null,"y",e-h),d.setAttributeNS(null,"width",o),d.setAttributeNS(null,"height",2*h),d.setAttributeNS(null,"class","outline"),"line"==this.options.style)r=DOMutil.getSVGElement("path",i,s),r.setAttributeNS(null,"class",this.className),r.setAttributeNS(null,"d","M"+t+","+e+" L"+(t+o)+","+e),1==this.options.shaded.enabled&&(a=DOMutil.getSVGElement("path",i,s),"top"==this.options.shaded.orientation?a.setAttributeNS(null,"d","M"+t+", "+(e-h)+"L"+t+","+e+" L"+(t+o)+","+e+" L"+(t+o)+","+(e-h)):a.setAttributeNS(null,"d","M"+t+","+e+" L"+t+","+(e+h)+" L"+(t+o)+","+(e+h)+"L"+(t+o)+","+e),a.setAttributeNS(null,"class",this.className+" iconFill")),1==this.options.drawPoints.enabled&&DOMutil.drawPoint(t+.5*o,e,this,i,s);else{var l=Math.round(.3*o),c=Math.round(.4*n),p=Math.round(.75*n),u=Math.round((o-2*l)/3);DOMutil.drawBar(t+.5*l+u,e+h-c-1,l,c,this.className+" bar",i,s),DOMutil.drawBar(t+1.5*l+u+2,e+h-p-1,l,p,this.className+" bar",i,s)}},Legend.prototype=new Component,Legend.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},Legend.prototype.updateGroup=function(t,e){this.groups[t]=e},Legend.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},Legend.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.className="legend",this.dom.frame.style.position="absolute",this.dom.frame.style.top="10px",this.dom.frame.style.display="block",this.dom.textArea=document.createElement("div"),this.dom.textArea.className="legendText",this.dom.textArea.style.position="relative",this.dom.textArea.style.top="0px",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.width=this.options.iconSize+5+"px",this.dom.frame.appendChild(this.svg),this.dom.frame.appendChild(this.dom.textArea)},Legend.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame)},Legend.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame)},Legend.prototype.setOptions=function(t){var e=["enabled","orientation","icons","left","right"];util.selectiveDeepExtend(e,this.options,t)},Legend.prototype.redraw=function(){if(0==this.options[this.side].visible||0==this.amountOfGroups||0==this.options.enabled)this.hide();else{this.show(),"top-left"==this.options[this.side].position||"bottom-left"==this.options[this.side].position?(this.dom.frame.style.left="4px",this.dom.frame.style.textAlign="left",this.dom.textArea.style.textAlign="left",this.dom.textArea.style.left=this.options.iconSize+15+"px",this.dom.textArea.style.right="",this.svg.style.left="0px",this.svg.style.right=""):(this.dom.frame.style.right="4px",this.dom.frame.style.textAlign="right",this.dom.textArea.style.textAlign="right",this.dom.textArea.style.right=this.options.iconSize+15+"px",this.dom.textArea.style.left="",this.svg.style.right="0px",this.svg.style.left=""),"top-left"==this.options[this.side].position||"top-right"==this.options[this.side].position?(this.dom.frame.style.top=4-Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.bottom=""):(this.dom.frame.style.bottom=4-Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.top=""),0==this.options.icons?(this.dom.frame.style.width=this.dom.textArea.offsetWidth+10+"px",this.dom.textArea.style.right="",this.dom.textArea.style.left="",this.svg.style.width="0px"):(this.dom.frame.style.width=this.options.iconSize+15+this.dom.textArea.offsetWidth+10+"px",this.drawLegendIcons());var t="";for(var e in this.groups)this.groups.hasOwnProperty(e)&&(t+=this.groups[e].content+"
");this.dom.textArea.innerHTML=t,this.dom.textArea.style.lineHeight=.75*this.options.iconSize+this.options.iconSpacing+"px"}},Legend.prototype.drawLegendIcons=function(){if(this.dom.frame.parentNode){DOMutil.prepareElements(this.svgElements);var t=window.getComputedStyle(this.dom.frame).paddingTop,e=Number(t.replace("px","")),i=e,s=this.options.iconSize,o=.75*this.options.iconSize,n=e+.5*o+3;this.svg.style.width=s+5+e+"px";for(var r in this.groups)this.groups.hasOwnProperty(r)&&(this.groups[r].drawIcon(i,n,this.svgElements,this.svg,s,o),n+=o+this.options.iconSpacing);DOMutil.cleanupElements(this.svgElements)}},DataAxis.prototype=new Component,DataAxis.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},DataAxis.prototype.updateGroup=function(t,e){this.groups[t]=e},DataAxis.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},DataAxis.prototype.setOptions=function(t){if(t){var e=!1;this.options.orientation!=t.orientation&&void 0!==t.orientation&&(e=!0);var i=["orientation","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","width","visible"];util.selectiveExtend(i,this.options,t),this.minWidth=Number((""+this.options.width).replace("px","")),1==e&&this.dom.frame&&(this.hide(),this.show())}},DataAxis.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.style.width=this.options.width,this.dom.frame.style.height=this.height,this.dom.lineContainer=document.createElement("div"),this.dom.lineContainer.style.width="100%",this.dom.lineContainer.style.height=this.height,this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.height="100%",this.svg.style.width="100%",this.svg.style.display="block",this.dom.frame.appendChild(this.svg)},DataAxis.prototype._redrawGroupIcons=function(){DOMutil.prepareElements(this.svgElements);var t,e=this.options.iconWidth,i=15,s=4,o=s+.5*i;t="left"==this.options.orientation?s:this.width-e-s;for(var n in this.groups)this.groups.hasOwnProperty(n)&&(this.groups[n].drawIcon(t,o,this.svgElements,this.svg,e,i),o+=i+s);DOMutil.cleanupElements(this.svgElements)},DataAxis.prototype.show=function(){this.dom.frame.parentNode||("left"==this.options.orientation?this.body.dom.left.appendChild(this.dom.frame):this.body.dom.right.appendChild(this.dom.frame)),this.dom.lineContainer.parentNode||this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer)},DataAxis.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.lineContainer.parentNode&&this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer)},DataAxis.prototype.setRange=function(t,e){this.range.start=t,this.range.end=e},DataAxis.prototype.redraw=function(){var t=!1;if(0==this.amountOfGroups)this.hide();else{this.show(),this.height=Number(this.linegraphSVG.style.height.replace("px","")),this.dom.lineContainer.style.height=this.height+"px",this.width=1==this.options.visible?Number((""+this.options.width).replace("px","")):0;var e=this.props,i=this.dom.frame;i.className="dataaxis",this._calculateCharSize();var s=this.options.orientation,o=this.options.showMinorLabels,n=this.options.showMajorLabels;e.minorLabelHeight=o?e.minorCharHeight:0,e.majorLabelHeight=n?e.majorCharHeight:0,e.minorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.minorLinesOffset,e.minorLineHeight=1,e.majorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.majorLinesOffset,e.majorLineHeight=1,"left"==s?(i.style.top="0",i.style.left="0",i.style.bottom="",i.style.width=this.width+"px",i.style.height=this.height+"px"):(i.style.top="",i.style.bottom="0",i.style.left="0",i.style.width=this.width+"px",i.style.height=this.height+"px"),t=this._redrawLabels(),1==this.options.icons&&this._redrawGroupIcons()}return t},DataAxis.prototype._redrawLabels=function(){DOMutil.prepareElements(this.DOMelements);var t=this.options.orientation,e=this.master?this.props.majorCharHeight||10:this.stepPixelsForced,i=new DataStep(this.range.start,this.range.end,e,this.dom.frame.offsetHeight);this.step=i,i.first();var s=this.dom.frame.offsetHeight/(i.marginRange/i.step+1);this.stepPixels=s;var o=this.height/s,n=0;if(0==this.master){s=this.stepPixelsForced,n=Math.round(this.height/s-o);for(var r=0;.5*n>r;r++)i.previous();o=this.height/s}this.valueAtZero=i.marginEnd;var a=0,h=1;i.next(),this.maxLabelSize=0;for(var d=0;h=0&&this._redrawLabel(d-2,i.getCurrent(),t,"yAxis major",this.props.majorCharHeight),this._redrawLine(d,t,"grid horizontal major",this.options.majorLinesOffset,this.props.majorLineWidth)):this._redrawLine(d,t,"grid horizontal minor",this.options.minorLinesOffset,this.props.minorLineWidth),i.next(),h++ -}this.conversionFactor=a/((o-1)*i.step);var c=1==this.options.icons?this.options.iconWidth+this.options.labelOffsetX+15:this.options.labelOffsetX+15;return this.maxLabelSize>this.width-c&&1==this.options.visible?(this.width=this.maxLabelSize+c,this.options.width=this.width+"px",DOMutil.cleanupElements(this.DOMelements),this.redraw(),!0):this.maxLabelSizethis.minWidth?(this.width=Math.max(this.minWidth,this.maxLabelSize+c),this.options.width=this.width+"px",DOMutil.cleanupElements(this.DOMelements),this.redraw(),!0):(DOMutil.cleanupElements(this.DOMelements),!1)},DataAxis.prototype._redrawLabel=function(t,e,i,s,o){var n=DOMutil.getDOMElement("div",this.DOMelements,this.dom.frame);n.className=s,n.innerHTML=e,"left"==i?(n.style.left="-"+this.options.labelOffsetX+"px",n.style.textAlign="right"):(n.style.right="-"+this.options.labelOffsetX+"px",n.style.textAlign="left"),n.style.top=t-.5*o+this.options.labelOffsetY+"px",e+="";var r=Math.max(this.props.majorCharWidth,this.props.minorCharWidth);this.maxLabelSize0){for(s=0;sc){e.push(m);break}e.push(m)}}else for(var u=0;ul&&m.x0){for(var p=0;pi?i:a,d=s>d?s:d):(r=!0,h=h>i?i:h,l=s>l?s:l)}1==n&&this.yAxisLeft.setRange(a,d),1==r&&this.yAxisRight.setRange(h,l)}return o=this._toggleAxisVisiblity(n,this.yAxisLeft)||o,o=this._toggleAxisVisiblity(r,this.yAxisRight)||o,1==r&&1==n?(this.yAxisLeft.drawIcons=!0,this.yAxisRight.drawIcons=!0):(this.yAxisLeft.drawIcons=!1,this.yAxisRight.drawIcons=!1),this.yAxisRight.master=!n,0==this.yAxisRight.master?(1==r&&(this.yAxisLeft.lineOffset=this.yAxisRight.width),o=this.yAxisLeft.redraw()||o,this.yAxisRight.stepPixelsForced=this.yAxisLeft.stepPixels,o=this.yAxisRight.redraw()||o):o=this.yAxisRight.redraw()||o,o},LineGraph.prototype._toggleAxisVisiblity=function(t,e){var i=!1;return 0==t?e.dom.frame.parentNode&&(e.hide(),i=!0):e.dom.frame.parentNode||(e.show(),i=!0),i},LineGraph.prototype._drawBarGraph=function(t,e){if(null!=t&&t.length>0){var i,s=.1*e.options.barChart.width,o=0,n=e.options.barChart.width;"left"==e.options.barChart.align?o-=.5*n:"right"==e.options.barChart.align&&(o+=.5*n);for(var r=0;r0&&(i=Math.min(i,Math.abs(t[r-1].x-t[r].x))),n>i&&(n=s>i?s:i),DOMutil.drawBar(t[r].x+o,t[r].y,n,e.zeroPosition-t[r].y,e.className+" bar",this.svgElements,this.svg);1==e.options.drawPoints.enabled&&this._drawPoints(t,e,this.svgElements,this.svg,o)}},LineGraph.prototype._drawLineGraph=function(t,e){if(null!=t&&t.length>0){var i,s,o=Number(this.svg.style.height.replace("px",""));if(i=DOMutil.getSVGElement("path",this.svgElements,this.svg),i.setAttributeNS(null,"class",e.className),s=1==e.options.catmullRom.enabled?this._catmullRom(t,e):this._linear(t),1==e.options.shaded.enabled){var n,r=DOMutil.getSVGElement("path",this.svgElements,this.svg);n="top"==e.options.shaded.orientation?"M"+t[0].x+",0 "+s+"L"+t[t.length-1].x+",0":"M"+t[0].x+","+o+" "+s+"L"+t[t.length-1].x+","+o,r.setAttributeNS(null,"class",e.className+" fill"),r.setAttributeNS(null,"d",n)}i.setAttributeNS(null,"d","M"+s),1==e.options.drawPoints.enabled&&this._drawPoints(t,e,this.svgElements,this.svg)}},LineGraph.prototype._drawPoints=function(t,e,i,s,o){void 0===o&&(o=0);for(var n=0;np;p+=r)i=n(t[p].x)+this.width-1,s=t[p].y,o.push({x:i,y:s}),h=h>s?s:h,d=s>d?s:d;return{min:h,max:d,data:o}},LineGraph.prototype._convertYvalues=function(t,e){var i,s,o=[],n=this.yAxisLeft,r=Number(this.svg.style.height.replace("px",""));"right"==e.options.yAxisOrientation&&(n=this.yAxisRight);for(var a=0;al;l++)e=0==l?t[0]:t[l-1],i=t[l],s=t[l+1],o=d>l+2?t[l+2]:s,n={x:(-e.x+6*i.x+s.x)*h,y:(-e.y+6*i.y+s.y)*h},r={x:(i.x+6*s.x-o.x)*h,y:(i.y+6*s.y-o.y)*h},a+="C"+n.x+","+n.y+" "+r.x+","+r.y+" "+s.x+","+s.y+" ";return a},LineGraph.prototype._catmullRom=function(t,e){var i=e.options.catmullRom.alpha;if(0==i||void 0===i)return this._catmullRomUniform(t);for(var s,o,n,r,a,h,d,l,c,p,u,m,g,f,v,y,b,_,w,x=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",S=t.length,D=0;S-1>D;D++)s=0==D?t[0]:t[D-1],o=t[D],n=t[D+1],r=S>D+2?t[D+2]:n,d=Math.sqrt(Math.pow(s.x-o.x,2)+Math.pow(s.y-o.y,2)),l=Math.sqrt(Math.pow(o.x-n.x,2)+Math.pow(o.y-n.y,2)),c=Math.sqrt(Math.pow(n.x-r.x,2)+Math.pow(n.y-r.y,2)),f=Math.pow(c,i),y=Math.pow(c,2*i),v=Math.pow(l,i),b=Math.pow(l,2*i),w=Math.pow(d,i),_=Math.pow(d,2*i),p=2*_+3*w*v+b,u=2*y+3*f*v+b,m=3*w*(w+v),m>0&&(m=1/m),g=3*f*(f+v),g>0&&(g=1/g),a={x:(-b*s.x+p*o.x+_*n.x)*m,y:(-b*s.y+p*o.y+_*n.y)*m},h={x:(y*o.x+u*n.x-b*r.x)*g,y:(y*o.y+u*n.y-b*r.y)*g},0==a.x&&0==a.y&&(a=o),0==h.x&&0==h.y&&(h=n),x+="C"+a.x+","+a.y+" "+h.x+","+h.y+" "+n.x+","+n.y+" ";return x},LineGraph.prototype._linear=function(t){for(var e="",i=0;in&&(h=n);for(var d=!1,l=h;Math.abs(l)<=Math.abs(n);l++){a=Math.pow(10,l);for(var c=0;c=o){d=!0,r=c;break}}if(1==d)break}this.stepIndex=r,this.scale=a,this.step=a*this.minorSteps[r]},DataStep.prototype.first=function(){this.setFirst()},DataStep.prototype.setFirst=function(){var t=this._start-this.scale*this.minorSteps[this.stepIndex],e=this._end+this.scale*this.minorSteps[this.stepIndex];this.marginEnd=this.roundToMinor(e),this.marginStart=this.roundToMinor(t),this.marginRange=this.marginEnd-this.marginStart,this.current=this.marginEnd},DataStep.prototype.roundToMinor=function(t){var e=t-t%(this.scale*this.minorSteps[this.stepIndex]);return t%(this.scale*this.minorSteps[this.stepIndex])>.5*this.scale*this.minorSteps[this.stepIndex]?e+this.scale*this.minorSteps[this.stepIndex]:e},DataStep.prototype.hasNext=function(){return this.current>=this.marginStart},DataStep.prototype.next=function(){var t=this.current;this.current-=this.step,this.current==t&&(this.current=this._end)},DataStep.prototype.previous=function(){this.current+=this.step,this.marginEnd+=this.step,this.marginRange=this.marginEnd-this.marginStart},DataStep.prototype.getCurrent=function(){for(var t=""+Number(this.current).toPrecision(5),e=t.length-1;e>0;e--){if("0"!=t[e]){if("."==t[e]||","==t[e]){t=t.slice(0,e);break}break}t=t.slice(0,e)}return t},DataStep.prototype.snap=function(){},DataStep.prototype.isMajor=function(){return this.current%(this.scale*this.majorSteps[this.stepIndex])==0};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,o;if(i)for(s=0,o=t.length;o>s;s++)t[s].top=null;for(s=0,o=t.length;o>s;s++){var n=t[s];if(null===n.top){n.top=e.axis;do{for(var r=null,a=0,h=t.length;h>a;a++){var d=t[a];if(null!==d.top&&d!==n&&stack.collision(n,d,e.item)){r=d;break}}null!=r&&(n.top=r.top+r.height+e.item)}while(r)}}},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,o=36e5,n=6e4,r=1e3,a=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*o>t&&(this.scale=TimeStep.SCALE.HOUR,this.step=4),o>t&&(this.scale=TimeStep.SCALE.HOUR,this.step=1),15*n>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=15),10*n>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=10),5*n>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=5),n>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=1),15*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=15),10*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=10),5*r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=5),r>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=1),200*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=200),100*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=100),50*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=50),10*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=10),5*a>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=5),a>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""}},Range.prototype=new Component,Range.prototype.setOptions=function(t){if(t){var e=["direction","min","max","zoomMin","zoomMax","moveable","zoomable"];util.selectiveExtend(e,this.options,t),("start"in t||"end"in t)&&this.setRange(t.start,t.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.body.emitter.emit("rangechange",s),this.body.emitter.emit("rangechanged",s)}},Range.prototype._applyRange=function(t,e){var i,s=null!=t?util.convert(t,"Date").valueOf():this.start,o=null!=e?util.convert(e,"Date").valueOf():this.end,n=null!=this.options.max?util.convert(this.options.max,"Date").valueOf():null,r=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(o)||null===o)throw new Error('Invalid end "'+e+'"');if(s>o&&(o=s),null!==r&&r>s&&(i=r-s,s+=i,o+=i,null!=n&&o>n&&(o=n)),null!==n&&o>n&&(i=o-n,s-=i,o-=i,null!=r&&r>s&&(s=r)),null!==this.options.zoomMin){var a=parseFloat(this.options.zoomMin);0>a&&(a=0),a>o-s&&(this.end-this.start===a?(s=this.start,o=this.end):(i=a-(o-s),s-=i/2,o+=i/2))}if(null!==this.options.zoomMax){var h=parseFloat(this.options.zoomMax);0>h&&(h=0),o-s>h&&(this.end-this.start===h?(s=this.start,o=this.end):(i=o-s-h,s+=i/2,o-=i/2))}var d=this.start!=s||this.end!=o;return this.start=s,this.end=o,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}},Range.prototype._onDragStart=function(){this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.start=this.start,this.props.touch.end=this.end,this.body.dom.root&&(this.body.dom.root.style.cursor="move"))},Range.prototype._onDrag=function(t){if(this.options.moveable){var e=this.options.direction;if(validateDirection(e),this.props.touch.allowDragging){var i="horizontal"==e?t.gesture.deltaX:t.gesture.deltaY,s=this.props.touch.end-this.props.touch.start,o="horizontal"==e?this.body.domProps.center.width:this.body.domProps.center.height,n=-i/o*s;this._applyRange(this.props.touch.start+n,this.props.touch.end+n),this.body.emitter.emit("rangechange",{start:new Date(this.start),end:new Date(this.end)})}}},Range.prototype._onDragEnd=function(){this.options.moveable&&this.props.touch.allowDragging&&(this.body.dom.root&&(this.body.dom.root.style.cursor="auto"),this.body.emitter.emit("rangechanged",{start:new Date(this.start),end:new Date(this.end)}))},Range.prototype._onMouseWheel=function(t){if(this.options.zoomable&&this.options.moveable){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i;i=0>e?1-e/5:1/(1+e/5);var s=util.fakeGesture(this,t),o=getPointer(s.center,this.body.dom.center),n=this._pointerToDate(o);this.zoom(i,n)}t.preventDefault()}},Range.prototype._onTouch=function(){this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.allowDragging=!0,this.props.touch.center=null},Range.prototype._onHold=function(){this.props.touch.allowDragging=!1},Range.prototype._onPinch=function(t){if(this.options.zoomable&&this.options.moveable&&(this.props.touch.allowDragging=!1,t.gesture.touches.length>1)){this.props.touch.center||(this.props.touch.center=getPointer(t.gesture.center,this.body.dom.center));var e=1/t.gesture.scale,i=this._pointerToDate(this.props.touch.center),s=parseInt(i+(this.props.touch.start-i)*e),o=parseInt(i+(this.props.touch.end-i)*e);this.setRange(s,o)}},Range.prototype._pointerToDate=function(t){var e,i=this.options.direction;if(validateDirection(i),"horizontal"==i){var s=this.body.domProps.center.width;return e=this.conversion(s),t.x/e.scale+e.offset}var o=this.body.domProps.center.height;return e=this.conversion(o),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,o=this.end-i;this.setRange(s,o)},Component.prototype.setOptions=function(t){t&&util.extend(this.options,t)},Component.prototype.redraw=function(){return!1},Component.prototype.destroy=function(){},Component.prototype._isResized=function(){var t=this.props._previousWidth!==this.props.width||this.props._previousHeight!==this.props.height;return this.props._previousWidth=this.props.width,this.props._previousHeight=this.props.height,t},TimeAxis.prototype=new Component,TimeAxis.prototype.setOptions=function(t){t&&util.selectiveExtend(["orientation","showMinorLabels","showMajorLabels"],this.options,t)},TimeAxis.prototype._create=function(){this.dom.foreground=document.createElement("div"),this.dom.background=document.createElement("div"),this.dom.foreground.className="timeaxis foreground",this.dom.background.className="timeaxis background"},TimeAxis.prototype.destroy=function(){this.dom.foreground.parentNode&&this.dom.foreground.parentNode.removeChild(this.dom.foreground),this.dom.background.parentNode&&this.dom.background.parentNode.removeChild(this.dom.background),this.body=null -},TimeAxis.prototype.redraw=function(){var t=this.options,e=this.props,i=this.dom.foreground,s=this.dom.background,o="top"==t.orientation?this.body.dom.top:this.body.dom.bottom,n=i.parentNode!==o;this._calculateCharSize();var r=(this.options.orientation,this.options.showMinorLabels),a=this.options.showMajorLabels;e.minorLabelHeight=r?e.minorCharHeight:0,e.majorLabelHeight=a?e.majorCharHeight:0,e.height=e.minorLabelHeight+e.majorLabelHeight,e.width=i.offsetWidth,e.minorLineHeight=this.body.domProps.root.height-e.majorLabelHeight-("top"==t.orientation?this.body.domProps.bottom.height:this.body.domProps.top.height),e.minorLineWidth=1,e.majorLineHeight=e.minorLineHeight+e.majorLabelHeight,e.majorLineWidth=1;var h=i.nextSibling,d=s.nextSibling;return i.parentNode&&i.parentNode.removeChild(i),s.parentNode&&s.parentNode.removeChild(s),i.style.height=this.props.height+"px",this._repaintLabels(),h?o.insertBefore(i,h):o.appendChild(i),d?this.body.dom.backgroundVertical.insertBefore(s,d):this.body.dom.backgroundVertical.appendChild(s),this._isResized()||n},TimeAxis.prototype._repaintLabels=function(){var t=this.options.orientation,e=util.convert(this.body.range.start,"Number"),i=util.convert(this.body.range.end,"Number"),s=this.body.util.toTime(7*(this.props.minorCharWidth||10)).valueOf()-this.body.util.toTime(0).valueOf(),o=new TimeStep(new Date(e),new Date(i),s);this.step=o;var n=this.dom;n.redundant.majorLines=n.majorLines,n.redundant.majorTexts=n.majorTexts,n.redundant.minorLines=n.minorLines,n.redundant.minorTexts=n.minorTexts,n.majorLines=[],n.majorTexts=[],n.minorLines=[],n.minorTexts=[],o.first();for(var r=void 0,a=0;o.hasNext()&&1e3>a;){a++;var h=o.getCurrent(),d=this.body.util.toScreen(h),l=o.isMajor();this.options.showMinorLabels&&this._repaintMinorText(d,o.getLabelMinor(),t),l&&this.options.showMajorLabels?(d>0&&(void 0==r&&(r=d),this._repaintMajorText(d,o.getLabelMajor(),t)),this._repaintMajorLine(d,t)):this._repaintMinorLine(d,t),o.next()}if(this.options.showMajorLabels){var c=this.body.util.toTime(0),p=o.getLabelMajor(c),u=p.length*(this.props.majorCharWidth||10)+10;(void 0==r||r>u)&&this._repaintMajorText(0,p,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 o=document.createTextNode("");s=document.createElement("div"),s.appendChild(o),s.className="text minor",this.dom.foreground.appendChild(s)}this.dom.minorTexts.push(s),s.childNodes[0].nodeValue=e,s.style.top="top"==i?this.props.majorLabelHeight+"px":"0",s.style.left=t+"px"},TimeAxis.prototype._repaintMajorText=function(t,e,i){var s=this.dom.redundant.majorTexts.shift();if(!s){var o=document.createTextNode(e);s=document.createElement("div"),s.className="text major",s.appendChild(o),this.dom.foreground.appendChild(s)}this.dom.majorTexts.push(s),s.childNodes[0].nodeValue=e,s.style.top="top"==i?"0":this.props.minorLabelHeight+"px",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.dom.background.appendChild(i)),this.dom.minorLines.push(i);var s=this.props;i.style.top="top"==e?s.majorLabelHeight+"px":this.body.domProps.top.height+"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.dom.background.appendChild(i)),this.dom.majorLines.push(i);var s=this.props;i.style.top="top"==e?"0":this.body.domProps.top.height+"px",i.style.left=t-s.majorLineWidth/2+"px",i.style.height=s.majorLineHeight+"px"},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.dom.foreground.appendChild(this.dom.measureCharMinor)),this.props.minorCharHeight=this.dom.measureCharMinor.clientHeight,this.props.minorCharWidth=this.dom.measureCharMinor.clientWidth,this.dom.measureCharMajor||(this.dom.measureCharMajor=document.createElement("DIV"),this.dom.measureCharMajor.className="text minor measure",this.dom.measureCharMajor.style.position="absolute",this.dom.measureCharMajor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMajor)),this.props.majorCharHeight=this.dom.measureCharMajor.clientHeight,this.props.majorCharWidth=this.dom.measureCharMajor.clientWidth},TimeAxis.prototype.snap=function(t){return this.step.snap(t)},CurrentTime.prototype=new Component,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.destroy=function(){this.options.showCurrentTime=!1,this.redraw(),this.body=null},CurrentTime.prototype.setOptions=function(t){t&&util.selectiveExtend(["showCurrentTime"],this.options,t)},CurrentTime.prototype.redraw=function(){if(this.options.showCurrentTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar),this.start());var e=new Date,i=this.body.util.toScreen(e);this.bar.style.left=i+"px",this.bar.title="Current time: "+e}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.stop();return!1},CurrentTime.prototype.start=function(){function t(){e.stop();var i=e.body.range.conversion(e.body.domProps.center.width).scale,s=1/i/10;30>s&&(s=30),s>1e3&&(s=1e3),e.redraw(),e.currentTimeTimer=setTimeout(t,s)}var e=this;t()},CurrentTime.prototype.stop=function(){void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer)},CustomTime.prototype=new Component,CustomTime.prototype.setOptions=function(t){t&&util.selectiveExtend(["showCustomTime"],this.options,t)},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.destroy=function(){this.options.showCustomTime=!1,this.redraw(),this.hammer.enable(!1),this.hammer=null,this.body=null},CustomTime.prototype.redraw=function(){if(this.options.showCustomTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar));var e=this.body.util.toScreen(this.customTime);this.bar.style.left=e+"px",this.bar.title="Time: "+this.customTime}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar);return!1},CustomTime.prototype.setCustomTime=function(t){this.customTime=new Date(t.valueOf()),this.redraw()},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.body.util.toScreen(this.eventParams.customTime)+e,s=this.body.util.toTime(i);this.setCustomTime(s),this.body.emitter.emit("timechange",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault()}},CustomTime.prototype._onDragEnd=function(t){this.eventParams.dragging&&(this.body.emitter.emit("timechanged",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault())};var UNGROUPED="__ungrouped__";ItemSet.prototype=new Component,ItemSet.types={box:ItemBox,range:ItemRange,point:ItemPoint},ItemSet.prototype._create=function(){var t=document.createElement("div");t.className="itemset",t["timeline-itemset"]=this,this.dom.frame=t;var e=document.createElement("div");e.className="background",t.appendChild(e),this.dom.background=e;var i=document.createElement("div");i.className="foreground",t.appendChild(i),this.dom.foreground=i;var s=document.createElement("div");s.className="axis",this.dom.axis=s;var o=document.createElement("div");o.className="labelset",this.dom.labelSet=o,this._updateUngrouped(),this.hammer=Hammer(this.body.dom.centerContainer,{prevent_default:!0}),this.hammer.on("touch",this._onTouch.bind(this)),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this)),this.hammer.on("tap",this._onSelectItem.bind(this)),this.hammer.on("hold",this._onMultiSelectItem.bind(this)),this.hammer.on("doubletap",this._onAddItem.bind(this)),this.show()},ItemSet.prototype.setOptions=function(t){if(t){var e=["type","align","orientation","padding","stack","selectable","groupOrder"];util.selectiveExtend(e,this.options,t),"margin"in t&&("number"==typeof t.margin?(this.options.margin.axis=t.margin,this.options.margin.item=t.margin):"object"==typeof t.margin&&util.selectiveExtend(["axis","item"],this.options.margin,t.margin)),"editable"in t&&("boolean"==typeof t.editable?(this.options.editable.updateTime=t.editable,this.options.editable.updateGroup=t.editable,this.options.editable.add=t.editable,this.options.editable.remove=t.editable):"object"==typeof t.editable&&util.selectiveExtend(["updateTime","updateGroup","add","remove"],this.options.editable,t.editable));var i=function(e){if(e in t){var i=t[e];if(!(i instanceof Function)||2!=i.length)throw new Error("option "+e+" must be a function "+e+"(item, callback)");this.options[e]=i}}.bind(this);["onAdd","onUpdate","onRemove","onMove"].forEach(i),this.markDirty()}},ItemSet.prototype.markDirty=function(){this.groupIds=[],this.stackDirty=!0},ItemSet.prototype.destroy=function(){this.hide(),this.setItems(null),this.setGroups(null),this.hammer=null,this.body=null,this.conversion=null},ItemSet.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.axis.parentNode&&this.dom.axis.parentNode.removeChild(this.dom.axis),this.dom.labelSet.parentNode&&this.dom.labelSet.parentNode.removeChild(this.dom.labelSet)},ItemSet.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame),this.dom.axis.parentNode||this.body.dom.backgroundVertical.appendChild(this.dom.axis),this.dom.labelSet.parentNode||this.body.dom.left.appendChild(this.dom.labelSet)},ItemSet.prototype.setSelection=function(t){var e,i,s,o;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],o=this.items[s],o&&o.unselect();for(this.selection=[],e=0,i=t.length;i>e;e++)s=t[e],o=this.items[s],o&&(this.selection.push(s),o.select())}},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.redraw=function(){var t=this.options.margin,e=this.body.range,i=util.option.asSize,s=this.options,o=s.orientation,n=!1,r=this.dom.frame,a=s.editable.updateTime||s.editable.updateGroup;r.className="itemset"+(a?" editable":""),n=this._orderGroups()||n;var h=e.end-e.start,d=h!=this.lastVisibleInterval||this.props.width!=this.props.lastWidth;d&&(this.stackDirty=!0),this.lastVisibleInterval=h,this.props.lastWidth=this.props.width;var l=this.stackDirty,c=this._firstGroup(),p={item:t.item,axis:t.axis},u={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?p:u,s=t.redraw(e,i,l);n=s||n,m+=t.height}),m=Math.max(m,g),this.stackDirty=!1,r.style.height=i(m),this.props.top=r.offsetTop,this.props.left=r.offsetLeft,this.props.width=r.offsetWidth,this.props.height=m,this.dom.axis.style.top=i("top"==o?this.body.domProps.top.height+this.body.domProps.border.top:this.body.domProps.top.height+this.body.domProps.centerContainer.height),this.dom.axis.style.left=this.body.domProps.border.left+"px",n=this._isResized()||n},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.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.off(e,t)}),e=s.getIds(),this._onRemove(e)),this.itemsData){var o=this.id;util.forEach(this.itemListeners,function(t,e){i.itemsData.on(e,t,o)}),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.body.emitter.emit("change")},ItemSet.prototype.getGroups=function(){return this.groupsData},ItemSet.prototype.removeItem=function(t){var e=this.itemsData.get(t),i=this.itemsData.getDataSet();e&&this.options.onRemove(e,function(e){e&&i.remove(t)})},ItemSet.prototype._onUpdate=function(t){var e=this;t.forEach(function(t){var i=e.itemsData.get(t,e.itemOptions),s=e.items[t],o=i.type||e.options.type||(i.end?"range":"box"),n=ItemSet.types[o];if(s&&(n&&s instanceof n?e._updateItem(s,i):(e._removeItem(s),s=null)),!s){if(!n)throw new TypeError("rangeoverflow"==o?'Item type "rangeoverflow" is deprecated. Use css styling instead: .vis.timeline .item.range .content {overflow: visible;}':'Unknown item type "'+o+'"');s=new n(i,e.conversion,e.options),s.id=t,e._addItem(s)}}),this._order(),this.stackDirty=!0,this.body.emitter.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.body.emitter.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 o=Object.create(e.options);util.extend(o,{height:null}),s=new Group(t,i,e),e.groups[t]=s;for(var n in e.items)if(e.items.hasOwnProperty(n)){var r=e.items[n];r.data.group==t&&s.add(r)}s.order(),s.show()}}),this.body.emitter.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.body.emitter.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.redraw(),i!=t.data.group){var s=this.groups[i];s&&s.remove(t);var o=this.groupsData?t.data.group:UNGROUPED,n=this.groups[o];n&&n.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;i0||s.length>0)&&this.body.emitter.emit("select",{items:this.getSelection()}),t.stopPropagation()}},ItemSet.prototype._onAddItem=function(t){if(this.options.selectable&&this.options.editable.add){var e=this,i=this.body.util.snap||null,s=ItemSet.itemFromTarget(t);if(s){var o=e.itemsData.get(s.id);this.options.onUpdate(o,function(t){t&&e.itemsData.update(t)})}else{var n=vis.util.getAbsoluteLeft(this.dom.frame),r=t.gesture.center.pageX-n,a=this.body.util.toTime(r),h={start:i?i(a):a,content:"new item"};if("range"===this.options.type){var d=this.body.util.toTime(r+this.props.width/5);h.end=i?i(d):d}h[this.itemsData.fieldId]=util.randomUUID();var l=ItemSet.groupFromTarget(t);l&&(h.group=l.groupId),this.options.onAdd(h,function(t){t&&e.itemsData.add(h)})}}},ItemSet.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.body.emitter.emit("select",{items:this.getSelection()}),t.stopPropagation()}}},ItemSet.itemFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-item"))return e["timeline-item"];e=e.parentNode}return null},ItemSet.groupFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-group"))return e["timeline-group"];e=e.parentNode}return null},ItemSet.itemSetFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-itemset"))return e["timeline-itemset"];e=e.parentNode}return null},Item.prototype.select=function(){this.selected=!0,this.displayed&&this.redraw()},Item.prototype.unselect=function(){this.selected=!1,this.displayed&&this.redraw()},Item.prototype.setParent=function(t){this.displayed?(this.hide(),this.parent=t,this.parent&&this.show()):this.parent=t},Item.prototype.isVisible=function(){return!1},Item.prototype.show=function(){return!1},Item.prototype.hide=function(){return!1},Item.prototype.redraw=function(){},Item.prototype.repositionX=function(){},Item.prototype.repositionY=function(){},Item.prototype._repaintDeleteButton=function(t){if(this.selected&&this.options.editable.remove&&!this.dom.deleteButton){var e=this,i=document.createElement("div");i.className="delete",i.title="Delete this item",Hammer(i,{preventDefault:!0}).on("tap",function(t){e.parent.removeFromDataSet(e),t.stopPropagation()}),t.appendChild(i),this.dom.deleteButton=i}else!this.selected&&this.dom.deleteButton&&(this.dom.deleteButton.parentNode&&this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton),this.dom.deleteButton=null)},ItemBox.prototype=new Item(null,null,null),ItemBox.prototype.isVisible=function(t){var e=(t.end-t.start)/4;return this.data.start>t.start-e&&this.data.startt.start-e&&this.data.startt.start},ItemRange.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),t.box["timeline-item"]=this),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.foreground;if(!e)throw new Error("Cannot redraw 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}this.data.title!=this.title&&(t.box.title=this.data.title,this.title=this.data.title);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.overflow="hidden"!==window.getComputedStyle(t.content).overflow,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.redraw()},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.conversion.toScreen(this.data.start),o=this.conversion.toScreen(this.data.end),n=this.options.padding;-i>s&&(s=-i),o>2*i&&(o=2*i);var r=Math.max(o-s,1);this.overflow?(t=Math.max(-s,0),this.left=s,this.width=r+this.props.content.width):(t=0>s?Math.min(-s,o-s-e.content.width-2*n):0,this.left=s,this.width=r),this.dom.box.style.left=this.left+"px",this.dom.box.style.width=r+"px",this.dom.content.style.left=t+"px"},ItemRange.prototype.repositionY=function(){var t=this.options.orientation,e=this.dom.box;e.style.top="top"==t?this.top+"px":this.parent.height-this.top-this.height+"px"},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)},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.background.className="group",this.dom.axis=document.createElement("div"),this.dom.axis.className="group",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,this.dom.label.title=t&&t.title||"",this.dom.inner.firstChild?util.removeClassName(this.dom.inner,"hidden"):util.addClassName(this.dom.inner,"hidden");var i=t&&t.className||null;i!=this.className&&(this.className&&(util.removeClassName(this.dom.label,i),util.removeClassName(this.dom.foreground,i),util.removeClassName(this.dom.background,i),util.removeClassName(this.dom.axis,i)),util.addClassName(this.dom.label,i),util.addClassName(this.dom.foreground,i),util.addClassName(this.dom.background,i),util.addClassName(this.dom.axis,i))},Group.prototype.getLabelWidth=function(){return this.props.label.width},Group.prototype.redraw=function(t,e,i){var s=!1;this.visibleItems=this._updateVisibleItems(this.orderedItems,this.visibleItems,t);var o=this.dom.marker.clientHeight;o!=this.lastMarkerHeight&&(this.lastMarkerHeight=o,util.forEach(this.items,function(t){t.dirty=!0,t.displayed&&t.redraw()}),i=!0),this.itemSet.options.stack?stack.stack(this.visibleItems,e,i):stack.nostack(this.visibleItems,e);var n,r=this.visibleItems;if(r.length){var a=r[0].top,h=r[0].top+r[0].height;if(util.forEach(r,function(t){a=Math.min(a,t.top),h=Math.max(h,t.top+t.height)}),a>e.axis){var d=a-e.axis;h-=d,util.forEach(r,function(t){t.top-=d})}n=h+e.item/2}else n=e.axis+e.item;n=Math.max(n,this.props.label.height);var l=this.dom.foreground;this.top=l.offsetTop,this.left=l.offsetLeft,this.width=l.offsetWidth,s=util.updateProperty(this,"height",n)||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,this.dom.background.style.height=n+"px",this.dom.foreground.style.height=n+"px",this.dom.label.style.height=n+"px";for(var c=0,p=this.visibleItems.length;p>c;c++){var u=this.visibleItems[c];u.repositionY()}return s},Group.prototype.show=function(){this.dom.label.parentNode||this.itemSet.dom.labelSet.appendChild(this.dom.label),this.dom.foreground.parentNode||this.itemSet.dom.foreground.appendChild(this.dom.foreground),this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background),this.dom.axis.parentNode||this.itemSet.dom.axis.appendChild(this.dom.axis)},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.body.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(o=0;o=0&&!this._checkIfInvisible(t.byStart[o],n,i);o--);for(o=s+1;o=0&&!this._checkIfInvisible(t.byEnd[o],n,i);o--);for(o=r+1;o=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.getDataSet(),e=null,i=null;if(t){var s=t.min("start");e=s?util.convert(s.start,"Date").valueOf():null;var o=t.max("start");o&&(i=util.convert(o.start,"Date").valueOf());var n=t.max("end");n&&(i=null==i?util.convert(n.end,"Date").valueOf():Math.max(i,util.convert(n.end,"Date").valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},Timeline.prototype.setSelection=function(t){this.itemSet&&this.itemSet.setSelection(t)},Timeline.prototype.getSelection=function(){return this.itemSet&&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(){var t=!1,e=this.options,i=this.props,s=this.dom;if(s){s.root.className="vis timeline root "+e.orientation,s.root.style.maxHeight=util.option.asSize(e.maxHeight,""),s.root.style.minHeight=util.option.asSize(e.minHeight,""),s.root.style.width=util.option.asSize(e.width,""),i.border.left=(s.centerContainer.offsetWidth-s.centerContainer.clientWidth)/2,i.border.right=i.border.left,i.border.top=(s.centerContainer.offsetHeight-s.centerContainer.clientHeight)/2,i.border.bottom=i.border.top;var o=s.root.offsetHeight-s.root.clientHeight,n=s.root.offsetWidth-s.root.clientWidth;i.center.height=s.center.offsetHeight,i.left.height=s.left.offsetHeight,i.right.height=s.right.offsetHeight,i.top.height=s.top.clientHeight||-i.border.top,i.bottom.height=s.bottom.clientHeight||-i.border.bottom;var r=Math.max(i.left.height,i.center.height,i.right.height),a=i.top.height+r+i.bottom.height+o+i.border.top+i.border.bottom;s.root.style.height=util.option.asSize(e.height,a+"px"),i.root.height=s.root.offsetHeight,i.background.height=i.root.height-o;var h=i.root.height-i.top.height-i.bottom.height-o;i.centerContainer.height=h,i.leftContainer.height=h,i.rightContainer.height=i.leftContainer.height,i.root.width=s.root.offsetWidth,i.background.width=i.root.width-n,i.left.width=s.leftContainer.clientWidth||-i.border.left,i.leftContainer.width=i.left.width,i.right.width=s.rightContainer.clientWidth||-i.border.right,i.rightContainer.width=i.right.width;var d=i.root.width-i.left.width-i.right.width-n;i.center.width=d,i.centerContainer.width=d,i.top.width=d,i.bottom.width=d,s.background.style.height=i.background.height+"px",s.backgroundVertical.style.height=i.background.height+"px",s.backgroundHorizontal.style.height=i.centerContainer.height+"px",s.centerContainer.style.height=i.centerContainer.height+"px",s.leftContainer.style.height=i.leftContainer.height+"px",s.rightContainer.style.height=i.rightContainer.height+"px",s.background.style.width=i.background.width+"px",s.backgroundVertical.style.width=i.centerContainer.width+"px",s.backgroundHorizontal.style.width=i.background.width+"px",s.centerContainer.style.width=i.center.width+"px",s.top.style.width=i.top.width+"px",s.bottom.style.width=i.bottom.width+"px",s.background.style.left="0",s.background.style.top="0",s.backgroundVertical.style.left=i.left.width+"px",s.backgroundVertical.style.top="0",s.backgroundHorizontal.style.left="0",s.backgroundHorizontal.style.top=i.top.height+"px",s.centerContainer.style.left=i.left.width+"px",s.centerContainer.style.top=i.top.height+"px",s.leftContainer.style.left="0",s.leftContainer.style.top=i.top.height+"px",s.rightContainer.style.left=i.left.width+i.center.width+"px",s.rightContainer.style.top=i.top.height+"px",s.top.style.left=i.left.width+"px",s.top.style.top="0",s.bottom.style.left=i.left.width+"px",s.bottom.style.top=i.top.height+i.centerContainer.height+"px",this._updateScrollTop();var l=this.props.scrollTop;"bottom"==e.orientation&&(l+=Math.max(this.props.centerContainer.height-this.props.center.height-this.props.border.top-this.props.border.bottom,0)),s.center.style.left="0",s.center.style.top=l+"px",s.left.style.left="0",s.left.style.top=l+"px",s.right.style.left="0",s.right.style.top=l+"px";var c=0==this.props.scrollTop?"hidden":"",p=this.props.scrollTop==this.props.scrollTopMin?"hidden":"";s.shadowTop.style.visibility=c,s.shadowBottom.style.visibility=p,s.shadowTopLeft.style.visibility=c,s.shadowBottomLeft.style.visibility=p,s.shadowTopRight.style.visibility=c,s.shadowBottomRight.style.visibility=p,this.components.forEach(function(e){t=e.redraw()||t}),t&&this.redraw()}},Timeline.prototype.repaint=function(){throw new Error("Function repaint is deprecated. Use redraw instead.")},Timeline.prototype._toTime=function(t){var e=this.range.conversion(this.props.center.width);return new Date(t/e.scale+e.offset)},Timeline.prototype._toGlobalTime=function(t){var e=this.range.conversion(this.props.root.width);return new Date(t/e.scale+e.offset)},Timeline.prototype._toScreen=function(t){var e=this.range.conversion(this.props.center.width);return(t.valueOf()-e.offset)*e.scale},Timeline.prototype._toGlobalScreen=function(t){var e=this.range.conversion(this.props.root.width);return(t.valueOf()-e.offset)*e.scale},Timeline.prototype._initAutoResize=function(){1==this.options.autoResize?this._startAutoResize():this._stopAutoResize()},Timeline.prototype._startAutoResize=function(){var t=this;this._stopAutoResize(),this._onResize=function(){return 1!=t.options.autoResize?void t._stopAutoResize():void(t.dom.root&&(t.dom.root.clientWidth!=t.props.lastWidth||t.dom.root.clientHeight!=t.props.lastHeight)&&(t.props.lastWidth=t.dom.root.clientWidth,t.props.lastHeight=t.dom.root.clientHeight,t.emit("change")))},util.addEventListener(window,"resize",this._onResize),this.watchTimer=setInterval(this._onResize,1e3)},Timeline.prototype._stopAutoResize=function(){this.watchTimer&&(clearInterval(this.watchTimer),this.watchTimer=void 0),util.removeEventListener(window,"resize",this._onResize),this._onResize=null},Timeline.prototype._onTouch=function(){this.touch.allowDragging=!0},Timeline.prototype._onPinch=function(){this.touch.allowDragging=!1},Timeline.prototype._onDragStart=function(){this.touch.initialScrollTop=this.props.scrollTop},Timeline.prototype._onDrag=function(t){if(this.touch.allowDragging){var e=t.gesture.deltaY,i=this._getScrollTop(),s=this._setScrollTop(this.touch.initialScrollTop+e);s!=i&&this.redraw()}},Timeline.prototype._setScrollTop=function(t){return this.props.scrollTop=t,this._updateScrollTop(),this.props.scrollTop},Timeline.prototype._updateScrollTop=function(){var t=Math.min(this.props.centerContainer.height-this.props.center.height,0);return t!=this.props.scrollTopMin&&("bottom"==this.options.orientation&&(this.props.scrollTop+=t-this.props.scrollTopMin),this.props.scrollTopMin=t),this.props.scrollTop>0&&(this.props.scrollTop=0),this.props.scrollTop=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)},Graph2d.prototype.getItemRange=function(){var t=this.itemsData,e=null,i=null;if(t){var s=t.min("start");e=s?util.convert(s.start,"Date").valueOf():null;var o=t.max("start");o&&(i=util.convert(o.start,"Date").valueOf());var n=t.max("end");n&&(i=null==i?util.convert(n.end,"Date").valueOf():Math.max(i,util.convert(n.end,"Date").valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},Graph2d.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)},Graph2d.prototype.getWindow=function(){var t=this.range.getRange();return{start:new Date(t.start),end:new Date(t.end)}},Graph2d.prototype.redraw=function(){var t=!1,e=this.options,i=this.props,s=this.dom;if(s){s.root.className="vis timeline root "+e.orientation,s.root.style.maxHeight=util.option.asSize(e.maxHeight,""),s.root.style.minHeight=util.option.asSize(e.minHeight,""),s.root.style.width=util.option.asSize(e.width,""),i.border.left=(s.centerContainer.offsetWidth-s.centerContainer.clientWidth)/2,i.border.right=i.border.left,i.border.top=(s.centerContainer.offsetHeight-s.centerContainer.clientHeight)/2,i.border.bottom=i.border.top;var o=s.root.offsetHeight-s.root.clientHeight,n=s.root.offsetWidth-s.root.clientWidth;i.center.height=s.center.offsetHeight,i.left.height=s.left.offsetHeight,i.right.height=s.right.offsetHeight,i.top.height=s.top.clientHeight||-i.border.top,i.bottom.height=s.bottom.clientHeight||-i.border.bottom;var r=Math.max(i.left.height,i.center.height,i.right.height),a=i.top.height+r+i.bottom.height+o+i.border.top+i.border.bottom;s.root.style.height=util.option.asSize(e.height,a+"px"),i.root.height=s.root.offsetHeight,i.background.height=i.root.height-o;var h=i.root.height-i.top.height-i.bottom.height-o;i.centerContainer.height=h,i.leftContainer.height=h,i.rightContainer.height=i.leftContainer.height,i.root.width=s.root.offsetWidth,i.background.width=i.root.width-n,i.left.width=s.leftContainer.clientWidth||-i.border.left,i.leftContainer.width=i.left.width,i.right.width=s.rightContainer.clientWidth||-i.border.right,i.rightContainer.width=i.right.width;var d=i.root.width-i.left.width-i.right.width-n;i.center.width=d,i.centerContainer.width=d,i.top.width=d,i.bottom.width=d,s.background.style.height=i.background.height+"px",s.backgroundVertical.style.height=i.background.height+"px",s.backgroundHorizontalContainer.style.height=i.centerContainer.height+"px",s.centerContainer.style.height=i.centerContainer.height+"px",s.leftContainer.style.height=i.leftContainer.height+"px",s.rightContainer.style.height=i.rightContainer.height+"px",s.background.style.width=i.background.width+"px",s.backgroundVertical.style.width=i.centerContainer.width+"px",s.backgroundHorizontalContainer.style.width=i.background.width+"px",s.backgroundHorizontal.style.width=i.background.width+"px",s.centerContainer.style.width=i.center.width+"px",s.top.style.width=i.top.width+"px",s.bottom.style.width=i.bottom.width+"px",s.background.style.left="0",s.background.style.top="0",s.backgroundVertical.style.left=i.left.width+"px",s.backgroundVertical.style.top="0",s.backgroundHorizontalContainer.style.left="0",s.backgroundHorizontalContainer.style.top=i.top.height+"px",s.centerContainer.style.left=i.left.width+"px",s.centerContainer.style.top=i.top.height+"px",s.leftContainer.style.left="0",s.leftContainer.style.top=i.top.height+"px",s.rightContainer.style.left=i.left.width+i.center.width+"px",s.rightContainer.style.top=i.top.height+"px",s.top.style.left=i.left.width+"px",s.top.style.top="0",s.bottom.style.left=i.left.width+"px",s.bottom.style.top=i.top.height+i.centerContainer.height+"px",this._updateScrollTop();var l=this.props.scrollTop;"bottom"==e.orientation&&(l+=Math.max(this.props.centerContainer.height-this.props.center.height-this.props.border.top-this.props.border.bottom,0)),s.center.style.left="0",s.center.style.top=l+"px",s.backgroundHorizontal.style.left="0",s.backgroundHorizontal.style.top=l+"px",s.left.style.left="0",s.left.style.top=l+"px",s.right.style.left="0",s.right.style.top=l+"px";var c=0==this.props.scrollTop?"hidden":"",p=this.props.scrollTop==this.props.scrollTopMin?"hidden":"";s.shadowTop.style.visibility=c,s.shadowBottom.style.visibility=p,s.shadowTopLeft.style.visibility=c,s.shadowBottomLeft.style.visibility=p,s.shadowTopRight.style.visibility=c,s.shadowBottomRight.style.visibility=p,this.components.forEach(function(e){t=e.redraw()||t}),t&&this.redraw()}},Graph2d.prototype._toTime=function(t){var e=this.range.conversion(this.props.center.width);return new Date(t/e.scale+e.offset)},Graph2d.prototype._toGlobalTime=function(t){var e=this.range.conversion(this.props.root.width);return new Date(t/e.scale+e.offset)},Graph2d.prototype._toScreen=function(t){var e=this.range.conversion(this.props.center.width);return(t.valueOf()-e.offset)*e.scale},Graph2d.prototype._toGlobalScreen=function(t){var e=this.range.conversion(this.props.root.width);return(t.valueOf()-e.offset)*e.scale},Graph2d.prototype._initAutoResize=function(){1==this.options.autoResize?this._startAutoResize():this._stopAutoResize()},Graph2d.prototype._startAutoResize=function(){var t=this;this._stopAutoResize(),this._onResize=function(){return 1!=t.options.autoResize?void t._stopAutoResize():void(t.dom.root&&(t.dom.root.clientWidth!=t.props.lastWidth||t.dom.root.clientHeight!=t.props.lastHeight)&&(t.props.lastWidth=t.dom.root.clientWidth,t.props.lastHeight=t.dom.root.clientHeight,t.emit("change")))},util.addEventListener(window,"resize",this._onResize),this.watchTimer=setInterval(this._onResize,1e3)},Graph2d.prototype._stopAutoResize=function(){this.watchTimer&&(clearInterval(this.watchTimer),this.watchTimer=void 0),util.removeEventListener(window,"resize",this._onResize),this._onResize=null},Graph2d.prototype._onTouch=function(){this.touch.allowDragging=!0},Graph2d.prototype._onPinch=function(){this.touch.allowDragging=!1},Graph2d.prototype._onDragStart=function(){this.touch.initialScrollTop=this.props.scrollTop},Graph2d.prototype._onDrag=function(t){if(this.touch.allowDragging){var e=t.gesture.deltaY,i=this._getScrollTop(),s=this._setScrollTop(this.touch.initialScrollTop+e);s!=i&&this.redraw()}},Graph2d.prototype._setScrollTop=function(t){return this.props.scrollTop=t,this._updateScrollTop(),this.props.scrollTop},Graph2d.prototype._updateScrollTop=function(){var t=Math.min(this.props.centerContainer.height-this.props.center.height,0);return t!=this.props.scrollTopMin&&("bottom"==this.options.orientation&&(this.props.scrollTop+=t-this.props.scrollTopMin),this.props.scrollTopMin=t),this.props.scrollTop>0&&(this.props.scrollTop=0),this.props.scrollTopi;i++)if(e.id===a.nodes[i].id){o=a.nodes[i];break}for(o||(o={id:e.id},t.node&&(o.attr=r(o.attr,t.node))),i=n.length-1;i>=0;i--){var h=n[i];h.nodes||(h.nodes=[]),-1==h.nodes.indexOf(o)&&h.nodes.push(o)}e.attr&&(o.attr=r(o.attr,e.attr))}function d(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=r({},t.edge);e.attr=r(i,e.attr)}}function l(t,e,i,s,o){var n={from:e,to:i,type:s};return t.edge&&(n.attr=r({},t.edge)),n.attr=r(n.attr||{},o),n}function c(){for(O=D.NULL,N="";" "==C||" "==C||"\n"==C||"\r"==C;)s();do{var t=!1;if("#"==C){for(var e=M-1;" "==T.charAt(e)||" "==T.charAt(e);)e--;if("\n"==T.charAt(e)||""==T.charAt(e)){for(;""!=C&&"\n"!=C;)s();t=!0}}if("/"==C&&"/"==o()){for(;""!=C&&"\n"!=C;)s();t=!0}if("/"==C&&"*"==o()){for(;""!=C;){if("*"==C&&"/"==o()){s(),s();break}s()}t=!0}for(;" "==C||" "==C||"\n"==C||"\r"==C;)s()}while(t);if(""==C)return void(O=D.DELIMITER);var i=C+o();if(E[i])return O=D.DELIMITER,N=i,s(),void s();if(E[C])return O=D.DELIMITER,N=C,void s();if(n(C)||"-"==C){for(N+=C,s();n(C);)N+=C,s();return"false"==N?N=!1:"true"==N?N=!0:isNaN(Number(N))||(N=Number(N)),void(O=D.IDENTIFIER)}if('"'==C){for(s();""!=C&&('"'!=C||'"'==C&&'"'==o());)N+=C,'"'==C&&s(),s();if('"'!=C)throw _('End of string " expected');return s(),void(O=D.IDENTIFIER)}for(O=D.UNKNOWN;""!=C;)N+=C,s();throw new SyntaxError('Syntax error in part "'+w(N,30)+'"')}function p(){var t={};if(i(),c(),"strict"==N&&(t.strict=!0,c()),("graph"==N||"digraph"==N)&&(t.type=N,c()),O==D.IDENTIFIER&&(t.id=N,c()),"{"!=N)throw _("Angle bracket { expected");if(c(),u(t),"}"!=N)throw _("Angle bracket } expected");if(c(),""!==N)throw _("End of file expected");return c(),delete t.node,delete t.edge,delete t.graph,t -}function u(t){for(;""!==N&&"}"!=N;)m(t),";"==N&&c()}function m(t){var e=g(t);if(e)return void y(t,e);var i=f(t);if(!i){if(O!=D.IDENTIFIER)throw _("Identifier expected");var s=N;if(c(),"="==N){if(c(),O!=D.IDENTIFIER)throw _("Identifier expected");t[s]=N,c()}else v(t,s)}}function g(t){var e=null;if("subgraph"==N&&(e={},e.type="subgraph",c(),O==D.IDENTIFIER&&(e.id=N,c())),"{"==N){if(c(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,u(e),"}"!=N)throw _("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"==N?(c(),t.node=b(),"node"):"edge"==N?(c(),t.edge=b(),"edge"):"graph"==N?(c(),t.graph=b(),"graph"):null}function v(t,e){var i={id:e},s=b();s&&(i.attr=s),h(t,i),y(t,e)}function y(t,e){for(;"->"==N||"--"==N;){var i,s=N;c();var o=g(t);if(o)i=o;else{if(O!=D.IDENTIFIER)throw _("Identifier or subgraph expected");i=N,h(t,{id:i}),c()}var n=b(),r=l(t,e,i,s,n);d(t,r),e=i}}function b(){for(var t=null;"["==N;){for(c(),t={};""!==N&&"]"!=N;){if(O!=D.IDENTIFIER)throw _("Attribute name expected");var e=N;if(c(),"="!=N)throw _("Equal sign = expected");if(c(),O!=D.IDENTIFIER)throw _("Attribute value expected");var i=N;a(t,e,i),c(),","==N&&c()}if("]"!=N)throw _("Bracket ] expected");c()}return t}function _(t){return new SyntaxError(t+', got "'+w(N,30)+'" (char '+M+")")}function w(t,e){return t.length<=e?t:t.substr(0,27)+"..."}function x(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 r(e,t.attr),e.style="->"==t.type?"arrow":"line",e}var s=e(t),o={nodes:[],edges:[],options:{}};return s.nodes&&s.nodes.forEach(function(t){var e={id:t.id,label:String(t.label||t.id)};r(e,t.attr),e.image&&(e.shape="image"),o.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);o.edges.push(e)}),x(e,s,function(e,s){var n=l(o,e.id,s.id,t.type,t.attr),r=i(n);o.edges.push(r)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var e=i(t);o.edges.push(e)})}),s.attr&&(o.options=s.attr),o}var D={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},E={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},T="",M=0,C="",N="",O=D.NULL,k=/[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,o=s/2,n=Math.sqrt(3)/6*s,r=Math.sqrt(s*s-o*o);this.moveTo(t,e-(r-n)),this.lineTo(t+o,e+n),this.lineTo(t-o,e+n),this.lineTo(t,e-(r-n)),this.closePath()},CanvasRenderingContext2D.prototype.triangleDown=function(t,e,i){this.beginPath();var s=2*i,o=s/2,n=Math.sqrt(3)/6*s,r=Math.sqrt(s*s-o*o);this.moveTo(t,e+(r-n)),this.lineTo(t+o,e-n),this.lineTo(t-o,e-n),this.lineTo(t,e+(r-n)),this.closePath()},CanvasRenderingContext2D.prototype.star=function(t,e,i){this.beginPath();for(var s=0;10>s;s++){var o=s%2===0?1.3*i:.5*i;this.lineTo(t+o*Math.sin(2*s*Math.PI/10),e-o*Math.cos(2*s*Math.PI/10))}this.closePath()},CanvasRenderingContext2D.prototype.roundRect=function(t,e,i,s,o){var n=Math.PI/180;0>i-2*o&&(o=i/2),0>s-2*o&&(o=s/2),this.beginPath(),this.moveTo(t+o,e),this.lineTo(t+i-o,e),this.arc(t+i-o,e+o,o,270*n,360*n,!1),this.lineTo(t+i,e+s-o),this.arc(t+i-o,e+s-o,o,0,90*n,!1),this.lineTo(t+o,e+s),this.arc(t+o,e+s-o,o,90*n,180*n,!1),this.lineTo(t,e+o),this.arc(t+o,e+o,o,180*n,270*n,!1)},CanvasRenderingContext2D.prototype.ellipse=function(t,e,i,s){var o=.5522848,n=i/2*o,r=s/2*o,a=t+i,h=e+s,d=t+i/2,l=e+s/2;this.beginPath(),this.moveTo(t,l),this.bezierCurveTo(t,l-r,d-n,e,d,e),this.bezierCurveTo(d+n,e,a,l-r,a,l),this.bezierCurveTo(a,l+r,d+n,h,d,h),this.bezierCurveTo(d-n,h,t,l+r,t,l)},CanvasRenderingContext2D.prototype.database=function(t,e,i,s){var o=1/3,n=i,r=s*o,a=.5522848,h=n/2*a,d=r/2*a,l=t+n,c=e+r,p=t+n/2,u=e+r/2,m=e+(s-r/2),g=e+s;this.beginPath(),this.moveTo(l,u),this.bezierCurveTo(l,u+d,p+h,c,p,c),this.bezierCurveTo(p-h,c,t,u+d,t,u),this.bezierCurveTo(t,u-d,p-h,e,p,e),this.bezierCurveTo(p+h,e,l,u-d,l,u),this.lineTo(l,m),this.bezierCurveTo(l,m+d,p+h,g,p,g),this.bezierCurveTo(p-h,g,t,m+d,t,m),this.lineTo(t,u)},CanvasRenderingContext2D.prototype.arrow=function(t,e,i,s){var o=t-s*Math.cos(i),n=e-s*Math.sin(i),r=t-.9*s*Math.cos(i),a=e-.9*s*Math.sin(i),h=o+s/3*Math.cos(i+.5*Math.PI),d=n+s/3*Math.sin(i+.5*Math.PI),l=o+s/3*Math.cos(i-.5*Math.PI),c=n+s/3*Math.sin(i-.5*Math.PI);this.beginPath(),this.moveTo(t,e),this.lineTo(h,d),this.lineTo(r,a),this.lineTo(l,c),this.closePath()},CanvasRenderingContext2D.prototype.dashedLine=function(t,e,i,s,o){o||(o=[10,5]),0==p&&(p=.001);var n=o.length;this.moveTo(t,e);for(var r=i-t,a=s-e,h=a/r,d=Math.sqrt(r*r+a*a),l=0,c=!0;d>=.1;){var p=o[l++%n];p>d&&(p=d);var u=Math.sqrt(p*p/(1+h*h));0>r&&(u=-u),t+=u,e+=h*u,this[c?"lineTo":"moveTo"](t,e),d-=p,c=!c}}),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,o=this.height/2,n=Math.sin(e)*s,r=Math.cos(e)*o;return s*o/Math.sqrt(n*n+r*r);case"box":case"image":case"text":default:return this.width?Math.min(Math.abs(this.width/2/Math.cos(e)),Math.abs(this.height/2/Math.sin(e)))+i:0}},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,o=(this.fy-s)/this.mass;this.vy+=o*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 o=this.damping*this.vy,n=(this.fy-o)/this.mass;this.vy+=n*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.networkScaleInv,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.networkScaleInv,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.networkScaleInv,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.networkScaleInv,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.networkScaleInv,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.networkScaleInv,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.networkScaleInv,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.networkScaleInv,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.networkScaleInv,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,o=2;switch(e){case"dot":o=2;break;case"square":o=2;break;case"triangle":o=3;break;case"triangleDown":o=3;break;case"star":o=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.networkScaleInv,t.lineWidth=Math.min(.1*this.width,t.lineWidth),t[e](this.x,this.y,this.radius+o*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:1)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,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",!0)},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,o,n,r){if(e&&this.fontSize*this.networkScale>this.fontDrawThreshold){t.font=(this.selected?"bold ":"")+this.fontSize+"px "+this.fontFace,t.fillStyle=this.fontColor||"black",t.textAlign=o||"center",t.textBaseline=n||"middle";var a=e.split("\n"),h=a.length,d=this.fontSize+4,l=s+(1-h)/2*d;1==r&&(l=s+(1-h)/(2*d));for(var c=0;h>c;c++)t.fillText(a[c],i,l),l+=d}},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,o=0,n=e.length;n>o;o++)s=Math.max(s,t.measureText(e[o]).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.networkScaleInv>=this.canvasTopLeft.x&&this.x-this.width*this.networkScaleInv=this.canvasTopLeft.y&&this.y-this.height*this.networkScaleInv=this.canvasTopLeft.x&&this.x=this.canvasTopLeft.y&&this.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 o,n,r=this.length/4,a=this.from;a.width||a.resize(t),a.width>a.height?(o=a.x+a.width/2,n=a.y-r):(o=a.x+r,n=a.y-a.height/2),this._circle(t,o,n,r),e=this._pointOnCircle(o,n,r,.5),this._label(t,this.label,e.x,e.y)}},Edge.prototype._getLineWidth=function(){return 1==this.selected?Math.min(this.widthSelected,this.widthMax)*this.networkScaleInv:1==this.hover?Math.min(this.hoverWidth,this.widthMax)*this.networkScaleInv:this.width*this.networkScaleInv},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 o=t.measureText(e).width,n=this.fontSize,r=i-o/2,a=s-n/2;t.fillRect(r,a,o,n),t.fillStyle=this.fontColor||"black",t.textAlign="left",t.textBaseline="top",t.fillText(e,r,a)}},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)),o=.5*(.5*(this.from.y+this.via.y)+.5*(this.to.y+this.via.y));i={x:s,y:o}}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 o=2*(s-3/8)*Math.PI;return{x:t+i*Math.cos(o),y:e-i*Math.sin(o)}},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 o=.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));e={x:o,y:n}}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 r,a,h=.25*Math.max(100,this.length),d=this.from;d.width||d.resize(t),d.width>d.height?(r=d.x+.5*d.width,a=d.y-h):(r=d.x+h,a=d.y-.5*d.height),this._circle(t,r,a,h);var i=.2*Math.PI,s=(10+5*this.width)*this.arrowScaleFactor;e=this._pointOnCircle(r,a,h,.5),t.arrow(e.x,e.y,i,s),t.fill(),t.stroke(),this.label&&(e=this._pointOnCircle(r,a,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,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.from.distanceToBorder(t,e+Math.PI),a=(n-r)/n,h=a*this.from.x+(1-a)*this.to.x,d=a*this.from.y+(1-a)*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,o=this.to.y-this.via.y,n=Math.sqrt(s*s+o*o));var l,c,p=this.to.distanceToBorder(t,e),u=(n-p)/n;if(1==this.smooth?(l=(1-u)*this.via.x+u*this.to.x,c=(1-u)*this.via.y+u*this.to.y):(l=(1-u)*this.from.x+u*this.to.x,c=(1-u)*this.from.y+u*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,w=.25*Math.max(100,this.length);_.width||_.resize(t),_.width>_.height?(v=_.x+.5*_.width,y=_.y-w,b={x:v,y:_.y,angle:.9*Math.PI}):(v=_.x+w,y=_.y-.5*_.height,b={x:_.x,y:y,angle:.6*Math.PI}),t.beginPath(),t.arc(v,y,w,0,2*Math.PI,!1),t.stroke();var i=(10+5*this.width)*this.arrowScaleFactor;t.arrow(b.x,b.y,b.angle,i),t.fill(),t.stroke(),this.label&&(m=this._pointOnCircle(v,y,w,.5),this._label(t,this.label,m.x,m.y))}},Edge.prototype._getDistanceToEdge=function(t,e,i,s,o,n){if(this.from!=this.to){if(1==this.smooth){var r,a,h,d,l,c,p=1e9;for(r=0;10>r;r++)a=.1*r,h=Math.pow(1-a,2)*t+2*a*(1-a)*this.via.x+Math.pow(a,2)*i,d=Math.pow(1-a,2)*e+2*a*(1-a)*this.via.y+Math.pow(a,2)*s,l=Math.abs(o-h),c=Math.abs(n-d),p=Math.min(p,Math.sqrt(l*l+c*c));return p}var u=i-t,m=s-e,g=u*u+m*m,f=((o-t)*u+(n-e)*m)/g;f>1?f=1:0>f&&(f=0);var h=t+f*u,d=e+f*m,l=h-o,c=d-n;return Math.sqrt(l*l+c*c)}var h,d,l,c,v=this.length/4,y=this.from;return y.width||y.resize(ctx),y.width>y.height?(h=y.x+y.width/2,d=y.y-v):(h=y.x+v,d=y.y-y.height/2),l=h-o,c=d-n,Math.abs(Math.sqrt(l*l+c*c)-v)},Edge.prototype.setScale=function(t){this.networkScaleInv=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))},Edge.prototype._drawControlNodes=function(t){if(1==this.controlNodesEnabled){if(null===this.controlNodes.from&&null===this.controlNodes.to){var e="edgeIdFrom:".concat(this.id),i="edgeIdTo:".concat(this.id),s={nodes:{group:"",radius:8},physics:{damping:0},clustering:{maxNodeSizeIncrements:0,nodeScaling:{width:0,height:0,radius:0}}};this.controlNodes.from=new Node({id:e,shape:"dot",color:{background:"#ff4e00",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s),this.controlNodes.to=new Node({id:i,shape:"dot",color:{background:"#ff4e00",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s)}0==this.controlNodes.from.selected&&0==this.controlNodes.to.selected&&(this.controlNodes.positions=this.getControlNodePositions(t),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(t),this.controlNodes.to.draw(t) -}else this.controlNodes={from:null,to:null,positions:{}}},Edge.prototype._enableControlNodes=function(){this.controlNodesEnabled=!0},Edge.prototype._disableControlNodes=function(){this.controlNodesEnabled=!1},Edge.prototype._getSelectedControlNode=function(t,e){var i=this.controlNodes.positions,s=Math.sqrt(Math.pow(t-i.from.x,2)+Math.pow(e-i.from.y,2)),o=Math.sqrt(Math.pow(t-i.to.x,2)+Math.pow(e-i.to.y,2));return 15>s?(this.connectedNode=this.from,this.from=this.controlNodes.from,this.controlNodes.from):15>o?(this.connectedNode=this.to,this.to=this.controlNodes.to,this.controlNodes.to):null},Edge.prototype._restoreControlNodes=function(){1==this.controlNodes.from.selected&&(this.from=this.connectedNode,this.connectedNode=null,this.controlNodes.from.unselect()),1==this.controlNodes.to.selected&&(this.to=this.connectedNode,this.connectedNode=null,this.controlNodes.to.unselect())},Edge.prototype.getControlNodePositions=function(t){var e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),i=this.to.x-this.from.x,s=this.to.y-this.from.y,o=Math.sqrt(i*i+s*s),n=this.from.distanceToBorder(t,e+Math.PI),r=(o-n)/o,a=r*this.from.x+(1-r)*this.to.x,h=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),i=this.to.x-this.via.x,s=this.to.y-this.via.y,o=Math.sqrt(i*i+s*s));var d,l,c=this.to.distanceToBorder(t,e),p=(o-c)/o;return 1==this.smooth?(d=(1-p)*this.via.x+p*this.to.x,l=(1-p)*this.via.y+p*this.to.y):(d=(1-p)*this.from.x+p*this.to.x,l=(1-p)*this.from.y+p*this.to.y),{from:{x:a,y:h},to:{x:d,y:l}}},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,o=this.frame.parentNode.clientWidth,n=this.y-e;n+e+this.padding>s&&(n=s-e-this.padding),no&&(r=o-i-this.padding),rthis.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():1==this.constants.physics.hierarchicalRepulsion.enabled?this._calculateHierarchicalSpringForces():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,o,n=this.calculationNodes,r=this.constants.physics.centralGravity,a=0;for(o=0;oSimulation Mode:Barnes HutRepulsionHierarchical
Options:
',this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement),this.optionsDiv=document.createElement("div"),this.optionsDiv.style.fontSize="14px",this.optionsDiv.style.fontFamily="verdana",this.containerElement.parentElement.insertBefore(this.optionsDiv,this.containerElement);var e;e=document.getElementById("graph_BH_gc"),e.onchange=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"),o=document.getElementById("graph_physicsMethod3");s.checked=!0,this.constants.physics.barnesHut.enabled&&(i.checked=!0),this.constants.hierarchicalLayout.enabled&&(o.checked=!0);var n=document.getElementById("graph_toggleSmooth"),r=document.getElementById("graph_repositionNodes"),a=document.getElementById("graph_generateOptions");n.onclick=graphToggleSmoothCurves.bind(this),r.onclick=graphRepositionNodes.bind(this),a.onclick=graphGenerateOptions.bind(this),n.style.background=1==this.constants.smoothCurves?"#A4FF56":"#FF8532",switchConfigurations.apply(this),i.onchange=switchConfigurations.bind(this),s.onchange=switchConfigurations.bind(this),o.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,o,n,r,a,h,d,l=this.calculationNodes,c=this.calculationNodeIndices,p=5,u=.5*-p,m=this.constants.physics.hierarchicalRepulsion.nodeDistance,g=m,f=u/g;for(h=0;hi)){n=f*i+p;var v=.05,y=2*g*2*v;n=v*Math.pow(i,2)-y*i+y*y/(4*v),0==i?i=.01:n/=i,s=t*n,o=e*n,r.fx-=s,r.fy-=o,a.fx+=s,a.fy+=o}},_calculateHierarchicalSpringForces:function(){var t,e,i,s,o,n,r,a,h,d=this.edges;for(i in d)if(d.hasOwnProperty(i)&&(e=d[i],e.connected&&this.nodes.hasOwnProperty(e.toId)&&this.nodes.hasOwnProperty(e.fromId))){t=e.customLength?e.length:this.constants.physics.springLength,t+=(e.to.clusterSize+e.from.clusterSize-2)*this.constants.clustering.edgeGrowth,s=e.from.x-e.to.x,o=e.from.y-e.to.y,h=Math.sqrt(s*s+o*o),0==h&&(h=.01),h=Math.max(.8*t,Math.min(5*t,h)),a=this.constants.physics.springConstant*(t-h)/h,n=s*a,r=o*a,e.to.fx-=n,e.to.fy-=r,e.from.fx+=n,e.from.fy+=r;var l=5;h>t&&(l=25),e.from.level>e.to.level?(e.to.fx-=l*n,e.to.fy-=l*r):e.from.leveln;n++)t=e[i[n]],this._getForceContribution(o.root.children.NW,t),this._getForceContribution(o.root.children.NE,t),this._getForceContribution(o.root.children.SW,t),this._getForceContribution(o.root.children.SE,t)}},_getForceContribution:function(t,e){if(t.childrenCount>0){var i,s,o;if(i=t.centerOfMass.x-e.x,s=t.centerOfMass.y-e.y,o=Math.sqrt(i*i+s*s),o*t.calcSize>this.constants.physics.barnesHut.theta){0==o&&(o=.1*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}else if(4==t.childrenCount)this._getForceContribution(t.children.NW,e),this._getForceContribution(t.children.NE,e),this._getForceContribution(t.children.SW,e),this._getForceContribution(t.children.SE,e);else if(t.children.data.id!=e.id){0==o&&(o=.5*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}}},_formBarnesHutTree:function(t,e){for(var i,s=e.length,o=Number.MAX_VALUE,n=Number.MAX_VALUE,r=-Number.MAX_VALUE,a=-Number.MAX_VALUE,h=0;s>h;h++){var d=t[e[h]].x,l=t[e[h]].y;o>d&&(o=d),d>r&&(r=d),n>l&&(n=l),l>a&&(a=l)}var c=Math.abs(r-o)-Math.abs(a-n);c>0?(n-=.5*c,a+=.5*c):(o+=.5*c,r-=.5*c);var p=1e-5,u=Math.max(p,Math.abs(r-o)),m=.5*u,g=.5*(o+r),f=.5*(n+a),v={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:g-m,maxX:g+m,minY:f-m,maxY:f+m},size:u,calcSize:1/u,children:{data:null},maxWidth:0,level:0,childrenCount:4}};for(this._splitBranch(v.root),h=0;s>h;h++)i=t[e[h]],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 o=Math.max(Math.max(e.height,e.radius),e.width);t.maxWidth=t.maxWidthe.x?t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NW"):this._placeInRegion(t,e,"SW"):t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NE"):this._placeInRegion(t,e,"SE")},_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,o,n,r=.5*t.size;switch(e){case"NW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY,n=t.range.minY+r;break;case"NE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY,n=t.range.minY+r;break;case"SW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY+r,n=t.range.maxY;break;case"SE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY+r,n=t.range.maxY}t.children[e]={centerOfMass:{x:0,y:0},mass:0,range:{minX:i,maxX:s,minY:o,maxY:n},size:.5*t.size,calcSize:2*t.calcSize,children:{data:null},maxWidth:0,level:t.level+1,childrenCount:0}},_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,o,n,r,a,h,d,l,c=this.calculationNodes,p=this.calculationNodeIndices,u=-2/3,m=4/3,g=this.constants.physics.repulsion.nodeDistance,f=g;for(d=0;di&&(r=.5*f>i?1:v*i+m,r*=0==n?1:1+n*this.constants.clustering.forceAmplification,r/=i,s=t*r,o=e*r,a.fx-=s,a.fy-=o,h.fx+=s,h.fy+=o)}}},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,o=!1;for(e in this.nodes)this.nodes.hasOwnProperty(e)&&(t=this.nodes[e],-1!=t.level?s=!0:o=!0,is&&(n.xFixed=!1,n.x=i[n.level].minPos,r=!0):n.yFixed&&n.level>s&&(n.yFixed=!1,n.y=i[n.level].minPos,r=!0),1==r&&(i[n.level].minPos+=i[n.level].nodeSpacing,n.edges.length>1&&this._placeBranchNodes(n.edges,n.id,i,n.level))}},_setLevel:function(t,e,i){for(var s=0;st)&&(o.level=t,e.length>1&&this._setLevel(t+1,o.edges,o.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("network-manipulationDiv"),e=document.getElementById("network-manipulation-closeDiv"),i=document.getElementById("network-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),void 0!==this.edgeBeingEdited&&(this.edgeBeingEdited._disableControlNodes(),this.edgeBeingEdited=void 0,this.selectedControlNode=null),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+"":1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDiv.innerHTML+="
"+this.constants.labels.editEdge+""),0==this._selectionIsEmpty()&&(this.manipulationDiv.innerHTML+="
"+this.constants.labels.del+"");var t=document.getElementById("network-manipulate-addNode");t.onclick=this._createAddNodeToolbar.bind(this);var e=document.getElementById("network-manipulate-connectNode");if(e.onclick=this._createAddEdgeToolbar.bind(this),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit){var i=document.getElementById("network-manipulate-editNode"); -i.onclick=this._editNode.bind(this)}else if(1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()){var i=document.getElementById("network-manipulate-editEdge");i.onclick=this._createEditEdgeToolbar.bind(this)}if(0==this._selectionIsEmpty()){var s=document.getElementById("network-manipulate-delete");s.onclick=this._deleteSelected.bind(this)}var o=document.getElementById("network-manipulation-closeDiv");o.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 n=document.getElementById("network-manipulate-editModeButton");n.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("network-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("network-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()},_createEditEdgeToolbar:function(){this._clearManipulatorBar(),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+"";var t=document.getElementById("network-manipulate-back");t.onclick=this._createManipulatorBar.bind(this),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,this._redraw()},_selectControlNode:function(t){this.edgeBeingEdited.controlNodes.from.unselect(),this.edgeBeingEdited.controlNodes.to.unselect(),this.selectedControlNode=this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(t.x),this._YconvertDOMtoCanvas(t.y)),null!==this.selectedControlNode&&(this.selectedControlNode.select(),this.freezeSimulation=!0),this._redraw()},_controlNodeDrag:function(t){var e=this._getPointer(t.gesture.center);null!==this.selectedControlNode&&void 0!==this.selectedControlNode&&(this.selectedControlNode.x=this._XconvertDOMtoCanvas(e.x),this.selectedControlNode.y=this._YconvertDOMtoCanvas(e.y)),this._redraw()},_releaseControlNode:function(t){var e=this._getNodeAt(t);null!=e?(1==this.edgeBeingEdited.controlNodes.from.selected&&(this._editEdge(e.id,this.edgeBeingEdited.to.id),this.edgeBeingEdited.controlNodes.from.unselect()),1==this.edgeBeingEdited.controlNodes.to.selected&&(this._editEdge(this.edgeBeingEdited.from.id,e.id),this.edgeBeingEdited.controlNodes.to.unselect())):this.edgeBeingEdited._restoreControlNodes(),this.freezeSimulation=!1,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()}},_editEdge:function(t,e){if(1==this.editMode){var i={id:this.edgeBeingEdited.id,from:t,to:e};if(this.triggerFunctions.editEdge)if(2==this.triggerFunctions.editEdge.length){var s=this;this.triggerFunctions.editEdge(i,function(t){s.edgesData.update(t),s.moving=!0,s.start()})}else alert(this.constants.labels.linkError),this.moving=!0,this.start();else this.edgesData.update(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,o=-1e9,n=1e9,r=-1e9;for(var a in this.sectors[e])if(this.sectors[e].hasOwnProperty(a)&&void 0!==this.sectors[e][a].drawingNode){this._switchToSector(a,e),s=1e9,o=-1e9,n=1e9,r=-1e9;for(var h in this.nodes)this.nodes.hasOwnProperty(h)&&(i=this.nodes[h],i.resize(t),n>i.x-.5*i.width&&(n=i.x-.5*i.width),ri.y-.5*i.height&&(s=i.y-.5*i.height),ot&&s>o;)o%3==0?(this.forceAggregateHubs(!0),this.normalizeClusterLevels()):this.increaseClusterLevel(),i=this.nodeIndices.length,o+=1;o>0&&1==e&&this.repositionNodes(),this._updateCalculationNodes()},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 o=this.moving,n=this.nodeIndices.length;this.previousScale>this.scale&&0==t&&this._collapseSector(),this.previousScale>this.scale||-1==t?this._formClusters(i):(this.previousScalethis.scale||-1==t)&&(this._aggregateHubs(i),this._updateNodeIndexList()),(this.previousScale>this.scale||-1==t)&&(this.handleChains(),this._updateNodeIndexList()),this.previousScale=this.scale,this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.lengththis.constants.clustering.chainThreshold&&this._reduceAmountOfChains(1-this.constants.clustering.chainThreshold/t)},_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 r=n.from,a=n.to;n.to.mass>n.from.mass&&(r=n.to,a=n.from),1==a.dynamicEdgesLength?this._addToCluster(r,a,!1):1==r.dynamicEdgesLength&&this._addToCluster(a,r,!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;so.clusterSessions.length&&(e=o.clusterSessions.length,i=o)}null!=o&&void 0!==this.nodes[o.id]&&this._addToCluster(o,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 o,n,r,a=this.constants.clustering.clusterEdgeThreshold/this.scale,h=!1,d=[],l=t.dynamicEdges.length,c=0;l>c;c++)d.push(t.dynamicEdges[c].id);if(0==e)for(h=!1,c=0;l>c;c++){var p=this.edges[d[c]];if(void 0!==p&&p.connected&&p.toId!=p.fromId&&(o=p.to.x-p.from.x,n=p.to.y-p.from.y,r=Math.sqrt(o*o+n*n),a>r)){h=!0;break}}if(!e&&h||e)for(c=0;l>c;c++)if(p=this.edges[d[c]],void 0!==p){var u=this.nodes[p.fromId==t.id?p.toId:p.fromId];u.dynamicEdges.length<=this.hubThreshold+s&&u.id!=t.id&&this._addToCluster(t,u,e)}}},_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 o=this.nodeIndices.length,n=e-this.constants.clustering.clusterLevelDifference;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodes[t].clusterSessions.lengths&&(s=n.dynamicEdgesLength),t+=n.dynamicEdgesLength,e+=Math.pow(n.dynamicEdgesLength,2),i+=1}t/=i,e/=i;var r=e-Math.pow(t,2),a=Math.sqrt(r);this.hubThreshold=Math.floor(t+2*a),this.hubThreshold>s&&(this.hubThreshold=s)},_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},_getSelectedEdge:function(){for(var t in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(t))return this.selectionObj.edges[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 o=this.nodes[s];if(!o)throw new RangeError('Node with id "'+s+'" not found');this._selectObject(o,!0,!0)}console.log("setSelection is deprecated. Please use selectNodes instead."),this.redraw()},selectNodes:function(t,e){var i,s,o;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(this._unselectAll(!0),i=0,s=t.length;s>i;i++){o=t[i];var n=this.nodes[o];if(!n)throw new RangeError('Node with id "'+o+'" not found');this._selectObject(n,!0,!0,e)}this.redraw()},selectEdges:function(t){var e,i,s;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(this._unselectAll(!0),e=0,i=t.length;i>e;e++){s=t[e];var o=this.edges[s];if(!o)throw new RangeError('Edge with id "'+s+'" not found');this._selectObject(o,!0,!0,highlightEdges)}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("network-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="network-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),ot.y&&(e=t.y),i=this.constants.clustering.initialMaxNodes?49.07548/(o+142.05338)+91444e-8:12.662/(o+7.4147)+.0964822:1==this.constants.clustering.enabled&&o>=this.constants.clustering.initialMaxNodes?77.5271985/(o+187.266146)+476710517e-13:30.5062972/(o+19.93597763)+.08413486;var n=Math.min(this.frame.canvas.clientWidth/600,this.frame.canvas.clientHeight/600);i*=n}else{var r=1.1*(Math.abs(s.minX)+Math.abs(s.maxX)),a=1.1*(Math.abs(s.minY)+Math.abs(s.maxY)),h=this.frame.canvas.clientWidth/r,d=this.frame.canvas.clientHeight/a;i=d>=h?h:d}i>1&&(i=1),this._setScale(i),this._centerNetwork(s),0==e&&(this.moving=!0,this.start())},Network.prototype._updateNodeIndexList=function(){this._clearNodeIndexList();for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodeIndices.push(t)},Network.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()},Network.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.dragNetwork&&(this.constants.dragNetwork=t.dragNetwork),void 0!==t.dragNodes&&(this.constants.dragNodes=t.dragNodes),void 0!==t.zoomable&&(this.constants.zoomable=t.zoomable),void 0!==t.hover&&(this.constants.hover=t.hover),void 0!==t.dragGraph)throw new Error("Option dragGraph is renamed to dragNetwork");if(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.onEditEdge&&(this.triggerFunctions.editEdge=t.onEditEdge),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]);this.editMode=this.constants.dataManipulation.initiallyVisible}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()},Network.prototype._create=function(){for(;this.containerElement.hasChildNodes();)this.containerElement.removeChild(this.containerElement.firstChild);if(this.frame=document.createElement("div"),this.frame.className="network-frame",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),!this.frame.canvas.getContext){var t=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)},Network.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)))},Network.prototype._getPointer=function(t){return{x:t.pageX-vis.util.getAbsoluteLeft(this.frame.canvas),y:t.pageY-vis.util.getAbsoluteTop(this.frame.canvas)}},Network.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)},Network.prototype._onDragStart=function(){this._handleDragStart()},Network.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],o={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(o)}}},Network.prototype._onDrag=function(t){this._handleOnDrag(t)},Network.prototype._handleOnDrag=function(t){if(!this.drag.pinched){var e=this._getPointer(t.gesture.center),i=this,s=this.drag,o=s.selection;if(o&&o.length&&1==this.constants.dragNodes){var n=e.x-s.pointer.x,r=e.y-s.pointer.y;o.forEach(function(t){var e=t.node;t.xFixed||(e.x=i._XconvertDOMtoCanvas(i._XconvertCanvasToDOM(t.x)+n)),t.yFixed||(e.y=i._YconvertDOMtoCanvas(i._YconvertCanvasToDOM(t.y)+r))}),this.moving||(this.moving=!0,this.start())}else if(1==this.constants.dragNetwork){var a=e.x-this.drag.pointer.x,h=e.y-this.drag.pointer.y;this._setTranslation(this.drag.translation.x+a,this.drag.translation.y+h),this._redraw(),this.moving=!0,this.start()}}},Network.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})},Network.prototype._onTap=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleTap(e)},Network.prototype._onDoubleTap=function(t){var e=this._getPointer(t.gesture.center);this._handleDoubleTap(e)},Network.prototype._onHold=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleOnHold(e)},Network.prototype._onRelease=function(t){var e=this._getPointer(t.gesture.center);this._handleOnRelease(e)},Network.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)},Network.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(),o=t/i,n=(1-o)*e.x+s.x*o,r=(1-o)*e.y+s.y*o;return this.areaCenter={x:this._XconvertDOMtoCanvas(e.x),y:this._YconvertDOMtoCanvas(e.y)},this._setScale(t),this._setTranslation(n,r),this.updateClustersDefault(),this._redraw(),t>i?this.emit("zoom",{direction:"+"}):this.emit("zoom",{direction:"-"}),t}},Network.prototype._onMouseWheel=function(t){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i=this._getScale(),s=e/10;0>e&&(s/=1-s),i*=1+s;var o=util.fakeGesture(this,t),n=this._getPointer(o.center);this._zoom(i,n)}t.preventDefault()},Network.prototype._onMouseMoveTitle=function(t){var e=util.fakeGesture(this,t),i=this._getPointer(e.center);this.popupObj&&this._checkHidePopup(i);var s=this,o=function(){s._checkShowPopup(i)};if(this.popupTimer&&clearInterval(this.popupTimer),this.drag.dragging||(this.popupTimer=setTimeout(o,this.constants.tooltip.delay)),1==this.constants.hover){for(var n in this.hoverObj.edges)this.hoverObj.edges.hasOwnProperty(n)&&(this.hoverObj.edges[n].hover=!1,delete this.hoverObj.edges[n]);var r=this._getNodeAt(i);null==r&&(r=this._getEdgeAt(i)),null!=r&&this._hoverObject(r);for(var a in this.hoverObj.nodes)this.hoverObj.nodes.hasOwnProperty(a)&&(r instanceof Node&&r.id!=a||r instanceof Edge||null==r)&&(this._blurObject(this.hoverObj.nodes[a]),delete this.hoverObj.nodes[a]);this.redraw()}},Network.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 o=this.nodes;for(e in o)if(o.hasOwnProperty(e)){var n=o[e];if(void 0!==n.getTitle()&&n.isOverlappingWith(i)){this.popupObj=n;break}}}if(void 0===this.popupObj){var r=this.edges;for(e in r)if(r.hasOwnProperty(e)){var a=r[e];if(a.connected&&void 0!==a.getTitle()&&a.isOverlappingWith(i)){this.popupObj=a;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()},Network.prototype._checkHidePopup=function(t){this.popupObj&&this._getNodeAt(t)||(this.popupObj=void 0,this.popup&&this.popup.hide())},Network.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})},Network.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()},Network.prototype._addNodes=function(t){for(var e,i=0,s=t.length;s>i;i++){e=t[i];var o=this.nodesData.get(e),n=new Node(o,this.images,this.groups,this.constants);if(this.nodes[e]=n,!(0!=n.xFixed&&0!=n.yFixed||null!==n.x&&null!==n.y)){var r=1*t.length,a=2*Math.PI*Math.random();0==n.xFixed&&(n.x=r*Math.cos(a)),0==n.yFixed&&(n.y=r*Math.sin(a))}this.moving=!0}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateValueRange(this.nodes),this.updateLabels()},Network.prototype._updateNodes=function(t){for(var e=this.nodes,i=this.nodesData,s=0,o=t.length;o>s;s++){var n=t[s],r=e[n],a=i.get(n);r?r.setProperties(a,this.constants):(r=new Node(properties,this.images,this.groups,this.constants),e[n]=r)}this.moving=!0,1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateNodeIndexList(),this._reconnectEdges(),this._updateValueRange(e)},Network.prototype._removeNodes=function(t){for(var e=this.nodes,i=0,s=t.length;s>i;i++){var o=t[i];delete e[o]}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateSelection(),this._updateValueRange(e)},Network.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()},Network.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=e[n];r&&r.disconnect();var a=i.get(n,{showInternalIds:!0});e[n]=new Edge(a,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()},Network.prototype._updateEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=i.get(n),a=e[n];a?(a.disconnect(),a.setProperties(r,this.constants),a.connect()):(a=new Edge(r,this,this.constants),this.edges[n]=a)}this._createBezierNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this.moving=!0,this._updateValueRange(e)},Network.prototype._removeEdges=function(t){for(var e=this.edges,i=0,s=t.length;s>i;i++){var o=t[i],n=e[o];n&&(null!=n.via&&delete this.sectors.support.nodes[n.via.id],n.disconnect(),delete e[o])}this.moving=!0,this._updateValueRange(e),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes()},Network.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()}},Network.prototype._updateValueRange=function(t){var e,i=void 0,s=void 0;for(e in t)if(t.hasOwnProperty(e)){var o=t[e].getValue();void 0!==o&&(i=void 0===i?o:Math.min(o,i),s=void 0===s?o:Math.max(o,s))}if(void 0!==i&&void 0!==s)for(e in t)t.hasOwnProperty(e)&&t[e].setValueRange(i,s)},Network.prototype.redraw=function(){this.setSize(this.width,this.height),this._redraw()},Network.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),this._doInAllSectors("_drawControlNodes",t),t.restore()},Network.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")},Network.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},Network.prototype._setScale=function(t){this.scale=t},Network.prototype._getScale=function(){return this.scale},Network.prototype._XconvertDOMtoCanvas=function(t){return(t-this.translation.x)/this.scale},Network.prototype._XconvertCanvasToDOM=function(t){return t*this.scale+this.translation.x},Network.prototype._YconvertDOMtoCanvas=function(t){return(t-this.translation.y)/this.scale},Network.prototype._YconvertCanvasToDOM=function(t){return t*this.scale+this.translation.y},Network.prototype.canvasToDOM=function(t){return{x:this._XconvertCanvasToDOM(t.x),y:this._YconvertCanvasToDOM(t.y)}},Network.prototype.DOMtoCanvas=function(t){return{x:this._XconvertDOMtoCanvas(t.x),y:this._YconvertDOMtoCanvas(t.y)}},Network.prototype._drawNodes=function(t,e){void 0===e&&(e=!1);var i=this.nodes,s=[];for(var o in i)i.hasOwnProperty(o)&&(i[o].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight),i[o].isSelected()?s.push(o):(i[o].inArea()||e)&&i[o].draw(t));for(var n=0,r=s.length;r>n;n++)(i[s[n]].inArea()||e)&&i[s[n]].draw(t)},Network.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)}},Network.prototype._drawControlNodes=function(t){var e=this.edges;for(var i in e)e.hasOwnProperty(i)&&e[i]._drawControlNodes(t)},Network.prototype._stabilize=function(){1==this.constants.freezeForStabilization&&this._freezeDefinedNodes();for(var t=0;this.moving&&t0)for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStepLimited(e,this.constants.maxVelocity),s=!0);else for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStep(e),s=!0);if(1==s){var o=this.constants.minVelocity/Math.max(this.scale,.05);this.moving=o>.5*this.constants.maxVelocity?!0:this._isMoving(o)}},Network.prototype._physicsTick=function(){this.freezeSimulation||this.moving&&(this._doInAllActiveSectors("_initializeForceCalculation"),this._doInAllActiveSectors("_discreteStepNodes"),this.constants.smoothCurves&&this._doInSupportSector("_discreteStepNodes"),this._findCenter(this._getRange()))},Network.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<.9*(this.renderTimestep-this.renderTime)&&e.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 u=(t-c)/(p-c),m=240*u,g=this._hsv2rgb(m,1,1);l.strokeStyle=g,l.beginPath(),l.moveTo(a,n+t),l.lineTo(r,n+t),l.stroke()}l.strokeStyle=this.colorAxis,l.strokeRect(a,n,i,o)}if(this.style===Graph3d.STYLE.DOTSIZE&&(l.strokeStyle=this.colorAxis,l.fillStyle=this.colorDot,l.beginPath(),l.moveTo(a,n),l.lineTo(r,n),l.lineTo(r-i+e,h),l.lineTo(a,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,o=this._convert3Dto2D(new Point3d(b,r,this.zMin)),Math.cos(2*y)>0?(m.textAlign="center",m.textBaseline="top",o.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()+" ",o.x,o.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,o=this._convert3Dto2D(new Point3d(n,i.getCurrent(),this.zMin)),Math.cos(2*y)<0?(m.textAlign="center",m.textBaseline="top",o.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()+" ",o.x,o.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,r=Math.sin(y)<0?this.yMin:this.yMax;!i.end();)t=this._convert3Dto2D(new Point3d(n,r,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(n,r,this.zMin)),e=this._convert3Dto2D(new Point3d(n,r,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)),p=this._convert3Dto2D(new Point3d(this.xMax,this.yMin,this.zMin)),m.strokeStyle=this.colorAxis,m.beginPath(),m.moveTo(c.x,c.y),m.lineTo(p.x,p.y),m.stroke(),c=this._convert3Dto2D(new Point3d(this.xMin,this.yMax,this.zMin)),p=this._convert3Dto2D(new Point3d(this.xMax,this.yMax,this.zMin)),m.strokeStyle=this.colorAxis,m.beginPath(),m.moveTo(c.x,c.y),m.lineTo(p.x,p.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 _=this.xLabel;_.length>0&&(l=.1/this.scale.y,n=(this.xMin+this.xMax)/2,r=Math.cos(y)>0?this.yMin-l:this.yMax+l,o=this._convert3Dto2D(new Point3d(n,r,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(_,o.x,o.y));var w=this.yLabel;w.length>0&&(d=.1/this.scale.x,n=Math.sin(y)>0?this.xMin-d:this.xMax+d,r=(this.yMin+this.yMax)/2,o=this._convert3Dto2D(new Point3d(n,r,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(w,o.x,o.y));var x=this.zLabel;x.length>0&&(h=30,n=Math.cos(y)>0?this.xMin:this.xMax,r=Math.sin(y)<0?this.yMin:this.yMax,a=(this.zMin+this.zMax)/2,o=this._convert3Dto2D(new Point3d(n,r,a)),m.textAlign="right",m.textBaseline="middle",m.fillStyle=this.colorAxis,m.fillText(x,o.x-h,o.y))},Graph3d.prototype._hsv2rgb=function(t,e,i){var s,o,n,r,a,h;switch(r=i*e,a=Math.floor(t/60),h=r*(1-Math.abs(t/60%2-1)),a){case 0:s=r,o=h,n=0;break;case 1:s=h,o=r,n=0;break;case 2:s=0,o=r,n=h;break;case 3:s=0,o=h,n=r;break;case 4:s=h,o=0,n=r;break;case 5:s=r,o=0,n=h;break;default:s=0,o=0,n=0}return"RGB("+parseInt(255*s)+","+parseInt(255*o)+","+parseInt(255*n)+")"},Graph3d.prototype._redrawDataGrid=function(){var t,e,i,s,o,n,r,a,h,d,l,c,p,u=this.frame.canvas,m=u.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(o=0;o0}else n=!0;n?(p=(t.point.z+e.point.z+i.point.z+s.point.z)/4,d=240*(1-(p-this.zMin)*this.scale.z/this.verticalRatio),l=1,this.showShadow?(c=Math.min(1+w.x/x/2,1),r=this._hsv2rgb(d,l,c),a=r):(c=1,r=this._hsv2rgb(d,l,c),a=this.colorAxis)):(r="gray",a=this.colorAxis),h=.5,m.lineWidth=h,m.fillStyle=r,m.strokeStyle=a,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(o=0;oc&&(c=0);var p,u,m;this.style===Graph3d.STYLE.DOTCOLOR?(p=240*(1-(h.point.value-this.valueMin)*this.scale.value),u=this._hsv2rgb(p,1,1),m=this._hsv2rgb(p,1,.8)):this.style===Graph3d.STYLE.DOTSIZE?(u=this.colorDot,m=this.colorDotBorder):(p=240*(1-(h.point.z-this.zMin)*this.scale.z/this.verticalRatio),u=this._hsv2rgb(p,1,1),m=this._hsv2rgb(p,1,.8)),i.lineWidth=1,i.strokeStyle=m,i.fillStyle=u,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,o=this.frame.canvas,n=o.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,o=this.startArmRotation.vertical+i/200,n=4,r=Math.sin(n/360*2*Math.PI);Math.abs(Math.sin(s))0?1:0>t?-1:0}var s=e[0],o=e[1],n=e[2],r=i((o.x-s.x)*(t.y-s.y)-(o.y-s.y)*(t.x-s.x)),a=i((n.x-o.x)*(t.y-o.y)-(n.y-o.y)*(t.x-o.x)),h=i((s.x-n.x)*(t.y-n.y)-(s.y-n.y)*(t.x-n.x));return!(0!=r&&0!=a&&r!=a||0!=a&&0!=h&&a!=h||0!=r&&0!=h&&r!=h)},Graph3d.prototype._dataPointFromXY=function(t,e){var i,s=100,o=null,n=null,r=null,a=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--){o=this.dataPoints[i];var h=o.surfaces;if(h)for(var d=h.length-1;d>=0;d--){var l=h[d],c=l.corners,p=[c[0].screen,c[1].screen,c[2].screen],u=[c[2].screen,c[3].screen,c[0].screen];if(this._insideTriangle(a,p)||this._insideTriangle(a,u))return o}}else for(i=0;iv)&&s>v&&(r=v,n=o)}}return n},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):"
x:"+t.point.x+"
y:"+t.point.y+"
z:"+t.point.z+"
",e.style.left="0",e.style.top="0",this.frame.appendChild(e),this.frame.appendChild(i),this.frame.appendChild(s);var o=e.offsetWidth,n=e.offsetHeight,r=i.offsetHeight,a=s.offsetWidth,h=s.offsetHeight,d=t.screen.x-o/2;d=Math.min(Math.max(d,10),this.frame.clientWidth-10-o),i.style.left=t.screen.x+"px",i.style.top=t.screen.y-r+"px",e.style.left=d+"px",e.style.top=t.screen.y-r-n+"px",s.style.left=t.screen.x-a/2+"px",s.style.top=t.screen.y-h/2+"px"},Graph3d.prototype._hideTooltip=function(){if(this.tooltip){this.tooltip.dataPoint=null;for(var t in this.tooltip.dom)if(this.tooltip.dom.hasOwnProperty(t)){var e=this.tooltip.dom[t];e&&e.parentNode&&e.parentNode.removeChild(e)}}},G3DaddEventListener=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)},G3DremoveEventListener=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)},G3DstopPropagation=function(t){t||(t=window.event),t.stopPropagation?t.stopPropagation():t.cancelBubble=!0},G3DpreventDefault=function(t){t||(t=window.event),t.preventDefault?t.preventDefault():t.returnValue=!1},Point3d.subtract=function(t,e){var i=new Point3d;return i.x=t.x-e.x,i.y=t.y-e.y,i.z=t.z-e.z,i},Point3d.add=function(t,e){var i=new Point3d;return i.x=t.x+e.x,i.y=t.y+e.y,i.z=t.z+e.z,i},Point3d.avg=function(t,e){return new Point3d((t.x+e.x)/2,(t.y+e.y)/2,(t.z+e.z)/2)},Point3d.crossProduct=function(t,e){var i=new Point3d;return i.x=t.y*e.z-t.z*e.y,i.y=t.z*e.x-t.x*e.z,i.z=t.x*e.y-t.y*e.x,i},Point3d.prototype.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},Point2d=function(t,e){this.x=void 0!==t?t:0,this.y=void 0!==e?e:0},Filter.prototype.isLoaded=function(){return this.loaded},Filter.prototype.getLoadedProgress=function(){for(var t=this.values.length,e=0;this.dataPoints[e];)e++;return Math.round(e/t*100)},Filter.prototype.getLabel=function(){return this.graph.filterLabel},Filter.prototype.getColumn=function(){return this.column},Filter.prototype.getSelectedValue=function(){return void 0===this.index?void 0:this.values[this.index]},Filter.prototype.getValues=function(){return this.values},Filter.prototype.getValue=function(t){if(t>=this.values.length)throw"Error: index out of range";return this.values[t]},Filter.prototype._getDataPoints=function(t){if(void 0===t&&(t=this.index),void 0===t)return[];var e;if(this.dataPoints[t])e=this.dataPoints[t];else{var i={};i.column=this.column,i.value=this.values[t];var s=new DataView(this.data,{filter:function(t){return t[i.column]==i.value}}).get();e=this.graph._getDataPoints(s),this.dataPoints[t]=e}return e},Filter.prototype.setOnLoadCallback=function(t){this.onLoadCallback=t},Filter.prototype.selectValue=function(t){if(t>=this.values.length)throw"Error: index out of range";this.index=t,this.value=this.values[t]},Filter.prototype.loadInBackground=function(t){void 0===t&&(t=0);var e=this.graph.frame;if(t=t||(void 0!==e&&(this.prettyStep=e),this._step=this.prettyStep===!0?StepNumber.calculatePrettyStep(t):t)},StepNumber.calculatePrettyStep=function(t){var e=function(t){return Math.log(t)/Math.LN10},i=Math.pow(10,Math.round(e(t))),s=2*Math.pow(10,Math.round(e(t/2))),o=5*Math.pow(10,Math.round(e(t/5))),n=i;return Math.abs(s-t)<=Math.abs(n-t)&&(n=s),Math.abs(o-t)<=Math.abs(n-t)&&(n=o),0>=n&&(n=1),n},StepNumber.prototype.getCurrent=function(){return parseFloat(this._current.toPrecision(this.precision))},StepNumber.prototype.getStep=function(){return this._step},StepNumber.prototype.start=function(){this._current=this._start-this._start%this._step},StepNumber.prototype.next=function(){this._current+=this._step},StepNumber.prototype.end=function(){return this._current>this._end},Slider.prototype.prev=function(){var t=this.getIndex();t>0&&(t--,this.setIndex(t))},Slider.prototype.next=function(){var t=this.getIndex();t0?this.setIndex(0):this.index=void 0},Slider.prototype.setIndex=function(t){if(!(ts&&(s=0),s>this.values.length-1&&(s=this.values.length-1),s},Slider.prototype.indexToLeft=function(t){var e=parseFloat(this.frame.bar.style.width)-this.frame.slide.clientWidth-10,i=t/(this.values.length-1)*e,s=i+3;return s},Slider.prototype._onMouseMove=function(t){var e=t.clientX-this.startClientX,i=this.startSlideX+e,s=this.leftToIndex(i);this.setIndex(s),G3DpreventDefault()},Slider.prototype._onMouseUp=function(){this.frame.style.cursor="auto",G3DremoveEventListener(document,"mousemove",this.onmousemove),G3DremoveEventListener(document,"mouseup",this.onmouseup),G3DpreventDefault()},getAbsoluteLeft=function(t){for(var e=0;null!==t;)e+=t.offsetLeft,e-=t.scrollLeft,t=t.offsetParent;return e},getAbsoluteTop=function(t){for(var e=0;null!==t;)e+=t.offsetTop,e-=t.scrollTop,t=t.offsetParent;return e},getMouseX=function(t){return"clientX"in t?t.clientX:t.targetTouches[0]&&t.targetTouches[0].clientX||0},getMouseY=function(t){return"clientY"in t?t.clientY:t.targetTouches[0]&&t.targetTouches[0].clientY||0};var vis={moment:moment,util:util,DOMutil:DOMutil,DataSet:DataSet,DataView:DataView,Timeline:Timeline,Graph2d:Graph2d,timeline:{DataStep:DataStep,Range:Range,stack:stack,TimeStep:TimeStep,components:{items:{Item:Item,ItemBox:ItemBox,ItemPoint:ItemPoint,ItemRange:ItemRange},Component:Component,CurrentTime:CurrentTime,CustomTime:CustomTime,DataAxis:DataAxis,GraphGroup:GraphGroup,Group:Group,ItemSet:ItemSet,Legend:Legend,LineGraph:LineGraph,TimeAxis:TimeAxis}},Network:Network,network:{Edge:Edge,Groups:Groups,Images:Images,Node:Node,Popup:Popup},Graph:function(){throw new Error("Graph is renamed to Network. Please create a graph as new vis.Network(...)")},Graph3d:Graph3d};"undefined"!=typeof exports&&(exports=vis),"undefined"!=typeof module&&"undefined"!=typeof module.exports&&(module.exports=vis),"function"==typeof define&&define(function(){return vis}),"undefined"!=typeof window&&(window.vis=vis)},{"emitter-component":2,hammerjs:3,moment:4,mousetrap:5}],2:[function(t,e){function i(t){return t?s(t):void 0}function s(t){for(var e in i.prototype)t[e]=i.prototype[e];return t}e.exports=i,i.prototype.on=i.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks[t]=this._callbacks[t]||[]).push(e),this},i.prototype.once=function(t,e){function i(){s.off(t,i),e.apply(this,arguments)}var s=this;return this._callbacks=this._callbacks||{},i.fn=e,this.on(t,i),this},i.prototype.off=i.prototype.removeListener=i.prototype.removeAllListeners=i.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i=this._callbacks[t];if(!i)return this;if(1==arguments.length)return delete this._callbacks[t],this;for(var s,o=0;os;++s)i[s].apply(this,e)}return this},i.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[t]||[]},i.prototype.hasListeners=function(t){return!!this.listeners(t).length}},{}],3:[function(t,e){!function(t,i){"use strict";function s(){if(!o.READY){o.event.determineEventTypes();for(var t in o.gestures)o.gestures.hasOwnProperty(t)&&o.detection.register(o.gestures[t]);o.event.onTouch(o.DOCUMENT,o.EVENT_MOVE,o.detection.detect),o.event.onTouch(o.DOCUMENT,o.EVENT_END,o.detection.detect),o.READY=!0}}var o=function(t,e){return new o.Instance(t,e||{})};o.defaults={stop_browser_behavior:{userSelect:"none",touchAction:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}},o.HAS_POINTEREVENTS=navigator.pointerEnabled||navigator.msPointerEnabled,o.HAS_TOUCHEVENTS="ontouchstart"in t,o.MOBILE_REGEX=/mobile|tablet|ip(ad|hone|od)|android/i,o.NO_MOUSEEVENTS=o.HAS_TOUCHEVENTS&&navigator.userAgent.match(o.MOBILE_REGEX),o.EVENT_TYPES={},o.DIRECTION_DOWN="down",o.DIRECTION_LEFT="left",o.DIRECTION_UP="up",o.DIRECTION_RIGHT="right",o.POINTER_MOUSE="mouse",o.POINTER_TOUCH="touch",o.POINTER_PEN="pen",o.EVENT_START="start",o.EVENT_MOVE="move",o.EVENT_END="end",o.DOCUMENT=document,o.plugins={},o.READY=!1,o.Instance=function(t,e){var i=this;return s(),this.element=t,this.enabled=!0,this.options=o.utils.extend(o.utils.extend({},o.defaults),e||{}),this.options.stop_browser_behavior&&o.utils.stopDefaultBrowserBehavior(this.element,this.options.stop_browser_behavior),o.event.onTouch(t,o.EVENT_START,function(t){i.enabled&&o.detection.startDetect(i,t)}),this},o.Instance.prototype={on:function(t,e){for(var i=t.split(" "),s=0;s0&&e==o.EVENT_END?e=o.EVENT_MOVE:l||(e=o.EVENT_END),l||null===n?n=h:h=n,i.call(o.detection,s.collectEventData(t,e,h)),o.HAS_POINTEREVENTS&&e==o.EVENT_END&&(l=o.PointerEvent.updatePointer(e,h))),l||(n=null,r=!1,a=!1,o.PointerEvent.reset())}})},determineEventTypes:function(){var t;t=o.HAS_POINTEREVENTS?o.PointerEvent.getEvents():o.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],o.EVENT_TYPES[o.EVENT_START]=t[0],o.EVENT_TYPES[o.EVENT_MOVE]=t[1],o.EVENT_TYPES[o.EVENT_END]=t[2]},getTouchList:function(t){return o.HAS_POINTEREVENTS?o.PointerEvent.getTouchList():t.touches?t.touches:[{identifier:1,pageX:t.pageX,pageY:t.pageY,target:t.target}]},collectEventData:function(t,e,i){var s=this.getTouchList(i,e),n=o.POINTER_TOUCH;return(i.type.match(/mouse/)||o.PointerEvent.matchType(o.POINTER_MOUSE,i))&&(n=o.POINTER_MOUSE),{center:o.utils.getCenter(s),timeStamp:(new Date).getTime(),target:i.target,touches:s,eventType:e,pointerType:n,srcEvent:i,preventDefault:function(){this.srcEvent.preventManipulation&&this.srcEvent.preventManipulation(),this.srcEvent.preventDefault&&this.srcEvent.preventDefault()},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return o.detection.stopDetect()}}}},o.PointerEvent={pointers:{},getTouchList:function(){var t=this,e=[];return Object.keys(t.pointers).sort().forEach(function(i){e.push(t.pointers[i])}),e},updatePointer:function(t,e){return t==o.EVENT_END?this.pointers={}:(e.identifier=e.pointerId,this.pointers[e.pointerId]=e),Object.keys(this.pointers).length},matchType:function(t,e){if(!e.pointerType)return!1;var i={};return i[o.POINTER_MOUSE]=e.pointerType==e.MSPOINTER_TYPE_MOUSE||e.pointerType==o.POINTER_MOUSE,i[o.POINTER_TOUCH]=e.pointerType==e.MSPOINTER_TYPE_TOUCH||e.pointerType==o.POINTER_TOUCH,i[o.POINTER_PEN]=e.pointerType==e.MSPOINTER_TYPE_PEN||e.pointerType==o.POINTER_PEN,i[t]},getEvents:function(){return["pointerdown MSPointerDown","pointermove MSPointerMove","pointerup pointercancel MSPointerUp MSPointerCancel"]},reset:function(){this.pointers={}}},o.utils={extend:function(t,e,s){for(var o in e)t[o]!==i&&s||(t[o]=e[o]);return t},hasParent:function(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1},getCenter:function(t){for(var e=[],i=[],s=0,o=t.length;o>s;s++)e.push(t[s].pageX),i.push(t[s].pageY);return{pageX:(Math.min.apply(Math,e)+Math.max.apply(Math,e))/2,pageY:(Math.min.apply(Math,i)+Math.max.apply(Math,i))/2}},getVelocity:function(t,e,i){return{x:Math.abs(e/t)||0,y:Math.abs(i/t)||0}},getAngle:function(t,e){var i=e.pageY-t.pageY,s=e.pageX-t.pageX;return 180*Math.atan2(i,s)/Math.PI},getDirection:function(t,e){var i=Math.abs(t.pageX-e.pageX),s=Math.abs(t.pageY-e.pageY);return i>=s?t.pageX-e.pageX>0?o.DIRECTION_LEFT:o.DIRECTION_RIGHT:t.pageY-e.pageY>0?o.DIRECTION_UP:o.DIRECTION_DOWN},getDistance:function(t,e){var i=e.pageX-t.pageX,s=e.pageY-t.pageY;return Math.sqrt(i*i+s*s)},getScale:function(t,e){return t.length>=2&&e.length>=2?this.getDistance(e[0],e[1])/this.getDistance(t[0],t[1]):1},getRotation:function(t,e){return t.length>=2&&e.length>=2?this.getAngle(e[1],e[0])-this.getAngle(t[1],t[0]):0},isVertical:function(t){return t==o.DIRECTION_UP||t==o.DIRECTION_DOWN},stopDefaultBrowserBehavior:function(t,e){var i,s=["webkit","khtml","moz","ms","o",""];if(e&&t.style){for(var o=0;oi;i++){var n=this.gestures[i];if(!this.stopped&&e[n.name]!==!1&&n.handler.call(n,t,this.current.inst)===!1){this.stopDetect();break}}return this.current&&(this.current.lastEvent=t),t.eventType==o.EVENT_END&&!t.touches.length-1&&this.stopDetect(),t}},stopDetect:function(){this.previous=o.utils.extend({},this.current),this.current=null,this.stopped=!0},extendEventData:function(t){var e=this.current.startEvent;if(e&&(t.touches.length!=e.touches.length||t.touches===e.touches)){e.touches=[];for(var i=0,s=t.touches.length;s>i;i++)e.touches.push(o.utils.extend({},t.touches[i]))}var n=t.timeStamp-e.timeStamp,r=t.center.pageX-e.center.pageX,a=t.center.pageY-e.center.pageY,h=o.utils.getVelocity(n,r,a);return o.utils.extend(t,{deltaTime:n,deltaX:r,deltaY:a,velocityX:h.x,velocityY:h.y,distance:o.utils.getDistance(e.center,t.center),angle:o.utils.getAngle(e.center,t.center),direction:o.utils.getDirection(e.center,t.center),scale:o.utils.getScale(e.touches,t.touches),rotation:o.utils.getRotation(e.touches,t.touches),startEvent:e}),t},register:function(t){var e=t.defaults||{};return e[t.name]===i&&(e[t.name]=!0),o.utils.extend(o.defaults,e,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(t,e){return t.indexe.index?1:0}),this.gestures}},o.gestures=o.gestures||{},o.gestures.Hold={name:"hold",index:10,defaults:{hold_timeout:500,hold_threshold:1},timer:null,handler:function(t,e){switch(t.eventType){case o.EVENT_START:clearTimeout(this.timer),o.detection.current.name=this.name,this.timer=setTimeout(function(){"hold"==o.detection.current.name&&e.trigger("hold",t)},e.options.hold_timeout);break;case o.EVENT_MOVE:t.distance>e.options.hold_threshold&&clearTimeout(this.timer);break;case o.EVENT_END:clearTimeout(this.timer)}}},o.gestures.Tap={name:"tap",index:100,defaults:{tap_max_touchtime:250,tap_max_distance:10,tap_always:!0,doubletap_distance:20,doubletap_interval:300},handler:function(t,e){if(t.eventType==o.EVENT_END){var i=o.detection.previous,s=!1;if(t.deltaTime>e.options.tap_max_touchtime||t.distance>e.options.tap_max_distance)return;i&&"tap"==i.name&&t.timeStamp-i.lastEvent.timeStamp0&&t.touches.length>e.options.swipe_max_touches)return;(t.velocityX>e.options.swipe_velocity||t.velocityY>e.options.swipe_velocity)&&(e.trigger(this.name,t),e.trigger(this.name+t.direction,t))}}},o.gestures.Drag={name:"drag",index:50,defaults:{drag_min_distance:10,drag_max_touches:1,drag_block_horizontal:!1,drag_block_vertical:!1,drag_lock_to_axis:!1,drag_lock_min_distance:25},triggered:!1,handler:function(t,e){if(o.detection.current.name!=this.name&&this.triggered)return e.trigger(this.name+"end",t),void(this.triggered=!1);if(!(e.options.drag_max_touches>0&&t.touches.length>e.options.drag_max_touches))switch(t.eventType){case o.EVENT_START:this.triggered=!1;break;case o.EVENT_MOVE:if(t.distancee.options.transform_min_rotation&&e.trigger("rotate",t),i>e.options.transform_min_scale&&(e.trigger("pinch",t),e.trigger("pinch"+(t.scale<1?"in":"out"),t));break;case o.EVENT_END:this.triggered&&e.trigger(this.name+"end",t),this.triggered=!1}}},o.gestures.Touch={name:"touch",index:-1/0,defaults:{prevent_default:!1,prevent_mouseevents:!1},handler:function(t,e){return e.options.prevent_mouseevents&&t.pointerType==o.POINTER_MOUSE?void t.stopDetect():(e.options.prevent_default&&t.preventDefault(),void(t.eventType==o.EVENT_START&&e.trigger(this.name,t)))}},o.gestures.Release={name:"release",index:1/0,handler:function(t,e){t.eventType==o.EVENT_END&&e.trigger(this.name,t)}},"object"==typeof e&&"object"==typeof e.exports?e.exports=o:(t.Hammer=o,"function"==typeof t.define&&t.define.amd&&t.define("hammer",[],function(){return o}))}(this)},{}],4:[function(t,e){var i="undefined"!=typeof self?self:"undefined"!=typeof window?window:{};(function(s){function o(t,e,i){switch(arguments.length){case 2:return null!=t?t:e;case 3:return null!=t?t:null!=e?e:i;default:throw new Error("Implement me")}}function n(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function r(t,e){function i(){ge.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}var s=!0;return p(function(){return s&&(i(),s=!1),e.apply(this,arguments)},e)}function a(t,e){return function(i){return g(t.call(this,i),e)}}function h(t,e){return function(i){return this.lang().ordinal(t.call(this,i),e)}}function d(){}function l(t){C(t),p(this,t)}function c(t){var e=w(t),i=e.year||0,s=e.quarter||0,o=e.month||0,n=e.week||0,r=e.day||0,a=e.hour||0,h=e.minute||0,d=e.second||0,l=e.millisecond||0;this._milliseconds=+l+1e3*d+6e4*h+36e5*a,this._days=+r+7*n,this._months=+o+3*s+12*i,this._data={},this._bubble()}function p(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return e.hasOwnProperty("toString")&&(t.toString=e.toString),e.hasOwnProperty("valueOf")&&(t.valueOf=e.valueOf),t}function u(t){var e,i={};for(e in t)t.hasOwnProperty(e)&&Ne.hasOwnProperty(e)&&(i[e]=t[e]);return i}function m(t){return 0>t?Math.ceil(t):Math.floor(t)}function g(t,e,i){for(var s=""+Math.abs(t),o=t>=0;s.lengths;s++)(i&&t[s]!==e[s]||!i&&S(t[s])!==S(e[s]))&&r++;return r+n}function _(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=oi[t]||ni[e]||e}return t}function w(t){var e,i,s={};for(i in t)t.hasOwnProperty(i)&&(e=_(i),e&&(s[e]=t[i]));return s}function x(t){var e,i;if(0===t.indexOf("week"))e=7,i="day";else{if(0!==t.indexOf("month"))return;e=12,i="month"}ge[t]=function(o,n){var r,a,h=ge.fn._lang[t],d=[];if("number"==typeof o&&(n=o,o=s),a=function(t){var e=ge().utc().set(i,t);return h.call(ge.fn._lang,e,o||"")},null!=n)return a(n);for(r=0;e>r;r++)d.push(a(r));return d}}function S(t){var e=+t,i=0;return 0!==e&&isFinite(e)&&(i=e>=0?Math.floor(e):Math.ceil(e)),i}function D(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function E(t,e,i){return oe(ge([t,11,31+e-i]),e,i).week}function T(t){return M(t)?366:365}function M(t){return t%4===0&&t%100!==0||t%400===0}function C(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[xe]<0||t._a[xe]>11?xe:t._a[Se]<1||t._a[Se]>D(t._a[we],t._a[xe])?Se:t._a[De]<0||t._a[De]>23?De:t._a[Ee]<0||t._a[Ee]>59?Ee:t._a[Te]<0||t._a[Te]>59?Te:t._a[Me]<0||t._a[Me]>999?Me:-1,t._pf._overflowDayOfYear&&(we>e||e>Se)&&(e=Se),t._pf.overflow=e)}function N(t){return null==t._isValid&&(t._isValid=!isNaN(t._d.getTime())&&t._pf.overflow<0&&!t._pf.empty&&!t._pf.invalidMonth&&!t._pf.nullInput&&!t._pf.invalidFormat&&!t._pf.userInvalidated,t._strict&&(t._isValid=t._isValid&&0===t._pf.charsLeftOver&&0===t._pf.unusedTokens.length)),t._isValid}function O(t){return t?t.toLowerCase().replace("_","-"):t}function k(t,e){return e._isUTC?ge(t).zone(e._offset||0):ge(t).local()}function L(t,e){return e.abbr=t,Ce[t]||(Ce[t]=new d),Ce[t].set(e),Ce[t]}function I(t){delete Ce[t]}function P(e){var i,s,o,n,r=0,a=function(e){if(!Ce[e]&&Oe)try{t("./lang/"+e)}catch(i){}return Ce[e]};if(!e)return ge.fn._lang;if(!v(e)){if(s=a(e))return s;e=[e]}for(;r0;){if(s=a(n.slice(0,i).join("-")))return s;if(o&&o.length>=i&&b(n,o,!0)>=i-1)break;i--}r++}return ge.fn._lang}function A(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function z(t){var e,i,s=t.match(Pe);for(e=0,i=s.length;i>e;e++)s[e]=li[s[e]]?li[s[e]]:A(s[e]);return function(o){var n=""; -for(e=0;i>e;e++)n+=s[e]instanceof Function?s[e].call(o,t):s[e];return n}}function R(t,e){return t.isValid()?(e=G(e,t.lang()),ri[e]||(ri[e]=z(e)),ri[e](t)):t.lang().invalidDate()}function G(t,e){function i(t){return e.longDateFormat(t)||t}var s=5;for(Ae.lastIndex=0;s>=0&&Ae.test(t);)t=t.replace(Ae,i),Ae.lastIndex=0,s-=1;return t}function F(t,e){var i,s=e._strict;switch(t){case"Q":return je;case"DDDD":return qe;case"YYYY":case"GGGG":case"gggg":return s?Ze:Ge;case"Y":case"G":case"g":return $e;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return s?Ke:Fe;case"S":if(s)return je;case"SS":if(s)return Xe;case"SSS":if(s)return qe;case"DDD":return Re;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Ye;case"a":case"A":return P(e._l)._meridiemParse;case"X":return Ve;case"Z":case"ZZ":return Be;case"T":return We;case"SSSS":return He;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return s?Xe:ze;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return ze;case"Do":return Ue;default:return i=new RegExp(q(X(t.replace("\\","")),"i"))}}function H(t){t=t||"";var e=t.match(Be)||[],i=e[e.length-1]||[],s=(i+"").match(ii)||["-",0,0],o=+(60*s[1])+S(s[2]);return"+"===s[0]?-o:o}function Y(t,e,i){var s,o=i._a;switch(t){case"Q":null!=e&&(o[xe]=3*(S(e)-1));break;case"M":case"MM":null!=e&&(o[xe]=S(e)-1);break;case"MMM":case"MMMM":s=P(i._l).monthsParse(e),null!=s?o[xe]=s:i._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(o[Se]=S(e));break;case"Do":null!=e&&(o[Se]=S(parseInt(e,10)));break;case"DDD":case"DDDD":null!=e&&(i._dayOfYear=S(e));break;case"YY":o[we]=ge.parseTwoDigitYear(e);break;case"YYYY":case"YYYYY":case"YYYYYY":o[we]=S(e);break;case"a":case"A":i._isPm=P(i._l).isPM(e);break;case"H":case"HH":case"h":case"hh":o[De]=S(e);break;case"m":case"mm":o[Ee]=S(e);break;case"s":case"ss":o[Te]=S(e);break;case"S":case"SS":case"SSS":case"SSSS":o[Me]=S(1e3*("0."+e));break;case"X":i._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":i._useUTC=!0,i._tzm=H(e);break;case"dd":case"ddd":case"dddd":s=P(i._l).weekdaysParse(e),null!=s?(i._w=i._w||{},i._w.d=s):i._pf.invalidWeekday=e;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":t=t.substr(0,1);case"gggg":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(i._w=i._w||{},i._w[t]=S(e));break;case"gg":case"GG":i._w=i._w||{},i._w[t]=ge.parseTwoDigitYear(e)}}function B(t){var e,i,s,n,r,a,h,d;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(r=1,a=4,i=o(e.GG,t._a[we],oe(ge(),1,4).year),s=o(e.W,1),n=o(e.E,1)):(d=P(t._l),r=d._week.dow,a=d._week.doy,i=o(e.gg,t._a[we],oe(ge(),r,a).year),s=o(e.w,1),null!=e.d?(n=e.d,r>n&&++s):n=null!=e.e?e.e+r:r),h=ne(i,s,n,a,r),t._a[we]=h.year,t._dayOfYear=h.dayOfYear}function W(t){var e,i,s,n,r=[];if(!t._d){for(s=U(t),t._w&&null==t._a[Se]&&null==t._a[xe]&&B(t),t._dayOfYear&&(n=o(t._a[we],s[we]),t._dayOfYear>T(n)&&(t._pf._overflowDayOfYear=!0),i=te(n,0,t._dayOfYear),t._a[xe]=i.getUTCMonth(),t._a[Se]=i.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=r[e]=s[e];for(;7>e;e++)t._a[e]=r[e]=null==t._a[e]?2===e?1:0:t._a[e];t._d=(t._useUTC?te:Q).apply(null,r),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()+t._tzm)}}function V(t){var e;t._d||(e=w(t._i),t._a=[e.year,e.month,e.day,e.hour,e.minute,e.second,e.millisecond],W(t))}function U(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function j(t){if(t._f===ge.ISO_8601)return void K(t);t._a=[],t._pf.empty=!0;var e,i,s,o,n,r=P(t._l),a=""+t._i,h=a.length,d=0;for(s=G(t._f,r).match(Pe)||[],e=0;e0&&t._pf.unusedInput.push(n),a=a.slice(a.indexOf(i)+i.length),d+=i.length),li[o]?(i?t._pf.empty=!1:t._pf.unusedTokens.push(o),Y(o,i,t)):t._strict&&!i&&t._pf.unusedTokens.push(o);t._pf.charsLeftOver=h-d,a.length>0&&t._pf.unusedInput.push(a),t._isPm&&t._a[De]<12&&(t._a[De]+=12),t._isPm===!1&&12===t._a[De]&&(t._a[De]=0),W(t),C(t)}function X(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,i,s,o){return e||i||s||o})}function q(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Z(t){var e,i,s,o,r;if(0===t._f.length)return t._pf.invalidFormat=!0,void(t._d=new Date(0/0));for(o=0;or)&&(s=r,i=e));p(t,i||e)}function K(t){var e,i,s=t._i,o=Je.exec(s);if(o){for(t._pf.iso=!0,e=0,i=ti.length;i>e;e++)if(ti[e][1].exec(s)){t._f=ti[e][0]+(o[6]||" ");break}for(e=0,i=ei.length;i>e;e++)if(ei[e][1].exec(s)){t._f+=ei[e][0];break}s.match(Be)&&(t._f+="Z"),j(t)}else t._isValid=!1}function $(t){K(t),t._isValid===!1&&(delete t._isValid,ge.createFromInputFallback(t))}function J(t){var e=t._i,i=ke.exec(e);e===s?t._d=new Date:i?t._d=new Date(+i[1]):"string"==typeof e?$(t):v(e)?(t._a=e.slice(0),W(t)):y(e)?t._d=new Date(+e):"object"==typeof e?V(t):"number"==typeof e?t._d=new Date(e):ge.createFromInputFallback(t)}function Q(t,e,i,s,o,n,r){var a=new Date(t,e,i,s,o,n,r);return 1970>t&&a.setFullYear(t),a}function te(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function ee(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function ie(t,e,i,s,o){return o.relativeTime(e||1,!!i,t,s)}function se(t,e,i){var s=_e(Math.abs(t)/1e3),o=_e(s/60),n=_e(o/60),r=_e(n/24),a=_e(r/365),h=s0,h[4]=i,ie.apply({},h)}function oe(t,e,i){var s,o=i-e,n=i-t.day();return n>o&&(n-=7),o-7>n&&(n+=7),s=ge(t).add("d",n),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function ne(t,e,i,s,o){var n,r,a=te(t,0,1).getUTCDay();return a=0===a?7:a,i=null!=i?i:o,n=o-a+(a>s?7:0)-(o>a?7:0),r=7*(e-1)+(i-o)+n+1,{year:r>0?t:t-1,dayOfYear:r>0?r:T(t-1)+r}}function re(t){var e=t._i,i=t._f;return null===e||i===s&&""===e?ge.invalid({nullInput:!0}):("string"==typeof e&&(t._i=e=P().preparse(e)),ge.isMoment(e)?(t=u(e),t._d=new Date(+e._d)):i?v(i)?Z(t):j(t):J(t),new l(t))}function ae(t,e){var i,s;if(1===e.length&&v(e[0])&&(e=e[0]),!e.length)return ge();for(i=e[0],s=1;s=0?"+":"-";return e+g(Math.abs(t),6)},gg:function(){return g(this.weekYear()%100,2)},gggg:function(){return g(this.weekYear(),4)},ggggg:function(){return g(this.weekYear(),5)},GG:function(){return g(this.isoWeekYear()%100,2)},GGGG:function(){return g(this.isoWeekYear(),4)},GGGGG:function(){return g(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return S(this.milliseconds()/100)},SS:function(){return g(S(this.milliseconds()/10),2)},SSS:function(){return g(this.milliseconds(),3)},SSSS:function(){return g(this.milliseconds(),3)},Z:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+g(S(t/60),2)+":"+g(S(t)%60,2)},ZZ:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+g(S(t/60),2)+g(S(t)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},ci=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];hi.length;)ve=hi.pop(),li[ve+"o"]=h(li[ve],ve);for(;di.length;)ve=di.pop(),li[ve+ve]=a(li[ve],2);for(li.DDDD=a(li.DDD,3),p(d.prototype,{set:function(t){var e,i;for(i in t)e=t[i],"function"==typeof e?this[i]=e:this["_"+i]=e},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t){var e,i,s;for(this._monthsParse||(this._monthsParse=[]),e=0;12>e;e++)if(this._monthsParse[e]||(i=ge.utc([2e3,e]),s="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[e]=new RegExp(s.replace(".",""),"i")),this._monthsParse[e].test(t))return e},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,i,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(i=ge([2e3,1]).day(e),s="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e){var i=this._calendar[t];return"function"==typeof i?i.apply(e):i},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,i,s){var o=this._relativeTime[i];return"function"==typeof o?o(t,e,i,s):o.replace(/%d/i,t)},pastFuture:function(t,e){var i=this._relativeTime[t>0?"future":"past"];return"function"==typeof i?i(e):i.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",preparse:function(t){return t},postformat:function(t){return t},week:function(t){return oe(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),ge=function(t,e,i,o){var r;return"boolean"==typeof i&&(o=i,i=s),r={},r._isAMomentObject=!0,r._i=t,r._f=e,r._l=i,r._strict=o,r._isUTC=!1,r._pf=n(),re(r)},ge.suppressDeprecationWarnings=!1,ge.createFromInputFallback=r("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i)}),ge.min=function(){var t=[].slice.call(arguments,0);return ae("isBefore",t)},ge.max=function(){var t=[].slice.call(arguments,0);return ae("isAfter",t)},ge.utc=function(t,e,i,o){var r;return"boolean"==typeof i&&(o=i,i=s),r={},r._isAMomentObject=!0,r._useUTC=!0,r._isUTC=!0,r._l=i,r._i=t,r._f=e,r._strict=o,r._pf=n(),re(r).utc()},ge.unix=function(t){return ge(1e3*t)},ge.duration=function(t,e){var i,s,o,n=t,r=null;return ge.isDuration(t)?n={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(n={},e?n[e]=t:n.milliseconds=t):(r=Le.exec(t))?(i="-"===r[1]?-1:1,n={y:0,d:S(r[Se])*i,h:S(r[De])*i,m:S(r[Ee])*i,s:S(r[Te])*i,ms:S(r[Me])*i}):(r=Ie.exec(t))&&(i="-"===r[1]?-1:1,o=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*i},n={y:o(r[2]),M:o(r[3]),d:o(r[4]),h:o(r[5]),m:o(r[6]),s:o(r[7]),w:o(r[8])}),s=new c(n),ge.isDuration(t)&&t.hasOwnProperty("_lang")&&(s._lang=t._lang),s},ge.version=ye,ge.defaultFormat=Qe,ge.ISO_8601=function(){},ge.momentProperties=Ne,ge.updateOffset=function(){},ge.relativeTimeThreshold=function(t,e){return ai[t]===s?!1:(ai[t]=e,!0)},ge.lang=function(t,e){var i;return t?(e?L(O(t),e):null===e?(I(t),t="en"):Ce[t]||P(t),i=ge.duration.fn._lang=ge.fn._lang=P(t),i._abbr):ge.fn._lang._abbr},ge.langData=function(t){return t&&t._lang&&t._lang._abbr&&(t=t._lang._abbr),P(t)},ge.isMoment=function(t){return t instanceof l||null!=t&&t.hasOwnProperty("_isAMomentObject")},ge.isDuration=function(t){return t instanceof c},ve=ci.length-1;ve>=0;--ve)x(ci[ve]);ge.normalizeUnits=function(t){return _(t)},ge.invalid=function(t){var e=ge.utc(0/0);return null!=t?p(e._pf,t):e._pf.userInvalidated=!0,e},ge.parseZone=function(){return ge.apply(null,arguments).parseZone()},ge.parseTwoDigitYear=function(t){return S(t)+(S(t)>68?1900:2e3)},p(ge.fn=l.prototype,{clone:function(){return ge(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var t=ge(this).utc();return 00:!1},parsingFlags:function(){return p({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(t){var e=R(this,t||ge.defaultFormat);return this.lang().postformat(e)},add:function(t,e){var i;return i="string"==typeof t&&"string"==typeof e?ge.duration(isNaN(+e)?+t:+e,isNaN(+e)?e:t):"string"==typeof t?ge.duration(+e,t):ge.duration(t,e),f(this,i,1),this},subtract:function(t,e){var i;return i="string"==typeof t&&"string"==typeof e?ge.duration(isNaN(+e)?+t:+e,isNaN(+e)?e:t):"string"==typeof t?ge.duration(+e,t):ge.duration(t,e),f(this,i,-1),this},diff:function(t,e,i){var s,o,n=k(t,this),r=6e4*(this.zone()-n.zone());return e=_(e),"year"===e||"month"===e?(s=432e5*(this.daysInMonth()+n.daysInMonth()),o=12*(this.year()-n.year())+(this.month()-n.month()),o+=(this-ge(this).startOf("month")-(n-ge(n).startOf("month")))/s,o-=6e4*(this.zone()-ge(this).startOf("month").zone()-(n.zone()-ge(n).startOf("month").zone()))/s,"year"===e&&(o/=12)):(s=this-n,o="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-r)/864e5:"week"===e?(s-r)/6048e5:s),i?o:m(o)},from:function(t,e){return ge.duration(this.diff(t)).lang(this.lang()._abbr).humanize(!e)},fromNow:function(t){return this.from(ge(),t)},calendar:function(t){var e=t||ge(),i=k(e,this).startOf("day"),s=this.diff(i,"days",!0),o=-6>s?"sameElse":-1>s?"lastWeek":0>s?"lastDay":1>s?"sameDay":2>s?"nextDay":7>s?"nextWeek":"sameElse";return this.format(this.lang().calendar(o,this))},isLeapYear:function(){return M(this.year())},isDST:function(){return this.zone()+ge(t).startOf(e)},isBefore:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)<+ge(t).startOf(e)},isSame:function(t,e){return e=e||"ms",+this.clone().startOf(e)===+k(t,this).startOf(e)},min:r("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(t){return t=ge.apply(null,arguments),this>t?this:t}),max:r("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(t){return t=ge.apply(null,arguments),t>this?this:t}),zone:function(t,e){var i=this._offset||0;return null==t?this._isUTC?i:this._d.getTimezoneOffset():("string"==typeof t&&(t=H(t)),Math.abs(t)<16&&(t=60*t),this._offset=t,this._isUTC=!0,i!==t&&(!e||this._changeInProgress?f(this,ge.duration(i-t,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,ge.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(t){return t=t?ge(t).zone():0,(this.zone()-t)%60===0},daysInMonth:function(){return D(this.year(),this.month())},dayOfYear:function(t){var e=_e((ge(this).startOf("day")-ge(this).startOf("year"))/864e5)+1;return null==t?e:this.add("d",t-e)},quarter:function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},weekYear:function(t){var e=oe(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==t?e:this.add("y",t-e)},isoWeekYear:function(t){var e=oe(this,1,4).year;return null==t?e:this.add("y",t-e)},week:function(t){var e=this.lang().week(this);return null==t?e:this.add("d",7*(t-e))},isoWeek:function(t){var e=oe(this,1,4).week;return null==t?e:this.add("d",7*(t-e))},weekday:function(t){var e=(this.day()+7-this.lang()._week.dow)%7;return null==t?e:this.add("d",t-e)},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},isoWeeksInYear:function(){return E(this.year(),1,4)},weeksInYear:function(){var t=this._lang._week;return E(this.year(),t.dow,t.doy)},get:function(t){return t=_(t),this[t]()},set:function(t,e){return t=_(t),"function"==typeof this[t]&&this[t](e),this},lang:function(t){return t===s?this._lang:(this._lang=P(t),this)}}),ge.fn.millisecond=ge.fn.milliseconds=ce("Milliseconds",!1),ge.fn.second=ge.fn.seconds=ce("Seconds",!1),ge.fn.minute=ge.fn.minutes=ce("Minutes",!1),ge.fn.hour=ge.fn.hours=ce("Hours",!0),ge.fn.date=ce("Date",!0),ge.fn.dates=r("dates accessor is deprecated. Use date instead.",ce("Date",!0)),ge.fn.year=ce("FullYear",!0),ge.fn.years=r("years accessor is deprecated. Use year instead.",ce("FullYear",!0)),ge.fn.days=ge.fn.day,ge.fn.months=ge.fn.month,ge.fn.weeks=ge.fn.week,ge.fn.isoWeeks=ge.fn.isoWeek,ge.fn.quarters=ge.fn.quarter,ge.fn.toJSON=ge.fn.toISOString,p(ge.duration.fn=c.prototype,{_bubble:function(){var t,e,i,s,o=this._milliseconds,n=this._days,r=this._months,a=this._data;a.milliseconds=o%1e3,t=m(o/1e3),a.seconds=t%60,e=m(t/60),a.minutes=e%60,i=m(e/60),a.hours=i%24,n+=m(i/24),a.days=n%30,r+=m(n/30),a.months=r%12,s=m(r/12),a.years=s},weeks:function(){return m(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*S(this._months/12)},humanize:function(t){var e=+this,i=se(e,!t,this.lang());return t&&(i=this.lang().pastFuture(e,i)),this.lang().postformat(i)},add:function(t,e){var i=ge.duration(t,e);return this._milliseconds+=i._milliseconds,this._days+=i._days,this._months+=i._months,this._bubble(),this},subtract:function(t,e){var i=ge.duration(t,e);return this._milliseconds-=i._milliseconds,this._days-=i._days,this._months-=i._months,this._bubble(),this},get:function(t){return t=_(t),this[t.toLowerCase()+"s"]()},as:function(t){return t=_(t),this["as"+t.charAt(0).toUpperCase()+t.slice(1)+"s"]()},lang:ge.fn.lang,toIsoString:function(){var t=Math.abs(this.years()),e=Math.abs(this.months()),i=Math.abs(this.days()),s=Math.abs(this.hours()),o=Math.abs(this.minutes()),n=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(t?t+"Y":"")+(e?e+"M":"")+(i?i+"D":"")+(s||o||n?"T":"")+(s?s+"H":"")+(o?o+"M":"")+(n?n+"S":""):"P0D"}});for(ve in si)si.hasOwnProperty(ve)&&(ue(ve,si[ve]),pe(ve.toLowerCase()));ue("Weeks",6048e5),ge.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},ge.lang("en",{ordinal:function(t){var e=t%10,i=1===S(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i}}),Oe?e.exports=ge:"function"==typeof define&&define.amd?(define("moment",function(t,e,i){return i.config&&i.config()&&i.config().noGlobal===!0&&(be.moment=fe),ge}),me(!0)):me()}).call(this)},{}],5:[function(t,e){function i(t,e,i){return t.addEventListener?t.addEventListener(e,i,!1):void t.attachEvent("on"+e,i)}function s(t){return"keypress"==t.type?String.fromCharCode(t.which):w[t.which]?w[t.which]:x[t.which]?x[t.which]:String.fromCharCode(t.which).toLowerCase()}function o(t){var e=t.target||t.srcElement,i=e.tagName;return(" "+e.className+" ").indexOf(" mousetrap ")>-1?!1:"INPUT"==i||"SELECT"==i||"TEXTAREA"==i||e.contentEditable&&"true"==e.contentEditable}function n(t,e){return t.sort().join(",")===e.sort().join(",")}function r(t){t=t||{};var e,i=!1;for(e in M)t[e]?i=!0:M[e]=0;i||(N=!1)}function a(t,e,i,s,o){var r,a,h=[];if(!E[t])return[];for("keyup"==i&&p(t)&&(e=[t]),r=0;r95&&112>t||w.hasOwnProperty(t)&&(b[w[t]]=t)}return b}function g(t,e,i){return i||(i=m()[t]?"keydown":"keypress"),"keypress"==i&&e.length&&(i="keydown"),i}function f(t,e,i,o){M[t]=0,o||(o=g(e[0],[]));var n,a=function(){N=o,++M[t],u()},h=function(t){d(i,t),"keyup"!==o&&(C=s(t)),setTimeout(r,10)};for(n=0;n1)return f(t,d,e,i);for(h="+"===t?["+"]:t.split("+"),n=0;n":".","?":"/","|":"\\"},D={option:"alt",command:"meta","return":"enter",escape:"esc"},E={},T={},M={},C=!1,N=!1,O=1;20>O;++O)w[111+O]="f"+O;for(O=0;9>=O;++O)w[O+96]=O;i(document,"keypress",c),i(document,"keydown",c),i(document,"keyup",c);var k={bind:function(t,e,i){return y(t instanceof Array?t:[t],e,i),T[t+":"+i]=e,this},unbind:function(t,e){return T[t+":"+e]&&(delete T[t+":"+e],this.bind(t,function(){},e)),this},trigger:function(t,e){return T[t+":"+e](),this},reset:function(){return E={},T={},this}};e.exports=k},{}]},{},[1])(1)}); \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):"object"==typeof exports?exports.vis=e():t.vis=e()}(this,function(){return function(t){function e(s){if(i[s])return i[s].exports;var n=i[s]={exports:{},id:s,loaded:!1};return t[s].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){e.util=i(1),e.DOMutil=i(2),e.DataSet=i(3),e.DataView=i(4),e.Graph3d=i(5),e.Timeline=i(6),e.Graph2d=i(7),e.timeline={DataStep:i(8),Range:i(9),stack:i(10),TimeStep:i(11),components:{items:{Item:i(22),ItemBox:i(23),ItemPoint:i(24),ItemRange:i(25)},Component:i(12),CurrentTime:i(13),CustomTime:i(14),DataAxis:i(15),GraphGroup:i(16),Group:i(17),ItemSet:i(18),Legend:i(19),LineGraph:i(20),TimeAxis:i(21)}},e.Network=i(26),e.network={Edge:i(27),Groups:i(28),Images:i(29),Node:i(30),Popup:i(31),dotparser:i(32)},e.Graph=function(){throw new Error("Graph is renamed to Network. Please create a graph as new vis.Network(...)")}},function(module,exports,__webpack_require__){var Hammer=__webpack_require__(38),moment=__webpack_require__(39);exports.isNumber=function(t){return t instanceof Number||"number"==typeof t},exports.isString=function(t){return t instanceof String||"string"==typeof t},exports.isDate=function(t){if(t instanceof Date)return!0;if(exports.isString(t)){var e=ASPDateRegex.exec(t);if(e)return!0;if(!isNaN(Date.parse(t)))return!0}return!1},exports.isDataTable=function(t){return"undefined"!=typeof google&&google.visualization&&google.visualization.DataTable&&t instanceof google.visualization.DataTable},exports.randomUUID=function(){var t=function(){return Math.floor(65536*Math.random()).toString(16)};return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()},exports.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)&&(t[n]=s[n])}return t},exports.selectiveExtend=function(t,e){if(!Array.isArray(t))throw new Error("Array with property names expected as first argument");for(var i=2;ii;i++)if(t[i]!=e[i])return!1;return!0},exports.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(exports.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(exports.isString(t))return i=ASPDateRegex.exec(t),i?new Date(Number(i[1])):moment(t).toDate();throw new Error("Cannot convert object of type "+exports.getType(t)+" to type Date");case"Moment":if(exports.isNumber(t))return moment(t);if(t instanceof Date)return moment(t.valueOf());if(moment.isMoment(t))return moment(t);if(exports.isString(t))return i=ASPDateRegex.exec(t),moment(i?Number(i[1]):t);throw new Error("Cannot convert object of type "+exports.getType(t)+" to type Date");case"ISODate":if(exports.isNumber(t))return new Date(t);if(t instanceof Date)return t.toISOString();if(moment.isMoment(t))return t.toDate().toISOString();if(exports.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 "+exports.getType(t)+" to type ISODate");case"ASPDate":if(exports.isNumber(t))return"/Date("+t+")/";if(t instanceof Date)return"/Date("+t.valueOf()+")/";if(exports.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 "+exports.getType(t)+" to type ASPDate");default:throw new Error('Unknown type "'+e+'"')}};var ASPDateRegex=/^\/?Date\((\-?\d+)/i;exports.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},exports.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},exports.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},exports.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)},exports.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)},exports.addClassName=function(t,e){var i=t.className.split(" ");-1==i.indexOf(e)&&(i.push(e),t.className=i.join(" "))},exports.removeClassName=function(t,e){var i=t.className.split(" "),s=i.indexOf(e);-1!=s&&(i.splice(s,1),t.className=i.join(" "))},exports.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)},exports.toArray=function(t){var e=[];for(var i in t)t.hasOwnProperty(i)&&e.push(t[i]);return e},exports.updateProperty=function(t,e,i){return t[e]!==i?(t[e]=i,!0):!1},exports.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)},exports.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)},exports.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},exports.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},exports.option={},exports.option.asBoolean=function(t,e){return"function"==typeof t&&(t=t()),null!=t?0!=t:e||null},exports.option.asNumber=function(t,e){return"function"==typeof t&&(t=t()),null!=t?Number(t)||e||null:e||null},exports.option.asString=function(t,e){return"function"==typeof t&&(t=t()),null!=t?String(t):e||null},exports.option.asSize=function(t,e){return"function"==typeof t&&(t=t()),exports.isString(t)?t:exports.isNumber(t)?t+"px":e||null},exports.option.asElement=function(t,e){return"function"==typeof t&&(t=t()),t||e||null},exports.GiveDec=function(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)},exports.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},exports.parseColor=function(t){var e;if(exports.isString(t))if(exports.isValidHex(t)){var i=exports.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=exports.HSVToHex(n.h,n.h,n.v),a=exports.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,exports.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),exports.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},exports.hexToRGB=function(t){t=t.replace("#","").toUpperCase();var e=exports.GiveDec(t.substring(0,1)),i=exports.GiveDec(t.substring(1,2)),s=exports.GiveDec(t.substring(2,3)),n=exports.GiveDec(t.substring(3,4)),o=exports.GiveDec(t.substring(4,5)),a=exports.GiveDec(t.substring(5,6)),r=16*e+i,h=16*s+n,i=16*o+a;return{r:r,g:h,b:i}},exports.RGBToHex=function(t,e,i){var s=exports.GiveHex(Math.floor(t/16)),n=exports.GiveHex(t%16),o=exports.GiveHex(Math.floor(e/16)),a=exports.GiveHex(e%16),r=exports.GiveHex(Math.floor(i/16)),h=exports.GiveHex(i%16),d=s+n+o+a+r+h;return"#"+d},exports.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}},exports.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)}},exports.HSVToHex=function(t,e,i){var s=exports.HSVToRGB(t,e,i);return exports.RGBToHex(s.r,s.g,s.b)},exports.hexToHSV=function(t){var e=exports.hexToRGB(t);return exports.RGBToHSV(e.r,e.g,e.b)},exports.isValidHex=function(t){var e=/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(t);return e},exports.selectiveBridgeObject=function(t,e){if("object"==typeof e){for(var i=Object.create(e),s=0;se.start-r&&oe.start-r&&oo&&a>e||e>a&&r>e?(d=!0,a!=e&&("before"==s?e>o&&a>e&&(u=Math.max(0,u-1)):e>a&&r>e&&(u=Math.min(h.length-1,u+1)))):(e>a?l=Math.floor(.5*(c+l)):c=Math.floor(.5*(c+l)),n=Math.floor(.5*(c+l)),u==n?(u=-2,d=!0):u=n);return u}},function(t,e){e.prepareElements=function(t){for(var e in t)t.hasOwnProperty(e)&&(t[e].redundant=t[e].used,t[e].used=[])},e.cleanupElements=function(t){for(var e in t)if(t.hasOwnProperty(e)&&t[e].redundant){for(var i=0;i0?(s=e[t].redundant[0],e[t].redundant.shift()):(s=document.createElementNS("http://www.w3.org/2000/svg",t),i.appendChild(s)):(s=document.createElementNS("http://www.w3.org/2000/svg",t),e[t]={used:[],redundant:[]},i.appendChild(s)),e[t].used.push(s),s},e.getDOMElement=function(t,e,i){var s;return e.hasOwnProperty(t)?e[t].redundant.length>0?(s=e[t].redundant[0],e[t].redundant.shift()):(s=document.createElement(t),i.appendChild(s)):(s=document.createElement(t),e[t]={used:[],redundant:[]},i.appendChild(s)),e[t].used.push(s),s},e.drawPoint=function(t,i,s,n,o){var a;return"circle"==s.options.drawPoints.style?(a=e.getSVGElement("circle",n,o),a.setAttributeNS(null,"cx",t),a.setAttributeNS(null,"cy",i),a.setAttributeNS(null,"r",.5*s.options.drawPoints.size),a.setAttributeNS(null,"class",s.className+" point")):(a=e.getSVGElement("rect",n,o),a.setAttributeNS(null,"x",t-.5*s.options.drawPoints.size),a.setAttributeNS(null,"y",i-.5*s.options.drawPoints.size),a.setAttributeNS(null,"width",s.options.drawPoints.size),a.setAttributeNS(null,"height",s.options.drawPoints.size),a.setAttributeNS(null,"class",s.className+" point")),a},e.drawBar=function(t,i,s,n,o,a,r){var h=e.getSVGElement("rect",a,r);h.setAttributeNS(null,"x",t-.5*s),h.setAttributeNS(null,"y",i),h.setAttributeNS(null,"width",s),h.setAttributeNS(null,"height",n),h.setAttributeNS(null,"class",o)}},function(t,e,i){function s(t,e){if(!t||Array.isArray(t)||n.isDataTable(t)||(e=t,t=null),this._options=e||{},this._data={},this._fieldId=this._options.fieldId||"id",this._type={},this._options.type)for(var i in this._options.type)if(this._options.type.hasOwnProperty(i)){var s=this._options.type[i];this._type[i]="Date"==s||"ISODate"==s||"ASPDate"==s?"Date":s}if(this._options.convert)throw new Error('Option "convert" is deprecated. Use "type" instead.');this._subscribers={},t&&this.add(t)}var n=i(1);s.prototype.on=function(t,e){var i=this._subscribers[t];i||(i=[],this._subscribers[t]=i),i.push({callback:e})},s.prototype.subscribe=s.prototype.on,s.prototype.off=function(t,e){var i=this._subscribers[t];i&&(this._subscribers[t]=i.filter(function(t){return t.callback!=e}))},s.prototype.unsubscribe=s.prototype.off,s.prototype._trigger=function(t,e,i){if("*"==t)throw new Error("Cannot trigger event *");var s=[];t in this._subscribers&&(s=s.concat(this._subscribers[t])),"*"in this._subscribers&&(s=s.concat(this._subscribers["*"]));for(var n=0;na;a++)i=o._addItem(t[a]),s.push(i);else if(n.isDataTable(t))for(var h=this._getColumnNames(t),d=0,l=t.getNumberOfRows();l>d;d++){for(var c={},u=0,p=h.length;p>u;u++){var m=h[u];c[m]=t.getValue(d,u)}i=o._addItem(c),s.push(i)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");i=o._addItem(t),s.push(i)}return s.length&&this._trigger("add",{items:s},e),s},s.prototype.update=function(t,e){var i=[],s=[],o=this,a=o._fieldId,r=function(t){var e=t[a];o._data[e]?(e=o._updateItem(t),s.push(e)):(e=o._addItem(t),i.push(e))};if(Array.isArray(t))for(var h=0,d=t.length;d>h;h++)r(t[h]);else if(n.isDataTable(t))for(var l=this._getColumnNames(t),c=0,u=t.getNumberOfRows();u>c;c++){for(var p={},m=0,_=l.length;_>m;m++){var f=l[m];p[f]=t.getValue(c,m)}r(p)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");r(t)}return i.length&&this._trigger("add",{items:i},e),s.length&&this._trigger("update",{items:s},e),i.concat(s)},s.prototype.get=function(){var t,e,i,s,o=this,a=n.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.returnType){if(r="DataTable"==i.returnType?"DataTable":"Array",s&&r!=n.getType(s))throw new Error('Type of parameter "data" ('+n.getType(s)+") does not correspond with specified options.type ("+i.type+")");if("DataTable"==r&&!n.isDataTable(s))throw new Error('Parameter "data" must be a DataTable when options.type is "DataTable"')}else r=s&&"DataTable"==n.getType(s)?"DataTable":"Array";var h,d,l,c,u=i&&i.type||this._options.type,p=i&&i.filter,m=[];if(void 0!=t)h=o._getItem(t,u),p&&!p(h)&&(h=null);else if(void 0!=e)for(l=0,c=e.length;c>l;l++)h=o._getItem(e[l],u),(!p||p(h))&&m.push(h);else for(d in this._data)this._data.hasOwnProperty(d)&&(h=o._getItem(d,u),(!p||p(h))&&m.push(h));if(i&&i.order&&void 0==t&&this._sort(m,i.order),i&&i.fields){var _=i.fields;if(void 0!=t)h=this._filterFields(h,_);else for(l=0,c=m.length;c>l;l++)m[l]=this._filterFields(m[l],_)}if("DataTable"==r){var f=this._getColumnNames(s);if(void 0!=t)o._appendRow(s,f,h);else for(l=0,c=m.length;c>l;l++)o._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},s.prototype.getIds=function(t){var e,i,s,n,o,a=this._data,r=t&&t.filter,h=t&&t.order,d=t&&t.type||this._options.type,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},s.prototype.getDataSet=function(){return this},s.prototype.forEach=function(t,e){var i,s,n=e&&e.filter,o=e&&e.type||this._options.type,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))},s.prototype.map=function(t,e){var i,s=e&&e.filter,n=e&&e.type||this._options.type,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},s.prototype._filterFields=function(t,e){var i={};for(var s in t)t.hasOwnProperty(s)&&-1!=e.indexOf(s)&&(i[s]=t[s]);return i},s.prototype._sort=function(t,e){if(n.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)}},s.prototype.remove=function(t,e){var i,s,n,o=[];if(Array.isArray(t))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},s.prototype._remove=function(t){if(n.isNumber(t)||n.isString(t)){if(this._data[t])return delete this._data[t],t}else if(t instanceof Object){var e=t[this._fieldId];if(e&&this._data[e])return delete this._data[e],e}return null},s.prototype.clear=function(t){var e=Object.keys(this._data);return this._data={},this._trigger("remove",{items:e},t),e},s.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},s.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},s.prototype.distinct=function(t){var e,i=this._data,s=[],o=this._options.type&&this._options.type[t]||null,a=0;for(var r in i)if(i.hasOwnProperty(r)){var h=i[r],d=h[t],l=!1;for(e=0;a>e;e++)if(s[e]==d){l=!0;break}l||void 0===d||(s[a]=d,a++)}if(o)for(e=0;ei;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},s.prototype._appendRow=function(t,e,i){for(var s=t.addRow(),n=0,o=e.length;o>n;n++){var a=e[n];t.setValue(s,n,i[a])}},t.exports=s},function(t,e,i){function s(t,e){this._data=null,this._ids={},this._options=e||{},this._fieldId="id",this._subscribers={};var i=this;this.listener=function(){i._onEvent.apply(i,arguments)},this.setData(t)}var n=i(1),o=i(3);s.prototype.setData=function(t){var e,i,s;if(this._data){this._data.unsubscribe&&this._data.unsubscribe("*",this.listener),e=[];for(var 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)}},s.prototype.get=function(){var t,e,i,s=this,o=n.getType(arguments[0]);"String"==o||"Number"==o||"Array"==o?(t=arguments[0],e=arguments[1],i=arguments[2]):(e=arguments[0],i=arguments[1]);var a=n.extend({},this._options,e);this._options.filter&&e&&e.filter&&(a.filter=function(t){return s._options.filter(t)&&e.filter(t)});var r=[];return void 0!=t&&r.push(t),r.push(a),r.push(i),this._data&&this._data.get.apply(this._data,r)},s.prototype.getIds=function(t){var e;if(this._data){var i,s=this._options.filter;i=t&&t.filter?s?function(e){return s(e)&&t.filter(e)}:t.filter:s,e=this._data.getIds({filter:i,order:t&&t.order})}else e=[];return e},s.prototype.getDataSet=function(){for(var t=this;t instanceof s;)t=t._data;return t||null},s.prototype._onEvent=function(t,e,i){var s,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)}},s.prototype.on=o.prototype.on,s.prototype.off=o.prototype.off,s.prototype._trigger=o.prototype._trigger,s.prototype.subscribe=s.prototype.on,s.prototype.unsubscribe=s.prototype.off,t.exports=s},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this.containerElement=t,this.width="400px",this.height="400px",this.margin=10,this.defaultXCenter="55%",this.defaultYCenter="50%",this.xLabel="x",this.yLabel="y",this.zLabel="z",this.filterLabel="time",this.legendLabel="value",this.style=s.STYLE.DOT,this.showPerspective=!0,this.showGrid=!0,this.keepAspectRatio=!0,this.showShadow=!1,this.showGrayBottom=!1,this.showTooltip=!1,this.verticalRatio=.5,this.animationInterval=1e3,this.animationPreload=!1,this.camera=new s.Camera,this.eye=new h(0,0,-1),this.dataTable=null,this.dataPoints=null,this.colX=void 0,this.colY=void 0,this.colZ=void 0,this.colValue=void 0,this.colFilter=void 0,this.xMin=0,this.xStep=void 0,this.xMax=1,this.yMin=0,this.yStep=void 0,this.yMax=1,this.zMin=0,this.zStep=void 0,this.zMax=1,this.valueMin=0,this.valueMax=1,this.xBarWidth=1,this.yBarWidth=1,this.colorAxis="#4D4D4D",this.colorGrid="#D3D3D3",this.colorDot="#7DC1FF",this.colorDotBorder="#3267D2",this.create(),this.setOptions(i),e&&this.setData(e)}function n(t,e){if(void 0===t)throw"Error: No container element defined";if(this.container=t,this.visible=e&&void 0!=e.visible?e.visible:!0,this.visible){this.frame=document.createElement("DIV"),this.frame.style.width="100%",this.frame.style.position="relative",this.container.appendChild(this.frame),this.frame.prev=document.createElement("INPUT"),this.frame.prev.type="BUTTON",this.frame.prev.value="Prev",this.frame.appendChild(this.frame.prev),this.frame.play=document.createElement("INPUT"),this.frame.play.type="BUTTON",this.frame.play.value="Play",this.frame.appendChild(this.frame.play),this.frame.next=document.createElement("INPUT"),this.frame.next.type="BUTTON",this.frame.next.value="Next",this.frame.appendChild(this.frame.next),this.frame.bar=document.createElement("INPUT"),this.frame.bar.type="BUTTON",this.frame.bar.style.position="absolute",this.frame.bar.style.border="1px solid red",this.frame.bar.style.width="100px",this.frame.bar.style.height="6px",this.frame.bar.style.borderRadius="2px",this.frame.bar.style.MozBorderRadius="2px",this.frame.bar.style.border="1px solid #7F7F7F",this.frame.bar.style.backgroundColor="#E5E5E5",this.frame.appendChild(this.frame.bar),this.frame.slide=document.createElement("INPUT"),this.frame.slide.type="BUTTON",this.frame.slide.style.margin="0px",this.frame.slide.value=" ",this.frame.slide.style.position="relative",this.frame.slide.style.left="-100px",this.frame.appendChild(this.frame.slide);var i=this;this.frame.slide.onmousedown=function(t){i._onMouseDown(t)},this.frame.prev.onclick=function(t){i.prev(t)},this.frame.play.onclick=function(t){i.togglePlay(t)},this.frame.next.onclick=function(t){i.next(t)}}this.onChangeCallback=void 0,this.values=[],this.index=void 0,this.playTimeout=void 0,this.playInterval=1e3,this.playLoop=!0}var o=i(41),a=i(3),r=i(4),h=i(33),d=i(34),l=i(35),c=i(36);o(s.prototype),s.Camera=function(){this.armLocation=new h,this.armRotation={},this.armRotation.horizontal=0,this.armRotation.vertical=0,this.armLength=1.7,this.cameraLocation=new h,this.cameraRotation=new h(.5*Math.PI,0,0),this.calculateCameraOrientation()},s.Camera.prototype.setArmLocation=function(t,e,i){this.armLocation.x=t,this.armLocation.y=e,this.armLocation.z=i,this.calculateCameraOrientation()},s.Camera.prototype.setArmRotation=function(t,e){void 0!==t&&(this.armRotation.horizontal=t),void 0!==e&&(this.armRotation.vertical=e,this.armRotation.vertical<0&&(this.armRotation.vertical=0),this.armRotation.vertical>.5*Math.PI&&(this.armRotation.vertical=.5*Math.PI)),(void 0!==t||void 0!==e)&&this.calculateCameraOrientation()},s.Camera.prototype.getArmRotation=function(){var t={};return t.horizontal=this.armRotation.horizontal,t.vertical=this.armRotation.vertical,t},s.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())},s.Camera.prototype.getArmLength=function(){return this.armLength},s.Camera.prototype.getCameraLocation=function(){return this.cameraLocation},s.Camera.prototype.getCameraRotation=function(){return this.cameraRotation},s.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},s.prototype._setScale=function(){this.scale=new h(1/(this.xMax-this.xMin),1/(this.yMax-this.yMin),1/(this.zMax-this.zMin)),this.keepAspectRatio&&(this.scale.x3&&(this.colFilter=3);else{if(this.style!==s.STYLE.DOTCOLOR&&this.style!==s.STYLE.DOTSIZE&&this.style!==s.STYLE.BARCOLOR&&this.style!==s.STYLE.BARSIZE)throw'Unknown style "'+this.style+'"';this.colX=0,this.colY=1,this.colZ=2,this.colValue=3,t.getNumberOfColumns()>4&&(this.colFilter=4)}},s.prototype.getNumberOfRows=function(t){return t.length},s.prototype.getNumberOfColumns=function(t){var e=0;for(var i in t[0])t[0].hasOwnProperty(i)&&e++;return e},s.prototype.getDistinctValues=function(t,e){for(var i=[],s=0;st[s][e]&&(i.min=t[s][e]),i.maxt;t++){var _=(t-p)/(m-p),f=240*_,g=this._hsv2rgb(f,1,1);u.strokeStyle=g,u.beginPath(),u.moveTo(h,a+t),u.lineTo(r,a+t),u.stroke()}u.strokeStyle=this.colorAxis,u.strokeRect(h,a,i,o)}if(this.style===s.STYLE.DOTSIZE&&(u.strokeStyle=this.colorAxis,u.fillStyle=this.colorDot,u.beginPath(),u.moveTo(h,a),u.lineTo(r,a),u.lineTo(r-i+e,d),u.lineTo(h,d),u.closePath(),u.fill(),u.stroke()),this.style===s.STYLE.DOTCOLOR||this.style===s.STYLE.DOTSIZE){var y=5,v=new c(this.valueMin,this.valueMax,(this.valueMax-this.valueMin)/5,!0);for(v.start(),v.getCurrent()0?this.yMin:this.yMax,n=this._convert3Dto2D(new h(b,a,this.zMin)),Math.cos(2*M)>0?(f.textAlign="center",f.textBaseline="top",n.y+=v):Math.sin(2*M)<0?(f.textAlign="right",f.textBaseline="middle"):(f.textAlign="left",f.textBaseline="middle"),f.fillStyle=this.colorAxis,f.fillText(" "+i.getCurrent()+" ",n.x,n.y),i.next()}for(f.lineWidth=1,s=void 0===this.defaultYStep,i=new c(this.yMin,this.yMax,this.yStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,n=this._convert3Dto2D(new h(o,i.getCurrent(),this.zMin)),Math.cos(2*M)<0?(f.textAlign="center",f.textBaseline="top",n.y+=v):Math.sin(2*M)>0?(f.textAlign="right",f.textBaseline="middle"):(f.textAlign="left",f.textBaseline="middle"),f.fillStyle=this.colorAxis,f.fillText(" "+i.getCurrent()+" ",n.x,n.y),i.next();for(f.lineWidth=1,s=void 0===this.defaultZStep,i=new c(this.zMin,this.zMax,this.zStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,a=Math.sin(M)<0?this.yMin:this.yMax;!i.end();)t=this._convert3Dto2D(new h(o,a,i.getCurrent())),f.strokeStyle=this.colorAxis,f.beginPath(),f.moveTo(t.x,t.y),f.lineTo(t.x-v,t.y),f.stroke(),f.textAlign="right",f.textBaseline="middle",f.fillStyle=this.colorAxis,f.fillText(i.getCurrent()+" ",t.x-5,t.y),i.next();f.lineWidth=1,t=this._convert3Dto2D(new h(o,a,this.zMin)),e=this._convert3Dto2D(new h(o,a,this.zMax)),f.strokeStyle=this.colorAxis,f.beginPath(),f.moveTo(t.x,t.y),f.lineTo(e.x,e.y),f.stroke(),f.lineWidth=1,p=this._convert3Dto2D(new h(this.xMin,this.yMin,this.zMin)),m=this._convert3Dto2D(new h(this.xMax,this.yMin,this.zMin)),f.strokeStyle=this.colorAxis,f.beginPath(),f.moveTo(p.x,p.y),f.lineTo(m.x,m.y),f.stroke(),p=this._convert3Dto2D(new h(this.xMin,this.yMax,this.zMin)),m=this._convert3Dto2D(new h(this.xMax,this.yMax,this.zMin)),f.strokeStyle=this.colorAxis,f.beginPath(),f.moveTo(p.x,p.y),f.lineTo(m.x,m.y),f.stroke(),f.lineWidth=1,t=this._convert3Dto2D(new h(this.xMin,this.yMin,this.zMin)),e=this._convert3Dto2D(new h(this.xMin,this.yMax,this.zMin)),f.strokeStyle=this.colorAxis,f.beginPath(),f.moveTo(t.x,t.y),f.lineTo(e.x,e.y),f.stroke(),t=this._convert3Dto2D(new h(this.xMax,this.yMin,this.zMin)),e=this._convert3Dto2D(new h(this.xMax,this.yMax,this.zMin)),f.strokeStyle=this.colorAxis,f.beginPath(),f.moveTo(t.x,t.y),f.lineTo(e.x,e.y),f.stroke();var w=this.xLabel;w.length>0&&(u=.1/this.scale.y,o=(this.xMin+this.xMax)/2,a=Math.cos(M)>0?this.yMin-u:this.yMax+u,n=this._convert3Dto2D(new h(o,a,this.zMin)),Math.cos(2*M)>0?(f.textAlign="center",f.textBaseline="top"):Math.sin(2*M)<0?(f.textAlign="right",f.textBaseline="middle"):(f.textAlign="left",f.textBaseline="middle"),f.fillStyle=this.colorAxis,f.fillText(w,n.x,n.y));var L=this.yLabel;L.length>0&&(l=.1/this.scale.x,o=Math.sin(M)>0?this.xMin-l:this.xMax+l,a=(this.yMin+this.yMax)/2,n=this._convert3Dto2D(new h(o,a,this.zMin)),Math.cos(2*M)<0?(f.textAlign="center",f.textBaseline="top"):Math.sin(2*M)>0?(f.textAlign="right",f.textBaseline="middle"):(f.textAlign="left",f.textBaseline="middle"),f.fillStyle=this.colorAxis,f.fillText(L,n.x,n.y));var x=this.zLabel;x.length>0&&(d=30,o=Math.cos(M)>0?this.xMin:this.xMax,a=Math.sin(M)<0?this.yMin:this.yMax,r=(this.zMin+this.zMax)/2,n=this._convert3Dto2D(new h(o,a,r)),f.textAlign="right",f.textBaseline="middle",f.fillStyle=this.colorAxis,f.fillText(x,n.x-d,n.y))},s.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)+")"},s.prototype._redrawDataGrid=function(){var t,e,i,n,o,a,r,d,l,c,u,p,m,_=this.frame.canvas,f=_.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(o=0;o0}else a=!0;a?(m=(t.point.z+e.point.z+i.point.z+n.point.z)/4,c=240*(1-(m-this.zMin)*this.scale.z/this.verticalRatio),u=1,this.showShadow?(p=Math.min(1+L.x/x/2,1),r=this._hsv2rgb(c,u,p),d=r):(p=1,r=this._hsv2rgb(c,u,p),d=this.colorAxis)):(r="gray",d=this.colorAxis),l=.5,f.lineWidth=l,f.fillStyle=r,f.strokeStyle=d,f.beginPath(),f.moveTo(t.screen.x,t.screen.y),f.lineTo(e.screen.x,e.screen.y),f.lineTo(n.screen.x,n.screen.y),f.lineTo(i.screen.x,i.screen.y),f.closePath(),f.fill(),f.stroke()}}else for(o=0;ou&&(u=0);var p,m,_;this.style===s.STYLE.DOTCOLOR?(p=240*(1-(d.point.value-this.valueMin)*this.scale.value),m=this._hsv2rgb(p,1,1),_=this._hsv2rgb(p,1,.8)):this.style===s.STYLE.DOTSIZE?(m=this.colorDot,_=this.colorDotBorder):(p=240*(1-(d.point.z-this.zMin)*this.scale.z/this.verticalRatio),m=this._hsv2rgb(p,1,1),_=this._hsv2rgb(p,1,.8)),i.lineWidth=1,i.strokeStyle=_,i.fillStyle=m,i.beginPath(),i.arc(d.screen.x,d.screen.y,u,0,2*Math.PI,!0),i.fill(),i.stroke()}}},s.prototype._redrawDataBar=function(){var t,e,i,n,o=this.frame.canvas,a=o.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(t=0;t0&&(t=this.dataPoints[0],s.lineWidth=1,s.strokeStyle="blue",s.beginPath(),s.moveTo(t.screen.x,t.screen.y)),e=1;e0&&s.stroke()}},s.prototype._onMouseDown=function(t){if(t=t||window.event,this.leftButtonDown&&this._onMouseUp(t),this.leftButtonDown=t.which?1===t.which:1===t.button,this.leftButtonDown||this.touchDown){this.startMouseX=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)}},s.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)},s.prototype._dataPointFromXY=function(t,e){var i,n=100,o=null,a=null,r=null,h=new d(t,e);if(this.style===s.STYLE.BAR||this.style===s.STYLE.BARCOLOR||this.style===s.STYLE.BARSIZE)for(i=this.dataPoints.length-1;i>=0;i--){o=this.dataPoints[i];var l=o.surfaces;if(l)for(var c=l.length-1;c>=0;c--){var u=l[c],p=u.corners,m=[p[0].screen,p[1].screen,p[2].screen],_=[p[2].screen,p[3].screen,p[0].screen];if(this._insideTriangle(h,m)||this._insideTriangle(h,_))return o}}else for(i=0;iv)&&n>v&&(r=v,a=o)}}return a},s.prototype._showTooltip=function(t){var e,i,s;this.tooltip?(e=this.tooltip.dom.content,i=this.tooltip.dom.line,s=this.tooltip.dom.dot):(e=document.createElement("div"),e.style.position="absolute",e.style.padding="10px",e.style.border="1px solid #4d4d4d",e.style.color="#1a1a1a",e.style.background="rgba(255,255,255,0.7)",e.style.borderRadius="2px",e.style.boxShadow="5px 5px 10px rgba(128,128,128,0.5)",i=document.createElement("div"),i.style.position="absolute",i.style.height="40px",i.style.width="0",i.style.borderLeft="1px solid #4d4d4d",s=document.createElement("div"),s.style.position="absolute",s.style.height="0",s.style.width="0",s.style.border="5px solid #4d4d4d",s.style.borderRadius="5px",this.tooltip={dataPoint:null,dom:{content:e,line:i,dot:s}}),this._hideTooltip(),this.tooltip.dataPoint=t,e.innerHTML="function"==typeof this.showTooltip?this.showTooltip(t.point):"
x:"+t.point.x+"
y:"+t.point.y+"
z:"+t.point.z+"
",e.style.left="0",e.style.top="0",this.frame.appendChild(e),this.frame.appendChild(i),this.frame.appendChild(s);var n=e.offsetWidth,o=e.offsetHeight,a=i.offsetHeight,r=s.offsetWidth,h=s.offsetHeight,d=t.screen.x-n/2;d=Math.min(Math.max(d,10),this.frame.clientWidth-10-n),i.style.left=t.screen.x+"px",i.style.top=t.screen.y-a+"px",e.style.left=d+"px",e.style.top=t.screen.y-a-o+"px",s.style.left=t.screen.x-r/2+"px",s.style.top=t.screen.y-h/2+"px"},s.prototype._hideTooltip=function(){if(this.tooltip){this.tooltip.dataPoint=null;for(var t in this.tooltip.dom)if(this.tooltip.dom.hasOwnProperty(t)){var e=this.tooltip.dom[t];e&&e.parentNode&&e.parentNode.removeChild(e)}}},G3DaddEventListener=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)},G3DremoveEventListener=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)},G3DstopPropagation=function(t){t||(t=window.event),t.stopPropagation?t.stopPropagation():t.cancelBubble=!0},G3DpreventDefault=function(t){t||(t=window.event),t.preventDefault?t.preventDefault():t.returnValue=!1},n.prototype.prev=function(){var t=this.getIndex();t>0&&(t--,this.setIndex(t))},n.prototype.next=function(){var t=this.getIndex();t0?this.setIndex(0):this.index=void 0},n.prototype.setIndex=function(t){if(!(ts&&(s=0),s>this.values.length-1&&(s=this.values.length-1),s},n.prototype.indexToLeft=function(t){var e=parseFloat(this.frame.bar.style.width)-this.frame.slide.clientWidth-10,i=t/(this.values.length-1)*e,s=i+3;return s},n.prototype._onMouseMove=function(t){var e=t.clientX-this.startClientX,i=this.startSlideX+e,s=this.leftToIndex(i);this.setIndex(s),G3DpreventDefault()},n.prototype._onMouseUp=function(){this.frame.style.cursor="auto",G3DremoveEventListener(document,"mousemove",this.onmousemove),G3DremoveEventListener(document,"mouseup",this.onmouseup),G3DpreventDefault()},getAbsoluteLeft=function(t){for(var e=0;null!==t;)e+=t.offsetLeft,e-=t.scrollLeft,t=t.offsetParent;return e},getAbsoluteTop=function(t){for(var e=0;null!==t;)e+=t.offsetTop,e-=t.scrollTop,t=t.offsetParent;return e},getMouseX=function(t){return"clientX"in t?t.clientX:t.targetTouches[0]&&t.targetTouches[0].clientX||0},getMouseY=function(t){return"clientY"in t?t.clientY:t.targetTouches[0]&&t.targetTouches[0].clientY||0},t.exports=s},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");var n=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=a.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},util:{snap:null,toScreen:n._toScreen.bind(n),toGlobalScreen:n._toGlobalScreen.bind(n),toTime:n._toTime.bind(n),toGlobalTime:n._toGlobalTime.bind(n)}},this.range=new d(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new l(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new c(this.body),this.components.push(this.currentTime),this.customTime=new u(this.body),this.components.push(this.customTime),this.itemSet=new p(this.body),this.components.push(this.itemSet),this.itemsData=null,this.groupsData=null,i&&this.setOptions(i),e?this.setItems(e):this.redraw()}var n=i(41),o=i(49),a=i(1),r=i(3),h=i(4),d=i(9),l=i(21),c=i(13),u=i(14),p=i(18);n(s.prototype),s.prototype._create=function(t){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)),this.hammer=o(this.dom.root,{prevent_default:!0}),this.listeners={};var e=this,i=["touch","pinch","tap","doubletap","hold","dragstart","drag","dragend","mousewheel","DOMMouseScroll"];if(i.forEach(function(t){var i=function(){var i=[t].concat(Array.prototype.slice.call(arguments,0));e.emit.apply(e,i)};e.hammer.on(t,i),e.listeners[t]=i}),this.props={root:{},background:{},centerContainer:{},leftContainer:{},rightContainer:{},center:{},left:{},right:{},top:{},bottom:{},border:{},scrollTop:0,scrollTopMin:0},this.touch={},!t)throw new Error("No container provided");t.appendChild(this.dom.root)},s.prototype.destroy=function(){this.clear(),this.off(),this._stopAutoResize(),this.dom.root.parentNode&&this.dom.root.parentNode.removeChild(this.dom.root),this.dom=null;for(var t in this.listeners)this.listeners.hasOwnProperty(t)&&delete this.listeners[t];this.listeners=null,this.hammer=null,this.components.forEach(function(t){t.destroy()}),this.body=null},s.prototype.setOptions=function(t){if(t){var e=["width","height","minHeight","maxHeight","autoResize","start","end","orientation"];a.selectiveExtend(e,this.options,t),this._initAutoResize()}if(this.components.forEach(function(e){e.setOptions(t)}),t&&t.order)throw new Error("Option order is deprecated. There is no replacement for this feature.");this.redraw()},s.prototype.setCustomTime=function(t){if(!this.customTime)throw new Error("Cannot get custom time: Custom time bar is not enabled");this.customTime.setCustomTime(t)},s.prototype.getCustomTime=function(){if(!this.customTime)throw new Error("Cannot get custom time: Custom time bar is not enabled");return this.customTime.getCustomTime()},s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof r||t instanceof h?t:new r(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.itemSet&&this.itemSet.setItems(e),i&&("start"in this.options||"end"in this.options)){this.fit();var s="start"in this.options?a.convert(this.options.start,"Date"):null,n="end"in this.options?a.convert(this.options.end,"Date"):null;this.setWindow(s,n)}},s.prototype.setGroups=function(t){var e;e=t?t instanceof r||t instanceof h?t:new r(t):null,this.groupsData=e,this.itemSet.setGroups(e)},s.prototype.clear=function(t){(!t||t.items)&&this.setItems(null),(!t||t.groups)&&this.setGroups(null),(!t||t.options)&&(this.components.forEach(function(t){t.setOptions(t.defaultOptions)}),this.setOptions(this.defaultOptions))},s.prototype.fit=function(){var t=this.getItemRange(),e=t.min,i=t.max;if(null!=e&&null!=i){var s=i.valueOf()-e.valueOf();0>=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)},s.prototype.getItemRange=function(){var t=this.itemsData.getDataSet(),e=null,i=null;if(t){var s=t.min("start");e=s?a.convert(s.start,"Date").valueOf():null;var n=t.max("start");n&&(i=a.convert(n.start,"Date").valueOf());var o=t.max("end");o&&(i=null==i?a.convert(o.end,"Date").valueOf():Math.max(i,a.convert(o.end,"Date").valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},s.prototype.setSelection=function(t){this.itemSet&&this.itemSet.setSelection(t)},s.prototype.getSelection=function(){return this.itemSet&&this.itemSet.getSelection()||[]},s.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)},s.prototype.getWindow=function(){var t=this.range.getRange();return{start:new Date(t.start),end:new Date(t.end)}},s.prototype.redraw=function(){var t=!1,e=this.options,i=this.props,s=this.dom;if(s){s.root.className="vis timeline root "+e.orientation,s.root.style.maxHeight=a.option.asSize(e.maxHeight,""),s.root.style.minHeight=a.option.asSize(e.minHeight,""),s.root.style.width=a.option.asSize(e.width,""),i.border.left=(s.centerContainer.offsetWidth-s.centerContainer.clientWidth)/2,i.border.right=i.border.left,i.border.top=(s.centerContainer.offsetHeight-s.centerContainer.clientHeight)/2,i.border.bottom=i.border.top;var n=s.root.offsetHeight-s.root.clientHeight,o=s.root.offsetWidth-s.root.clientWidth;i.center.height=s.center.offsetHeight,i.left.height=s.left.offsetHeight,i.right.height=s.right.offsetHeight,i.top.height=s.top.clientHeight||-i.border.top,i.bottom.height=s.bottom.clientHeight||-i.border.bottom;var r=Math.max(i.left.height,i.center.height,i.right.height),h=i.top.height+r+i.bottom.height+n+i.border.top+i.border.bottom;s.root.style.height=a.option.asSize(e.height,h+"px"),i.root.height=s.root.offsetHeight,i.background.height=i.root.height-n;var d=i.root.height-i.top.height-i.bottom.height-n;i.centerContainer.height=d,i.leftContainer.height=d,i.rightContainer.height=i.leftContainer.height,i.root.width=s.root.offsetWidth,i.background.width=i.root.width-o,i.left.width=s.leftContainer.clientWidth||-i.border.left,i.leftContainer.width=i.left.width,i.right.width=s.rightContainer.clientWidth||-i.border.right,i.rightContainer.width=i.right.width;var l=i.root.width-i.left.width-i.right.width-o;i.center.width=l,i.centerContainer.width=l,i.top.width=l,i.bottom.width=l,s.background.style.height=i.background.height+"px",s.backgroundVertical.style.height=i.background.height+"px",s.backgroundHorizontal.style.height=i.centerContainer.height+"px",s.centerContainer.style.height=i.centerContainer.height+"px",s.leftContainer.style.height=i.leftContainer.height+"px",s.rightContainer.style.height=i.rightContainer.height+"px",s.background.style.width=i.background.width+"px",s.backgroundVertical.style.width=i.centerContainer.width+"px",s.backgroundHorizontal.style.width=i.background.width+"px",s.centerContainer.style.width=i.center.width+"px",s.top.style.width=i.top.width+"px",s.bottom.style.width=i.bottom.width+"px",s.background.style.left="0",s.background.style.top="0",s.backgroundVertical.style.left=i.left.width+"px",s.backgroundVertical.style.top="0",s.backgroundHorizontal.style.left="0",s.backgroundHorizontal.style.top=i.top.height+"px",s.centerContainer.style.left=i.left.width+"px",s.centerContainer.style.top=i.top.height+"px",s.leftContainer.style.left="0",s.leftContainer.style.top=i.top.height+"px",s.rightContainer.style.left=i.left.width+i.center.width+"px",s.rightContainer.style.top=i.top.height+"px",s.top.style.left=i.left.width+"px",s.top.style.top="0",s.bottom.style.left=i.left.width+"px",s.bottom.style.top=i.top.height+i.centerContainer.height+"px",this._updateScrollTop();var c=this.props.scrollTop;"bottom"==e.orientation&&(c+=Math.max(this.props.centerContainer.height-this.props.center.height-this.props.border.top-this.props.border.bottom,0)),s.center.style.left="0",s.center.style.top=c+"px",s.left.style.left="0",s.left.style.top=c+"px",s.right.style.left="0",s.right.style.top=c+"px";var u=0==this.props.scrollTop?"hidden":"",p=this.props.scrollTop==this.props.scrollTopMin?"hidden":"";s.shadowTop.style.visibility=u,s.shadowBottom.style.visibility=p,s.shadowTopLeft.style.visibility=u,s.shadowBottomLeft.style.visibility=p,s.shadowTopRight.style.visibility=u,s.shadowBottomRight.style.visibility=p,this.components.forEach(function(e){t=e.redraw()||t}),t&&this.redraw()}},s.prototype.repaint=function(){throw new Error("Function repaint is deprecated. Use redraw instead.")},s.prototype._toTime=function(t){var e=this.range.conversion(this.props.center.width);return new Date(t/e.scale+e.offset)},s.prototype._toGlobalTime=function(t){var e=this.range.conversion(this.props.root.width);return new Date(t/e.scale+e.offset)},s.prototype._toScreen=function(t){var e=this.range.conversion(this.props.center.width);return(t.valueOf()-e.offset)*e.scale},s.prototype._toGlobalScreen=function(t){var e=this.range.conversion(this.props.root.width);return(t.valueOf()-e.offset)*e.scale},s.prototype._initAutoResize=function(){1==this.options.autoResize?this._startAutoResize():this._stopAutoResize()},s.prototype._startAutoResize=function(){var t=this;this._stopAutoResize(),this._onResize=function(){return 1!=t.options.autoResize?void t._stopAutoResize():void(t.dom.root&&(t.dom.root.clientWidth!=t.props.lastWidth||t.dom.root.clientHeight!=t.props.lastHeight)&&(t.props.lastWidth=t.dom.root.clientWidth,t.props.lastHeight=t.dom.root.clientHeight,t.emit("change")))},a.addEventListener(window,"resize",this._onResize),this.watchTimer=setInterval(this._onResize,1e3)},s.prototype._stopAutoResize=function(){this.watchTimer&&(clearInterval(this.watchTimer),this.watchTimer=void 0),a.removeEventListener(window,"resize",this._onResize),this._onResize=null},s.prototype._onTouch=function(){this.touch.allowDragging=!0},s.prototype._onPinch=function(){this.touch.allowDragging=!1},s.prototype._onDragStart=function(){this.touch.initialScrollTop=this.props.scrollTop},s.prototype._onDrag=function(t){if(this.touch.allowDragging){var e=t.gesture.deltaY,i=this._getScrollTop(),s=this._setScrollTop(this.touch.initialScrollTop+e);s!=i&&this.redraw()}},s.prototype._setScrollTop=function(t){return this.props.scrollTop=t,this._updateScrollTop(),this.props.scrollTop},s.prototype._updateScrollTop=function(){var t=Math.min(this.props.centerContainer.height-this.props.center.height,0);return t!=this.props.scrollTopMin&&("bottom"==this.options.orientation&&(this.props.scrollTop+=t-this.props.scrollTopMin),this.props.scrollTopMin=t),this.props.scrollTop>0&&(this.props.scrollTop=0),this.props.scrollTop=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)},s.prototype.getItemRange=function(){var t=this.itemsData,e=null,i=null;if(t){var s=t.min("start");e=s?a.convert(s.start,"Date").valueOf():null;var n=t.max("start");n&&(i=a.convert(n.start,"Date").valueOf());var o=t.max("end");o&&(i=null==i?a.convert(o.end,"Date").valueOf():Math.max(i,a.convert(o.end,"Date").valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},s.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)},s.prototype.getWindow=function(){var t=this.range.getRange();return{start:new Date(t.start),end:new Date(t.end)}},s.prototype.redraw=function(){var t=!1,e=this.options,i=this.props,s=this.dom;if(s){s.root.className="vis timeline root "+e.orientation,s.root.style.maxHeight=a.option.asSize(e.maxHeight,""),s.root.style.minHeight=a.option.asSize(e.minHeight,""),s.root.style.width=a.option.asSize(e.width,""),i.border.left=(s.centerContainer.offsetWidth-s.centerContainer.clientWidth)/2,i.border.right=i.border.left,i.border.top=(s.centerContainer.offsetHeight-s.centerContainer.clientHeight)/2,i.border.bottom=i.border.top;var n=s.root.offsetHeight-s.root.clientHeight,o=s.root.offsetWidth-s.root.clientWidth;i.center.height=s.center.offsetHeight,i.left.height=s.left.offsetHeight,i.right.height=s.right.offsetHeight,i.top.height=s.top.clientHeight||-i.border.top,i.bottom.height=s.bottom.clientHeight||-i.border.bottom;var r=Math.max(i.left.height,i.center.height,i.right.height),h=i.top.height+r+i.bottom.height+n+i.border.top+i.border.bottom;s.root.style.height=a.option.asSize(e.height,h+"px"),i.root.height=s.root.offsetHeight,i.background.height=i.root.height-n;var d=i.root.height-i.top.height-i.bottom.height-n;i.centerContainer.height=d,i.leftContainer.height=d,i.rightContainer.height=i.leftContainer.height,i.root.width=s.root.offsetWidth,i.background.width=i.root.width-o,i.left.width=s.leftContainer.clientWidth||-i.border.left,i.leftContainer.width=i.left.width,i.right.width=s.rightContainer.clientWidth||-i.border.right,i.rightContainer.width=i.right.width;var l=i.root.width-i.left.width-i.right.width-o;i.center.width=l,i.centerContainer.width=l,i.top.width=l,i.bottom.width=l,s.background.style.height=i.background.height+"px",s.backgroundVertical.style.height=i.background.height+"px",s.backgroundHorizontalContainer.style.height=i.centerContainer.height+"px",s.centerContainer.style.height=i.centerContainer.height+"px",s.leftContainer.style.height=i.leftContainer.height+"px",s.rightContainer.style.height=i.rightContainer.height+"px",s.background.style.width=i.background.width+"px",s.backgroundVertical.style.width=i.centerContainer.width+"px",s.backgroundHorizontalContainer.style.width=i.background.width+"px",s.backgroundHorizontal.style.width=i.background.width+"px",s.centerContainer.style.width=i.center.width+"px",s.top.style.width=i.top.width+"px",s.bottom.style.width=i.bottom.width+"px",s.background.style.left="0",s.background.style.top="0",s.backgroundVertical.style.left=i.left.width+"px",s.backgroundVertical.style.top="0",s.backgroundHorizontalContainer.style.left="0",s.backgroundHorizontalContainer.style.top=i.top.height+"px",s.centerContainer.style.left=i.left.width+"px",s.centerContainer.style.top=i.top.height+"px",s.leftContainer.style.left="0",s.leftContainer.style.top=i.top.height+"px",s.rightContainer.style.left=i.left.width+i.center.width+"px",s.rightContainer.style.top=i.top.height+"px",s.top.style.left=i.left.width+"px",s.top.style.top="0",s.bottom.style.left=i.left.width+"px",s.bottom.style.top=i.top.height+i.centerContainer.height+"px",this._updateScrollTop();var c=this.props.scrollTop;"bottom"==e.orientation&&(c+=Math.max(this.props.centerContainer.height-this.props.center.height-this.props.border.top-this.props.border.bottom,0)),s.center.style.left="0",s.center.style.top=c+"px",s.backgroundHorizontal.style.left="0",s.backgroundHorizontal.style.top=c+"px",s.left.style.left="0",s.left.style.top=c+"px",s.right.style.left="0",s.right.style.top=c+"px";var u=0==this.props.scrollTop?"hidden":"",p=this.props.scrollTop==this.props.scrollTopMin?"hidden":"";s.shadowTop.style.visibility=u,s.shadowBottom.style.visibility=p,s.shadowTopLeft.style.visibility=u,s.shadowBottomLeft.style.visibility=p,s.shadowTopRight.style.visibility=u,s.shadowBottomRight.style.visibility=p,this.components.forEach(function(e){t=e.redraw()||t}),t&&this.redraw()}},s.prototype._toTime=function(t){var e=this.range.conversion(this.props.center.width);return new Date(t/e.scale+e.offset)},s.prototype._toGlobalTime=function(t){var e=this.range.conversion(this.props.root.width);return new Date(t/e.scale+e.offset)},s.prototype._toScreen=function(t){var e=this.range.conversion(this.props.center.width);return(t.valueOf()-e.offset)*e.scale},s.prototype._toGlobalScreen=function(t){var e=this.range.conversion(this.props.root.width);return(t.valueOf()-e.offset)*e.scale},s.prototype._initAutoResize=function(){1==this.options.autoResize?this._startAutoResize():this._stopAutoResize()},s.prototype._startAutoResize=function(){var t=this;this._stopAutoResize(),this._onResize=function(){return 1!=t.options.autoResize?void t._stopAutoResize():void(t.dom.root&&(t.dom.root.clientWidth!=t.props.lastWidth||t.dom.root.clientHeight!=t.props.lastHeight)&&(t.props.lastWidth=t.dom.root.clientWidth,t.props.lastHeight=t.dom.root.clientHeight,t.emit("change")))},a.addEventListener(window,"resize",this._onResize),this.watchTimer=setInterval(this._onResize,1e3)},s.prototype._stopAutoResize=function(){this.watchTimer&&(clearInterval(this.watchTimer),this.watchTimer=void 0),a.removeEventListener(window,"resize",this._onResize),this._onResize=null},s.prototype._onTouch=function(){this.touch.allowDragging=!0},s.prototype._onPinch=function(){this.touch.allowDragging=!1},s.prototype._onDragStart=function(){this.touch.initialScrollTop=this.props.scrollTop},s.prototype._onDrag=function(t){if(this.touch.allowDragging){var e=t.gesture.deltaY,i=this._getScrollTop(),s=this._setScrollTop(this.touch.initialScrollTop+e);s!=i&&this.redraw()}},s.prototype._setScrollTop=function(t){return this.props.scrollTop=t,this._updateScrollTop(),this.props.scrollTop},s.prototype._updateScrollTop=function(){var t=Math.min(this.props.centerContainer.height-this.props.center.height,0);return t!=this.props.scrollTopMin&&("bottom"==this.options.orientation&&(this.props.scrollTop+=t-this.props.scrollTopMin),this.props.scrollTopMin=t),this.props.scrollTop>0&&(this.props.scrollTop=0),this.props.scrollTopo&&(h=o);for(var d=!1,l=h;Math.abs(l)<=Math.abs(o);l++){r=Math.pow(10,l);for(var c=0;c=n){d=!0,a=c;break}}if(1==d)break}this.stepIndex=a,this.scale=r,this.step=r*this.minorSteps[a]},e.prototype.first=function(){this.setFirst()},e.prototype.setFirst=function(){var t=this._start-this.scale*this.minorSteps[this.stepIndex],e=this._end+this.scale*this.minorSteps[this.stepIndex];this.marginEnd=this.roundToMinor(e),this.marginStart=this.roundToMinor(t),this.marginRange=this.marginEnd-this.marginStart,this.current=this.marginEnd},e.prototype.roundToMinor=function(t){var e=t-t%(this.scale*this.minorSteps[this.stepIndex]);return t%(this.scale*this.minorSteps[this.stepIndex])>.5*this.scale*this.minorSteps[this.stepIndex]?e+this.scale*this.minorSteps[this.stepIndex]:e},e.prototype.hasNext=function(){return this.current>=this.marginStart},e.prototype.next=function(){var t=this.current;this.current-=this.step,this.current==t&&(this.current=this._end)},e.prototype.previous=function(){this.current+=this.step,this.marginEnd+=this.step,this.marginRange=this.marginEnd-this.marginStart},e.prototype.getCurrent=function(){for(var t=""+Number(this.current).toPrecision(5),e=t.length-1;e>0;e--){if("0"!=t[e]){if("."==t[e]||","==t[e]){t=t.slice(0,e);break}break}t=t.slice(0,e)}return t},e.prototype.snap=function(){},e.prototype.isMajor=function(){return this.current%(this.scale*this.majorSteps[this.stepIndex])==0},t.exports=e},function(t,e,i){function s(t,e){var i=r().hours(0).minutes(0).seconds(0).milliseconds(0);this.start=i.clone().add("days",-3).valueOf(),this.end=i.clone().add("days",4).valueOf(),this.body=t,this.defaultOptions={start:null,end:null,direction:"horizontal",moveable:!0,zoomable:!0,min:null,max:null,zoomMin:10,zoomMax:31536e10},this.options=a.extend({},this.defaultOptions),this.props={touch:{}},this.body.emitter.on("dragstart",this._onDragStart.bind(this)),this.body.emitter.on("drag",this._onDrag.bind(this)),this.body.emitter.on("dragend",this._onDragEnd.bind(this)),this.body.emitter.on("hold",this._onHold.bind(this)),this.body.emitter.on("mousewheel",this._onMouseWheel.bind(this)),this.body.emitter.on("DOMMouseScroll",this._onMouseWheel.bind(this)),this.body.emitter.on("touch",this._onTouch.bind(this)),this.body.emitter.on("pinch",this._onPinch.bind(this)),this.setOptions(e)}function n(t){if("horizontal"!=t&&"vertical"!=t)throw new TypeError('Unknown direction "'+t+'". Choose "horizontal" or "vertical".')}function o(t,e){return{x:t.pageX-vis.util.getAbsoluteLeft(e),y:t.pageY-vis.util.getAbsoluteTop(e)} +}var a=i(1),r=i(39),h=i(12);s.prototype=new h,s.prototype.setOptions=function(t){if(t){var e=["direction","min","max","zoomMin","zoomMax","moveable","zoomable"];a.selectiveExtend(e,this.options,t),("start"in t||"end"in t)&&this.setRange(t.start,t.end)}},s.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.body.emitter.emit("rangechange",s),this.body.emitter.emit("rangechanged",s)}},s.prototype._applyRange=function(t,e){var i,s=null!=t?a.convert(t,"Date").valueOf():this.start,n=null!=e?a.convert(e,"Date").valueOf():this.end,o=null!=this.options.max?a.convert(this.options.max,"Date").valueOf():null,r=null!=this.options.min?a.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!==r&&r>s&&(i=r-s,s+=i,n+=i,null!=o&&n>o&&(n=o)),null!==o&&n>o&&(i=n-o,s-=i,n-=i,null!=r&&r>s&&(s=r)),null!==this.options.zoomMin){var h=parseFloat(this.options.zoomMin);0>h&&(h=0),h>n-s&&(this.end-this.start===h?(s=this.start,n=this.end):(i=h-(n-s),s-=i/2,n+=i/2))}if(null!==this.options.zoomMax){var d=parseFloat(this.options.zoomMax);0>d&&(d=0),n-s>d&&(this.end-this.start===d?(s=this.start,n=this.end):(i=n-s-d,s+=i/2,n-=i/2))}var l=this.start!=s||this.end!=n;return this.start=s,this.end=n,l},s.prototype.getRange=function(){return{start:this.start,end:this.end}},s.prototype.conversion=function(t){return s.conversion(this.start,this.end,t)},s.conversion=function(t,e,i){return 0!=i&&e-t!=0?{offset:t,scale:i/(e-t)}:{offset:0,scale:1}},s.prototype._onDragStart=function(){this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.start=this.start,this.props.touch.end=this.end,this.body.dom.root&&(this.body.dom.root.style.cursor="move"))},s.prototype._onDrag=function(t){if(this.options.moveable){var e=this.options.direction;if(n(e),this.props.touch.allowDragging){var i="horizontal"==e?t.gesture.deltaX:t.gesture.deltaY,s=this.props.touch.end-this.props.touch.start,o="horizontal"==e?this.body.domProps.center.width:this.body.domProps.center.height,a=-i/o*s;this._applyRange(this.props.touch.start+a,this.props.touch.end+a),this.body.emitter.emit("rangechange",{start:new Date(this.start),end:new Date(this.end)})}}},s.prototype._onDragEnd=function(){this.options.moveable&&this.props.touch.allowDragging&&(this.body.dom.root&&(this.body.dom.root.style.cursor="auto"),this.body.emitter.emit("rangechanged",{start:new Date(this.start),end:new Date(this.end)}))},s.prototype._onMouseWheel=function(t){if(this.options.zoomable&&this.options.moveable){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i;i=0>e?1-e/5:1/(1+e/5);var s=a.fakeGesture(this,t),n=o(s.center,this.body.dom.center),r=this._pointerToDate(n);this.zoom(i,r)}t.preventDefault()}},s.prototype._onTouch=function(){this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.allowDragging=!0,this.props.touch.center=null},s.prototype._onHold=function(){this.props.touch.allowDragging=!1},s.prototype._onPinch=function(t){if(this.options.zoomable&&this.options.moveable&&(this.props.touch.allowDragging=!1,t.gesture.touches.length>1)){this.props.touch.center||(this.props.touch.center=o(t.gesture.center,this.body.dom.center));var e=1/t.gesture.scale,i=this._pointerToDate(this.props.touch.center),s=parseInt(i+(this.props.touch.start-i)*e),n=parseInt(i+(this.props.touch.end-i)*e);this.setRange(s,n)}},s.prototype._pointerToDate=function(t){var e,i=this.options.direction;if(n(i),"horizontal"==i){var s=this.body.domProps.center.width;return e=this.conversion(s),t.x/e.scale+e.offset}var o=this.body.domProps.center.height;return e=this.conversion(o),t.y/e.scale+e.offset},s.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)},s.prototype.move=function(t){var e=this.end-this.start,i=this.start+e*t,s=this.end+e*t;this.start=i,this.end=s},s.prototype.moveTo=function(t){var e=(this.start+this.end)/2,i=e-t,s=this.start-i,n=this.end-i;this.setRange(s,n)},t.exports=s},function(t,e){e.orderByStart=function(t){t.sort(function(t,e){return t.data.start-e.data.start})},e.orderByEnd=function(t){t.sort(function(t,e){var i="end"in t.data?t.data.end:t.data.start,s="end"in e.data?e.data.end:e.data.start;return i-s})},e.stack=function(t,i,s){var n,o;if(s)for(n=0,o=t.length;o>n;n++)t[n].top=null;for(n=0,o=t.length;o>n;n++){var a=t[n];if(null===a.top){a.top=i.axis;do{for(var r=null,h=0,d=t.length;d>h;h++){var l=t[h];if(null!==l.top&&l!==a&&e.collision(a,l,i.item)){r=l;break}}null!=r&&(a.top=r.top+r.height+i.item)}while(r)}}},e.nostack=function(t,e){var i,s;for(i=0,s=t.length;s>i;i++)t[i].top=e.axis},e.collision=function(t,e,i){return t.left-ie.left&&t.top-ie.top}},function(t,e,i){function s(t,e,i){this.current=new Date,this._start=new Date,this._end=new Date,this.autoScale=!0,this.scale=s.SCALE.DAY,this.step=1,this.setRange(t,e,i)}var n=i(39);s.SCALE={MILLISECOND:1,SECOND:2,MINUTE:3,HOUR:4,DAY:5,WEEKDAY:6,MONTH:7,YEAR:8},s.prototype.setRange=function(t,e,i){if(!(t instanceof Date&&e instanceof Date))throw"No legal start or end date in method setRange";this._start=void 0!=t?new Date(t.valueOf()):new Date,this._end=void 0!=e?new Date(e.valueOf()):new Date,this.autoScale&&this.setMinimumStep(i)},s.prototype.first=function(){this.current=new Date(this._start.valueOf()),this.roundToMinor()},s.prototype.roundToMinor=function(){switch(this.scale){case s.SCALE.YEAR:this.current.setFullYear(this.step*Math.floor(this.current.getFullYear()/this.step)),this.current.setMonth(0);case s.SCALE.MONTH:this.current.setDate(1);case s.SCALE.DAY:case s.SCALE.WEEKDAY:this.current.setHours(0);case s.SCALE.HOUR:this.current.setMinutes(0);case s.SCALE.MINUTE:this.current.setSeconds(0);case s.SCALE.SECOND:this.current.setMilliseconds(0)}if(1!=this.step)switch(this.scale){case s.SCALE.MILLISECOND:this.current.setMilliseconds(this.current.getMilliseconds()-this.current.getMilliseconds()%this.step);break;case s.SCALE.SECOND:this.current.setSeconds(this.current.getSeconds()-this.current.getSeconds()%this.step);break;case s.SCALE.MINUTE:this.current.setMinutes(this.current.getMinutes()-this.current.getMinutes()%this.step);break;case s.SCALE.HOUR:this.current.setHours(this.current.getHours()-this.current.getHours()%this.step);break;case s.SCALE.WEEKDAY:case s.SCALE.DAY:this.current.setDate(this.current.getDate()-1-(this.current.getDate()-1)%this.step+1);break;case s.SCALE.MONTH:this.current.setMonth(this.current.getMonth()-this.current.getMonth()%this.step);break;case s.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()-this.current.getFullYear()%this.step)}},s.prototype.hasNext=function(){return this.current.valueOf()<=this._end.valueOf()},s.prototype.next=function(){var t=this.current.valueOf();if(this.current.getMonth()<6)switch(this.scale){case s.SCALE.MILLISECOND:this.current=new Date(this.current.valueOf()+this.step);break;case s.SCALE.SECOND:this.current=new Date(this.current.valueOf()+1e3*this.step);break;case s.SCALE.MINUTE:this.current=new Date(this.current.valueOf()+1e3*this.step*60);break;case s.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 s.SCALE.WEEKDAY:case s.SCALE.DAY:this.current.setDate(this.current.getDate()+this.step);break;case s.SCALE.MONTH:this.current.setMonth(this.current.getMonth()+this.step);break;case s.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()+this.step)}else switch(this.scale){case s.SCALE.MILLISECOND:this.current=new Date(this.current.valueOf()+this.step);break;case s.SCALE.SECOND:this.current.setSeconds(this.current.getSeconds()+this.step);break;case s.SCALE.MINUTE:this.current.setMinutes(this.current.getMinutes()+this.step);break;case s.SCALE.HOUR:this.current.setHours(this.current.getHours()+this.step);break;case s.SCALE.WEEKDAY:case s.SCALE.DAY:this.current.setDate(this.current.getDate()+this.step);break;case s.SCALE.MONTH:this.current.setMonth(this.current.getMonth()+this.step);break;case s.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()+this.step)}if(1!=this.step)switch(this.scale){case s.SCALE.MILLISECOND:this.current.getMilliseconds()0&&(this.step=e),this.autoScale=!1},s.prototype.setAutoScale=function(t){this.autoScale=t},s.prototype.setMinimumStep=function(t){if(void 0!=t){var e=31104e6,i=2592e6,n=864e5,o=36e5,a=6e4,r=1e3,h=1;1e3*e>t&&(this.scale=s.SCALE.YEAR,this.step=1e3),500*e>t&&(this.scale=s.SCALE.YEAR,this.step=500),100*e>t&&(this.scale=s.SCALE.YEAR,this.step=100),50*e>t&&(this.scale=s.SCALE.YEAR,this.step=50),10*e>t&&(this.scale=s.SCALE.YEAR,this.step=10),5*e>t&&(this.scale=s.SCALE.YEAR,this.step=5),e>t&&(this.scale=s.SCALE.YEAR,this.step=1),3*i>t&&(this.scale=s.SCALE.MONTH,this.step=3),i>t&&(this.scale=s.SCALE.MONTH,this.step=1),5*n>t&&(this.scale=s.SCALE.DAY,this.step=5),2*n>t&&(this.scale=s.SCALE.DAY,this.step=2),n>t&&(this.scale=s.SCALE.DAY,this.step=1),n/2>t&&(this.scale=s.SCALE.WEEKDAY,this.step=1),4*o>t&&(this.scale=s.SCALE.HOUR,this.step=4),o>t&&(this.scale=s.SCALE.HOUR,this.step=1),15*a>t&&(this.scale=s.SCALE.MINUTE,this.step=15),10*a>t&&(this.scale=s.SCALE.MINUTE,this.step=10),5*a>t&&(this.scale=s.SCALE.MINUTE,this.step=5),a>t&&(this.scale=s.SCALE.MINUTE,this.step=1),15*r>t&&(this.scale=s.SCALE.SECOND,this.step=15),10*r>t&&(this.scale=s.SCALE.SECOND,this.step=10),5*r>t&&(this.scale=s.SCALE.SECOND,this.step=5),r>t&&(this.scale=s.SCALE.SECOND,this.step=1),200*h>t&&(this.scale=s.SCALE.MILLISECOND,this.step=200),100*h>t&&(this.scale=s.SCALE.MILLISECOND,this.step=100),50*h>t&&(this.scale=s.SCALE.MILLISECOND,this.step=50),10*h>t&&(this.scale=s.SCALE.MILLISECOND,this.step=10),5*h>t&&(this.scale=s.SCALE.MILLISECOND,this.step=5),h>t&&(this.scale=s.SCALE.MILLISECOND,this.step=1)}},s.prototype.snap=function(t){var e=new Date(t.valueOf());if(this.scale==s.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==s.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==s.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==s.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==s.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==s.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==s.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==s.SCALE.MILLISECOND){var n=this.step>5?this.step/2:1;e.setMilliseconds(Math.round(e.getMilliseconds()/n)*n)}return e},s.prototype.isMajor=function(){switch(this.scale){case s.SCALE.MILLISECOND:return 0==this.current.getMilliseconds();case s.SCALE.SECOND:return 0==this.current.getSeconds();case s.SCALE.MINUTE:return 0==this.current.getHours()&&0==this.current.getMinutes();case s.SCALE.HOUR:return 0==this.current.getHours();case s.SCALE.WEEKDAY:case s.SCALE.DAY:return 1==this.current.getDate();case s.SCALE.MONTH:return 0==this.current.getMonth();case s.SCALE.YEAR:return!1;default:return!1}},s.prototype.getLabelMinor=function(t){switch(void 0==t&&(t=this.current),this.scale){case s.SCALE.MILLISECOND:return n(t).format("SSS");case s.SCALE.SECOND:return n(t).format("s");case s.SCALE.MINUTE:return n(t).format("HH:mm");case s.SCALE.HOUR:return n(t).format("HH:mm");case s.SCALE.WEEKDAY:return n(t).format("ddd D");case s.SCALE.DAY:return n(t).format("D");case s.SCALE.MONTH:return n(t).format("MMM");case s.SCALE.YEAR:return n(t).format("YYYY");default:return""}},s.prototype.getLabelMajor=function(t){switch(void 0==t&&(t=this.current),this.scale){case s.SCALE.MILLISECOND:return n(t).format("HH:mm:ss");case s.SCALE.SECOND:return n(t).format("D MMMM HH:mm");case s.SCALE.MINUTE:case s.SCALE.HOUR:return n(t).format("ddd D MMMM");case s.SCALE.WEEKDAY:case s.SCALE.DAY:return n(t).format("MMMM YYYY");case s.SCALE.MONTH:return n(t).format("YYYY");case s.SCALE.YEAR:return"";default:return""}},t.exports=s},function(t){function e(){this.options=null,this.props=null}e.prototype.setOptions=function(t){t&&util.extend(this.options,t)},e.prototype.redraw=function(){return!1},e.prototype.destroy=function(){},e.prototype._isResized=function(){var t=this.props._previousWidth!==this.props.width||this.props._previousHeight!==this.props.height;return this.props._previousWidth=this.props.width,this.props._previousHeight=this.props.height,t},t.exports=e},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCurrentTime:!0},this.options=n.extend({},this.defaultOptions),this._create(),this.setOptions(e)}var n=i(1),o=i(12);s.prototype=new o,s.prototype._create=function(){var t=document.createElement("div");t.className="currenttime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t},s.prototype.destroy=function(){this.options.showCurrentTime=!1,this.redraw(),this.body=null},s.prototype.setOptions=function(t){t&&n.selectiveExtend(["showCurrentTime"],this.options,t)},s.prototype.redraw=function(){if(this.options.showCurrentTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar),this.start());var e=new Date,i=this.body.util.toScreen(e);this.bar.style.left=i+"px",this.bar.title="Current time: "+e}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.stop();return!1},s.prototype.start=function(){function t(){e.stop();var i=e.body.range.conversion(e.body.domProps.center.width).scale,s=1/i/10;30>s&&(s=30),s>1e3&&(s=1e3),e.redraw(),e.currentTimeTimer=setTimeout(t,s)}var e=this;t()},s.prototype.stop=function(){void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCustomTime:!1},this.options=o.extend({},this.defaultOptions),this.customTime=new Date,this.eventParams={},this._create(),this.setOptions(e)}var n=i(49),o=i(1),a=i(12);s.prototype=new a,s.prototype.setOptions=function(t){t&&o.selectiveExtend(["showCustomTime"],this.options,t)},s.prototype._create=function(){var t=document.createElement("div");t.className="customtime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t;var e=document.createElement("div");e.style.position="relative",e.style.top="0px",e.style.left="-10px",e.style.height="100%",e.style.width="20px",t.appendChild(e),this.hammer=n(t,{prevent_default:!0}),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this))},s.prototype.destroy=function(){this.options.showCustomTime=!1,this.redraw(),this.hammer.enable(!1),this.hammer=null,this.body=null},s.prototype.redraw=function(){if(this.options.showCustomTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar));var e=this.body.util.toScreen(this.customTime);this.bar.style.left=e+"px",this.bar.title="Time: "+this.customTime}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar);return!1},s.prototype.setCustomTime=function(t){this.customTime=new Date(t.valueOf()),this.redraw()},s.prototype.getCustomTime=function(){return new Date(this.customTime.valueOf())},s.prototype._onDragStart=function(t){this.eventParams.dragging=!0,this.eventParams.customTime=this.customTime,t.stopPropagation(),t.preventDefault()},s.prototype._onDrag=function(t){if(this.eventParams.dragging){var e=t.gesture.deltaX,i=this.body.util.toScreen(this.eventParams.customTime)+e,s=this.body.util.toTime(i);this.setCustomTime(s),this.body.emitter.emit("timechange",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault()}},s.prototype._onDragEnd=function(t){this.eventParams.dragging&&(this.body.emitter.emit("timechanged",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault())},t.exports=s},function(t,e,i){function s(t,e,i){this.id=n.randomUUID(),this.body=t,this.defaultOptions={orientation:"left",showMinorLabels:!0,showMajorLabels:!0,icons:!0,majorLinesOffset:7,minorLinesOffset:4,labelOffsetX:10,labelOffsetY:2,iconWidth:20,width:"40px",visible:!0},this.linegraphSVG=i,this.props={},this.DOMelements={lines:{},labels:{}},this.dom={},this.range={start:0,end:0},this.options=n.extend({},this.defaultOptions),this.conversionFactor=1,this.setOptions(e),this.width=Number((""+this.options.width).replace("px","")),this.minWidth=this.width,this.height=this.linegraphSVG.offsetHeight,this.stepPixels=25,this.stepPixelsForced=25,this.lineOffset=0,this.master=!0,this.svgElements={},this.groups={},this.amountOfGroups=0,this._create()}var n=i(1),o=i(2),a=i(12),r=i(8);s.prototype=new a,s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype.setOptions=function(t){if(t){var e=!1;this.options.orientation!=t.orientation&&void 0!==t.orientation&&(e=!0);var i=["orientation","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","width","visible"];n.selectiveExtend(i,this.options,t),this.minWidth=Number((""+this.options.width).replace("px","")),1==e&&this.dom.frame&&(this.hide(),this.show())}},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.style.width=this.options.width,this.dom.frame.style.height=this.height,this.dom.lineContainer=document.createElement("div"),this.dom.lineContainer.style.width="100%",this.dom.lineContainer.style.height=this.height,this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.height="100%",this.svg.style.width="100%",this.svg.style.display="block",this.dom.frame.appendChild(this.svg)},s.prototype._redrawGroupIcons=function(){o.prepareElements(this.svgElements);var t,e=this.options.iconWidth,i=15,s=4,n=s+.5*i;t="left"==this.options.orientation?s:this.width-e-s;for(var a in this.groups)this.groups.hasOwnProperty(a)&&(this.groups[a].drawIcon(t,n,this.svgElements,this.svg,e,i),n+=i+s);o.cleanupElements(this.svgElements)},s.prototype.show=function(){this.dom.frame.parentNode||("left"==this.options.orientation?this.body.dom.left.appendChild(this.dom.frame):this.body.dom.right.appendChild(this.dom.frame)),this.dom.lineContainer.parentNode||this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer)},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.lineContainer.parentNode&&this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer)},s.prototype.setRange=function(t,e){this.range.start=t,this.range.end=e},s.prototype.redraw=function(){var t=!1;if(0==this.amountOfGroups)this.hide();else{this.show(),this.height=Number(this.linegraphSVG.style.height.replace("px","")),this.dom.lineContainer.style.height=this.height+"px",this.width=1==this.options.visible?Number((""+this.options.width).replace("px","")):0;var e=this.props,i=this.dom.frame;i.className="dataaxis",this._calculateCharSize();var s=this.options.orientation,n=this.options.showMinorLabels,o=this.options.showMajorLabels;e.minorLabelHeight=n?e.minorCharHeight:0,e.majorLabelHeight=o?e.majorCharHeight:0,e.minorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.minorLinesOffset,e.minorLineHeight=1,e.majorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.majorLinesOffset,e.majorLineHeight=1,"left"==s?(i.style.top="0",i.style.left="0",i.style.bottom="",i.style.width=this.width+"px",i.style.height=this.height+"px"):(i.style.top="",i.style.bottom="0",i.style.left="0",i.style.width=this.width+"px",i.style.height=this.height+"px"),t=this._redrawLabels(),1==this.options.icons&&this._redrawGroupIcons()}return t},s.prototype._redrawLabels=function(){o.prepareElements(this.DOMelements);var t=this.options.orientation,e=this.master?this.props.majorCharHeight||10:this.stepPixelsForced,i=new r(this.range.start,this.range.end,e,this.dom.frame.offsetHeight);this.step=i,i.first();var s=this.dom.frame.offsetHeight/(i.marginRange/i.step+1);this.stepPixels=s;var n=this.height/s,a=0;if(0==this.master){s=this.stepPixelsForced,a=Math.round(this.height/s-n);for(var h=0;.5*a>h;h++)i.previous();n=this.height/s}this.valueAtZero=i.marginEnd;var d=0,l=1;i.next(),this.maxLabelSize=0;for(var c=0;l=0&&this._redrawLabel(c-2,i.getCurrent(),t,"yAxis major",this.props.majorCharHeight),this._redrawLine(c,t,"grid horizontal major",this.options.majorLinesOffset,this.props.majorLineWidth)):this._redrawLine(c,t,"grid horizontal minor",this.options.minorLinesOffset,this.props.minorLineWidth),i.next(),l++}this.conversionFactor=d/((n-1)*i.step);var p=1==this.options.icons?this.options.iconWidth+this.options.labelOffsetX+15:this.options.labelOffsetX+15;return this.maxLabelSize>this.width-p&&1==this.options.visible?(this.width=this.maxLabelSize+p,this.options.width=this.width+"px",o.cleanupElements(this.DOMelements),this.redraw(),!0):this.maxLabelSizethis.minWidth?(this.width=Math.max(this.minWidth,this.maxLabelSize+p),this.options.width=this.width+"px",o.cleanupElements(this.DOMelements),this.redraw(),!0):(o.cleanupElements(this.DOMelements),!1)},s.prototype._redrawLabel=function(t,e,i,s,n){var a=o.getDOMElement("div",this.DOMelements,this.dom.frame);a.className=s,a.innerHTML=e,"left"==i?(a.style.left="-"+this.options.labelOffsetX+"px",a.style.textAlign="right"):(a.style.right="-"+this.options.labelOffsetX+"px",a.style.textAlign="left"),a.style.top=t-.5*n+this.options.labelOffsetY+"px",e+="";var r=Math.max(this.props.majorCharWidth,this.props.minorCharWidth);this.maxLabelSizee.axis){var c=d-e.axis;l-=c,n.forEach(h,function(t){t.top-=c})}r=l+e.item/2}else r=e.axis+e.item;r=Math.max(r,this.props.label.height);var u=this.dom.foreground;this.top=u.offsetTop,this.left=u.offsetLeft,this.width=u.offsetWidth,s=n.updateProperty(this,"height",r)||s,s=n.updateProperty(this.props.label,"width",this.dom.inner.clientWidth)||s,s=n.updateProperty(this.props.label,"height",this.dom.inner.clientHeight)||s,this.dom.background.style.height=r+"px",this.dom.foreground.style.height=r+"px",this.dom.label.style.height=r+"px";for(var p=0,m=this.visibleItems.length;m>p;p++){var _=this.visibleItems[p];_.repositionY()}return s},s.prototype.show=function(){this.dom.label.parentNode||this.itemSet.dom.labelSet.appendChild(this.dom.label),this.dom.foreground.parentNode||this.itemSet.dom.foreground.appendChild(this.dom.foreground),this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background),this.dom.axis.parentNode||this.itemSet.dom.axis.appendChild(this.dom.axis)},s.prototype.hide=function(){var t=this.dom.label;t.parentNode&&t.parentNode.removeChild(t);var e=this.dom.foreground;e.parentNode&&e.parentNode.removeChild(e);var i=this.dom.background;i.parentNode&&i.parentNode.removeChild(i);var s=this.dom.axis;s.parentNode&&s.parentNode.removeChild(s)},s.prototype.add=function(t){if(this.items[t.id]=t,t.setParent(this),t instanceof a&&-1==this.visibleItems.indexOf(t)){var e=this.itemSet.body.range;this._checkIfVisible(t,this.visibleItems,e)}},s.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)},s.prototype.removeFromDataSet=function(t){this.itemSet.removeItem(t.id)},s.prototype.order=function(){var t=n.toArray(this.items);this.orderedItems.byStart=t,this.orderedItems.byEnd=this._constructByEndArray(t),o.orderByStart(this.orderedItems.byStart),o.orderByEnd(this.orderedItems.byEnd) +},s.prototype._constructByEndArray=function(t){for(var e=[],i=0;i0)for(o=0;o=0&&!this._checkIfInvisible(t.byStart[o],a,i);o--);for(o=s+1;o=0&&!this._checkIfInvisible(t.byEnd[o],a,i);o--);for(o=r+1;oe;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())}},s.prototype.getSelection=function(){return this.selection.concat([])},s.prototype._deselect=function(t){for(var e=this.selection,i=0,s=e.length;s>i;i++)if(e[i]==t){e.splice(i,1);break}},s.prototype.redraw=function(){var t=this.options.margin,e=this.body.range,i=o.option.asSize,s=this.options,n=s.orientation,a=!1,r=this.dom.frame,h=s.editable.updateTime||s.editable.updateGroup;r.className="itemset"+(h?" editable":""),a=this._orderGroups()||a;var d=e.end-e.start,l=d!=this.lastVisibleInterval||this.props.width!=this.props.lastWidth;l&&(this.stackDirty=!0),this.lastVisibleInterval=d,this.props.lastWidth=this.props.width;var c=this.stackDirty,u=this._firstGroup(),p={item:t.item,axis:t.axis},m={item:t.item,axis:t.item/2},_=0,f=t.axis+t.item;return o.forEach(this.groups,function(t){var i=t==u?p:m,s=t.redraw(e,i,c);a=s||a,_+=t.height}),_=Math.max(_,f),this.stackDirty=!1,r.style.height=i(_),this.props.top=r.offsetTop,this.props.left=r.offsetLeft,this.props.width=r.offsetWidth,this.props.height=_,this.dom.axis.style.top=i("top"==n?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",a=this._isResized()||a},s.prototype._firstGroup=function(){var t="top"==this.options.orientation?0:this.groupIds.length-1,e=this.groupIds[t],i=this.groups[e]||this.groups[p];return i||null},s.prototype._updateUngrouped=function(){var t=this.groups[p];if(this.groupsData)t&&(t.hide(),delete this.groups[p]);else if(!t){var e=null,i=null;t=new d(e,i,this),this.groups[p]=t;for(var s in this.items)this.items.hasOwnProperty(s)&&t.add(this.items[s]);t.show()}},s.prototype.getLabelSet=function(){return this.dom.labelSet},s.prototype.setItems=function(t){var e,i=this,s=this.itemsData;if(t){if(!(t instanceof a||t instanceof r))throw new TypeError("Data must be an instance of DataSet or DataView");this.itemsData=t}else this.itemsData=null;if(s&&(o.forEach(this.itemListeners,function(t,e){s.off(e,t)}),e=s.getIds(),this._onRemove(e)),this.itemsData){var n=this.id;o.forEach(this.itemListeners,function(t,e){i.itemsData.on(e,t,n)}),e=this.itemsData.getIds(),this._onAdd(e),this._updateUngrouped()}},s.prototype.getItems=function(){return this.itemsData},s.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(o.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 a||t instanceof r))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;o.forEach(this.groupListeners,function(t,e){i.groupsData.on(e,t,s)}),e=this.groupsData.getIds(),this._onAddGroups(e)}this._updateUngrouped(),this._order(),this.body.emitter.emit("change")},s.prototype.getGroups=function(){return this.groupsData},s.prototype.removeItem=function(t){var e=this.itemsData.get(t),i=this.itemsData.getDataSet();e&&this.options.onRemove(e,function(e){e&&i.remove(t)})},s.prototype._onUpdate=function(t){var e=this;t.forEach(function(t){var i=e.itemsData.get(t,e.itemOptions),n=e.items[t],o=i.type||e.options.type||(i.end?"range":"box"),a=s.types[o];if(n&&(a&&n instanceof a?e._updateItem(n,i):(e._removeItem(n),n=null)),!n){if(!a)throw new TypeError("rangeoverflow"==o?'Item type "rangeoverflow" is deprecated. Use css styling instead: .vis.timeline .item.range .content {overflow: visible;}':'Unknown item type "'+o+'"');n=new a(i,e.conversion,e.options),n.id=t,e._addItem(n)}}),this._order(),this.stackDirty=!0,this.body.emitter.emit("change")},s.prototype._onAdd=s.prototype._onUpdate,s.prototype._onRemove=function(t){var e=0,i=this;t.forEach(function(t){var s=i.items[t];s&&(e++,i._removeItem(s))}),e&&(this._order(),this.stackDirty=!0,this.body.emitter.emit("change"))},s.prototype._order=function(){o.forEach(this.groups,function(t){t.order()})},s.prototype._onUpdateGroups=function(t){this._onAddGroups(t)},s.prototype._onAddGroups=function(t){var e=this;t.forEach(function(t){var i=e.groupsData.get(t),s=e.groups[t];if(s)s.setData(i);else{if(t==p)throw new Error("Illegal group id. "+t+" is a reserved id.");var n=Object.create(e.options);o.extend(n,{height:null}),s=new d(t,i,e),e.groups[t]=s;for(var a in e.items)if(e.items.hasOwnProperty(a)){var r=e.items[a];r.data.group==t&&s.add(r)}s.order(),s.show()}}),this.body.emitter.emit("change")},s.prototype._onRemoveGroups=function(t){var e=this.groups;t.forEach(function(t){var i=e[t];i&&(i.hide(),delete e[t])}),this.markDirty(),this.body.emitter.emit("change")},s.prototype._orderGroups=function(){if(this.groupsData){var t=this.groupsData.getIds({order:this.options.groupOrder}),e=!o.equalArray(t,this.groupIds);if(e){var i=this.groups;t.forEach(function(t){i[t].hide()}),t.forEach(function(t){i[t].show()}),this.groupIds=t}return e}return!1},s.prototype._addItem=function(t){this.items[t.id]=t;var e=this.groupsData?t.data.group:p,i=this.groups[e];i&&i.add(t)},s.prototype._updateItem=function(t,e){var i=t.data.group;if(t.data=e,t.displayed&&t.redraw(),i!=t.data.group){var s=this.groups[i];s&&s.remove(t);var n=this.groupsData?t.data.group:p,o=this.groups[n];o&&o.add(t)}},s.prototype._removeItem=function(t){t.hide(),delete this.items[t.id];var e=this.selection.indexOf(t.id);-1!=e&&this.selection.splice(e,1);var i=this.groupsData?t.data.group:p,s=this.groups[i];s&&s.remove(t)},s.prototype._constructByEndArray=function(t){for(var e=[],i=0;i0||n.length>0)&&this.body.emitter.emit("select",{items:this.getSelection()}),t.stopPropagation()}},s.prototype._onAddItem=function(t){if(this.options.selectable&&this.options.editable.add){var e=this,i=this.body.util.snap||null,n=s.itemFromTarget(t);if(n){var a=e.itemsData.get(n.id);this.options.onUpdate(a,function(t){t&&e.itemsData.update(t)})}else{var r=vis.util.getAbsoluteLeft(this.dom.frame),h=t.gesture.center.pageX-r,d=this.body.util.toTime(h),l={start:i?i(d):d,content:"new item"};if("range"===this.options.type){var c=this.body.util.toTime(h+this.props.width/5);l.end=i?i(c):c}l[this.itemsData.fieldId]=o.randomUUID();var u=s.groupFromTarget(t);u&&(l.group=u.groupId),this.options.onAdd(l,function(t){t&&e.itemsData.add(l)})}}},s.prototype._onMultiSelectItem=function(t){if(this.options.selectable){var e,i=s.itemFromTarget(t);if(i){e=this.getSelection();var n=e.indexOf(i.id);-1==n?e.push(i.id):e.splice(n,1),this.setSelection(e),this.body.emitter.emit("select",{items:this.getSelection()}),t.stopPropagation()}}},s.itemFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-item"))return e["timeline-item"];e=e.parentNode}return null},s.groupFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-group"))return e["timeline-group"];e=e.parentNode}return null},s.itemSetFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-itemset"))return e["timeline-itemset"];e=e.parentNode}return null},t.exports=s},function(t,e,i){function s(t,e,i){this.body=t,this.defaultOptions={enabled:!0,icons:!0,iconSize:20,iconSpacing:6,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-left"}},this.side=i,this.options=n.extend({},this.defaultOptions),this.svgElements={},this.dom={},this.groups={},this.amountOfGroups=0,this._create(),this.setOptions(e)}var n=i(1),o=i(2),a=i(12);s.prototype=new a,s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.className="legend",this.dom.frame.style.position="absolute",this.dom.frame.style.top="10px",this.dom.frame.style.display="block",this.dom.textArea=document.createElement("div"),this.dom.textArea.className="legendText",this.dom.textArea.style.position="relative",this.dom.textArea.style.top="0px",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.width=this.options.iconSize+5+"px",this.dom.frame.appendChild(this.svg),this.dom.frame.appendChild(this.dom.textArea)},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame)},s.prototype.setOptions=function(t){var e=["enabled","orientation","icons","left","right"];n.selectiveDeepExtend(e,this.options,t)},s.prototype.redraw=function(){if(0==this.options[this.side].visible||0==this.amountOfGroups||0==this.options.enabled)this.hide();else{this.show(),"top-left"==this.options[this.side].position||"bottom-left"==this.options[this.side].position?(this.dom.frame.style.left="4px",this.dom.frame.style.textAlign="left",this.dom.textArea.style.textAlign="left",this.dom.textArea.style.left=this.options.iconSize+15+"px",this.dom.textArea.style.right="",this.svg.style.left="0px",this.svg.style.right=""):(this.dom.frame.style.right="4px",this.dom.frame.style.textAlign="right",this.dom.textArea.style.textAlign="right",this.dom.textArea.style.right=this.options.iconSize+15+"px",this.dom.textArea.style.left="",this.svg.style.right="0px",this.svg.style.left=""),"top-left"==this.options[this.side].position||"top-right"==this.options[this.side].position?(this.dom.frame.style.top=4-Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.bottom=""):(this.dom.frame.style.bottom=4-Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.top=""),0==this.options.icons?(this.dom.frame.style.width=this.dom.textArea.offsetWidth+10+"px",this.dom.textArea.style.right="",this.dom.textArea.style.left="",this.svg.style.width="0px"):(this.dom.frame.style.width=this.options.iconSize+15+this.dom.textArea.offsetWidth+10+"px",this.drawLegendIcons());var t="";for(var e in this.groups)this.groups.hasOwnProperty(e)&&(t+=this.groups[e].content+"
");this.dom.textArea.innerHTML=t,this.dom.textArea.style.lineHeight=.75*this.options.iconSize+this.options.iconSpacing+"px"}},s.prototype.drawLegendIcons=function(){if(this.dom.frame.parentNode){o.prepareElements(this.svgElements);var t=window.getComputedStyle(this.dom.frame).paddingTop,e=Number(t.replace("px","")),i=e,s=this.options.iconSize,n=.75*this.options.iconSize,a=e+.5*n+3;this.svg.style.width=s+5+e+"px";for(var r in this.groups)this.groups.hasOwnProperty(r)&&(this.groups[r].drawIcon(i,a,this.svgElements,this.svg,s,n),a+=n+this.options.iconSpacing);o.cleanupElements(this.svgElements)}},t.exports=s},function(t,e,i){function s(t,e){this.id=n.randomUUID(),this.body=t,this.defaultOptions={yAxisOrientation:"left",defaultGroup:"default",sort:!0,sampling:!0,graphHeight:"400px",shaded:{enabled:!1,orientation:"bottom"},style:"line",barChart:{width:50,align:"center"},catmullRom:{enabled:!0,parametrization:"centripetal",alpha:.5},drawPoints:{enabled:!0,size:6,style:"square"},dataAxis:{showMinorLabels:!0,showMajorLabels:!0,icons:!1,width:"40px",visible:!0},legend:{enabled:!1,icons:!0,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-right"}}},this.options=n.extend({},this.defaultOptions),this.dom={},this.props={},this.hammer=null,this.groups={};var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.selection=[],this.lastStart=this.body.range.start,this.touchParams={},this.svgElements={},this.setOptions(e),this.groupsUsingDefaultStyles=[0],this.body.emitter.on("rangechange",function(){if(0!=i.lastStart){var t=i.body.range.start-i.lastStart,e=i.body.range.end-i.body.range.start;if(0!=i.width){var s=i.width/e,n=t*s;i.svg.style.left=-i.width-n+"px"}}}),this.body.emitter.on("rangechanged",function(){i.lastStart=i.body.range.start,i.svg.style.left=n.option.asSize(-i.width),i._updateGraph.apply(i)}),this._create(),this.body.emitter.emit("change")}var n=i(1),o=i(2),a=i(3),r=i(4),h=i(12),d=i(15),l=i(16),c=i(19),u="__ungrouped__";s.prototype=new h,s.prototype._create=function(){var t=document.createElement("div");t.className="LineGraph",this.dom.frame=t,this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="relative",this.svg.style.height=(""+this.options.graphHeight).replace("px","")+"px",this.svg.style.display="block",t.appendChild(this.svg),this.options.dataAxis.orientation="left",this.yAxisLeft=new d(this.body,this.options.dataAxis,this.svg),this.options.dataAxis.orientation="right",this.yAxisRight=new d(this.body,this.options.dataAxis,this.svg),delete this.options.dataAxis.orientation,this.legendLeft=new c(this.body,this.options.legend,"left"),this.legendRight=new c(this.body,this.options.legend,"right"),this.show()},s.prototype.setOptions=function(t){if(t){var e=["sampling","defaultGroup","graphHeight","yAxisOrientation","style","barChart","dataAxis","sort"];n.selectiveDeepExtend(e,this.options,t),n.mergeOptions(this.options,t,"catmullRom"),n.mergeOptions(this.options,t,"drawPoints"),n.mergeOptions(this.options,t,"shaded"),n.mergeOptions(this.options,t,"legend"),t.catmullRom&&"object"==typeof t.catmullRom&&t.catmullRom.parametrization&&("uniform"==t.catmullRom.parametrization?this.options.catmullRom.alpha=0:"chordal"==t.catmullRom.parametrization?this.options.catmullRom.alpha=1:(this.options.catmullRom.parametrization="centripetal",this.options.catmullRom.alpha=.5)),this.yAxisLeft&&void 0!==t.dataAxis&&(this.yAxisLeft.setOptions(this.options.dataAxis),this.yAxisRight.setOptions(this.options.dataAxis)),this.legendLeft&&void 0!==t.legend&&(this.legendLeft.setOptions(this.options.legend),this.legendRight.setOptions(this.options.legend)),this.groups.hasOwnProperty(u)&&this.groups[u].setOptions(t)}this.dom.frame&&this._updateGraph()},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame)},s.prototype.setItems=function(t){var e,i=this,s=this.itemsData;if(t){if(!(t instanceof a||t instanceof r))throw new TypeError("Data must be an instance of DataSet or DataView");this.itemsData=t}else this.itemsData=null;if(s&&(n.forEach(this.itemListeners,function(t,e){s.off(e,t)}),e=s.getIds(),this._onRemove(e)),this.itemsData){var o=this.id;n.forEach(this.itemListeners,function(t,e){i.itemsData.on(e,t,o)}),e=this.itemsData.getIds(),this._onAdd(e)}this._updateUngrouped(),this._updateGraph(),this.redraw()},s.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(n.forEach(this.groupListeners,function(t,e){i.groupsData.unsubscribe(e,t)}),e=this.groupsData.getIds(),this.groupsData=null,this._onRemoveGroups(e)),t){if(!(t instanceof a||t instanceof r))throw new TypeError("Data must be an instance of DataSet or DataView");this.groupsData=t}else this.groupsData=null;if(this.groupsData){var s=this.id;n.forEach(this.groupListeners,function(t,e){i.groupsData.on(e,t,s)}),e=this.groupsData.getIds(),this._onAddGroups(e)}this._onUpdate()},s.prototype._onUpdate=function(){this._updateUngrouped(),this._updateAllGroupData(),this._updateGraph(),this.redraw()},s.prototype._onAdd=function(t){this._onUpdate(t)},s.prototype._onRemove=function(t){this._onUpdate(t)},s.prototype._onUpdateGroups=function(t){for(var e=0;e0){for(s=0;sp){e.push(f);break}e.push(f)}}else for(var _=0;_u&&f.x0){for(var u=0;ui?i:r,d=s>d?s:d):(a=!0,h=h>i?i:h,l=s>l?s:l)}1==o&&this.yAxisLeft.setRange(r,d),1==a&&this.yAxisRight.setRange(h,l)}return n=this._toggleAxisVisiblity(o,this.yAxisLeft)||n,n=this._toggleAxisVisiblity(a,this.yAxisRight)||n,1==a&&1==o?(this.yAxisLeft.drawIcons=!0,this.yAxisRight.drawIcons=!0):(this.yAxisLeft.drawIcons=!1,this.yAxisRight.drawIcons=!1),this.yAxisRight.master=!o,0==this.yAxisRight.master?(1==a&&(this.yAxisLeft.lineOffset=this.yAxisRight.width),n=this.yAxisLeft.redraw()||n,this.yAxisRight.stepPixelsForced=this.yAxisLeft.stepPixels,n=this.yAxisRight.redraw()||n):n=this.yAxisRight.redraw()||n,n},s.prototype._toggleAxisVisiblity=function(t,e){var i=!1;return 0==t?e.dom.frame.parentNode&&(e.hide(),i=!0):e.dom.frame.parentNode||(e.show(),i=!0),i},s.prototype._drawBarGraph=function(t,e){if(null!=t&&t.length>0){var i,s=.1*e.options.barChart.width,n=0,a=e.options.barChart.width;"left"==e.options.barChart.align?n-=.5*a:"right"==e.options.barChart.align&&(n+=.5*a);for(var r=0;r0&&(i=Math.min(i,Math.abs(t[r-1].x-t[r].x))),a>i&&(a=s>i?s:i),o.drawBar(t[r].x+n,t[r].y,a,e.zeroPosition-t[r].y,e.className+" bar",this.svgElements,this.svg);1==e.options.drawPoints.enabled&&this._drawPoints(t,e,this.svgElements,this.svg,n)}},s.prototype._drawLineGraph=function(t,e){if(null!=t&&t.length>0){var i,s,n=Number(this.svg.style.height.replace("px",""));if(i=o.getSVGElement("path",this.svgElements,this.svg),i.setAttributeNS(null,"class",e.className),s=1==e.options.catmullRom.enabled?this._catmullRom(t,e):this._linear(t),1==e.options.shaded.enabled){var a,r=o.getSVGElement("path",this.svgElements,this.svg);a="top"==e.options.shaded.orientation?"M"+t[0].x+",0 "+s+"L"+t[t.length-1].x+",0":"M"+t[0].x+","+n+" "+s+"L"+t[t.length-1].x+","+n,r.setAttributeNS(null,"class",e.className+" fill"),r.setAttributeNS(null,"d",a)}i.setAttributeNS(null,"d","M"+s),1==e.options.drawPoints.enabled&&this._drawPoints(t,e,this.svgElements,this.svg)}},s.prototype._drawPoints=function(t,e,i,s,n){void 0===n&&(n=0);for(var a=0;au;u+=a)i=o(t[u].x)+this.width-1,s=t[u].y,n.push({x:i,y:s}),h=h>s?s:h,d=s>d?s:d;return{min:h,max:d,data:n}},s.prototype._convertYvalues=function(t,e){var i,s,n=[],o=this.yAxisLeft,a=Number(this.svg.style.height.replace("px",""));"right"==e.options.yAxisOrientation&&(o=this.yAxisRight);for(var r=0;rl;l++)e=0==l?t[0]:t[l-1],i=t[l],s=t[l+1],n=d>l+2?t[l+2]:s,o={x:(-e.x+6*i.x+s.x)*h,y:(-e.y+6*i.y+s.y)*h},a={x:(i.x+6*s.x-n.x)*h,y:(i.y+6*s.y-n.y)*h},r+="C"+o.x+","+o.y+" "+a.x+","+a.y+" "+s.x+","+s.y+" ";return r},s.prototype._catmullRom=function(t,e){var i=e.options.catmullRom.alpha;if(0==i||void 0===i)return this._catmullRomUniform(t);for(var s,n,o,a,r,h,d,l,c,u,p,m,_,f,g,y,v,M,b,w=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",L=t.length,x=0;L-1>x;x++)s=0==x?t[0]:t[x-1],n=t[x],o=t[x+1],a=L>x+2?t[x+2]:o,d=Math.sqrt(Math.pow(s.x-n.x,2)+Math.pow(s.y-n.y,2)),l=Math.sqrt(Math.pow(n.x-o.x,2)+Math.pow(n.y-o.y,2)),c=Math.sqrt(Math.pow(o.x-a.x,2)+Math.pow(o.y-a.y,2)),f=Math.pow(c,i),y=Math.pow(c,2*i),g=Math.pow(l,i),v=Math.pow(l,2*i),b=Math.pow(d,i),M=Math.pow(d,2*i),u=2*M+3*b*g+v,p=2*y+3*f*g+v,m=3*b*(b+g),m>0&&(m=1/m),_=3*f*(f+g),_>0&&(_=1/_),r={x:(-v*s.x+u*n.x+M*o.x)*m,y:(-v*s.y+u*n.y+M*o.y)*m},h={x:(y*n.x+p*o.x-v*a.x)*_,y:(y*n.y+p*o.y-v*a.y)*_},0==r.x&&0==r.y&&(r=n),0==h.x&&0==h.y&&(h=o),w+="C"+r.x+","+r.y+" "+h.x+","+h.y+" "+o.x+","+o.y+" "; +return w},s.prototype._linear=function(t){for(var e="",i=0;id;){d++;var l=o.getCurrent(),c=this.body.util.toScreen(l),u=o.isMajor();this.options.showMinorLabels&&this._repaintMinorText(c,o.getLabelMinor(),t),u&&this.options.showMajorLabels?(c>0&&(void 0==h&&(h=c),this._repaintMajorText(c,o.getLabelMajor(),t)),this._repaintMajorLine(c,t)):this._repaintMinorLine(c,t),o.next()}if(this.options.showMajorLabels){var p=this.body.util.toTime(0),m=o.getLabelMajor(p),_=m.length*(this.props.majorCharWidth||10)+10;(void 0==h||h>_)&&this._repaintMajorText(0,m,t)}n.forEach(this.dom.redundant,function(t){for(;t.length;){var e=t.pop();e&&e.parentNode&&e.parentNode.removeChild(e)}})},s.prototype._repaintMinorText=function(t,e,i){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.dom.foreground.appendChild(s)}this.dom.minorTexts.push(s),s.childNodes[0].nodeValue=e,s.style.top="top"==i?this.props.majorLabelHeight+"px":"0",s.style.left=t+"px"},s.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.dom.foreground.appendChild(s)}this.dom.majorTexts.push(s),s.childNodes[0].nodeValue=e,s.style.top="top"==i?"0":this.props.minorLabelHeight+"px",s.style.left=t+"px"},s.prototype._repaintMinorLine=function(t,e){var i=this.dom.redundant.minorLines.shift();i||(i=document.createElement("div"),i.className="grid vertical minor",this.dom.background.appendChild(i)),this.dom.minorLines.push(i);var s=this.props;i.style.top="top"==e?s.majorLabelHeight+"px":this.body.domProps.top.height+"px",i.style.height=s.minorLineHeight+"px",i.style.left=t-s.minorLineWidth/2+"px"},s.prototype._repaintMajorLine=function(t,e){var i=this.dom.redundant.majorLines.shift();i||(i=document.createElement("DIV"),i.className="grid vertical major",this.dom.background.appendChild(i)),this.dom.majorLines.push(i);var s=this.props;i.style.top="top"==e?"0":this.body.domProps.top.height+"px",i.style.left=t-s.majorLineWidth/2+"px",i.style.height=s.majorLineHeight+"px"},s.prototype._calculateCharSize=function(){this.dom.measureCharMinor||(this.dom.measureCharMinor=document.createElement("DIV"),this.dom.measureCharMinor.className="text minor measure",this.dom.measureCharMinor.style.position="absolute",this.dom.measureCharMinor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMinor)),this.props.minorCharHeight=this.dom.measureCharMinor.clientHeight,this.props.minorCharWidth=this.dom.measureCharMinor.clientWidth,this.dom.measureCharMajor||(this.dom.measureCharMajor=document.createElement("DIV"),this.dom.measureCharMajor.className="text minor measure",this.dom.measureCharMajor.style.position="absolute",this.dom.measureCharMajor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMajor)),this.props.majorCharHeight=this.dom.measureCharMajor.clientHeight,this.props.majorCharWidth=this.dom.measureCharMajor.clientWidth},s.prototype.snap=function(t){return this.step.snap(t)},t.exports=s},function(t,e,i){function s(t,e,i){this.id=null,this.parent=null,this.data=t,this.dom=null,this.conversion=e||{},this.options=i||{},this.selected=!1,this.displayed=!1,this.dirty=!0,this.top=null,this.left=null,this.width=null,this.height=null}var n=i(49);s.prototype.select=function(){this.selected=!0,this.displayed&&this.redraw()},s.prototype.unselect=function(){this.selected=!1,this.displayed&&this.redraw()},s.prototype.setParent=function(t){this.displayed?(this.hide(),this.parent=t,this.parent&&this.show()):this.parent=t},s.prototype.isVisible=function(){return!1},s.prototype.show=function(){return!1},s.prototype.hide=function(){return!1},s.prototype.redraw=function(){},s.prototype.repositionX=function(){},s.prototype.repositionY=function(){},s.prototype._repaintDeleteButton=function(t){if(this.selected&&this.options.editable.remove&&!this.dom.deleteButton){var e=this,i=document.createElement("div");i.className="delete",i.title="Delete this item",n(i,{preventDefault:!0}).on("tap",function(t){e.parent.removeFromDataSet(e),t.stopPropagation()}),t.appendChild(i),this.dom.deleteButton=i}else!this.selected&&this.dom.deleteButton&&(this.dom.deleteButton.parentNode&&this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton),this.dom.deleteButton=null)},t.exports=s},function(t,e,i){function s(t,e,i){if(this.props={dot:{width:0,height:0},line:{width:0,height:0}},t&&void 0==t.start)throw new Error('Property "start" missing in item '+t);n.call(this,t,e,i)}var n=i(22);s.prototype=new n(null,null,null),s.prototype.isVisible=function(t){var e=(t.end-t.start)/4;return this.data.start>t.start-e&&this.data.startt.start-e&&this.data.startt.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),t.box["timeline-item"]=this),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.foreground;if(!e)throw new Error("Cannot redraw 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}this.data.title!=this.title&&(t.box.title=this.data.title,this.title=this.data.title);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.overflow="hidden"!==window.getComputedStyle(t.content).overflow,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()},s.prototype.show=function(){this.displayed||this.redraw()},s.prototype.hide=function(){if(this.displayed){var t=this.dom.box;t.parentNode&&t.parentNode.removeChild(t),this.top=null,this.left=null,this.displayed=!1}},s.prototype.repositionX=function(){var t,e=this.props,i=this.parent.width,s=this.conversion.toScreen(this.data.start),n=this.conversion.toScreen(this.data.end),o=this.options.padding;-i>s&&(s=-i),n>2*i&&(n=2*i);var a=Math.max(n-s,1);this.overflow?(t=Math.max(-s,0),this.left=s,this.width=a+this.props.content.width):(t=0>s?Math.min(-s,n-s-e.content.width-2*o):0,this.left=s,this.width=a),this.dom.box.style.left=this.left+"px",this.dom.box.style.width=a+"px",this.dom.content.style.left=t+"px"},s.prototype.repositionY=function(){var t=this.options.orientation,e=this.dom.box;e.style.top="top"==t?this.top+"px":this.parent.height-this.top-this.height+"px"},s.prototype._repaintDragLeft=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragLeft){var t=document.createElement("div");t.className="drag-left",t.dragLeftItem=this,n(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragLeft=t}else!this.selected&&this.dom.dragLeft&&(this.dom.dragLeft.parentNode&&this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft),this.dom.dragLeft=null)},s.prototype._repaintDragRight=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragRight){var t=document.createElement("div");t.className="drag-right",t.dragRightItem=this,n(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragRight=t}else!this.selected&&this.dom.dragRight&&(this.dom.dragRight.parentNode&&this.dom.dragRight.parentNode.removeChild(this.dom.dragRight),this.dom.dragRight=null)},t.exports=s},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this._initializeMixinLoaders(),this.containerElement=t,this.width="100%",this.height="100%",this.renderRefreshRate=60,this.renderTimestep=1e3/this.renderRefreshRate,this.renderTime=.5*this.renderTimestep,this.maxPhysicsTicksPerRender=3,this.physicsDiscreteStepsize=.5,this.stabilize=!0,this.selectable=!0,this.initializing=!0,this.triggerFunctions={add:null,edit:null,editEdge:null,connect:null,del:null},this.constants={nodes:{radiusMin:5,radiusMax:20,radius:5,shape:"ellipse",image:void 0,widthMin:16,widthMax:64,fixed:!1,fontColor:"black",fontSize:14,fontFace:"verdana",level:-1,color:{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},borderColor:"#2B7CE9",backgroundColor:"#97C2FC",highlightColor:"#D2E5FF",group:void 0},edges:{widthMin:1,widthMax:15,width:1,widthSelectionMultiplier:2,hoverWidth:1.5,style:"line",color:{color:"#848484",highlight:"#848484",hover:"#848484"},fontColor:"#343434",fontSize:14,fontFace:"arial",fontFill:"white",arrowScaleFactor:1,dash:{length:10,gap:5,altLength:void 0}},configurePhysics:!1,physics:{barnesHut:{enabled:!0,theta:1/.6,gravitationalConstant:-2e3,centralGravity:.3,springLength:95,springConstant:.04,damping:.09},repulsion:{centralGravity:.1,springLength:200,springConstant:.05,nodeDistance:100,damping:.09},hierarchicalRepulsion:{enabled:!1,centralGravity:.5,springLength:150,springConstant:.01,nodeDistance:60,damping:.09},damping:null,centralGravity:null,springLength:null,springConstant:null},clustering:{enabled:!1,initialMaxNodes:100,clusterThreshold:500,reduceToNodes:300,chainThreshold:.4,clusterEdgeThreshold:20,sectorThreshold:100,screenSizeThreshold:.2,fontSizeMultiplier:4,maxFontSize:1e3,forceAmplification:.1,distanceAmplification:.1,edgeGrowth:20,nodeScaling:{width:1,height:1,radius:1},maxNodeSizeIncrements:600,activeAreaBoxSize:80,clusterLevelDifference:2},navigation:{enabled:!1},keyboard:{enabled:!1,speed:{x:10,y:10,zoom:.02}},dataManipulation:{enabled:!1,initiallyVisible:!1},hierarchicalLayout:{enabled:!1,levelSeparation:150,nodeSpacing:100,direction:"UD"},freezeForStabilization:!1,smoothCurves:!0,maxVelocity:10,minVelocity:.1,stabilizationIterations:1e3,labels:{add:"Add Node",edit:"Edit",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).",editBoundError:"No edit function has been bound to this button.",deleteError:"The function for delete does not support two arguments (data, callback).",deleteClusterError:"Clusters cannot be deleted."},tooltip:{delay:300,fontColor:"black",fontSize:14,fontFace:"verdana",color:{border:"#666",background:"#FFFFC6"}},dragNetwork:!0,dragNodes:!0,zoomable:!0,hover:!1},this.hoverObj={nodes:{},edges:{}};var n=this;this.groups=new c,this.images=new u,this.images.setOnloadCallback(function(){n._redraw()}),this.xIncrement=0,this.yIncrement=0,this.zoomIncrement=0,this._loadPhysicsSystem(),this._create(),this._loadSectorSystem(),this._loadClusterSystem(),this._loadSelectionSystem(),this._loadHierarchySystem(),this._setTranslation(this.frame.clientWidth/2,this.frame.clientHeight/2),this._setScale(1),this.setOptions(i),this.freezeSimulation=!1,this.cachedFunctions={},this.calculationNodes={},this.calculationNodeIndices=[],this.nodeIndices=[],this.nodes={},this.edges={},this.canvasTopLeft={x:0,y:0},this.canvasBottomRight={x:0,y:0},this.pointerPosition={x:0,y:0},this.areaCenter={},this.scale=1,this.previousScale=this.scale,this.nodesData=null,this.edgesData=null,this.nodesListeners={add:function(t,e){n._addNodes(e.items),n.start()},update:function(t,e){n._updateNodes(e.items),n.start()},remove:function(t,e){n._removeNodes(e.items),n.start()}},this.edgesListeners={add:function(t,e){n._addEdges(e.items),n.start()},update:function(t,e){n._updateEdges(e.items),n.start()},remove:function(t,e){n._removeEdges(e.items),n.start()}},this.moving=!0,this.timer=void 0,this.setData(e,this.constants.clustering.enabled||this.constants.hierarchicalLayout.enabled),this.initializing=!1,1==this.constants.hierarchicalLayout.enabled?this._setupHierarchicalLayout():0==this.stabilize&&this.zoomExtent(!0,this.constants.clustering.enabled),this.constants.clustering.enabled&&this.startWithClustering()}var n=i(41),o=i(49),a=i(42),r=i(1),h=i(3),d=i(4),l=i(32),c=i(28),u=i(29),p=i(30),m=i(27),_=i(31),f=i(40);i(37),n(s.prototype),s.prototype._getScriptPath=function(){for(var t=document.getElementsByTagName("script"),e=0;et.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._centerNetwork(s),0==e&&(this.moving=!0,this.start())},s.prototype._updateNodeIndexList=function(){this._clearNodeIndexList();for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodeIndices.push(t)},s.prototype.setData=function(t,e){if(void 0===e&&(e=!1),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=l.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()},s.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.dragNetwork&&(this.constants.dragNetwork=t.dragNetwork),void 0!==t.dragNodes&&(this.constants.dragNodes=t.dragNodes),void 0!==t.zoomable&&(this.constants.zoomable=t.zoomable),void 0!==t.hover&&(this.constants.hover=t.hover),void 0!==t.dragGraph)throw new Error("Option dragGraph is renamed to dragNetwork");if(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.onEditEdge&&(this.triggerFunctions.editEdge=t.onEditEdge),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]);this.editMode=this.constants.dataManipulation.initiallyVisible}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&&(r.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&&(r.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=r.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=r.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()},s.prototype._create=function(){for(;this.containerElement.hasChildNodes();)this.containerElement.removeChild(this.containerElement.firstChild);if(this.frame=document.createElement("div"),this.frame.className="network-frame",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),!this.frame.canvas.getContext){var t=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=o(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)},s.prototype._createKeyBinds=function(){var t=this;this.mousetrap=a,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)))},s.prototype._getPointer=function(t){return{x:t.pageX-r.getAbsoluteLeft(this.frame.canvas),y:t.pageY-r.getAbsoluteTop(this.frame.canvas)}},s.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) +},s.prototype._onDragStart=function(){this._handleDragStart()},s.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)}}},s.prototype._onDrag=function(t){this._handleOnDrag(t)},s.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&&1==this.constants.dragNodes){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.dragNetwork){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()}}},s.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})},s.prototype._onTap=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleTap(e)},s.prototype._onDoubleTap=function(t){var e=this._getPointer(t.gesture.center);this._handleDoubleTap(e)},s.prototype._onHold=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleOnHold(e)},s.prototype._onRelease=function(t){var e=this._getPointer(t.gesture.center);this._handleOnRelease(e)},s.prototype._onPinch=function(t){var e=this._getPointer(t.gesture.center);this.drag.pinched=!0,"scale"in this.pinch||(this.pinch.scale=1);var i=this.pinch.scale*t.gesture.scale;this._zoom(i,e)},s.prototype._zoom=function(t,e){if(1==this.constants.zoomable){var i=this._getScale();1e-5>t&&(t=1e-5),t>10&&(t=10);var s=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}},s.prototype._onMouseWheel=function(t){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i=this._getScale(),s=e/10;0>e&&(s/=1-s),i*=1+s;var n=r.fakeGesture(this,t),o=this._getPointer(n.center);this._zoom(i,o)}t.preventDefault()},s.prototype._onMouseMoveTitle=function(t){var e=r.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 h in this.hoverObj.nodes)this.hoverObj.nodes.hasOwnProperty(h)&&(a instanceof p&&a.id!=h||a instanceof m||null==a)&&(this._blurObject(this.hoverObj.nodes[h]),delete this.hoverObj.nodes[h]);this.redraw()}},s.prototype._checkShowPopup=function(t){var e,i={left:this._XconvertDOMtoCanvas(t.x),top:this._YconvertDOMtoCanvas(t.y),right:this._XconvertDOMtoCanvas(t.x),bottom:this._YconvertDOMtoCanvas(t.y)},s=this.popupObj;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 _(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()},s.prototype._checkHidePopup=function(t){this.popupObj&&this._getNodeAt(t)||(this.popupObj=void 0,this.popup&&this.popup.hide())},s.prototype.setSize=function(t,e){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})},s.prototype._setNodes=function(t){var e=this.nodesData;if(t instanceof h||t instanceof d)this.nodesData=t;else if(t instanceof Array)this.nodesData=new h,this.nodesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.nodesData=new h}if(e&&r.forEach(this.nodesListeners,function(t,i){e.off(i,t)}),this.nodes={},this.nodesData){var i=this;r.forEach(this.nodesListeners,function(t,e){i.nodesData.on(e,t)});var s=this.nodesData.getIds();this._addNodes(s)}this._updateSelection()},s.prototype._addNodes=function(t){for(var e,i=0,s=t.length;s>i;i++){e=t[i];var n=this.nodesData.get(e),o=new p(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()},s.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 p(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)},s.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)},s.prototype._setEdges=function(t){var e=this.edgesData;if(t instanceof h||t instanceof d)this.edgesData=t;else if(t instanceof Array)this.edgesData=new h,this.edgesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.edgesData=new h}if(e&&r.forEach(this.edgesListeners,function(t,i){e.off(i,t)}),this.edges={},this.edgesData){var i=this;r.forEach(this.edgesListeners,function(t,e){i.edgesData.on(e,t)});var s=this.edgesData.getIds();this._addEdges(s)}this._reconnectEdges()},s.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,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 m(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()},s.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 m(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)},s.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()},s.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()}},s.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)},s.prototype.redraw=function(){this.setSize(this.width,this.height),this._redraw()},s.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),this._doInAllSectors("_drawControlNodes",t),t.restore()},s.prototype._setTranslation=function(t,e){void 0===this.translation&&(this.translation={x:0,y:0}),void 0!==t&&(this.translation.x=t),void 0!==e&&(this.translation.y=e),this.emit("viewChanged")},s.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},s.prototype._setScale=function(t){this.scale=t},s.prototype._getScale=function(){return this.scale},s.prototype._XconvertDOMtoCanvas=function(t){return(t-this.translation.x)/this.scale},s.prototype._XconvertCanvasToDOM=function(t){return t*this.scale+this.translation.x},s.prototype._YconvertDOMtoCanvas=function(t){return(t-this.translation.y)/this.scale},s.prototype._YconvertCanvasToDOM=function(t){return t*this.scale+this.translation.y},s.prototype.canvasToDOM=function(t){return{x:this._XconvertCanvasToDOM(t.x),y:this._YconvertCanvasToDOM(t.y)}},s.prototype.DOMtoCanvas=function(t){return{x:this._XconvertDOMtoCanvas(t.x),y:this._YconvertDOMtoCanvas(t.y)}},s.prototype._drawNodes=function(t,e){void 0===e&&(e=!1);var i=this.nodes,s=[];for(var 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)},s.prototype._drawEdges=function(t){var e=this.edges;for(var i in e)if(e.hasOwnProperty(i)){var s=e[i];s.setScale(this.scale),s.connected&&e[i].draw(t)}},s.prototype._drawControlNodes=function(t){var e=this.edges;for(var i in e)e.hasOwnProperty(i)&&e[i]._drawControlNodes(t)},s.prototype._stabilize=function(){1==this.constants.freezeForStabilization&&this._freezeDefinedNodes();for(var t=0;this.moving&&t0)for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStepLimited(e,this.constants.maxVelocity),s=!0);else for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStep(e),s=!0);if(1==s){var n=this.constants.minVelocity/Math.max(this.scale,.05);this.moving=n>.5*this.constants.maxVelocity?!0:this._isMoving(n)}},s.prototype._physicsTick=function(){this.freezeSimulation||this.moving&&(this._doInAllActiveSectors("_initializeForceCalculation"),this._doInAllActiveSectors("_discreteStepNodes"),this.constants.smoothCurves&&this._doInSupportSector("_discreteStepNodes"),this._findCenter(this._getRange()))},s.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<.9*(this.renderTimestep-this.renderTime)&&eh}return!1},s.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)}},s.prototype._getLineWidth=function(){return 1==this.selected?Math.min(this.widthSelected,this.widthMax)*this.networkScaleInv:1==this.hover?Math.min(this.hoverWidth,this.widthMax)*this.networkScaleInv:this.width*this.networkScaleInv},s.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()},s.prototype._circle=function(t,e,i,s){t.beginPath(),t.arc(e,i,s,0,2*Math.PI,!1),t.stroke()},s.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)}},s.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)}},s.prototype._pointOnLine=function(t){return{x:(1-t)*this.from.x+t*this.to.x,y:(1-t)*this.from.y+t*this.to.y}},s.prototype._pointOnCircle=function(t,e,i,s){var n=2*(s-3/8)*Math.PI;return{x:t+i*Math.cos(n),y:e-i*Math.sin(n)}},s.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))}},s.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 _=.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:_,y:f}}else m=this._pointOnLine(.5);this._label(t,this.label,m.x,m.y)}}else{var g,y,v,M=this.from,b=.25*Math.max(100,this.length);M.width||M.resize(t),M.width>M.height?(g=M.x+.5*M.width,y=M.y-b,v={x:g,y:M.y,angle:.9*Math.PI}):(g=M.x+b,y=M.y-.5*M.height,v={x:M.x,y:y,angle:.6*Math.PI}),t.beginPath(),t.arc(g,y,b,0,2*Math.PI,!1),t.stroke();var i=(10+5*this.width)*this.arrowScaleFactor;t.arrow(v.x,v.y,v.angle,i),t.fill(),t.stroke(),this.label&&(m=this._pointOnCircle(g,y,b,.5),this._label(t,this.label,m.x,m.y))}},s.prototype._getDistanceToEdge=function(t,e,i,s,n,o){if(this.from!=this.to){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,_=p*p+m*m,f=((n-t)*p+(o-e)*m)/_;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)}var h,d,l,c,g=this.length/4,y=this.from;return y.width||y.resize(ctx),y.width>y.height?(h=y.x+y.width/2,d=y.y-g):(h=y.x+g,d=y.y-y.height/2),l=h-n,c=d-o,Math.abs(Math.sqrt(l*l+c*c)-g)},s.prototype.setScale=function(t){this.networkScaleInv=1/t},s.prototype.select=function(){this.selected=!0},s.prototype.unselect=function(){this.selected=!1},s.prototype.positionBezierNode=function(){null!==this.via&&(this.via.x=.5*(this.from.x+this.to.x),this.via.y=.5*(this.from.y+this.to.y))},s.prototype._drawControlNodes=function(t){if(1==this.controlNodesEnabled){if(null===this.controlNodes.from&&null===this.controlNodes.to){var e="edgeIdFrom:".concat(this.id),i="edgeIdTo:".concat(this.id),s={nodes:{group:"",radius:8},physics:{damping:0},clustering:{maxNodeSizeIncrements:0,nodeScaling:{width:0,height:0,radius:0}}};this.controlNodes.from=new Node({id:e,shape:"dot",color:{background:"#ff4e00",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s),this.controlNodes.to=new Node({id:i,shape:"dot",color:{background:"#ff4e00",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s)}0==this.controlNodes.from.selected&&0==this.controlNodes.to.selected&&(this.controlNodes.positions=this.getControlNodePositions(t),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(t),this.controlNodes.to.draw(t)}else this.controlNodes={from:null,to:null,positions:{}}},s.prototype._enableControlNodes=function(){this.controlNodesEnabled=!0},s.prototype._disableControlNodes=function(){this.controlNodesEnabled=!1},s.prototype._getSelectedControlNode=function(t,e){var i=this.controlNodes.positions,s=Math.sqrt(Math.pow(t-i.from.x,2)+Math.pow(e-i.from.y,2)),n=Math.sqrt(Math.pow(t-i.to.x,2)+Math.pow(e-i.to.y,2));return 15>s?(this.connectedNode=this.from,this.from=this.controlNodes.from,this.controlNodes.from):15>n?(this.connectedNode=this.to,this.to=this.controlNodes.to,this.controlNodes.to):null},s.prototype._restoreControlNodes=function(){1==this.controlNodes.from.selected&&(this.from=this.connectedNode,this.connectedNode=null,this.controlNodes.from.unselect()),1==this.controlNodes.to.selected&&(this.to=this.connectedNode,this.connectedNode=null,this.controlNodes.to.unselect())},s.prototype.getControlNodePositions=function(t){var e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),i=this.to.x-this.from.x,s=this.to.y-this.from.y,n=Math.sqrt(i*i+s*s),o=this.from.distanceToBorder(t,e+Math.PI),a=(n-o)/n,r=a*this.from.x+(1-a)*this.to.x,h=a*this.from.y+(1-a)*this.to.y;1==this.smooth&&(e=Math.atan2(this.to.y-this.via.y,this.to.x-this.via.x),i=this.to.x-this.via.x,s=this.to.y-this.via.y,n=Math.sqrt(i*i+s*s));var d,l,c=this.to.distanceToBorder(t,e),u=(n-c)/n;return 1==this.smooth?(d=(1-u)*this.via.x+u*this.to.x,l=(1-u)*this.via.y+u*this.to.y):(d=(1-u)*this.from.x+u*this.to.x,l=(1-u)*this.from.y+u*this.to.y),{from:{x:r,y:h},to:{x:d,y:l}}},t.exports=s},function(t,e,i){function s(){this.clear(),this.defaultIndex=0}var n=i(1);s.DEFAULT=[{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"}},{border:"#FFA500",background:"#FFFF00",highlight:{border:"#FFA500",background:"#FFFFA3"}},{border:"#FA0A10",background:"#FB7E81",highlight:{border:"#FA0A10",background:"#FFAFB1"}},{border:"#41A906",background:"#7BE141",highlight:{border:"#41A906",background:"#A1EC76"}},{border:"#E129F0",background:"#EB7DF4",highlight:{border:"#E129F0",background:"#F0B3F5"}},{border:"#7C29F0",background:"#AD85E4",highlight:{border:"#7C29F0",background:"#D3BDF0"}},{border:"#C37F00",background:"#FFA807",highlight:{border:"#C37F00",background:"#FFCA66"}},{border:"#4220FB",background:"#6E6EFD",highlight:{border:"#4220FB",background:"#9B9BFD"}},{border:"#FD5A77",background:"#FFC0CB",highlight:{border:"#FD5A77",background:"#FFD1D9"}},{border:"#4AD63A",background:"#C2FABC",highlight:{border:"#4AD63A",background:"#E6FFE3"}}],s.prototype.clear=function(){this.groups={},this.groups.length=function(){var t=0;for(var e in this)this.hasOwnProperty(e)&&t++;return t}},s.prototype.get=function(t){var e=this.groups[t];if(void 0==e){var i=this.defaultIndex%s.DEFAULT.length;this.defaultIndex++,e={},e.color=s.DEFAULT[i],this.groups[t]=e}return e},s.prototype.add=function(t,e){return this.groups[t]=e,e.color&&(e.color=n.parseColor(e.color)),e},t.exports=s},function(t){function e(){this.images={},this.callback=void 0}e.prototype.setOnloadCallback=function(t){this.callback=t},e.prototype.load=function(t){var e=this.images[t];if(void 0==e){var i=this;e=new Image,this.images[t]=e,e.onload=function(){i.callback&&i.callback(this)},e.src=t}return e},t.exports=e},function(t,e,i){function s(t,e,i,s){this.selected=!1,this.hover=!1,this.edges=[],this.dynamicEdges=[],this.reroutedEdges={},this.group=s.nodes.group,this.fontSize=Number(s.nodes.fontSize),this.fontFace=s.nodes.fontFace,this.fontColor=s.nodes.fontColor,this.fontDrawThreshold=3,this.color=s.nodes.color,this.id=void 0,this.shape=s.nodes.shape,this.image=s.nodes.image,this.x=null,this.y=null,this.xFixed=!1,this.yFixed=!1,this.horizontalAlignLeft=!0,this.verticalAlignTop=!0,this.radius=s.nodes.radius,this.baseRadiusValue=s.nodes.radius,this.radiusFixed=!1,this.radiusMin=s.nodes.radiusMin,this.radiusMax=s.nodes.radiusMax,this.level=-1,this.preassignedLevel=!1,this.imagelist=e,this.grouplist=i,this.fx=0,this.fy=0,this.vx=0,this.vy=0,this.minForce=s.minForce,this.damping=s.physics.damping,this.mass=1,this.fixedData={x:null,y:null},this.setProperties(t,s),this.resetCluster(),this.dynamicEdgesLength=0,this.clusterSession=0,this.clusterSizeWidthFactor=s.clustering.nodeScaling.width,this.clusterSizeHeightFactor=s.clustering.nodeScaling.height,this.clusterSizeRadiusFactor=s.clustering.nodeScaling.radius,this.maxNodeSizeIncrements=s.clustering.maxNodeSizeIncrements,this.growthIndicator=0,this.networkScaleInv=1,this.networkScale=1,this.canvasTopLeft={x:-300,y:-300},this.canvasBottomRight={x:300,y:300},this.parentEdgeId=null +}var n=i(1);s.prototype.resetCluster=function(){this.formationScale=void 0,this.clusterSize=1,this.containedNodes={},this.containedEdges={},this.clusterSessions=[]},s.prototype.attachEdge=function(t){-1==this.edges.indexOf(t)&&this.edges.push(t),-1==this.dynamicEdges.indexOf(t)&&this.dynamicEdges.push(t),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.detachEdge=function(t){var e=this.edges.indexOf(t);-1!=e&&(this.edges.splice(e,1),this.dynamicEdges.splice(e,1)),this.dynamicEdgesLength=this.dynamicEdges.length},s.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=n.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()}},s.prototype.select=function(){this.selected=!0,this._reset()},s.prototype.unselect=function(){this.selected=!1,this._reset()},s.prototype.clearSizeCache=function(){this._reset()},s.prototype._reset=function(){this.width=void 0,this.height=void 0},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.distanceToBorder=function(t,e){var i=1;switch(this.width||this.resize(t),this.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}},s.prototype._setForce=function(t,e){this.fx=t,this.fy=e},s.prototype._addForce=function(t,e){this.fx+=t,this.fy+=e},s.prototype.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}},s.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}},s.prototype.isFixed=function(){return this.xFixed&&this.yFixed},s.prototype.isMoving=function(t){return Math.abs(this.vx)>t||Math.abs(this.vy)>t},s.prototype.isSelected=function(){return this.selected},s.prototype.getValue=function(){return this.value},s.prototype.getDistance=function(t,e){var i=this.x-t,s=this.y-e;return Math.sqrt(i*i+s*s)},s.prototype.setValueRange=function(t,e){if(!this.radiusFixed&&void 0!==this.value)if(e==t)this.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},s.prototype.draw=function(){throw"Draw method not initialized for node"},s.prototype.resize=function(){throw"Resize method not initialized for node"},s.prototype.isOverlappingWith=function(t){return this.leftt.left&&this.topt.top},s.prototype._resizeImage=function(){if(!this.width||!this.height){var t,e;if(this.value){this.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)}},s.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.networkScaleInv,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")},s.prototype._resizeBox=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawBox=function(t){this._resizeBox(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=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.networkScaleInv,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.networkScaleInv,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)},s.prototype._resizeDatabase=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=i.width+2*e;this.width=s,this.height=s,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-s}},s.prototype._drawDatabase=function(t){this._resizeDatabase(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=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.networkScaleInv,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.networkScaleInv,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)},s.prototype._resizeCircle=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=Math.max(i.width,i.height)+2*e;this.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}},s.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.networkScaleInv,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.networkScaleInv,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)},s.prototype._resizeEllipse=function(t){if(!this.width){var e=this.getTextSize(t);this.width=1.5*e.width,this.height=2*e.height,this.width1&&(t.lineWidth=(this.selected?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,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.networkScaleInv,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)},s.prototype._drawDot=function(t){this._drawShape(t,"circle")},s.prototype._drawTriangle=function(t){this._drawShape(t,"triangle")},s.prototype._drawTriangleDown=function(t){this._drawShape(t,"triangleDown")},s.prototype._drawSquare=function(t){this._drawShape(t,"square")},s.prototype._drawStar=function(t){this._drawShape(t,"star")},s.prototype._resizeShape=function(){if(!this.width){this.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}},s.prototype._drawShape=function(t,e){this._resizeShape(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var i=2.5,s=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.networkScaleInv,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.networkScaleInv,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",!0)},s.prototype._resizeText=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawText=function(t){this._resizeText(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._label(t,this.label,this.x,this.y)},s.prototype._label=function(t,e,i,s,n,o,a){if(e&&this.fontSize*this.networkScale>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";var r=e.split("\n"),h=r.length,d=this.fontSize+4,l=s+(1-h)/2*d;1==a&&(l=s+(1-h)/(2*d));for(var c=0;h>c;c++)t.fillText(r[c],i,l),l+=d}},s.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}},s.prototype.inArea=function(){return void 0!==this.width?this.x+this.width*this.networkScaleInv>=this.canvasTopLeft.x&&this.x-this.width*this.networkScaleInv=this.canvasTopLeft.y&&this.y-this.height*this.networkScaleInv=this.canvasTopLeft.x&&this.x=this.canvasTopLeft.y&&this.ys&&(o=s-e-this.padding),on&&(a=n-i-this.padding),ai;i++)if(e.id===a.nodes[i].id){n=a.nodes[i];break}for(n||(n={id:e.id},t.node&&(n.attr=r(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=r(n.attr,e.attr))}function l(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=r({},t.edge);e.attr=r(i,e.attr)}}function c(t,e,i,s,n){var o={from:e,to:i,type:s};return t.edge&&(o.attr=r({},t.edge)),o.attr=r(o.attr||{},n),o}function u(){for(C=D.NULL,E="";" "==k||" "==k||"\n"==k||"\r"==k;)n();do{var t=!1;if("#"==k){for(var e=S-1;" "==Y.charAt(e)||" "==Y.charAt(e);)e--;if("\n"==Y.charAt(e)||""==Y.charAt(e)){for(;""!=k&&"\n"!=k;)n();t=!0}}if("/"==k&&"/"==o()){for(;""!=k&&"\n"!=k;)n();t=!0}if("/"==k&&"*"==o()){for(;""!=k;){if("*"==k&&"/"==o()){n(),n();break}n()}t=!0}for(;" "==k||" "==k||"\n"==k||"\r"==k;)n()}while(t);if(""==k)return void(C=D.DELIMITER);var i=k+o();if(T[i])return C=D.DELIMITER,E=i,n(),void n();if(T[k])return C=D.DELIMITER,E=k,void n();if(a(k)||"-"==k){for(E+=k,n();a(k);)E+=k,n();return"false"==E?E=!1:"true"==E?E=!0:isNaN(Number(E))||(E=Number(E)),void(C=D.IDENTIFIER)}if('"'==k){for(n();""!=k&&('"'!=k||'"'==k&&'"'==o());)E+=k,'"'==k&&n(),n();if('"'!=k)throw b('End of string " expected');return n(),void(C=D.IDENTIFIER)}for(C=D.UNKNOWN;""!=k;)E+=k,n();throw new SyntaxError('Syntax error in part "'+w(E,30)+'"')}function p(){var t={};if(s(),u(),"strict"==E&&(t.strict=!0,u()),("graph"==E||"digraph"==E)&&(t.type=E,u()),C==D.IDENTIFIER&&(t.id=E,u()),"{"!=E)throw b("Angle bracket { expected");if(u(),m(t),"}"!=E)throw b("Angle bracket } expected");if(u(),""!==E)throw b("End of file expected");return u(),delete t.node,delete t.edge,delete t.graph,t}function m(t){for(;""!==E&&"}"!=E;)_(t),";"==E&&u()}function _(t){var e=f(t);if(e)return void v(t,e);var i=g(t);if(!i){if(C!=D.IDENTIFIER)throw b("Identifier expected");var s=E;if(u(),"="==E){if(u(),C!=D.IDENTIFIER)throw b("Identifier expected");t[s]=E,u()}else y(t,s)}}function f(t){var e=null;if("subgraph"==E&&(e={},e.type="subgraph",u(),C==D.IDENTIFIER&&(e.id=E,u())),"{"==E){if(u(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,m(e),"}"!=E)throw b("Angle bracket } expected");u(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function g(t){return"node"==E?(u(),t.node=M(),"node"):"edge"==E?(u(),t.edge=M(),"edge"):"graph"==E?(u(),t.graph=M(),"graph"):null}function y(t,e){var i={id:e},s=M();s&&(i.attr=s),d(t,i),v(t,e)}function v(t,e){for(;"->"==E||"--"==E;){var i,s=E;u();var n=f(t);if(n)i=n;else{if(C!=D.IDENTIFIER)throw b("Identifier or subgraph expected");i=E,d(t,{id:i}),u()}var o=M(),a=c(t,e,i,s,o);l(t,a),e=i}}function M(){for(var t=null;"["==E;){for(u(),t={};""!==E&&"]"!=E;){if(C!=D.IDENTIFIER)throw b("Attribute name expected");var e=E;if(u(),"="!=E)throw b("Equal sign = expected");if(u(),C!=D.IDENTIFIER)throw b("Attribute value expected");var i=E;h(t,e,i),u(),","==E&&u()}if("]"!=E)throw b("Bracket ] expected");u()}return t}function b(t){return new SyntaxError(t+', got "'+w(E,30)+'" (char '+S+")")}function w(t,e){return t.length<=e?t:t.substr(0,27)+"..."}function L(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 x(t){function e(t){var e={from:t.from,to:t.to};return r(e,t.attr),e.style="->"==t.type?"arrow":"line",e}var s=i(t),n={nodes:[],edges:[],options:{}};return s.nodes&&s.nodes.forEach(function(t){var e={id:t.id,label:String(t.label||t.id)};r(e,t.attr),e.image&&(e.shape="image"),n.nodes.push(e)}),s.edges&&s.edges.forEach(function(t){var i,s;i=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 i=e(t);n.edges.push(i)}),L(i,s,function(i,s){var o=c(n,i.id,s.id,t.type,t.attr),a=e(o);n.edges.push(a)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var i=e(t);n.edges.push(i)})}),s.attr&&(n.options=s.attr),n}var D={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},T={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},Y="",S=0,k="",E="",C=D.NULL,N=/[a-zA-Z_0-9.:#]/;e.parseDOT=i,e.DOTToGraph=x},function(t){function e(t,e,i){this.x=void 0!==t?t:0,this.y=void 0!==e?e:0,this.z=void 0!==i?i:0}e.subtract=function(t,i){var s=new e;return s.x=t.x-i.x,s.y=t.y-i.y,s.z=t.z-i.z,s},e.add=function(t,i){var s=new e;return s.x=t.x+i.x,s.y=t.y+i.y,s.z=t.z+i.z,s},e.avg=function(t,i){return new e((t.x+i.x)/2,(t.y+i.y)/2,(t.z+i.z)/2)},e.crossProduct=function(t,i){var s=new e;return s.x=t.y*i.z-t.z*i.y,s.y=t.z*i.x-t.x*i.z,s.z=t.x*i.y-t.y*i.x,s},e.prototype.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},t.exports=e},function(t){Point2d=function(t,e){this.x=void 0!==t?t:0,this.y=void 0!==e?e:0},t.exports=Point2d},function(t,e,i){function s(t,e,i){this.data=t,this.column=e,this.graph=i,this.index=void 0,this.value=void 0,this.values=i.getDistinctValues(t.get(),this.column),this.values.sort(function(t,e){return t>e?1:e>t?-1:0}),this.values.length>0&&this.selectValue(0),this.dataPoints=[],this.loaded=!1,this.onLoadCallback=void 0,i.animationPreload?(this.loaded=!1,this.loadInBackground()):this.loaded=!0}var n=i(4);s.prototype.isLoaded=function(){return this.loaded},s.prototype.getLoadedProgress=function(){for(var t=this.values.length,e=0;this.dataPoints[e];)e++;return Math.round(e/t*100)},s.prototype.getLabel=function(){return this.graph.filterLabel},s.prototype.getColumn=function(){return this.column},s.prototype.getSelectedValue=function(){return void 0===this.index?void 0:this.values[this.index]},s.prototype.getValues=function(){return this.values},s.prototype.getValue=function(t){if(t>=this.values.length)throw"Error: index out of range";return this.values[t]},s.prototype._getDataPoints=function(t){if(void 0===t&&(t=this.index),void 0===t)return[];var e;if(this.dataPoints[t])e=this.dataPoints[t];else{var i={};i.column=this.column,i.value=this.values[t];var s=new n(this.data,{filter:function(t){return t[i.column]==i.value}}).get();e=this.graph._getDataPoints(s),this.dataPoints[t]=e}return e},s.prototype.setOnLoadCallback=function(t){this.onLoadCallback=t},s.prototype.selectValue=function(t){if(t>=this.values.length)throw"Error: index out of range";this.index=t,this.value=this.values[t]},s.prototype.loadInBackground=function(t){void 0===t&&(t=0);var e=this.graph.frame;if(t=t||(void 0!==i&&(this.prettyStep=i),this._step=this.prettyStep===!0?e.calculatePrettyStep(t):t)},e.calculatePrettyStep=function(t){var e=function(t){return Math.log(t)/Math.LN10},i=Math.pow(10,Math.round(e(t))),s=2*Math.pow(10,Math.round(e(t/2))),n=5*Math.pow(10,Math.round(e(t/5))),o=i;return Math.abs(s-t)<=Math.abs(o-t)&&(o=s),Math.abs(n-t)<=Math.abs(o-t)&&(o=n),0>=o&&(o=1),o},e.prototype.getCurrent=function(){return parseFloat(this._current.toPrecision(this.precision))},e.prototype.getStep=function(){return this._step},e.prototype.start=function(){this._current=this._start-this._start%this._step},e.prototype.next=function(){this._current+=this._step},e.prototype.end=function(){return this._current>this._end},t.exports=e},function(){"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),_=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,_,u,_),this.bezierCurveTo(u-h,_,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}})},function(t,e,i){t.exports="undefined"!=typeof window?i(49):function(){throw Error("hammer.js is only available in a browser, not in node.js.")}},function(t,e,i){t.exports="undefined"!=typeof window&&window.moment||i(51)},function(t,e,i){var s=i(50),n=i(43),o=i(44),a=i(45),r=i(46),h=i(47),d=i(48);e._loadMixin=function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e])},e._clearMixin=function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=void 0)},e._loadPhysicsSystem=function(){this._loadMixin(s),this._loadSelectedForceSolver(),1==this.constants.configurePhysics&&this._loadPhysicsConfiguration()},e._loadClusterSystem=function(){this.clusterSession=0,this.hubThreshold=5,this._loadMixin(n)},e._loadSectorSystem=function(){this.sectors={},this.activeSector=["default"],this.sectors.active={},this.sectors.active["default"]={nodes:{},edges:{},nodeIndices:[],formationScale:1,drawingNode:void 0},this.sectors.frozen={},this.sectors.support={nodes:{},edges:{},nodeIndices:[],formationScale:1,drawingNode:void 0},this.nodeIndices=this.sectors.active["default"].nodeIndices,this._loadMixin(o)},e._loadSelectionSystem=function(){this.selectionObj={nodes:{},edges:{}},this._loadMixin(a)},e._loadManipulationSystem=function(){this.blockConnectingEdgeSelection=!1,this.forceAppendSelection=!1,1==this.constants.dataManipulation.enabled?(void 0===this.manipulationDiv&&(this.manipulationDiv=document.createElement("div"),this.manipulationDiv.className="network-manipulationDiv",this.manipulationDiv.id="network-manipulationDiv",this.manipulationDiv.style.display=1==this.editMode?"block":"none",this.containerElement.insertBefore(this.manipulationDiv,this.frame)),void 0===this.editModeDiv&&(this.editModeDiv=document.createElement("div"),this.editModeDiv.className="network-manipulation-editMode",this.editModeDiv.id="network-manipulation-editMode",this.editModeDiv.style.display=1==this.editMode?"none":"block",this.containerElement.insertBefore(this.editModeDiv,this.frame)),void 0===this.closeDiv&&(this.closeDiv=document.createElement("div"),this.closeDiv.className="network-manipulation-closeDiv",this.closeDiv.id="network-manipulation-closeDiv",this.closeDiv.style.display=this.manipulationDiv.style.display,this.containerElement.insertBefore(this.closeDiv,this.frame)),this._loadMixin(r),this._createManipulatorBar()):void 0!==this.manipulationDiv&&(this._createManipulatorBar(),this.containerElement.removeChild(this.manipulationDiv),this.containerElement.removeChild(this.editModeDiv),this.containerElement.removeChild(this.closeDiv),this.manipulationDiv=void 0,this.editModeDiv=void 0,this.closeDiv=void 0,this._clearMixin(r))},e._loadNavigationControls=function(){this._loadMixin(h),this._cleanNavigation(),1==this.constants.navigation.enabled&&this._loadNavigationElements()},e._loadHierarchySystem=function(){this._loadMixin(d)}},function(t){function e(t){return t?i(t):void 0}function i(t){for(var i in e.prototype)t[i]=e.prototype[i];return t}t.exports=e,e.prototype.on=e.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks[t]=this._callbacks[t]||[]).push(e),this},e.prototype.once=function(t,e){function i(){s.off(t,i),e.apply(this,arguments)}var s=this;return this._callbacks=this._callbacks||{},i.fn=e,this.on(t,i),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this; +var i=this._callbacks[t];if(!i)return this;if(1==arguments.length)return delete this._callbacks[t],this;for(var s,n=0;ns;++s)i[s].apply(this,e)}return this},e.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[t]||[]},e.prototype.hasListeners=function(t){return!!this.listeners(t).length}},function(t){function e(t,e,i){return t.addEventListener?t.addEventListener(e,i,!1):void t.attachEvent("on"+e,i)}function i(t){return"keypress"==t.type?String.fromCharCode(t.which):M[t.which]?M[t.which]:b[t.which]?b[t.which]:String.fromCharCode(t.which).toLowerCase()}function s(t){var e=t.target||t.srcElement,i=e.tagName;return(" "+e.className+" ").indexOf(" mousetrap ")>-1?!1:"INPUT"==i||"SELECT"==i||"TEXTAREA"==i||e.contentEditable&&"true"==e.contentEditable}function n(t,e){return t.sort().join(",")===e.sort().join(",")}function o(t){t=t||{};var e,i=!1;for(e in T)t[e]?i=!0:T[e]=0;i||(S=!1)}function a(t,e,i,s,o){var a,r,h=[];if(!x[t])return[];for("keyup"==i&&c(t)&&(e=[t]),a=0;a95&&112>t||M.hasOwnProperty(t)&&(y[M[t]]=t)}return y}function m(t,e,i){return i||(i=p()[t]?"keydown":"keypress"),"keypress"==i&&e.length&&(i="keydown"),i}function _(t,e,s,n){T[t]=0,n||(n=m(e[0],[]));var a,r=function(){S=n,++T[t],u()},d=function(t){h(s,t),"keyup"!==n&&(Y=i(t)),setTimeout(o,10)};for(a=0;a1)return _(t,d,e,i);for(h="+"===t?["+"]:t.split("+"),o=0;o":".","?":"/","|":"\\"},L={option:"alt",command:"meta","return":"enter",escape:"esc"},x={},D={},T={},Y=!1,S=!1,k=1;20>k;++k)M[111+k]="f"+k;for(k=0;9>=k;++k)M[k+96]=k;e(document,"keypress",l),e(document,"keydown",l),e(document,"keyup",l);var E={bind:function(t,e,i){return g(t instanceof Array?t:[t],e,i),D[t+":"+i]=e,this},unbind:function(t,e){return D[t+":"+e]&&(delete D[t+":"+e],this.bind(t,function(){},e)),this},trigger:function(t,e){return D[t+":"+e](),this},reset:function(){return x={},D={},this}};t.exports=E},function(t,e){e.startWithClustering=function(){this.clusterToFit(this.constants.clustering.initialMaxNodes,!0),this.updateLabels(),this.stabilize&&this._stabilize(),this.start()},e.clusterToFit=function(t,e){for(var i=this.nodeIndices.length,s=50,n=0;i>t&&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()},e.openCluster=function(t){var e=this.moving;if(t.clusterSize>this.constants.clustering.sectorThreshold&&this._nodeInActiveArea(t)&&("default"!=this._sector()||1!=this.nodeIndices.length)){this._addSector(t);for(var i=0;this.nodeIndices.lengthi;)this.decreaseClusterLevel(),i+=1}else this._expandClusterNode(t,!1,!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this._updateCalculationNodes(),this.updateLabels();this.moving!=e&&this.start()},e.updateClustersDefault=function(){1==this.constants.clustering.enabled&&this.updateClusters(0,!1,!1)},e.increaseClusterLevel=function(){this.updateClusters(-1,!1,!0)},e.decreaseClusterLevel=function(){this.updateClusters(1,!1,!0)},e.updateClusters=function(t,e,i,s){var 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)},e._aggregateHubs=function(t){this._getHubSize(),this._formClustersByHub(t,!1)},e.forceAggregateHubs=function(t){var e=this.moving,i=this.nodeIndices.length;this._aggregateHubs(!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.length!=i&&(this.clusterSession+=1),(0==t||void 0===t)&&this.moving!=e&&this.start()},e._openClustersBySize=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];1==e.inView()&&(e.width*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientWidth||e.height*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientHeight)&&this.openCluster(e)}},e._openClusters=function(t,e){for(var i=0;i1&&(t.clusterSizei)){var 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)}}},e._forceClustersByZoom=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];if(1==e.dynamicEdgesLength&&0!=e.dynamicEdges.length){var i=e.dynamicEdges[0],s=i.toId==e.id?this.nodes[i.fromId]:this.nodes[i.toId];e.id!=s.id&&(s.mass>e.mass?this._addToCluster(s,e,!0):this._addToCluster(e,s,!0))}}},e._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)},e._formClustersByHub=function(t,e){for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&this._formClusterFromHub(this.nodes[i],t,e)},e._formClusterFromHub=function(t,e,i,s){if(void 0===s&&(s=0),t.dynamicEdgesLength>=this.hubThreshold&&0==i||t.dynamicEdgesLength==this.hubThreshold&&1==i){for(var 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)}}},e._addToCluster=function(t,e,i){t.containedNodes[e.id]=e;for(var s=0;s1)for(var s=0;s1&&(e.label="[".concat(String(e.clusterSize),"]"))}for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(e=this.nodes[t],1==e.clusterSize&&(e.label=void 0!==e.originalLabel?e.originalLabel:String(e.id)))},e.normalizeClusterLevels=function(){var t,e=0,i=1e9,s=0;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(s=this.nodes[t].clusterSessions.length,s>e&&(e=s),i>s&&(i=s));if(e-i>this.constants.clustering.clusterLevelDifference){var 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)},e._reduceAmountOfChains=function(t){this.hubThreshold=2;var e=Math.floor(this.nodeIndices.length*t);for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&e>0&&(this._formClusterFromHub(this.nodes[i],!0,!0,1),e-=1)},e._getChainFraction=function(){var t=0,e=0;for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&(2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&(t+=1),e+=1);return t/e}},function(t,e,i){var s=i(1);e._putDataInSector=function(){this.sectors.active[this._sector()].nodes=this.nodes,this.sectors.active[this._sector()].edges=this.edges,this.sectors.active[this._sector()].nodeIndices=this.nodeIndices},e._switchToSector=function(t,e){void 0===e||"active"==e?this._switchToActiveSector(t):this._switchToFrozenSector(t)},e._switchToActiveSector=function(t){this.nodeIndices=this.sectors.active[t].nodeIndices,this.nodes=this.sectors.active[t].nodes,this.edges=this.sectors.active[t].edges},e._switchToSupportSector=function(){this.nodeIndices=this.sectors.support.nodeIndices,this.nodes=this.sectors.support.nodes,this.edges=this.sectors.support.edges},e._switchToFrozenSector=function(t){this.nodeIndices=this.sectors.frozen[t].nodeIndices,this.nodes=this.sectors.frozen[t].nodes,this.edges=this.sectors.frozen[t].edges},e._loadLatestSector=function(){this._switchToSector(this._sector())},e._sector=function(){return this.activeSector[this.activeSector.length-1]},e._previousSector=function(){if(this.activeSector.length>1)return this.activeSector[this.activeSector.length-2];throw new TypeError("there are not enough sectors in the this.activeSector array.")},e._setActiveSector=function(t){this.activeSector.push(t)},e._forgetLastSector=function(){this.activeSector.pop()},e._createNewSector=function(t){this.sectors.active[t]={nodes:{},edges:{},nodeIndices:[],formationScale:this.scale,drawingNode:void 0},this.sectors.active[t].drawingNode=new Node({id:t,color:{background:"#eaefef",border:"495c5e"}},{},{},this.constants),this.sectors.active[t].drawingNode.clusterSize=2},e._deleteActiveSector=function(t){delete this.sectors.active[t]},e._deleteFrozenSector=function(t){delete this.sectors.frozen[t]},e._freezeSector=function(t){this.sectors.frozen[t]=this.sectors.active[t],this._deleteActiveSector(t)},e._activateSector=function(t){this.sectors.active[t]=this.sectors.frozen[t],this._deleteFrozenSector(t)},e._mergeThisWithFrozen=function(t){for(var e in this.nodes)this.nodes.hasOwnProperty(e)&&(this.sectors.frozen[t].nodes[e]=this.nodes[e]);for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.sectors.frozen[t].edges[i]=this.edges[i]);for(var s=0;s1?this[t](s[0],s[1]):this[t](e)}this._loadLatestSector()},e._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()},e._doInAllFrozenSectors=function(t,e){if(void 0===e)for(var i in this.sectors.frozen)this.sectors.frozen.hasOwnProperty(i)&&(this._switchToFrozenSector(i),this[t]());else for(var i in this.sectors.frozen)if(this.sectors.frozen.hasOwnProperty(i)){this._switchToFrozenSector(i);var s=Array.prototype.splice.call(arguments,1);s.length>1?this[t](s[0],s[1]):this[t](e)}this._loadLatestSector()},e._doInAllSectors=function(t,e){var i=Array.prototype.splice.call(arguments,1);void 0===e?(this._doInAllActiveSectors(t),this._doInAllFrozenSectors(t)):i.length>1?(this._doInAllActiveSectors(t,i[0],i[1]),this._doInAllFrozenSectors(t,i[0],i[1])):(this._doInAllActiveSectors(t,e),this._doInAllFrozenSectors(t,e))},e._clearNodeIndexList=function(){var t=this._sector();this.sectors.active[t].nodeIndices=[],this.nodeIndices=this.sectors.active[t].nodeIndices},e._drawSectorNodes=function(t,e){var i,s=1e9,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),n0?this.nodes[i[i.length-1]]:null},e._getEdgesOverlappingWith=function(t,e){var i=this.edges;for(var s in i)i.hasOwnProperty(s)&&i[s].isOverlappingWith(t)&&e.push(s)},e._getAllEdgesOverlappingWith=function(t){var e=[];return this._doInAllActiveSectors("_getEdgesOverlappingWith",t,e),e},e._getEdgeAt=function(t){var e=this._pointerToPositionObject(t),i=this._getAllEdgesOverlappingWith(e);return i.length>0?this.edges[i[i.length-1]]:null},e._addToSelection=function(t){t instanceof s?this.selectionObj.nodes[t.id]=t:this.selectionObj.edges[t.id]=t},e._addToHover=function(t){t instanceof s?this.hoverObj.nodes[t.id]=t:this.hoverObj.edges[t.id]=t},e._removeFromSelection=function(t){t instanceof s?delete this.selectionObj.nodes[t.id]:delete this.selectionObj.edges[t.id]},e._unselectAll=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].unselect();for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&this.selectionObj.edges[i].unselect();this.selectionObj={nodes:{},edges:{}},0==t&&this.emit("select",this.getSelection())},e._unselectClusters=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].clusterSize>1&&(this.selectionObj.nodes[e].unselect(),this._removeFromSelection(this.selectionObj.nodes[e]));0==t&&this.emit("select",this.getSelection())},e._getSelectedNodeCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedNode=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return this.selectionObj.nodes[t];return null},e._getSelectedEdge=function(){for(var t in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(t))return this.selectionObj.edges[t];return null},e._getSelectedEdgeCount=function(){var t=0;for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedObjectCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&(t+=1);return t},e._selectionIsEmpty=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return!1;for(var e in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(e))return!1;return!0},e._clusterInSelection=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t)&&this.selectionObj.nodes[t].clusterSize>1)return!0;return!1},e._selectConnectedEdges=function(t){for(var e=0;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)}console.log("setSelection is deprecated. Please use selectNodes instead."),this.redraw()},e.selectNodes=function(t,e){var i,s,n;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(this._unselectAll(!0),i=0,s=t.length;s>i;i++){n=t[i];var o=this.nodes[n];if(!o)throw new RangeError('Node with id "'+n+'" not found');this._selectObject(o,!0,!0,e)}this.redraw()},e.selectEdges=function(t){var e,i,s;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(this._unselectAll(!0),e=0,i=t.length;i>e;e++){s=t[e];var n=this.edges[s];if(!n)throw new RangeError('Edge with id "'+s+'" not found');this._selectObject(n,!0,!0,highlightEdges)}this.redraw()},e._updateSelection=function(){for(var t in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(t)&&(this.nodes.hasOwnProperty(t)||delete this.selectionObj.nodes[t]);for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(this.edges.hasOwnProperty(e)||delete this.selectionObj.edges[e])}},function(t,e,i){var s=i(1);e._clearManipulatorBar=function(){for(;this.manipulationDiv.hasChildNodes();)this.manipulationDiv.removeChild(this.manipulationDiv.firstChild)},e._restoreOverloadedFunctions=function(){for(var t in this.cachedFunctions)this.cachedFunctions.hasOwnProperty(t)&&(this[t]=this.cachedFunctions[t])},e._toggleEditMode=function(){this.editMode=!this.editMode;var t=document.getElementById("network-manipulationDiv"),e=document.getElementById("network-manipulation-closeDiv"),i=document.getElementById("network-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()},e._createManipulatorBar=function(){if(this.boundFunction&&this.off("select",this.boundFunction),void 0!==this.edgeBeingEdited&&(this.edgeBeingEdited._disableControlNodes(),this.edgeBeingEdited=void 0,this.selectedControlNode=null),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+"":1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDiv.innerHTML+="
"+this.constants.labels.editEdge+""),0==this._selectionIsEmpty()&&(this.manipulationDiv.innerHTML+="
"+this.constants.labels.del+"");var t=document.getElementById("network-manipulate-addNode");t.onclick=this._createAddNodeToolbar.bind(this);var e=document.getElementById("network-manipulate-connectNode");if(e.onclick=this._createAddEdgeToolbar.bind(this),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit){var i=document.getElementById("network-manipulate-editNode");i.onclick=this._editNode.bind(this)}else if(1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()){var i=document.getElementById("network-manipulate-editEdge");i.onclick=this._createEditEdgeToolbar.bind(this)}if(0==this._selectionIsEmpty()){var s=document.getElementById("network-manipulate-delete");s.onclick=this._deleteSelected.bind(this)}var n=document.getElementById("network-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("network-manipulate-editModeButton");o.onclick=this._toggleEditMode.bind(this)}},e._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("network-manipulate-back");t.onclick=this._createManipulatorBar.bind(this),this.boundFunction=this._addNode.bind(this),this.on("select",this.boundFunction)},e._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("network-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()},e._createEditEdgeToolbar=function(){this._clearManipulatorBar(),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+"";var t=document.getElementById("network-manipulate-back");t.onclick=this._createManipulatorBar.bind(this),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,this._redraw()},e._selectControlNode=function(t){this.edgeBeingEdited.controlNodes.from.unselect(),this.edgeBeingEdited.controlNodes.to.unselect(),this.selectedControlNode=this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(t.x),this._YconvertDOMtoCanvas(t.y)),null!==this.selectedControlNode&&(this.selectedControlNode.select(),this.freezeSimulation=!0),this._redraw()},e._controlNodeDrag=function(t){var e=this._getPointer(t.gesture.center);null!==this.selectedControlNode&&void 0!==this.selectedControlNode&&(this.selectedControlNode.x=this._XconvertDOMtoCanvas(e.x),this.selectedControlNode.y=this._YconvertDOMtoCanvas(e.y)),this._redraw()},e._releaseControlNode=function(t){var e=this._getNodeAt(t);null!=e?(1==this.edgeBeingEdited.controlNodes.from.selected&&(this._editEdge(e.id,this.edgeBeingEdited.to.id),this.edgeBeingEdited.controlNodes.from.unselect()),1==this.edgeBeingEdited.controlNodes.to.selected&&(this._editEdge(this.edgeBeingEdited.from.id,e.id),this.edgeBeingEdited.controlNodes.to.unselect())):this.edgeBeingEdited._restoreControlNodes(),this.freezeSimulation=!1,this._redraw()},e._handleConnect=function(t){if(0==this._getSelectedNodeCount()){var e=this._getNodeAt(t);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()))}},e._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()}},e._addNode=function(){if(this._selectionIsEmpty()&&1==this.editMode){var t=this._pointerToPositionObject(this.pointerPosition),e={id:s.randomUUID(),x:t.left,y:t.top,label:"new",allowedToMoveX:!0,allowedToMoveY:!0};if(this.triggerFunctions.add)if(2==this.triggerFunctions.add.length){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()}},e._createEdge=function(t,e){if(1==this.editMode){var i={from:t,to:e};if(this.triggerFunctions.connect)if(2==this.triggerFunctions.connect.length){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()}},e._editEdge=function(t,e){if(1==this.editMode){var i={id:this.edgeBeingEdited.id,from:t,to:e};if(this.triggerFunctions.editEdge)if(2==this.triggerFunctions.editEdge.length){var s=this;this.triggerFunctions.editEdge(i,function(t){s.edgesData.update(t),s.moving=!0,s.start()})}else alert(this.constants.labels.linkError),this.moving=!0,this.start();else this.edgesData.update(i),this.moving=!0,this.start()}},e._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)},e._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()}}},function(t,e){e._cleanNavigation=function(){var t=document.getElementById("network-navigation_wrapper");null!=t&&this.containerElement.removeChild(t),document.onmouseup=null},e._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="network-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;i0){"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))}},e._setLevel=function(t,e,i){for(var s=0;st)&&(n.level=t,e.length>1&&this._setLevel(t+1,n.edges,n.id))}},e._restoreNodes=function(){for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.nodes[t].xFixed=!1,this.nodes[t].yFixed=!1)}},function(t){!function(e,i){"use strict";function s(){if(!n.READY){n.event.determineEventTypes();for(var t in n.gestures)n.gestures.hasOwnProperty(t)&&n.detection.register(n.gestures[t]);n.event.onTouch(n.DOCUMENT,n.EVENT_MOVE,n.detection.detect),n.event.onTouch(n.DOCUMENT,n.EVENT_END,n.detection.detect),n.READY=!0}}var n=function(t,e){return new n.Instance(t,e||{})};n.defaults={stop_browser_behavior:{userSelect:"none",touchAction:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}},n.HAS_POINTEREVENTS=navigator.pointerEnabled||navigator.msPointerEnabled,n.HAS_TOUCHEVENTS="ontouchstart"in e,n.MOBILE_REGEX=/mobile|tablet|ip(ad|hone|od)|android/i,n.NO_MOUSEEVENTS=n.HAS_TOUCHEVENTS&&navigator.userAgent.match(n.MOBILE_REGEX),n.EVENT_TYPES={},n.DIRECTION_DOWN="down",n.DIRECTION_LEFT="left",n.DIRECTION_UP="up",n.DIRECTION_RIGHT="right",n.POINTER_MOUSE="mouse",n.POINTER_TOUCH="touch",n.POINTER_PEN="pen",n.EVENT_START="start",n.EVENT_MOVE="move",n.EVENT_END="end",n.DOCUMENT=document,n.plugins={},n.READY=!1,n.Instance=function(t,e){var i=this;return s(),this.element=t,this.enabled=!0,this.options=n.utils.extend(n.utils.extend({},n.defaults),e||{}),this.options.stop_browser_behavior&&n.utils.stopDefaultBrowserBehavior(this.element,this.options.stop_browser_behavior),n.event.onTouch(t,n.EVENT_START,function(t){i.enabled&&n.detection.startDetect(i,t)}),this},n.Instance.prototype={on:function(t,e){for(var i=t.split(" "),s=0;s0&&e==n.EVENT_END?e=n.EVENT_MOVE:l||(e=n.EVENT_END),l||null===o?o=h:h=o,i.call(n.detection,s.collectEventData(t,e,h)),n.HAS_POINTEREVENTS&&e==n.EVENT_END&&(l=n.PointerEvent.updatePointer(e,h))),l||(o=null,a=!1,r=!1,n.PointerEvent.reset())}})},determineEventTypes:function(){var t;t=n.HAS_POINTEREVENTS?n.PointerEvent.getEvents():n.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],n.EVENT_TYPES[n.EVENT_START]=t[0],n.EVENT_TYPES[n.EVENT_MOVE]=t[1],n.EVENT_TYPES[n.EVENT_END]=t[2]},getTouchList:function(t){return n.HAS_POINTEREVENTS?n.PointerEvent.getTouchList():t.touches?t.touches:[{identifier:1,pageX:t.pageX,pageY:t.pageY,target:t.target}]},collectEventData:function(t,e,i){var s=this.getTouchList(i,e),o=n.POINTER_TOUCH;return(i.type.match(/mouse/)||n.PointerEvent.matchType(n.POINTER_MOUSE,i))&&(o=n.POINTER_MOUSE),{center:n.utils.getCenter(s),timeStamp:(new Date).getTime(),target:i.target,touches:s,eventType:e,pointerType:o,srcEvent:i,preventDefault:function(){this.srcEvent.preventManipulation&&this.srcEvent.preventManipulation(),this.srcEvent.preventDefault&&this.srcEvent.preventDefault()},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return n.detection.stopDetect()}}}},n.PointerEvent={pointers:{},getTouchList:function(){var t=this,e=[];return Object.keys(t.pointers).sort().forEach(function(i){e.push(t.pointers[i])}),e},updatePointer:function(t,e){return t==n.EVENT_END?this.pointers={}:(e.identifier=e.pointerId,this.pointers[e.pointerId]=e),Object.keys(this.pointers).length},matchType:function(t,e){if(!e.pointerType)return!1;var i={};return i[n.POINTER_MOUSE]=e.pointerType==e.MSPOINTER_TYPE_MOUSE||e.pointerType==n.POINTER_MOUSE,i[n.POINTER_TOUCH]=e.pointerType==e.MSPOINTER_TYPE_TOUCH||e.pointerType==n.POINTER_TOUCH,i[n.POINTER_PEN]=e.pointerType==e.MSPOINTER_TYPE_PEN||e.pointerType==n.POINTER_PEN,i[t]},getEvents:function(){return["pointerdown MSPointerDown","pointermove MSPointerMove","pointerup pointercancel MSPointerUp MSPointerCancel"]},reset:function(){this.pointers={}}},n.utils={extend:function(t,e,s){for(var n in e)t[n]!==i&&s||(t[n]=e[n]);return t},hasParent:function(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1},getCenter:function(t){for(var e=[],i=[],s=0,n=t.length;n>s;s++)e.push(t[s].pageX),i.push(t[s].pageY);return{pageX:(Math.min.apply(Math,e)+Math.max.apply(Math,e))/2,pageY:(Math.min.apply(Math,i)+Math.max.apply(Math,i))/2}},getVelocity:function(t,e,i){return{x:Math.abs(e/t)||0,y:Math.abs(i/t)||0}},getAngle:function(t,e){var i=e.pageY-t.pageY,s=e.pageX-t.pageX;return 180*Math.atan2(i,s)/Math.PI},getDirection:function(t,e){var i=Math.abs(t.pageX-e.pageX),s=Math.abs(t.pageY-e.pageY);return i>=s?t.pageX-e.pageX>0?n.DIRECTION_LEFT:n.DIRECTION_RIGHT:t.pageY-e.pageY>0?n.DIRECTION_UP:n.DIRECTION_DOWN},getDistance:function(t,e){var i=e.pageX-t.pageX,s=e.pageY-t.pageY;return Math.sqrt(i*i+s*s)},getScale:function(t,e){return t.length>=2&&e.length>=2?this.getDistance(e[0],e[1])/this.getDistance(t[0],t[1]):1},getRotation:function(t,e){return t.length>=2&&e.length>=2?this.getAngle(e[1],e[0])-this.getAngle(t[1],t[0]):0},isVertical:function(t){return t==n.DIRECTION_UP||t==n.DIRECTION_DOWN},stopDefaultBrowserBehavior:function(t,e){var i,s=["webkit","khtml","moz","ms","o",""];if(e&&t.style){for(var n=0;ni;i++){var o=this.gestures[i];if(!this.stopped&&e[o.name]!==!1&&o.handler.call(o,t,this.current.inst)===!1){this.stopDetect();break}}return this.current&&(this.current.lastEvent=t),t.eventType==n.EVENT_END&&!t.touches.length-1&&this.stopDetect(),t}},stopDetect:function(){this.previous=n.utils.extend({},this.current),this.current=null,this.stopped=!0},extendEventData:function(t){var e=this.current.startEvent;if(e&&(t.touches.length!=e.touches.length||t.touches===e.touches)){e.touches=[];for(var i=0,s=t.touches.length;s>i;i++)e.touches.push(n.utils.extend({},t.touches[i]))}var o=t.timeStamp-e.timeStamp,a=t.center.pageX-e.center.pageX,r=t.center.pageY-e.center.pageY,h=n.utils.getVelocity(o,a,r);return n.utils.extend(t,{deltaTime:o,deltaX:a,deltaY:r,velocityX:h.x,velocityY:h.y,distance:n.utils.getDistance(e.center,t.center),angle:n.utils.getAngle(e.center,t.center),direction:n.utils.getDirection(e.center,t.center),scale:n.utils.getScale(e.touches,t.touches),rotation:n.utils.getRotation(e.touches,t.touches),startEvent:e}),t},register:function(t){var e=t.defaults||{};return e[t.name]===i&&(e[t.name]=!0),n.utils.extend(n.defaults,e,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(t,e){return t.indexe.index?1:0}),this.gestures}},n.gestures=n.gestures||{},n.gestures.Hold={name:"hold",index:10,defaults:{hold_timeout:500,hold_threshold:1},timer:null,handler:function(t,e){switch(t.eventType){case n.EVENT_START:clearTimeout(this.timer),n.detection.current.name=this.name,this.timer=setTimeout(function(){"hold"==n.detection.current.name&&e.trigger("hold",t)},e.options.hold_timeout);break;case n.EVENT_MOVE:t.distance>e.options.hold_threshold&&clearTimeout(this.timer);break;case n.EVENT_END:clearTimeout(this.timer)}}},n.gestures.Tap={name:"tap",index:100,defaults:{tap_max_touchtime:250,tap_max_distance:10,tap_always:!0,doubletap_distance:20,doubletap_interval:300},handler:function(t,e){if(t.eventType==n.EVENT_END){var i=n.detection.previous,s=!1;if(t.deltaTime>e.options.tap_max_touchtime||t.distance>e.options.tap_max_distance)return;i&&"tap"==i.name&&t.timeStamp-i.lastEvent.timeStamp0&&t.touches.length>e.options.swipe_max_touches)return;(t.velocityX>e.options.swipe_velocity||t.velocityY>e.options.swipe_velocity)&&(e.trigger(this.name,t),e.trigger(this.name+t.direction,t))}}},n.gestures.Drag={name:"drag",index:50,defaults:{drag_min_distance:10,drag_max_touches:1,drag_block_horizontal:!1,drag_block_vertical:!1,drag_lock_to_axis:!1,drag_lock_min_distance:25},triggered:!1,handler:function(t,e){if(n.detection.current.name!=this.name&&this.triggered)return e.trigger(this.name+"end",t),void(this.triggered=!1);if(!(e.options.drag_max_touches>0&&t.touches.length>e.options.drag_max_touches))switch(t.eventType){case n.EVENT_START:this.triggered=!1;break;case n.EVENT_MOVE:if(t.distancee.options.transform_min_rotation&&e.trigger("rotate",t),i>e.options.transform_min_scale&&(e.trigger("pinch",t),e.trigger("pinch"+(t.scale<1?"in":"out"),t));break;case n.EVENT_END:this.triggered&&e.trigger(this.name+"end",t),this.triggered=!1}}},n.gestures.Touch={name:"touch",index:-1/0,defaults:{prevent_default:!1,prevent_mouseevents:!1},handler:function(t,e){return e.options.prevent_mouseevents&&t.pointerType==n.POINTER_MOUSE?void t.stopDetect():(e.options.prevent_default&&t.preventDefault(),void(t.eventType==n.EVENT_START&&e.trigger(this.name,t)))}},n.gestures.Release={name:"release",index:1/0,handler:function(t,e){t.eventType==n.EVENT_END&&e.trigger(this.name,t)}},"object"==typeof t&&"object"==typeof t.exports?t.exports=n:(e.Hammer=n,"function"==typeof e.define&&e.define.amd&&e.define("hammer",[],function(){return n}))}(this)},function(t,e,i){function s(){this.constants.smoothCurves=!this.constants.smoothCurves;var t=document.getElementById("graph_toggleSmooth");t.style.background=1==this.constants.smoothCurves?"#A4FF56":"#FF8532",this._configureSmoothCurves(!1)}function n(){for(var t in this.calculationNodes)this.calculationNodes.hasOwnProperty(t)&&(this.calculationNodes[t].vx=0,this.calculationNodes[t].vy=0,this.calculationNodes[t].fx=0,this.calculationNodes[t].fy=0);1==this.constants.hierarchicalLayout.enabled?this._setupHierarchicalLayout():this.repositionNodes(),this.moving=!0,this.start()}function o(){var t="No options are required, default values used.",e=[],i=document.getElementById("graph_physicsMethod1"),s=document.getElementById("graph_physicsMethod2");if(1==i.checked){if(this.constants.physics.barnesHut.gravitationalConstant!=this.backupConstants.physics.barnesHut.gravitationalConstant&&e.push("gravitationalConstant: "+this.constants.physics.barnesHut.gravitationalConstant),this.constants.physics.centralGravity!=this.backupConstants.physics.barnesHut.centralGravity&&e.push("centralGravity: "+this.constants.physics.centralGravity),this.constants.physics.springLength!=this.backupConstants.physics.barnesHut.springLength&&e.push("springLength: "+this.constants.physics.springLength),this.constants.physics.springConstant!=this.backupConstants.physics.barnesHut.springConstant&&e.push("springConstant: "+this.constants.physics.springConstant),this.constants.physics.damping!=this.backupConstants.physics.barnesHut.damping&&e.push("damping: "+this.constants.physics.damping),0!=e.length){t="var options = {",t+="physics: {barnesHut: {";for(var n=0;nthis.constants.clustering.clusterThreshold&&1==this.constants.clustering.enabled&&this.clusterToFit(this.constants.clustering.reduceToNodes,!1),this._calculateForces())},e._calculateForces=function(){this._calculateGravitationalForces(),this._calculateNodeForces(),1==this.constants.smoothCurves?this._calculateSpringForcesWithSupport():1==this.constants.physics.hierarchicalRepulsion.enabled?this._calculateHierarchicalSpringForces():this._calculateSpringForces()},e._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},e._calculateGravitationalForces=function(){var t,e,i,s,n,o=this.calculationNodes,a=this.constants.physics.centralGravity,r=0;for(n=0;nSimulation Mode:Barnes HutRepulsionHierarchical
Options:
',this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement),this.optionsDiv=document.createElement("div"),this.optionsDiv.style.fontSize="14px",this.optionsDiv.style.fontFamily="verdana",this.containerElement.parentElement.insertBefore(this.optionsDiv,this.containerElement);var e;e=document.getElementById("graph_BH_gc"),e.onchange=r.bind(this,"graph_BH_gc",-1,"physics_barnesHut_gravitationalConstant"),e=document.getElementById("graph_BH_cg"),e.onchange=r.bind(this,"graph_BH_cg",1,"physics_centralGravity"),e=document.getElementById("graph_BH_sc"),e.onchange=r.bind(this,"graph_BH_sc",1,"physics_springConstant"),e=document.getElementById("graph_BH_sl"),e.onchange=r.bind(this,"graph_BH_sl",1,"physics_springLength"),e=document.getElementById("graph_BH_damp"),e.onchange=r.bind(this,"graph_BH_damp",1,"physics_damping"),e=document.getElementById("graph_R_nd"),e.onchange=r.bind(this,"graph_R_nd",1,"physics_repulsion_nodeDistance"),e=document.getElementById("graph_R_cg"),e.onchange=r.bind(this,"graph_R_cg",1,"physics_centralGravity"),e=document.getElementById("graph_R_sc"),e.onchange=r.bind(this,"graph_R_sc",1,"physics_springConstant"),e=document.getElementById("graph_R_sl"),e.onchange=r.bind(this,"graph_R_sl",1,"physics_springLength"),e=document.getElementById("graph_R_damp"),e.onchange=r.bind(this,"graph_R_damp",1,"physics_damping"),e=document.getElementById("graph_H_nd"),e.onchange=r.bind(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),e=document.getElementById("graph_H_cg"),e.onchange=r.bind(this,"graph_H_cg",1,"physics_centralGravity"),e=document.getElementById("graph_H_sc"),e.onchange=r.bind(this,"graph_H_sc",1,"physics_springConstant"),e=document.getElementById("graph_H_sl"),e.onchange=r.bind(this,"graph_H_sl",1,"physics_springLength"),e=document.getElementById("graph_H_damp"),e.onchange=r.bind(this,"graph_H_damp",1,"physics_damping"),e=document.getElementById("graph_H_direction"),e.onchange=r.bind(this,"graph_H_direction",t,"hierarchicalLayout_direction"),e=document.getElementById("graph_H_levsep"),e.onchange=r.bind(this,"graph_H_levsep",1,"hierarchicalLayout_levelSeparation"),e=document.getElementById("graph_H_nspac"),e.onchange=r.bind(this,"graph_H_nspac",1,"hierarchicalLayout_nodeSpacing");var i=document.getElementById("graph_physicsMethod1"),d=document.getElementById("graph_physicsMethod2"),l=document.getElementById("graph_physicsMethod3");d.checked=!0,this.constants.physics.barnesHut.enabled&&(i.checked=!0),this.constants.hierarchicalLayout.enabled&&(l.checked=!0);var c=document.getElementById("graph_toggleSmooth"),u=document.getElementById("graph_repositionNodes"),p=document.getElementById("graph_generateOptions");c.onclick=s.bind(this),u.onclick=n.bind(this),p.onclick=o.bind(this),c.style.background=1==this.constants.smoothCurves?"#A4FF56":"#FF8532",a.apply(this),i.onchange=a.bind(this),d.onchange=a.bind(this),l.onchange=a.bind(this)}},e._overWriteGraphConstants=function(t,e){var i=t.split("_");1==i.length?this.constants[i[0]]=e:2==i.length?this.constants[i[0]][i[1]]=e:3==i.length&&(this.constants[i[0]][i[1]][i[2]]=e)}},function(t,e,i){var s;(function(t,n){(function(o){function a(t,e,i){switch(arguments.length){case 2:return null!=t?t:e;case 3:return null!=t?t:null!=e?e:i;default:throw new Error("Implement me")}}function r(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function h(t,e){function i(){ge.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}var s=!0;return m(function(){return s&&(i(),s=!1),e.apply(this,arguments)},e)}function d(t,e){return function(i){return g(t.call(this,i),e)}}function l(t,e){return function(i){return this.lang().ordinal(t.call(this,i),e)}}function c(){}function u(t){E(t),m(this,t)}function p(t){var e=L(t),i=e.year||0,s=e.quarter||0,n=e.month||0,o=e.week||0,a=e.day||0,r=e.hour||0,h=e.minute||0,d=e.second||0,l=e.millisecond||0;this._milliseconds=+l+1e3*d+6e4*h+36e5*r,this._days=+a+7*o,this._months=+n+3*s+12*i,this._data={},this._bubble()}function m(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return e.hasOwnProperty("toString")&&(t.toString=e.toString),e.hasOwnProperty("valueOf")&&(t.valueOf=e.valueOf),t}function _(t){var e,i={};for(e in t)t.hasOwnProperty(e)&&Ce.hasOwnProperty(e)&&(i[e]=t[e]);return i}function f(t){return 0>t?Math.ceil(t):Math.floor(t)}function g(t,e,i){for(var s=""+Math.abs(t),n=t>=0;s.lengths;s++)(i&&t[s]!==e[s]||!i&&D(t[s])!==D(e[s]))&&a++;return a+o}function w(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=ai[t]||ri[e]||e}return t}function L(t){var e,i,s={};for(i in t)t.hasOwnProperty(i)&&(e=w(i),e&&(s[e]=t[i]));return s}function x(t){var e,i;if(0===t.indexOf("week"))e=7,i="day";else{if(0!==t.indexOf("month"))return;e=12,i="month"}ge[t]=function(s,n){var a,r,h=ge.fn._lang[t],d=[];if("number"==typeof s&&(n=s,s=o),r=function(t){var e=ge().utc().set(i,t);return h.call(ge.fn._lang,e,s||"")},null!=n)return r(n);for(a=0;e>a;a++)d.push(r(a));return d}}function D(t){var e=+t,i=0;return 0!==e&&isFinite(e)&&(i=e>=0?Math.floor(e):Math.ceil(e)),i}function T(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function Y(t,e,i){return ae(ge([t,11,31+e-i]),e,i).week}function S(t){return k(t)?366:365}function k(t){return t%4===0&&t%100!==0||t%400===0}function E(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[xe]<0||t._a[xe]>11?xe:t._a[De]<1||t._a[De]>T(t._a[Le],t._a[xe])?De:t._a[Te]<0||t._a[Te]>23?Te:t._a[Ye]<0||t._a[Ye]>59?Ye:t._a[Se]<0||t._a[Se]>59?Se:t._a[ke]<0||t._a[ke]>999?ke:-1,t._pf._overflowDayOfYear&&(Le>e||e>De)&&(e=De),t._pf.overflow=e)}function C(t){return null==t._isValid&&(t._isValid=!isNaN(t._d.getTime())&&t._pf.overflow<0&&!t._pf.empty&&!t._pf.invalidMonth&&!t._pf.nullInput&&!t._pf.invalidFormat&&!t._pf.userInvalidated,t._strict&&(t._isValid=t._isValid&&0===t._pf.charsLeftOver&&0===t._pf.unusedTokens.length)),t._isValid}function N(t){return t?t.toLowerCase().replace("_","-"):t}function O(t,e){return e._isUTC?ge(t).zone(e._offset||0):ge(t).local()}function z(t,e){return e.abbr=t,Ee[t]||(Ee[t]=new c),Ee[t].set(e),Ee[t]}function I(t){delete Ee[t]}function A(t){var e,s,n,o,a=0,r=function(t){if(!Ee[t]&&Ne)try{i(55)("./"+t)}catch(e){}return Ee[t]};if(!t)return ge.fn._lang;if(!v(t)){if(s=r(t))return s;t=[t]}for(;a0;){if(s=r(o.slice(0,e).join("-")))return s;if(n&&n.length>=e&&b(o,n,!0)>=e-1)break;e--}a++}return ge.fn._lang}function P(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function F(t){var e,i,s=t.match(Ae);for(e=0,i=s.length;i>e;e++)s[e]=ui[s[e]]?ui[s[e]]:P(s[e]);return function(n){var o="";for(e=0;i>e;e++)o+=s[e]instanceof Function?s[e].call(n,t):s[e];return o}}function j(t,e){return t.isValid()?(e=H(e,t.lang()),hi[e]||(hi[e]=F(e)),hi[e](t)):t.lang().invalidDate()}function H(t,e){function i(t){return e.longDateFormat(t)||t}var s=5;for(Pe.lastIndex=0;s>=0&&Pe.test(t);)t=t.replace(Pe,i),Pe.lastIndex=0,s-=1;return t}function R(t,e){var i,s=e._strict;switch(t){case"Q":return qe;case"DDDD":return Ze;case"YYYY":case"GGGG":case"gggg":return s?Ke:He;case"Y":case"G":case"g":return Qe;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return s?$e:Re;case"S":if(s)return qe;case"SS":if(s)return Je;case"SSS":if(s)return Ze;case"DDD":return je;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Be;case"a":case"A":return A(e._l)._meridiemParse;case"X":return Ue;case"Z":case"ZZ":return Ge;case"T":return Ve;case"SSSS":return We;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return s?Je:Fe;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Fe;case"Do":return Xe;default:return i=new RegExp(Z(J(t.replace("\\","")),"i"))}}function W(t){t=t||"";var e=t.match(Ge)||[],i=e[e.length-1]||[],s=(i+"").match(ni)||["-",0,0],n=+(60*s[1])+D(s[2]);return"+"===s[0]?-n:n}function B(t,e,i){var s,n=i._a;switch(t){case"Q":null!=e&&(n[xe]=3*(D(e)-1));break;case"M":case"MM":null!=e&&(n[xe]=D(e)-1);break;case"MMM":case"MMMM":s=A(i._l).monthsParse(e),null!=s?n[xe]=s:i._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(n[De]=D(e));break;case"Do":null!=e&&(n[De]=D(parseInt(e,10)));break;case"DDD":case"DDDD":null!=e&&(i._dayOfYear=D(e));break;case"YY":n[Le]=ge.parseTwoDigitYear(e);break;case"YYYY":case"YYYYY":case"YYYYYY":n[Le]=D(e);break;case"a":case"A":i._isPm=A(i._l).isPM(e);break;case"H":case"HH":case"h":case"hh":n[Te]=D(e);break;case"m":case"mm":n[Ye]=D(e);break;case"s":case"ss":n[Se]=D(e);break;case"S":case"SS":case"SSS":case"SSSS":n[ke]=D(1e3*("0."+e));break;case"X":i._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":i._useUTC=!0,i._tzm=W(e);break;case"dd":case"ddd":case"dddd":s=A(i._l).weekdaysParse(e),null!=s?(i._w=i._w||{},i._w.d=s):i._pf.invalidWeekday=e;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":t=t.substr(0,1);case"gggg":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(i._w=i._w||{},i._w[t]=D(e));break;case"gg":case"GG":i._w=i._w||{},i._w[t]=ge.parseTwoDigitYear(e)}}function G(t){var e,i,s,n,o,r,h,d;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(o=1,r=4,i=a(e.GG,t._a[Le],ae(ge(),1,4).year),s=a(e.W,1),n=a(e.E,1)):(d=A(t._l),o=d._week.dow,r=d._week.doy,i=a(e.gg,t._a[Le],ae(ge(),o,r).year),s=a(e.w,1),null!=e.d?(n=e.d,o>n&&++s):n=null!=e.e?e.e+o:o),h=re(i,s,n,r,o),t._a[Le]=h.year,t._dayOfYear=h.dayOfYear}function V(t){var e,i,s,n,o=[];if(!t._d){for(s=X(t),t._w&&null==t._a[De]&&null==t._a[xe]&&G(t),t._dayOfYear&&(n=a(t._a[Le],s[Le]),t._dayOfYear>S(n)&&(t._pf._overflowDayOfYear=!0),i=ie(n,0,t._dayOfYear),t._a[xe]=i.getUTCMonth(),t._a[De]=i.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=o[e]=s[e];for(;7>e;e++)t._a[e]=o[e]=null==t._a[e]?2===e?1:0:t._a[e];t._d=(t._useUTC?ie:ee).apply(null,o),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()+t._tzm)}}function U(t){var e;t._d||(e=L(t._i),t._a=[e.year,e.month,e.day,e.hour,e.minute,e.second,e.millisecond],V(t))}function X(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function q(t){if(t._f===ge.ISO_8601)return void $(t);t._a=[],t._pf.empty=!0;var e,i,s,n,o,a=A(t._l),r=""+t._i,h=r.length,d=0;for(s=H(t._f,a).match(Ae)||[],e=0;e0&&t._pf.unusedInput.push(o),r=r.slice(r.indexOf(i)+i.length),d+=i.length),ui[n]?(i?t._pf.empty=!1:t._pf.unusedTokens.push(n),B(n,i,t)):t._strict&&!i&&t._pf.unusedTokens.push(n);t._pf.charsLeftOver=h-d,r.length>0&&t._pf.unusedInput.push(r),t._isPm&&t._a[Te]<12&&(t._a[Te]+=12),t._isPm===!1&&12===t._a[Te]&&(t._a[Te]=0),V(t),E(t)}function J(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,i,s,n){return e||i||s||n})}function Z(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function K(t){var e,i,s,n,o;if(0===t._f.length)return t._pf.invalidFormat=!0,void(t._d=new Date(0/0));for(n=0;no)&&(s=o,i=e));m(t,i||e)}function $(t){var e,i,s=t._i,n=ti.exec(s);if(n){for(t._pf.iso=!0,e=0,i=ii.length;i>e;e++)if(ii[e][1].exec(s)){t._f=ii[e][0]+(n[6]||" ");break}for(e=0,i=si.length;i>e;e++)if(si[e][1].exec(s)){t._f+=si[e][0];break}s.match(Ge)&&(t._f+="Z"),q(t)}else t._isValid=!1}function Q(t){$(t),t._isValid===!1&&(delete t._isValid,ge.createFromInputFallback(t))}function te(t){var e=t._i,i=Oe.exec(e);e===o?t._d=new Date:i?t._d=new Date(+i[1]):"string"==typeof e?Q(t):v(e)?(t._a=e.slice(0),V(t)):M(e)?t._d=new Date(+e):"object"==typeof e?U(t):"number"==typeof e?t._d=new Date(e):ge.createFromInputFallback(t)}function ee(t,e,i,s,n,o,a){var r=new Date(t,e,i,s,n,o,a);return 1970>t&&r.setFullYear(t),r}function ie(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function se(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function ne(t,e,i,s,n){return n.relativeTime(e||1,!!i,t,s)}function oe(t,e,i){var s=we(Math.abs(t)/1e3),n=we(s/60),o=we(n/60),a=we(o/24),r=we(a/365),h=s0,h[4]=i,ne.apply({},h)}function ae(t,e,i){var s,n=i-e,o=i-t.day();return o>n&&(o-=7),n-7>o&&(o+=7),s=ge(t).add("d",o),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function re(t,e,i,s,n){var o,a,r=ie(t,0,1).getUTCDay();return r=0===r?7:r,i=null!=i?i:n,o=n-r+(r>s?7:0)-(n>r?7:0),a=7*(e-1)+(i-n)+o+1,{year:a>0?t:t-1,dayOfYear:a>0?a:S(t-1)+a}}function he(t){var e=t._i,i=t._f;return null===e||i===o&&""===e?ge.invalid({nullInput:!0}):("string"==typeof e&&(t._i=e=A().preparse(e)),ge.isMoment(e)?(t=_(e),t._d=new Date(+e._d)):i?v(i)?K(t):q(t):te(t),new u(t))}function de(t,e){var i,s;if(1===e.length&&v(e[0])&&(e=e[0]),!e.length)return ge();for(i=e[0],s=1;s=0?"+":"-";return e+g(Math.abs(t),6)},gg:function(){return g(this.weekYear()%100,2)},gggg:function(){return g(this.weekYear(),4)},ggggg:function(){return g(this.weekYear(),5)},GG:function(){return g(this.isoWeekYear()%100,2)},GGGG:function(){return g(this.isoWeekYear(),4)},GGGGG:function(){return g(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return D(this.milliseconds()/100)},SS:function(){return g(D(this.milliseconds()/10),2)},SSS:function(){return g(this.milliseconds(),3)},SSSS:function(){return g(this.milliseconds(),3)},Z:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+g(D(t/60),2)+":"+g(D(t)%60,2)},ZZ:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+g(D(t/60),2)+g(D(t)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},pi=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];li.length;)ve=li.pop(),ui[ve+"o"]=l(ui[ve],ve);for(;ci.length;)ve=ci.pop(),ui[ve+ve]=d(ui[ve],2);for(ui.DDDD=d(ui.DDD,3),m(c.prototype,{set:function(t){var e,i;for(i in t)e=t[i],"function"==typeof e?this[i]=e:this["_"+i]=e},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t){var e,i,s;for(this._monthsParse||(this._monthsParse=[]),e=0;12>e;e++)if(this._monthsParse[e]||(i=ge.utc([2e3,e]),s="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[e]=new RegExp(s.replace(".",""),"i")),this._monthsParse[e].test(t))return e},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()] +},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,i,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(i=ge([2e3,1]).day(e),s="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e){var i=this._calendar[t];return"function"==typeof i?i.apply(e):i},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,i,s){var n=this._relativeTime[i];return"function"==typeof n?n(t,e,i,s):n.replace(/%d/i,t)},pastFuture:function(t,e){var i=this._relativeTime[t>0?"future":"past"];return"function"==typeof i?i(e):i.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",preparse:function(t){return t},postformat:function(t){return t},week:function(t){return ae(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),ge=function(t,e,i,s){var n;return"boolean"==typeof i&&(s=i,i=o),n={},n._isAMomentObject=!0,n._i=t,n._f=e,n._l=i,n._strict=s,n._isUTC=!1,n._pf=r(),he(n)},ge.suppressDeprecationWarnings=!1,ge.createFromInputFallback=h("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i)}),ge.min=function(){var t=[].slice.call(arguments,0);return de("isBefore",t)},ge.max=function(){var t=[].slice.call(arguments,0);return de("isAfter",t)},ge.utc=function(t,e,i,s){var n;return"boolean"==typeof i&&(s=i,i=o),n={},n._isAMomentObject=!0,n._useUTC=!0,n._isUTC=!0,n._l=i,n._i=t,n._f=e,n._strict=s,n._pf=r(),he(n).utc()},ge.unix=function(t){return ge(1e3*t)},ge.duration=function(t,e){var i,s,n,o=t,a=null;return ge.isDuration(t)?o={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(o={},e?o[e]=t:o.milliseconds=t):(a=ze.exec(t))?(i="-"===a[1]?-1:1,o={y:0,d:D(a[De])*i,h:D(a[Te])*i,m:D(a[Ye])*i,s:D(a[Se])*i,ms:D(a[ke])*i}):(a=Ie.exec(t))&&(i="-"===a[1]?-1:1,n=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*i},o={y:n(a[2]),M:n(a[3]),d:n(a[4]),h:n(a[5]),m:n(a[6]),s:n(a[7]),w:n(a[8])}),s=new p(o),ge.isDuration(t)&&t.hasOwnProperty("_lang")&&(s._lang=t._lang),s},ge.version=Me,ge.defaultFormat=ei,ge.ISO_8601=function(){},ge.momentProperties=Ce,ge.updateOffset=function(){},ge.relativeTimeThreshold=function(t,e){return di[t]===o?!1:(di[t]=e,!0)},ge.lang=function(t,e){var i;return t?(e?z(N(t),e):null===e?(I(t),t="en"):Ee[t]||A(t),i=ge.duration.fn._lang=ge.fn._lang=A(t),i._abbr):ge.fn._lang._abbr},ge.langData=function(t){return t&&t._lang&&t._lang._abbr&&(t=t._lang._abbr),A(t)},ge.isMoment=function(t){return t instanceof u||null!=t&&t.hasOwnProperty("_isAMomentObject")},ge.isDuration=function(t){return t instanceof p},ve=pi.length-1;ve>=0;--ve)x(pi[ve]);ge.normalizeUnits=function(t){return w(t)},ge.invalid=function(t){var e=ge.utc(0/0);return null!=t?m(e._pf,t):e._pf.userInvalidated=!0,e},ge.parseZone=function(){return ge.apply(null,arguments).parseZone()},ge.parseTwoDigitYear=function(t){return D(t)+(D(t)>68?1900:2e3)},m(ge.fn=u.prototype,{clone:function(){return ge(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var t=ge(this).utc();return 00:!1},parsingFlags:function(){return m({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(t){var e=j(this,t||ge.defaultFormat);return this.lang().postformat(e)},add:function(t,e){var i;return i="string"==typeof t&&"string"==typeof e?ge.duration(isNaN(+e)?+t:+e,isNaN(+e)?e:t):"string"==typeof t?ge.duration(+e,t):ge.duration(t,e),y(this,i,1),this},subtract:function(t,e){var i;return i="string"==typeof t&&"string"==typeof e?ge.duration(isNaN(+e)?+t:+e,isNaN(+e)?e:t):"string"==typeof t?ge.duration(+e,t):ge.duration(t,e),y(this,i,-1),this},diff:function(t,e,i){var s,n,o=O(t,this),a=6e4*(this.zone()-o.zone());return e=w(e),"year"===e||"month"===e?(s=432e5*(this.daysInMonth()+o.daysInMonth()),n=12*(this.year()-o.year())+(this.month()-o.month()),n+=(this-ge(this).startOf("month")-(o-ge(o).startOf("month")))/s,n-=6e4*(this.zone()-ge(this).startOf("month").zone()-(o.zone()-ge(o).startOf("month").zone()))/s,"year"===e&&(n/=12)):(s=this-o,n="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-a)/864e5:"week"===e?(s-a)/6048e5:s),i?n:f(n)},from:function(t,e){return ge.duration(this.diff(t)).lang(this.lang()._abbr).humanize(!e)},fromNow:function(t){return this.from(ge(),t)},calendar:function(t){var e=t||ge(),i=O(e,this).startOf("day"),s=this.diff(i,"days",!0),n=-6>s?"sameElse":-1>s?"lastWeek":0>s?"lastDay":1>s?"sameDay":2>s?"nextDay":7>s?"nextWeek":"sameElse";return this.format(this.lang().calendar(n,this))},isLeapYear:function(){return k(this.year())},isDST:function(){return this.zone()+ge(t).startOf(e)},isBefore:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)<+ge(t).startOf(e)},isSame:function(t,e){return e=e||"ms",+this.clone().startOf(e)===+O(t,this).startOf(e)},min:h("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(t){return t=ge.apply(null,arguments),this>t?this:t}),max:h("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(t){return t=ge.apply(null,arguments),t>this?this:t}),zone:function(t,e){var i=this._offset||0;return null==t?this._isUTC?i:this._d.getTimezoneOffset():("string"==typeof t&&(t=W(t)),Math.abs(t)<16&&(t=60*t),this._offset=t,this._isUTC=!0,i!==t&&(!e||this._changeInProgress?y(this,ge.duration(i-t,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,ge.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(t){return t=t?ge(t).zone():0,(this.zone()-t)%60===0},daysInMonth:function(){return T(this.year(),this.month())},dayOfYear:function(t){var e=we((ge(this).startOf("day")-ge(this).startOf("year"))/864e5)+1;return null==t?e:this.add("d",t-e)},quarter:function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},weekYear:function(t){var e=ae(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==t?e:this.add("y",t-e)},isoWeekYear:function(t){var e=ae(this,1,4).year;return null==t?e:this.add("y",t-e)},week:function(t){var e=this.lang().week(this);return null==t?e:this.add("d",7*(t-e))},isoWeek:function(t){var e=ae(this,1,4).week;return null==t?e:this.add("d",7*(t-e))},weekday:function(t){var e=(this.day()+7-this.lang()._week.dow)%7;return null==t?e:this.add("d",t-e)},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},isoWeeksInYear:function(){return Y(this.year(),1,4)},weeksInYear:function(){var t=this._lang._week;return Y(this.year(),t.dow,t.doy)},get:function(t){return t=w(t),this[t]()},set:function(t,e){return t=w(t),"function"==typeof this[t]&&this[t](e),this},lang:function(t){return t===o?this._lang:(this._lang=A(t),this)}}),ge.fn.millisecond=ge.fn.milliseconds=pe("Milliseconds",!1),ge.fn.second=ge.fn.seconds=pe("Seconds",!1),ge.fn.minute=ge.fn.minutes=pe("Minutes",!1),ge.fn.hour=ge.fn.hours=pe("Hours",!0),ge.fn.date=pe("Date",!0),ge.fn.dates=h("dates accessor is deprecated. Use date instead.",pe("Date",!0)),ge.fn.year=pe("FullYear",!0),ge.fn.years=h("years accessor is deprecated. Use year instead.",pe("FullYear",!0)),ge.fn.days=ge.fn.day,ge.fn.months=ge.fn.month,ge.fn.weeks=ge.fn.week,ge.fn.isoWeeks=ge.fn.isoWeek,ge.fn.quarters=ge.fn.quarter,ge.fn.toJSON=ge.fn.toISOString,m(ge.duration.fn=p.prototype,{_bubble:function(){var t,e,i,s,n=this._milliseconds,o=this._days,a=this._months,r=this._data;r.milliseconds=n%1e3,t=f(n/1e3),r.seconds=t%60,e=f(t/60),r.minutes=e%60,i=f(e/60),r.hours=i%24,o+=f(i/24),r.days=o%30,a+=f(o/30),r.months=a%12,s=f(a/12),r.years=s},weeks:function(){return f(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*D(this._months/12)},humanize:function(t){var e=+this,i=oe(e,!t,this.lang());return t&&(i=this.lang().pastFuture(e,i)),this.lang().postformat(i)},add:function(t,e){var i=ge.duration(t,e);return this._milliseconds+=i._milliseconds,this._days+=i._days,this._months+=i._months,this._bubble(),this},subtract:function(t,e){var i=ge.duration(t,e);return this._milliseconds-=i._milliseconds,this._days-=i._days,this._months-=i._months,this._bubble(),this},get:function(t){return t=w(t),this[t.toLowerCase()+"s"]()},as:function(t){return t=w(t),this["as"+t.charAt(0).toUpperCase()+t.slice(1)+"s"]()},lang:ge.fn.lang,toIsoString:function(){var t=Math.abs(this.years()),e=Math.abs(this.months()),i=Math.abs(this.days()),s=Math.abs(this.hours()),n=Math.abs(this.minutes()),o=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(t?t+"Y":"")+(e?e+"M":"")+(i?i+"D":"")+(s||n||o?"T":"")+(s?s+"H":"")+(n?n+"M":"")+(o?o+"S":""):"P0D"}});for(ve in oi)oi.hasOwnProperty(ve)&&(_e(ve,oi[ve]),me(ve.toLowerCase()));_e("Weeks",6048e5),ge.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},ge.lang("en",{ordinal:function(t){var e=t%10,i=1===D(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i}}),Ne?n.exports=ge:(s=function(t,e,i){return i.config&&i.config()&&i.config().noGlobal===!0&&(be.moment=ye),ge}.call(e,i,e,n),!(s!==o&&(n.exports=s)),fe(!0))}).call(this)}).call(e,function(){return this}(),i(130)(t))},function(t,e){e._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,_=this.constants.physics.repulsion.nodeDistance,f=_;for(d=0;di&&(a=.5*f>i?1:g*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)}}},function(t,e){e._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,_=m,f=p/_;for(h=0;hi)){o=f*i+u;var g=.05,y=2*_*2*g;o=g*Math.pow(i,2)-y*i+y*y/(4*g),0==i?i=.01:o/=i,s=t*o,n=e*o,a.fx-=s,a.fy-=n,r.fx+=s,r.fy+=n}},e._calculateHierarchicalSpringForces=function(){var t,e,i,s,n,o,a,r,h,d=this.edges;for(i in d)if(d.hasOwnProperty(i)&&(e=d[i],e.connected&&this.nodes.hasOwnProperty(e.toId)&&this.nodes.hasOwnProperty(e.fromId))){t=e.customLength?e.length:this.constants.physics.springLength,t+=(e.to.clusterSize+e.from.clusterSize-2)*this.constants.clustering.edgeGrowth,s=e.from.x-e.to.x,n=e.from.y-e.to.y,h=Math.sqrt(s*s+n*n),0==h&&(h=.01),h=Math.max(.8*t,Math.min(5*t,h)),r=this.constants.physics.springConstant*(t-h)/h,o=s*r,a=n*r,e.to.fx-=o,e.to.fy-=a,e.from.fx+=o,e.from.fy+=a;var l=5;h>t&&(l=25),e.from.level>e.to.level?(e.to.fx-=l*o,e.to.fy-=l*a):e.from.levelo;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)}},e._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}}},e._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,_=.5*(n+a),f=.5*(o+r),g={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:_-m,maxX:_+m,minY:f-m,maxY:f+m},size:p,calcSize:1/p,children:{data:null},maxWidth:0,level:0,childrenCount:4}};for(this._splitBranch(g.root),h=0;s>h;h++)i=t[e[h]],this._placeInTree(g.root,i);this.barnesHutTree=g},e._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")},e._placeInRegion=function(t,e,i){switch(t.children[i].childrenCount){case 0:t.children[i].children.data=e,t.children[i].childrenCount=1,this._updateBranchMass(t.children[i],e);break;case 1:t.children[i].children.data.x==e.x&&t.children[i].children.data.y==e.y?(e.x+=Math.random(),e.y+=Math.random()):(this._splitBranch(t.children[i]),this._placeInTree(t.children[i],e));break;case 4:this._placeInTree(t.children[i],e)}},e._splitBranch=function(t){var e=null;1==t.childrenCount&&(e=t.children.data,t.mass=0,t.centerOfMass.x=0,t.centerOfMass.y=0),t.childrenCount=4,t.children.data=null,this._insertRegion(t,"NW"),this._insertRegion(t,"NE"),this._insertRegion(t,"SW"),this._insertRegion(t,"SE"),null!=e&&this._placeInTree(t,e)},e._insertRegion=function(t,e){var i,s,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}},e._drawTree=function(t,e){void 0!==this.barnesHutTree&&(t.lineWidth=1,this._drawBranch(this.barnesHutTree.root,t,e))},e._drawBranch=function(t,e,i){void 0===i&&(i="#FF0000"),4==t.childrenCount&&(this._drawBranch(t.children.NW,e),this._drawBranch(t.children.NE,e),this._drawBranch(t.children.SE,e),this._drawBranch(t.children.SW,e)),e.strokeStyle=i,e.beginPath(),e.moveTo(t.range.minX,t.range.minY),e.lineTo(t.range.maxX,t.range.minY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.minY),e.lineTo(t.range.maxX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.maxY),e.lineTo(t.range.minX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.minX,t.range.maxY),e.lineTo(t.range.minX,t.range.minY),e.stroke()}},function(t,e,i){function s(t){return i(n(t))}function n(t){return o[t]||function(){throw new Error("Cannot find module '"+t+"'.")}()}var o={"./ar":58,"./ar-ma":56,"./ar-ma.js":56,"./ar-sa":57,"./ar-sa.js":57,"./ar.js":58,"./az":59,"./az.js":59,"./bg":60,"./bg.js":60,"./bn":61,"./bn.js":61,"./br":62,"./br.js":62,"./bs":63,"./bs.js":63,"./ca":64,"./ca.js":64,"./cs":65,"./cs.js":65,"./cv":66,"./cv.js":66,"./cy":67,"./cy.js":67,"./da":68,"./da.js":68,"./de":70,"./de-at":69,"./de-at.js":69,"./de.js":70,"./el":71,"./el.js":71,"./en-au":72,"./en-au.js":72,"./en-ca":73,"./en-ca.js":73,"./en-gb":74,"./en-gb.js":74,"./eo":75,"./eo.js":75,"./es":76,"./es.js":76,"./et":77,"./et.js":77,"./eu":78,"./eu.js":78,"./fa":79,"./fa.js":79,"./fi":80,"./fi.js":80,"./fo":81,"./fo.js":81,"./fr":83,"./fr-ca":82,"./fr-ca.js":82,"./fr.js":83,"./gl":84,"./gl.js":84,"./he":85,"./he.js":85,"./hi":86,"./hi.js":86,"./hr":87,"./hr.js":87,"./hu":88,"./hu.js":88,"./hy-am":89,"./hy-am.js":89,"./id":90,"./id.js":90,"./is":91,"./is.js":91,"./it":92,"./it.js":92,"./ja":93,"./ja.js":93,"./ka":94,"./ka.js":94,"./km":95,"./km.js":95,"./ko":96,"./ko.js":96,"./lb":97,"./lb.js":97,"./lt":98,"./lt.js":98,"./lv":99,"./lv.js":99,"./mk":100,"./mk.js":100,"./ml":101,"./ml.js":101,"./mr":102,"./mr.js":102,"./ms-my":103,"./ms-my.js":103,"./nb":104,"./nb.js":104,"./ne":105,"./ne.js":105,"./nl":106,"./nl.js":106,"./nn":107,"./nn.js":107,"./pl":108,"./pl.js":108,"./pt":110,"./pt-br":109,"./pt-br.js":109,"./pt.js":110,"./ro":111,"./ro.js":111,"./ru":112,"./ru.js":112,"./sk":113,"./sk.js":113,"./sl":114,"./sl.js":114,"./sq":115,"./sq.js":115,"./sr":117,"./sr-cyrl":116,"./sr-cyrl.js":116,"./sr.js":117,"./sv":118,"./sv.js":118,"./ta":119,"./ta.js":119,"./th":120,"./th.js":120,"./tl-ph":121,"./tl-ph.js":121,"./tr":122,"./tr.js":122,"./tzm":124,"./tzm-latn":123,"./tzm-latn.js":123,"./tzm.js":124,"./uk":125,"./uk.js":125,"./uz":126,"./uz.js":126,"./vi":127,"./vi.js":127,"./zh-cn":128,"./zh-cn.js":128,"./zh-tw":129,"./zh-tw.js":129};s.keys=function(){return Object.keys(o)},s.resolve=n,t.exports=s},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("ar-ma",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:6,doy:12}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},i={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};return t.lang("ar-sa",{months:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},meridiem:function(t){return 12>t?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(t){return t.replace(/[۰-۹]/g,function(t){return i[t]}).replace(/،/g,",")},postformat:function(t){return t.replace(/\d/g,function(t){return e[t]}).replace(/,/g,"،")},week:{dow:6,doy:12}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},i={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};return t.lang("ar",{months:"يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"),monthsShort:"يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},meridiem:function(t){return 12>t?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(t){return t.replace(/[۰-۹]/g,function(t){return i[t]}).replace(/،/g,",")},postformat:function(t){return t.replace(/\d/g,function(t){return e[t]}).replace(/,/g,"،")},week:{dow:6,doy:12}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"-inci",5:"-inci",8:"-inci",70:"-inci",80:"-inci",2:"-nci",7:"-nci",20:"-nci",50:"-nci",3:"-üncü",4:"-üncü",100:"-üncü",6:"-ncı",9:"-uncu",10:"-uncu",30:"-uncu",60:"-ıncı",90:"-ıncı"};return t.lang("az",{months:"yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"),monthsShort:"yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"),weekdays:"Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə".split("_"),weekdaysShort:"Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən".split("_"),weekdaysMin:"Bz_BE_ÇA_Çə_CA_Cü_Şə".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[sabah saat] LT",nextWeek:"[gələn həftə] dddd [saat] LT",lastDay:"[dünən] LT",lastWeek:"[keçən həftə] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s əvvəl",s:"birneçə saniyyə",m:"bir dəqiqə",mm:"%d dəqiqə",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir il",yy:"%d il"},meridiem:function(t){return 4>t?"gecə":12>t?"səhər":17>t?"gündüz":"axşam"},ordinal:function(t){if(0===t)return t+"-ıncı";var i=t%10,s=t%100-i,n=t>=100?100:null;return t+(e[i]||e[s]||e[n])},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("bg",{months:"януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"),monthsShort:"янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"),weekdays:"неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"),weekdaysShort:"нед_пон_вто_сря_чет_пет_съб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"H:mm",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Днес в] LT",nextDay:"[Утре в] LT",nextWeek:"dddd [в] LT",lastDay:"[Вчера в] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[В изминалата] dddd [в] LT";case 1:case 2:case 4:case 5:return"[В изминалия] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"след %s",past:"преди %s",s:"няколко секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дни",M:"месец",MM:"%d месеца",y:"година",yy:"%d години"},ordinal:function(t){var e=t%10,i=t%100;return 0===t?t+"-ев":0===i?t+"-ен":i>10&&20>i?t+"-ти":1===e?t+"-ви":2===e?t+"-ри":7===e||8===e?t+"-ми":t+"-ти"},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"১",2:"২",3:"৩",4:"৪",5:"৫",6:"৬",7:"৭",8:"৮",9:"৯",0:"০"},i={"১":"1","২":"2","৩":"3","৪":"4","৫":"5","৬":"6","৭":"7","৮":"8","৯":"9","০":"0"};return t.lang("bn",{months:"জানুয়ারী_ফেবুয়ারী_মার্চ_এপ্রিল_মে_জুন_জুলাই_অগাস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর".split("_"),monthsShort:"জানু_ফেব_মার্চ_এপর_মে_জুন_জুল_অগ_সেপ্ট_অক্টো_নভ_ডিসেম্".split("_"),weekdays:"রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পত্তিবার_শুক্রুবার_শনিবার".split("_"),weekdaysShort:"রবি_সোম_মঙ্গল_বুধ_বৃহস্পত্তি_শুক্রু_শনি".split("_"),weekdaysMin:"রব_সম_মঙ্গ_বু_ব্রিহ_শু_শনি".split("_"),longDateFormat:{LT:"A h:mm সময়",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[আজ] LT",nextDay:"[আগামীকাল] LT",nextWeek:"dddd, LT",lastDay:"[গতকাল] LT",lastWeek:"[গত] dddd, LT",sameElse:"L"},relativeTime:{future:"%s পরে",past:"%s আগে",s:"কএক সেকেন্ড",m:"এক মিনিট",mm:"%d মিনিট",h:"এক ঘন্টা",hh:"%d ঘন্টা",d:"এক দিন",dd:"%d দিন",M:"এক মাস",MM:"%d মাস",y:"এক বছর",yy:"%d বছর"},preparse:function(t){return t.replace(/[১২৩৪৫৬৭৮৯০]/g,function(t){return i[t]})},postformat:function(t){return t.replace(/\d/g,function(t){return e[t]})},meridiem:function(t){return 4>t?"রাত":10>t?"শকাল":17>t?"দুপুর":20>t?"বিকেল":"রাত"},week:{dow:0,doy:6}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s={mm:"munutenn",MM:"miz",dd:"devezh"};return t+" "+n(s[i],t)}function i(t){switch(s(t)){case 1:case 3:case 4:case 5:case 9:return t+" bloaz";default:return t+" vloaz"}}function s(t){return t>9?s(t%10):t}function n(t,e){return 2===e?o(t):t}function o(t){var e={m:"v",b:"v",d:"z"};return void 0===e[t.charAt(0)]?t:e[t.charAt(0)]+t.substring(1)}return t.lang("br",{months:"Genver_C'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),monthsShort:"Gen_C'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),weekdays:"Sul_Lun_Meurzh_Merc'her_Yaou_Gwener_Sadorn".split("_"),weekdaysShort:"Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),weekdaysMin:"Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),longDateFormat:{LT:"h[e]mm A",L:"DD/MM/YYYY",LL:"D [a viz] MMMM YYYY",LLL:"D [a viz] MMMM YYYY LT",LLLL:"dddd, D [a viz] MMMM YYYY LT"},calendar:{sameDay:"[Hiziv da] LT",nextDay:"[Warc'hoazh da] LT",nextWeek:"dddd [da] LT",lastDay:"[Dec'h da] LT",lastWeek:"dddd [paset da] LT",sameElse:"L"},relativeTime:{future:"a-benn %s",past:"%s 'zo",s:"un nebeud segondennoù",m:"ur vunutenn",mm:e,h:"un eur",hh:"%d eur",d:"un devezh",dd:e,M:"ur miz",MM:e,y:"ur bloaz",yy:i},ordinal:function(t){var e=1===t?"añ":"vet";return t+e},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s=t+" ";switch(i){case"m":return e?"jedna minuta":"jedne minute";case"mm":return s+=1===t?"minuta":2===t||3===t||4===t?"minute":"minuta";case"h":return e?"jedan sat":"jednog sata";case"hh":return s+=1===t?"sat":2===t||3===t||4===t?"sata":"sati";case"dd":return s+=1===t?"dan":"dana";case"MM":return s+=1===t?"mjesec":2===t||3===t||4===t?"mjeseca":"mjeseci";case"yy":return s+=1===t?"godina":2===t||3===t||4===t?"godine":"godina"}}return t.lang("bs",{months:"januar_februar_mart_april_maj_juni_juli_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:e,mm:e,h:e,hh:e,d:"dan",dd:e,M:"mjesec",MM:e,y:"godinu",yy:e},ordinal:"%d.",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("ca",{months:"gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),monthsShort:"gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.".split("_"),weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"Dg_Dl_Dt_Dc_Dj_Dv_Ds".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:function(){return"[avui a "+(1!==this.hours()?"les":"la")+"] LT" +},nextDay:function(){return"[demà a "+(1!==this.hours()?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(1!==this.hours()?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(1!==this.hours()?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(1!==this.hours()?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"fa %s",s:"uns segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},ordinal:"%dº",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t){return t>1&&5>t&&1!==~~(t/10)}function i(t,i,s,n){var o=t+" ";switch(s){case"s":return i||n?"pár sekund":"pár sekundami";case"m":return i?"minuta":n?"minutu":"minutou";case"mm":return i||n?o+(e(t)?"minuty":"minut"):o+"minutami";case"h":return i?"hodina":n?"hodinu":"hodinou";case"hh":return i||n?o+(e(t)?"hodiny":"hodin"):o+"hodinami";case"d":return i||n?"den":"dnem";case"dd":return i||n?o+(e(t)?"dny":"dní"):o+"dny";case"M":return i||n?"měsíc":"měsícem";case"MM":return i||n?o+(e(t)?"měsíce":"měsíců"):o+"měsíci";case"y":return i||n?"rok":"rokem";case"yy":return i||n?o+(e(t)?"roky":"let"):o+"lety"}}var s="leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_"),n="led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_");return t.lang("cs",{months:s,monthsShort:n,monthsParse:function(t,e){var i,s=[];for(i=0;12>i;i++)s[i]=new RegExp("^"+t[i]+"$|^"+e[i]+"$","i");return s}(s,n),weekdays:"neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_čt_pá_so".split("_"),weekdaysMin:"ne_po_út_st_čt_pá_so".split("_"),longDateFormat:{LT:"H.mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd D. MMMM YYYY LT"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zítra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:i,m:i,mm:i,h:i,hh:i,d:i,dd:i,M:i,MM:i,y:i,yy:i},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("cv",{months:"кăрлач_нарăс_пуш_ака_май_çĕртме_утă_çурла_авăн_юпа_чӳк_раштав".split("_"),monthsShort:"кăр_нар_пуш_ака_май_çĕр_утă_çур_ав_юпа_чӳк_раш".split("_"),weekdays:"вырсарникун_тунтикун_ытларикун_юнкун_кĕçнерникун_эрнекун_шăматкун".split("_"),weekdaysShort:"выр_тун_ытл_юн_кĕç_эрн_шăм".split("_"),weekdaysMin:"вр_тн_ыт_юн_кç_эр_шм".split("_"),longDateFormat:{LT:"HH:mm",L:"DD-MM-YYYY",LL:"YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ]",LLL:"YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ], LT",LLLL:"dddd, YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ], LT"},calendar:{sameDay:"[Паян] LT [сехетре]",nextDay:"[Ыран] LT [сехетре]",lastDay:"[Ĕнер] LT [сехетре]",nextWeek:"[Çитес] dddd LT [сехетре]",lastWeek:"[Иртнĕ] dddd LT [сехетре]",sameElse:"L"},relativeTime:{future:function(t){var e=/сехет$/i.exec(t)?"рен":/çул$/i.exec(t)?"тан":"ран";return t+e},past:"%s каялла",s:"пĕр-ик çеккунт",m:"пĕр минут",mm:"%d минут",h:"пĕр сехет",hh:"%d сехет",d:"пĕр кун",dd:"%d кун",M:"пĕр уйăх",MM:"%d уйăх",y:"пĕр çул",yy:"%d çул"},ordinal:"%d-мĕш",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("cy",{months:"Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),monthsShort:"Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),weekdays:"Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),weekdaysShort:"Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),weekdaysMin:"Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Heddiw am] LT",nextDay:"[Yfory am] LT",nextWeek:"dddd [am] LT",lastDay:"[Ddoe am] LT",lastWeek:"dddd [diwethaf am] LT",sameElse:"L"},relativeTime:{future:"mewn %s",past:"%s yn ôl",s:"ychydig eiliadau",m:"munud",mm:"%d munud",h:"awr",hh:"%d awr",d:"diwrnod",dd:"%d diwrnod",M:"mis",MM:"%d mis",y:"blwyddyn",yy:"%d flynedd"},ordinal:function(t){var e=t,i="",s=["","af","il","ydd","ydd","ed","ed","ed","fed","fed","fed","eg","fed","eg","eg","fed","eg","eg","fed","eg","fed"];return e>20?i=40===e||50===e||60===e||80===e||100===e?"fed":"ain":e>0&&(i=s[e]),t+i},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd [d.] D. MMMM YYYY LT"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I går kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?s[i][0]:s[i][1]}return t.lang("de-at",{months:"Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm [Uhr]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT",sameElse:"L",nextDay:"[Morgen um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gestern um] LT",lastWeek:"[letzten] dddd [um] LT"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:e,mm:"%d Minuten",h:e,hh:"%d Stunden",d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?s[i][0]:s[i][1]}return t.lang("de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm [Uhr]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT",sameElse:"L",nextDay:"[Morgen um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gestern um] LT",lastWeek:"[letzten] dddd [um] LT"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:e,mm:"%d Minuten",h:e,hh:"%d Stunden",d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(t,e){return/D/.test(e.substring(0,e.indexOf("MMMM")))?this._monthsGenitiveEl[t.month()]:this._monthsNominativeEl[t.month()]},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(t,e,i){return t>11?i?"μμ":"ΜΜ":i?"πμ":"ΠΜ"},longDateFormat:{LT:"h:mm A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[το προηγούμενο] dddd [{}] LT";default:return"[την προηγούμενη] dddd [{}] LT"}},sameElse:"L"},calendar:function(t,e){var i=this._calendarEl[t],s=e&&e.hours();return"function"==typeof i&&(i=i.apply(e)),i.replace("{}",s%12===1?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},ordinal:function(t){return t+"η"},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(t){var e=t%10,i=1===~~(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",L:"YYYY-MM-DD",LL:"D MMMM, YYYY",LLL:"D MMMM, YYYY LT",LLLL:"dddd, D MMMM, YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(t){var e=t%10,i=1===~~(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(t){var e=t%10,i=1===~~(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("eo",{months:"januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec".split("_"),weekdays:"Dimanĉo_Lundo_Mardo_Merkredo_Ĵaŭdo_Vendredo_Sabato".split("_"),weekdaysShort:"Dim_Lun_Mard_Merk_Ĵaŭ_Ven_Sab".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Ĵa_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"D[-an de] MMMM, YYYY",LLL:"D[-an de] MMMM, YYYY LT",LLLL:"dddd, [la] D[-an de] MMMM, YYYY LT"},meridiem:function(t,e,i){return t>11?i?"p.t.m.":"P.T.M.":i?"a.t.m.":"A.T.M."},calendar:{sameDay:"[Hodiaŭ je] LT",nextDay:"[Morgaŭ je] LT",nextWeek:"dddd [je] LT",lastDay:"[Hieraŭ je] LT",lastWeek:"[pasinta] dddd [je] LT",sameElse:"L"},relativeTime:{future:"je %s",past:"antaŭ %s",s:"sekundoj",m:"minuto",mm:"%d minutoj",h:"horo",hh:"%d horoj",d:"tago",dd:"%d tagoj",M:"monato",MM:"%d monatoj",y:"jaro",yy:"%d jaroj"},ordinal:"%da",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),i="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");return t.lang("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(t,s){return/-MMM-/.test(s)?i[t.month()]:e[t.month()]},weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [del] YYYY",LLL:"D [de] MMMM [del] YYYY LT",LLLL:"dddd, D [de] MMMM [del] YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinal:"%dº",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i,s){var n={s:["mõne sekundi","mõni sekund","paar sekundit"],m:["ühe minuti","üks minut"],mm:[t+" minuti",t+" minutit"],h:["ühe tunni","tund aega","üks tund"],hh:[t+" tunni",t+" tundi"],d:["ühe päeva","üks päev"],M:["kuu aja","kuu aega","üks kuu"],MM:[t+" kuu",t+" kuud"],y:["ühe aasta","aasta","üks aasta"],yy:[t+" aasta",t+" aastat"]};return e?n[i][2]?n[i][2]:n[i][1]:s?n[i][0]:n[i][1]}return t.lang("et",{months:"jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),monthsShort:"jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),weekdays:"pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev".split("_"),weekdaysShort:"P_E_T_K_N_R_L".split("_"),weekdaysMin:"P_E_T_K_N_R_L".split("_"),longDateFormat:{LT:"H:mm",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Täna,] LT",nextDay:"[Homme,] LT",nextWeek:"[Järgmine] dddd LT",lastDay:"[Eile,] LT",lastWeek:"[Eelmine] dddd LT",sameElse:"L"},relativeTime:{future:"%s pärast",past:"%s tagasi",s:e,m:e,mm:e,h:e,hh:e,d:e,dd:"%d päeva",M:e,MM:e,y:e,yy:e},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] LT",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] LT",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] LT",llll:"ddd, YYYY[ko] MMM D[a] LT"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat",dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},ordinal:"%d.",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"۱",2:"۲",3:"۳",4:"۴",5:"۵",6:"۶",7:"۷",8:"۸",9:"۹",0:"۰"},i={"۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","۰":"0"};return t.lang("fa",{months:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ی_د_س_چ_پ_ج_ش".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},meridiem:function(t){return 12>t?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[فردا ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چندین ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(t){return t.replace(/[۰-۹]/g,function(t){return i[t]}).replace(/،/g,",")},postformat:function(t){return t.replace(/\d/g,function(t){return e[t]}).replace(/,/g,"،")},ordinal:"%dم",week:{dow:6,doy:12}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,s,n){var o="";switch(s){case"s":return n?"muutaman sekunnin":"muutama sekunti";case"m":return n?"minuutin":"minuutti";case"mm":o=n?"minuutin":"minuuttia";break;case"h":return n?"tunnin":"tunti";case"hh":o=n?"tunnin":"tuntia";break;case"d":return n?"päivän":"päivä";case"dd":o=n?"päivän":"päivää";break;case"M":return n?"kuukauden":"kuukausi";case"MM":o=n?"kuukauden":"kuukautta";break;case"y":return n?"vuoden":"vuosi";case"yy":o=n?"vuoden":"vuotta"}return o=i(t,n)+" "+o}function i(t,e){return 10>t?e?n[t]:s[t]:t}var s="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),n=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",s[7],s[8],s[9]];return t.lang("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] LT",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] LT",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] LT",llll:"ddd, Do MMM YYYY, [klo] LT"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:e,m:e,mm:e,h:e,hh:e,d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("fo",{months:"januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur".split("_"),weekdaysShort:"sun_mán_týs_mik_hós_frí_ley".split("_"),weekdaysMin:"su_má_tý_mi_hó_fr_le".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D. MMMM, YYYY LT"},calendar:{sameDay:"[Í dag kl.] LT",nextDay:"[Í morgin kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[Í gjár kl.] LT",lastWeek:"[síðstu] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"um %s",past:"%s síðani",s:"fá sekund",m:"ein minutt",mm:"%d minuttir",h:"ein tími",hh:"%d tímar",d:"ein dagur",dd:"%d dagar",M:"ein mánaði",MM:"%d mánaðir",y:"eitt ár",yy:"%d ár"},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("fr-ca",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinal:function(t){return t+(1===t?"er":"")}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinal:function(t){return t+(1===t?"er":"")},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("gl",{months:"Xaneiro_Febreiro_Marzo_Abril_Maio_Xuño_Xullo_Agosto_Setembro_Outubro_Novembro_Decembro".split("_"),monthsShort:"Xan._Feb._Mar._Abr._Mai._Xuñ._Xul._Ago._Set._Out._Nov._Dec.".split("_"),weekdays:"Domingo_Luns_Martes_Mércores_Xoves_Venres_Sábado".split("_"),weekdaysShort:"Dom._Lun._Mar._Mér._Xov._Ven._Sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mé_Xo_Ve_Sá".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:function(){return"[hoxe "+(1!==this.hours()?"ás":"á")+"] LT"},nextDay:function(){return"[mañá "+(1!==this.hours()?"ás":"á")+"] LT"},nextWeek:function(){return"dddd ["+(1!==this.hours()?"ás":"a")+"] LT"},lastDay:function(){return"[onte "+(1!==this.hours()?"á":"a")+"] LT"},lastWeek:function(){return"[o] dddd [pasado "+(1!==this.hours()?"ás":"a")+"] LT"},sameElse:"L"},relativeTime:{future:function(t){return"uns segundos"===t?"nuns segundos":"en "+t},past:"hai %s",s:"uns segundos",m:"un minuto",mm:"%d minutos",h:"unha hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un ano",yy:"%d anos"},ordinal:"%dº",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("he",{months:"ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר".split("_"),monthsShort:"ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳".split("_"),weekdays:"ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת".split("_"),weekdaysShort:"א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"א_ב_ג_ד_ה_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY LT",LLLL:"dddd, D [ב]MMMM YYYY LT",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY LT",llll:"ddd, D MMM YYYY LT"},calendar:{sameDay:"[היום ב־]LT",nextDay:"[מחר ב־]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[אתמול ב־]LT",lastWeek:"[ביום] dddd [האחרון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"לפני %s",s:"מספר שניות",m:"דקה",mm:"%d דקות",h:"שעה",hh:function(t){return 2===t?"שעתיים":t+" שעות"},d:"יום",dd:function(t){return 2===t?"יומיים":t+" ימים"},M:"חודש",MM:function(t){return 2===t?"חודשיים":t+" חודשים"},y:"שנה",yy:function(t){return 2===t?"שנתיים":t+" שנים"}}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},i={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};return t.lang("hi",{months:"जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर".split("_"),monthsShort:"जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.".split("_"),weekdays:"रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कुछ ही क्षण",m:"एक मिनट",mm:"%d मिनट",h:"एक घंटा",hh:"%d घंटे",d:"एक दिन",dd:"%d दिन",M:"एक महीने",MM:"%d महीने",y:"एक वर्ष",yy:"%d वर्ष"},preparse:function(t){return t.replace(/[१२३४५६७८९०]/g,function(t){return i[t]})},postformat:function(t){return t.replace(/\d/g,function(t){return e[t]})},meridiem:function(t){return 4>t?"रात":10>t?"सुबह":17>t?"दोपहर":20>t?"शाम":"रात"},week:{dow:0,doy:6}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s=t+" ";switch(i){case"m":return e?"jedna minuta":"jedne minute";case"mm":return s+=1===t?"minuta":2===t||3===t||4===t?"minute":"minuta";case"h":return e?"jedan sat":"jednog sata";case"hh":return s+=1===t?"sat":2===t||3===t||4===t?"sata":"sati";case"dd":return s+=1===t?"dan":"dana";case"MM":return s+=1===t?"mjesec":2===t||3===t||4===t?"mjeseca":"mjeseci";case"yy":return s+=1===t?"godina":2===t||3===t||4===t?"godine":"godina"}}return t.lang("hr",{months:"sječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_"),monthsShort:"sje._vel._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:e,mm:e,h:e,hh:e,d:"dan",dd:e,M:"mjesec",MM:e,y:"godinu",yy:e},ordinal:"%d.",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i,s){var n=t;switch(i){case"s":return s||e?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(s||e?" perc":" perce");case"mm":return n+(s||e?" perc":" perce");case"h":return"egy"+(s||e?" óra":" órája");case"hh":return n+(s||e?" óra":" órája");case"d":return"egy"+(s||e?" nap":" napja");case"dd":return n+(s||e?" nap":" napja");case"M":return"egy"+(s||e?" hónap":" hónapja");case"MM":return n+(s||e?" hónap":" hónapja");case"y":return"egy"+(s||e?" év":" éve");case"yy":return n+(s||e?" év":" éve")}return""}function i(t){return(t?"":"[múlt] ")+"["+s[this.day()]+"] LT[-kor]"}var s="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");return t.lang("hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D., LT",LLLL:"YYYY. MMMM D., dddd LT"},meridiem:function(t,e,i){return 12>t?i===!0?"de":"DE":i===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return i.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return i.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:e,m:e,mm:e,h:e,hh:e,d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinal:"%d.",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e){var i={nominative:"հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր".split("_"),accusative:"հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի".split("_")},s=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(e)?"accusative":"nominative";return i[s][t.month()] +}function i(t){var e="հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ".split("_");return e[t.month()]}function s(t){var e="կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ".split("_");return e[t.day()]}return t.lang("hy-am",{months:e,monthsShort:i,weekdays:s,weekdaysShort:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),weekdaysMin:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY թ.",LLL:"D MMMM YYYY թ., LT",LLLL:"dddd, D MMMM YYYY թ., LT"},calendar:{sameDay:"[այսօր] LT",nextDay:"[վաղը] LT",lastDay:"[երեկ] LT",nextWeek:function(){return"dddd [օրը ժամը] LT"},lastWeek:function(){return"[անցած] dddd [օրը ժամը] LT"},sameElse:"L"},relativeTime:{future:"%s հետո",past:"%s առաջ",s:"մի քանի վայրկյան",m:"րոպե",mm:"%d րոպե",h:"ժամ",hh:"%d ժամ",d:"օր",dd:"%d օր",M:"ամիս",MM:"%d ամիս",y:"տարի",yy:"%d տարի"},meridiem:function(t){return 4>t?"գիշերվա":12>t?"առավոտվա":17>t?"ցերեկվա":"երեկոյան"},ordinal:function(t,e){switch(e){case"DDD":case"w":case"W":case"DDDo":return 1===t?t+"-ին":t+"-րդ";default:return t}},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiem:function(t){return 11>t?"pagi":15>t?"siang":19>t?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t){return t%100===11?!0:t%10===1?!1:!0}function i(t,i,s,n){var o=t+" ";switch(s){case"s":return i||n?"nokkrar sekúndur":"nokkrum sekúndum";case"m":return i?"mínúta":"mínútu";case"mm":return e(t)?o+(i||n?"mínútur":"mínútum"):i?o+"mínúta":o+"mínútu";case"hh":return e(t)?o+(i||n?"klukkustundir":"klukkustundum"):o+"klukkustund";case"d":return i?"dagur":n?"dag":"degi";case"dd":return e(t)?i?o+"dagar":o+(n?"daga":"dögum"):i?o+"dagur":o+(n?"dag":"degi");case"M":return i?"mánuður":n?"mánuð":"mánuði";case"MM":return e(t)?i?o+"mánuðir":o+(n?"mánuði":"mánuðum"):i?o+"mánuður":o+(n?"mánuð":"mánuði");case"y":return i||n?"ár":"ári";case"yy":return e(t)?o+(i||n?"ár":"árum"):o+(i||n?"ár":"ári")}}return t.lang("is",{months:"janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des".split("_"),weekdays:"sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"),weekdaysShort:"sun_mán_þri_mið_fim_fös_lau".split("_"),weekdaysMin:"Su_Má_Þr_Mi_Fi_Fö_La".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] LT",LLLL:"dddd, D. MMMM YYYY [kl.] LT"},calendar:{sameDay:"[í dag kl.] LT",nextDay:"[á morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[í gær kl.] LT",lastWeek:"[síðasta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s síðan",s:i,m:i,mm:i,h:"klukkustund",hh:i,d:i,dd:i,M:i,MM:i,y:i,yy:i},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"D_L_Ma_Me_G_V_S".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:"[lo scorso] dddd [alle] LT",sameElse:"L"},relativeTime:{future:function(t){return(/^[0-9].+$/.test(t)?"tra":"in")+" "+t},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinal:"%dº",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日LT",LLLL:"YYYY年M月D日LT dddd"},meridiem:function(t){return 12>t?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[来週]dddd LT",lastDay:"[昨日] LT",lastWeek:"[前週]dddd LT",sameElse:"L"},relativeTime:{future:"%s後",past:"%s前",s:"数秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e){var i={nominative:"იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი".split("_"),accusative:"იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს".split("_")},s=/D[oD] *MMMM?/.test(e)?"accusative":"nominative";return i[s][t.month()]}function i(t,e){var i={nominative:"კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი".split("_"),accusative:"კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს".split("_")},s=/(წინა|შემდეგ)/.test(e)?"accusative":"nominative";return i[s][t.day()]}return t.lang("ka",{months:e,monthsShort:"იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ".split("_"),weekdays:i,weekdaysShort:"კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ".split("_"),weekdaysMin:"კვ_ორ_სა_ოთ_ხუ_პა_შა".split("_"),longDateFormat:{LT:"h:mm A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[დღეს] LT[-ზე]",nextDay:"[ხვალ] LT[-ზე]",lastDay:"[გუშინ] LT[-ზე]",nextWeek:"[შემდეგ] dddd LT[-ზე]",lastWeek:"[წინა] dddd LT-ზე",sameElse:"L"},relativeTime:{future:function(t){return/(წამი|წუთი|საათი|წელი)/.test(t)?t.replace(/ი$/,"ში"):t+"ში"},past:function(t){return/(წამი|წუთი|საათი|დღე|თვე)/.test(t)?t.replace(/(ი|ე)$/,"ის წინ"):/წელი/.test(t)?t.replace(/წელი$/,"წლის წინ"):void 0},s:"რამდენიმე წამი",m:"წუთი",mm:"%d წუთი",h:"საათი",hh:"%d საათი",d:"დღე",dd:"%d დღე",M:"თვე",MM:"%d თვე",y:"წელი",yy:"%d წელი"},ordinal:function(t){return 0===t?t:1===t?t+"-ლი":20>t||100>=t&&t%20===0||t%100===0?"მე-"+t:t+"-ე"},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("km",{months:"មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),monthsShort:"មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),weekdays:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),weekdaysShort:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),weekdaysMin:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[ថ្ងៃនៈ ម៉ោង] LT",nextDay:"[ស្អែក ម៉ោង] LT",nextWeek:"dddd [ម៉ោង] LT",lastDay:"[ម្សិលមិញ ម៉ោង] LT",lastWeek:"dddd [សប្តាហ៍មុន] [ម៉ោង] LT",sameElse:"L"},relativeTime:{future:"%sទៀត",past:"%sមុន",s:"ប៉ុន្មានវិនាទី",m:"មួយនាទី",mm:"%d នាទី",h:"មួយម៉ោង",hh:"%d ម៉ោង",d:"មួយថ្ងៃ",dd:"%d ថ្ងៃ",M:"មួយខែ",MM:"%d ខែ",y:"មួយឆ្នាំ",yy:"%d ឆ្នាំ"},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h시 mm분",L:"YYYY.MM.DD",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 LT",LLLL:"YYYY년 MMMM D일 dddd LT"},meridiem:function(t){return 12>t?"오전":"오후"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇초",ss:"%d초",m:"일분",mm:"%d분",h:"한시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한달",MM:"%d달",y:"일년",yy:"%d년"},ordinal:"%d일",meridiemParse:/(오전|오후)/,isPM:function(t){return"오후"===t}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s={m:["eng Minutt","enger Minutt"],h:["eng Stonn","enger Stonn"],d:["een Dag","engem Dag"],dd:[t+" Deeg",t+" Deeg"],M:["ee Mount","engem Mount"],MM:[t+" Méint",t+" Méint"],y:["ee Joer","engem Joer"],yy:[t+" Joer",t+" Joer"]};return e?s[i][0]:s[i][1]}function i(t){var e=t.substr(0,t.indexOf(" "));return a(e)?"a "+t:"an "+t}function s(t){var e=t.substr(0,t.indexOf(" "));return a(e)?"viru "+t:"virun "+t}function n(){var t=this.format("d");return o(t)?"[Leschte] dddd [um] LT":"[Leschten] dddd [um] LT"}function o(t){switch(t=parseInt(t,10)){case 0:case 1:case 3:case 5:case 6:return!0;default:return!1}}function a(t){if(t=parseInt(t,10),isNaN(t))return!1;if(0>t)return!0;if(10>t)return t>=4&&7>=t?!0:!1;if(100>t){var e=t%10,i=t/10;return a(0===e?i:e)}if(1e4>t){for(;t>=10;)t/=10;return a(t)}return t/=1e3,a(t)}return t.lang("lb",{months:"Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),weekdaysShort:"So._Mé._Dë._Më._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mé_Dë_Më_Do_Fr_Sa".split("_"),longDateFormat:{LT:"H:mm [Auer]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Haut um] LT",sameElse:"L",nextDay:"[Muer um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gëschter um] LT",lastWeek:n},relativeTime:{future:i,past:s,s:"e puer Sekonnen",m:e,mm:"%d Minutten",h:e,hh:"%d Stonnen",d:e,dd:e,M:e,MM:e,y:e,yy:e},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i,s){return e?"kelios sekundės":s?"kelių sekundžių":"kelias sekundes"}function i(t,e,i,s){return e?n(i)[0]:s?n(i)[1]:n(i)[2]}function s(t){return t%10===0||t>10&&20>t}function n(t){return r[t].split("_")}function o(t,e,o,a){var r=t+" ";return 1===t?r+i(t,e,o[0],a):e?r+(s(t)?n(o)[1]:n(o)[0]):a?r+n(o)[1]:r+(s(t)?n(o)[1]:n(o)[2])}function a(t,e){var i=-1===e.indexOf("dddd HH:mm"),s=h[t.day()];return i?s:s.substring(0,s.length-2)+"į"}var r={m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesį",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"},h="sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_");return t.lang("lt",{months:"sausio_vasario_kovo_balandžio_gegužės_biržėlio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:a,weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Š".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], LT [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, LT [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], LT [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, LT [val.]"},calendar:{sameDay:"[Šiandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[Praėjusį] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieš %s",s:e,m:i,mm:o,h:i,hh:o,d:i,dd:o,M:i,MM:o,y:i,yy:o},ordinal:function(t){return t+"-oji"},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s=t.split("_");return i?e%10===1&&11!==e?s[2]:s[3]:e%10===1&&11!==e?s[0]:s[1]}function i(t,i,n){return t+" "+e(s[n],t,i)}var s={mm:"minūti_minūtes_minūte_minūtes",hh:"stundu_stundas_stunda_stundas",dd:"dienu_dienas_diena_dienas",MM:"mēnesi_mēnešus_mēnesis_mēneši",yy:"gadu_gadus_gads_gadi"};return t.lang("lv",{months:"janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, LT",LLLL:"YYYY. [gada] D. MMMM, dddd, LT"},calendar:{sameDay:"[Šodien pulksten] LT",nextDay:"[Rīt pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[Pagājušā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"%s vēlāk",past:"%s agrāk",s:"dažas sekundes",m:"minūti",mm:i,h:"stundu",hh:i,d:"dienu",dd:i,M:"mēnesi",MM:i,y:"gadu",yy:i},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("mk",{months:"јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември".split("_"),monthsShort:"јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек".split("_"),weekdays:"недела_понеделник_вторник_среда_четврток_петок_сабота".split("_"),weekdaysShort:"нед_пон_вто_сре_чет_пет_саб".split("_"),weekdaysMin:"нe_пo_вт_ср_че_пе_сa".split("_"),longDateFormat:{LT:"H:mm",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Денес во] LT",nextDay:"[Утре во] LT",nextWeek:"dddd [во] LT",lastDay:"[Вчера во] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Во изминатата] dddd [во] LT";case 1:case 2:case 4:case 5:return"[Во изминатиот] dddd [во] LT"}},sameElse:"L"},relativeTime:{future:"после %s",past:"пред %s",s:"неколку секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дена",M:"месец",MM:"%d месеци",y:"година",yy:"%d години"},ordinal:function(t){var e=t%10,i=t%100;return 0===t?t+"-ев":0===i?t+"-ен":i>10&&20>i?t+"-ти":1===e?t+"-ви":2===e?t+"-ри":7===e||8===e?t+"-ми":t+"-ти"},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("ml",{months:"ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ".split("_"),monthsShort:"ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.".split("_"),weekdays:"ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച".split("_"),weekdaysShort:"ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി".split("_"),weekdaysMin:"ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ".split("_"),longDateFormat:{LT:"A h:mm -നു",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[ഇന്ന്] LT",nextDay:"[നാളെ] LT",nextWeek:"dddd, LT",lastDay:"[ഇന്നലെ] LT",lastWeek:"[കഴിഞ്ഞ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s കഴിഞ്ഞ്",past:"%s മുൻപ്",s:"അൽപ നിമിഷങ്ങൾ",m:"ഒരു മിനിറ്റ്",mm:"%d മിനിറ്റ്",h:"ഒരു മണിക്കൂർ",hh:"%d മണിക്കൂർ",d:"ഒരു ദിവസം",dd:"%d ദിവസം",M:"ഒരു മാസം",MM:"%d മാസം",y:"ഒരു വർഷം",yy:"%d വർഷം"},meridiem:function(t){return 4>t?"രാത്രി":12>t?"രാവിലെ":17>t?"ഉച്ച കഴിഞ്ഞ്":20>t?"വൈകുന്നേരം":"രാത്രി"}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},i={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};return t.lang("mr",{months:"जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर".split("_"),monthsShort:"जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.".split("_"),weekdays:"रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm वाजता",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[आज] LT",nextDay:"[उद्या] LT",nextWeek:"dddd, LT",lastDay:"[काल] LT",lastWeek:"[मागील] dddd, LT",sameElse:"L"},relativeTime:{future:"%s नंतर",past:"%s पूर्वी",s:"सेकंद",m:"एक मिनिट",mm:"%d मिनिटे",h:"एक तास",hh:"%d तास",d:"एक दिवस",dd:"%d दिवस",M:"एक महिना",MM:"%d महिने",y:"एक वर्ष",yy:"%d वर्षे"},preparse:function(t){return t.replace(/[१२३४५६७८९०]/g,function(t){return i[t]})},postformat:function(t){return t.replace(/\d/g,function(t){return e[t]})},meridiem:function(t){return 4>t?"रात्री":10>t?"सकाळी":17>t?"दुपारी":20>t?"सायंकाळी":"रात्री"},week:{dow:0,doy:6}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiem:function(t){return 11>t?"pagi":15>t?"tengahari":19>t?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"sø._ma._ti._on._to._fr._lø.".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"H.mm",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] LT",LLLL:"dddd D. MMMM YYYY [kl.] LT"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i går kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"for %s siden",s:"noen sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",M:"en måned",MM:"%d måneder",y:"ett år",yy:"%d år"},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},i={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};return t.lang("ne",{months:"जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर".split("_"),monthsShort:"जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.".split("_"),weekdays:"आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार".split("_"),weekdaysShort:"आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.".split("_"),weekdaysMin:"आइ._सो._मङ्_बु._बि._शु._श.".split("_"),longDateFormat:{LT:"Aको h:mm बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},preparse:function(t){return t.replace(/[१२३४५६७८९०]/g,function(t){return i[t]})},postformat:function(t){return t.replace(/\d/g,function(t){return e[t]})},meridiem:function(t){return 3>t?"राती":10>t?"बिहान":15>t?"दिउँसो":18>t?"बेलुका":20>t?"साँझ":"राती"},calendar:{sameDay:"[आज] LT",nextDay:"[भोली] LT",nextWeek:"[आउँदो] dddd[,] LT",lastDay:"[हिजो] LT",lastWeek:"[गएको] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%sमा",past:"%s अगाडी",s:"केही समय",m:"एक मिनेट",mm:"%d मिनेट",h:"एक घण्टा",hh:"%d घण्टा",d:"एक दिन",dd:"%d दिन",M:"एक महिना",MM:"%d महिना",y:"एक बर्ष",yy:"%d बर्ष"},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),i="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_");return t.lang("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(t,s){return/-MMM-/.test(s)?i[t.month()]:e[t.month()]},weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),longDateFormat:{LT:"HH:mm",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinal:function(t){return t+(1===t||8===t||t>=20?"ste":"de")},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"sun_mån_tys_ons_tor_fre_lau".split("_"),weekdaysMin:"su_må_ty_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I går klokka] LT",lastWeek:"[Føregåande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"for %s sidan",s:"nokre sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",M:"ein månad",MM:"%d månader",y:"eit år",yy:"%d år"},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t){return 5>t%10&&t%10>1&&~~(t/10)%10!==1}function i(t,i,s){var n=t+" ";switch(s){case"m":return i?"minuta":"minutę";case"mm":return n+(e(t)?"minuty":"minut");case"h":return i?"godzina":"godzinę";case"hh":return n+(e(t)?"godziny":"godzin");case"MM":return n+(e(t)?"miesiące":"miesięcy");case"yy":return n+(e(t)?"lata":"lat")}}var s="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),n="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");return t.lang("pl",{months:function(t,e){return/D MMMM/.test(e)?n[t.month()]:s[t.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"nie_pon_wt_śr_czw_pt_sb".split("_"),weekdaysMin:"N_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:i,mm:i,h:i,hh:i,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:i,y:"rok",yy:i},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("pt-br",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] LT",LLLL:"dddd, D [de] MMMM [de] YYYY [às] LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinal:"%dº"})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("pt",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinal:"%dº",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s={mm:"minute",hh:"ore",dd:"zile",MM:"luni",yy:"ani"},n=" ";return(t%100>=20||t>=100&&t%100===0)&&(n=" de "),t+n+s[i]}return t.lang("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),weekdays:"duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",m:"un minut",mm:e,h:"o oră",hh:e,d:"o zi",dd:e,M:"o lună",MM:e,y:"un an",yy:e},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e){var i=t.split("_");return e%10===1&&e%100!==11?i[0]:e%10>=2&&4>=e%10&&(10>e%100||e%100>=20)?i[1]:i[2]}function i(t,i,s){var n={mm:i?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===s?i?"минута":"минуту":t+" "+e(n[s],+t)}function s(t,e){var i={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},s=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(e)?"accusative":"nominative";return i[s][t.month()]}function n(t,e){var i={nominative:"янв_фев_мар_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},s=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(e)?"accusative":"nominative";return i[s][t.month()]}function o(t,e){var i={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},s=/\[ ?[Вв] ?(?:прошлую|следующую)? ?\] ?dddd/.test(e)?"accusative":"nominative";return i[s][t.day()]}return t.lang("ru",{months:s,monthsShort:n,weekdays:o,weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[й|я]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i],longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., LT",LLLL:"dddd, D MMMM YYYY г., LT"},calendar:{sameDay:"[Сегодня в] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(){return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT"},lastWeek:function(){switch(this.day()){case 0:return"[В прошлое] dddd [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",m:i,mm:i,h:"час",hh:i,d:"день",dd:i,M:"месяц",MM:i,y:"год",yy:i},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(t){return/^(дня|вечера)$/.test(t)},meridiem:function(t){return 4>t?"ночи":12>t?"утра":17>t?"дня":"вечера"},ordinal:function(t,e){switch(e){case"M":case"d":case"DDD":return t+"-й";case"D":return t+"-го";case"w":case"W":return t+"-я";default:return t}},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t){return t>1&&5>t}function i(t,i,s,n){var o=t+" ";switch(s){case"s":return i||n?"pár sekúnd":"pár sekundami";case"m":return i?"minúta":n?"minútu":"minútou";case"mm":return i||n?o+(e(t)?"minúty":"minút"):o+"minútami";case"h":return i?"hodina":n?"hodinu":"hodinou";case"hh":return i||n?o+(e(t)?"hodiny":"hodín"):o+"hodinami";case"d":return i||n?"deň":"dňom";case"dd":return i||n?o+(e(t)?"dni":"dní"):o+"dňami";case"M":return i||n?"mesiac":"mesiacom";case"MM":return i||n?o+(e(t)?"mesiace":"mesiacov"):o+"mesiacmi";case"y":return i||n?"rok":"rokom";case"yy":return i||n?o+(e(t)?"roky":"rokov"):o+"rokmi"}}var s="január_február_marec_apríl_máj_jún_júl_august_september_október_november_december".split("_"),n="jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_");return t.lang("sk",{months:s,monthsShort:n,monthsParse:function(t,e){var i,s=[];for(i=0;12>i;i++)s[i]=new RegExp("^"+t[i]+"$|^"+e[i]+"$","i");return s}(s,n),weekdays:"nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_št_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_št_pi_so".split("_"),longDateFormat:{LT:"H:mm",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd D. MMMM YYYY LT"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT"; +case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo štvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulú nedeľu o] LT";case 1:case 2:return"[minulý] dddd [o] LT";case 3:return"[minulú stredu o] LT";case 4:case 5:return"[minulý] dddd [o] LT";case 6:return"[minulú sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:i,m:i,mm:i,h:i,hh:i,d:i,dd:i,M:i,MM:i,y:i,yy:i},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e,i){var s=t+" ";switch(i){case"m":return e?"ena minuta":"eno minuto";case"mm":return s+=1===t?"minuta":2===t?"minuti":3===t||4===t?"minute":"minut";case"h":return e?"ena ura":"eno uro";case"hh":return s+=1===t?"ura":2===t?"uri":3===t||4===t?"ure":"ur";case"dd":return s+=1===t?"dan":"dni";case"MM":return s+=1===t?"mesec":2===t?"meseca":3===t||4===t?"mesece":"mesecev";case"yy":return s+=1===t?"leto":2===t?"leti":3===t||4===t?"leta":"let"}}return t.lang("sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[prejšnja] dddd [ob] LT";case 1:case 2:case 4:case 5:return"[prejšnji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"%s nazaj",s:"nekaj sekund",m:e,mm:e,h:e,hh:e,d:"en dan",dd:e,M:"en mesec",MM:e,y:"eno leto",yy:e},ordinal:"%d.",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("sq",{months:"Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor".split("_"),monthsShort:"Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj".split("_"),weekdays:"E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë".split("_"),weekdaysShort:"Die_Hën_Mar_Mër_Enj_Pre_Sht".split("_"),weekdaysMin:"D_H_Ma_Më_E_P_Sh".split("_"),meridiem:function(t){return 12>t?"PD":"MD"},longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Sot në] LT",nextDay:"[Nesër në] LT",nextWeek:"dddd [në] LT",lastDay:"[Dje në] LT",lastWeek:"dddd [e kaluar në] LT",sameElse:"L"},relativeTime:{future:"në %s",past:"%s më parë",s:"disa sekonda",m:"një minutë",mm:"%d minuta",h:"një orë",hh:"%d orë",d:"një ditë",dd:"%d ditë",M:"një muaj",MM:"%d muaj",y:"një vit",yy:"%d vite"},ordinal:"%d.",week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={words:{m:["један минут","једне минуте"],mm:["минут","минуте","минута"],h:["један сат","једног сата"],hh:["сат","сата","сати"],dd:["дан","дана","дана"],MM:["месец","месеца","месеци"],yy:["година","године","година"]},correctGrammaticalCase:function(t,e){return 1===t?e[0]:t>=2&&4>=t?e[1]:e[2]},translate:function(t,i,s){var n=e.words[s];return 1===s.length?i?n[0]:n[1]:t+" "+e.correctGrammaticalCase(t,n)}};return t.lang("sr-cyrl",{months:["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],monthsShort:["јан.","феб.","мар.","апр.","мај","јун","јул","авг.","сеп.","окт.","нов.","дец."],weekdays:["недеља","понедељак","уторак","среда","четвртак","петак","субота"],weekdaysShort:["нед.","пон.","уто.","сре.","чет.","пет.","суб."],weekdaysMin:["не","по","ут","ср","че","пе","су"],longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[данас у] LT",nextDay:"[сутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [среду] [у] LT";case 6:return"[у] [суботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){var t=["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [среде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [суботе] [у] LT"];return t[this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико секунди",m:e.translate,mm:e.translate,h:e.translate,hh:e.translate,d:"дан",dd:e.translate,M:"месец",MM:e.translate,y:"годину",yy:e.translate},ordinal:"%d.",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={words:{m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(t,e){return 1===t?e[0]:t>=2&&4>=t?e[1]:e[2]},translate:function(t,i,s){var n=e.words[s];return 1===s.length?i?n[0]:n[1]:t+" "+e.correctGrammaticalCase(t,n)}};return t.lang("sr",{months:["januar","februar","mart","april","maj","jun","jul","avgust","septembar","oktobar","novembar","decembar"],monthsShort:["jan.","feb.","mar.","apr.","maj","jun","jul","avg.","sep.","okt.","nov.","dec."],weekdays:["nedelja","ponedeljak","utorak","sreda","četvrtak","petak","subota"],weekdaysShort:["ned.","pon.","uto.","sre.","čet.","pet.","sub."],weekdaysMin:["ne","po","ut","sr","če","pe","su"],longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var t=["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return t[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:e.translate,mm:e.translate,h:e.translate,hh:e.translate,d:"dan",dd:e.translate,M:"mesec",MM:e.translate,y:"godinu",yy:e.translate},ordinal:"%d.",week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"dddd LT",lastWeek:"[Förra] dddd[en] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},ordinal:function(t){var e=t%10,i=1===~~(t%100/10)?"e":1===e?"a":2===e?"a":3===e?"e":"e";return t+i},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("ta",{months:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),monthsShort:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),weekdays:"ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை".split("_"),weekdaysShort:"ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி".split("_"),weekdaysMin:"ஞா_தி_செ_பு_வி_வெ_ச".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[இன்று] LT",nextDay:"[நாளை] LT",nextWeek:"dddd, LT",lastDay:"[நேற்று] LT",lastWeek:"[கடந்த வாரம்] dddd, LT",sameElse:"L"},relativeTime:{future:"%s இல்",past:"%s முன்",s:"ஒரு சில விநாடிகள்",m:"ஒரு நிமிடம்",mm:"%d நிமிடங்கள்",h:"ஒரு மணி நேரம்",hh:"%d மணி நேரம்",d:"ஒரு நாள்",dd:"%d நாட்கள்",M:"ஒரு மாதம்",MM:"%d மாதங்கள்",y:"ஒரு வருடம்",yy:"%d ஆண்டுகள்"},ordinal:function(t){return t+"வது"},meridiem:function(t){return t>=6&&10>=t?" காலை":t>=10&&14>=t?" நண்பகல்":t>=14&&18>=t?" எற்பாடு":t>=18&&20>=t?" மாலை":t>=20&&24>=t?" இரவு":t>=0&&6>=t?" வைகறை":void 0},week:{dow:0,doy:6}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"),weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),longDateFormat:{LT:"H นาฬิกา m นาที",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา LT",LLLL:"วันddddที่ D MMMM YYYY เวลา LT"},meridiem:function(t){return 12>t?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("tl-ph",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM DD, YYYY LT"},calendar:{sameDay:"[Ngayon sa] LT",nextDay:"[Bukas sa] LT",nextWeek:"dddd [sa] LT",lastDay:"[Kahapon sa] LT",lastWeek:"dddd [huling linggo] LT",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},ordinal:function(t){return t},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){var e={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"};return t.lang("tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[haftaya] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen hafta] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinal:function(t){if(0===t)return t+"'ıncı";var i=t%10,s=t%100-i,n=t>=100?100:null;return t+(e[i]||e[s]||e[n])},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("tzm-latn",{months:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),monthsShort:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),weekdays:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysShort:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysMin:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[asdkh g] LT",nextDay:"[aska g] LT",nextWeek:"dddd [g] LT",lastDay:"[assant g] LT",lastWeek:"dddd [g] LT",sameElse:"L"},relativeTime:{future:"dadkh s yan %s",past:"yan %s",s:"imik",m:"minuḍ",mm:"%d minuḍ",h:"saɛa",hh:"%d tassaɛin",d:"ass",dd:"%d ossan",M:"ayowr",MM:"%d iyyirn",y:"asgas",yy:"%d isgasn"},week:{dow:6,doy:12}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("tzm",{months:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),monthsShort:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),weekdays:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysShort:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysMin:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[ⴰⵙⴷⵅ ⴴ] LT",nextDay:"[ⴰⵙⴽⴰ ⴴ] LT",nextWeek:"dddd [ⴴ] LT",lastDay:"[ⴰⵚⴰⵏⵜ ⴴ] LT",lastWeek:"dddd [ⴴ] LT",sameElse:"L"},relativeTime:{future:"ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s",past:"ⵢⴰⵏ %s",s:"ⵉⵎⵉⴽ",m:"ⵎⵉⵏⵓⴺ",mm:"%d ⵎⵉⵏⵓⴺ",h:"ⵙⴰⵄⴰ",hh:"%d ⵜⴰⵙⵙⴰⵄⵉⵏ",d:"ⴰⵙⵙ",dd:"%d oⵙⵙⴰⵏ",M:"ⴰⵢoⵓⵔ",MM:"%d ⵉⵢⵢⵉⵔⵏ",y:"ⴰⵙⴳⴰⵙ",yy:"%d ⵉⵙⴳⴰⵙⵏ"},week:{dow:6,doy:12}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){function e(t,e){var i=t.split("_");return e%10===1&&e%100!==11?i[0]:e%10>=2&&4>=e%10&&(10>e%100||e%100>=20)?i[1]:i[2]}function i(t,i,s){var n={mm:"хвилина_хвилини_хвилин",hh:"година_години_годин",dd:"день_дні_днів",MM:"місяць_місяці_місяців",yy:"рік_роки_років"};return"m"===s?i?"хвилина":"хвилину":"h"===s?i?"година":"годину":t+" "+e(n[s],+t)}function s(t,e){var i={nominative:"січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень".split("_"),accusative:"січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня".split("_")},s=/D[oD]? *MMMM?/.test(e)?"accusative":"nominative";return i[s][t.month()]}function n(t,e){var i={nominative:"неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота".split("_"),accusative:"неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу".split("_"),genitive:"неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи".split("_")},s=/(\[[ВвУу]\]) ?dddd/.test(e)?"accusative":/\[?(?:минулої|наступної)? ?\] ?dddd/.test(e)?"genitive":"nominative";return i[s][t.day()]}function o(t){return function(){return t+"о"+(11===this.hours()?"б":"")+"] LT"}}return t.lang("uk",{months:s,monthsShort:"січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),weekdays:n,weekdaysShort:"нд_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., LT",LLLL:"dddd, D MMMM YYYY р., LT"},calendar:{sameDay:o("[Сьогодні "),nextDay:o("[Завтра "),lastDay:o("[Вчора "),nextWeek:o("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return o("[Минулої] dddd [").call(this);case 1:case 2:case 4:return o("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька секунд",m:i,mm:i,h:"годину",hh:i,d:"день",dd:i,M:"місяць",MM:i,y:"рік",yy:i},meridiem:function(t){return 4>t?"ночі":12>t?"ранку":17>t?"дня":"вечора"},ordinal:function(t,e){switch(e){case"M":case"d":case"DDD":case"w":case"W":return t+"-й";case"D":return t+"-го";default:return t}},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("uz",{months:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),monthsShort:"янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"),weekdays:"Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба".split("_"),weekdaysShort:"Якш_Душ_Сеш_Чор_Пай_Жум_Шан".split("_"),weekdaysMin:"Як_Ду_Се_Чо_Па_Жу_Ша".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"D MMMM YYYY, dddd LT"},calendar:{sameDay:"[Бугун соат] LT [да]",nextDay:"[Эртага] LT [да]",nextWeek:"dddd [куни соат] LT [да]",lastDay:"[Кеча соат] LT [да]",lastWeek:"[Утган] dddd [куни соат] LT [да]",sameElse:"L"},relativeTime:{future:"Якин %s ичида",past:"Бир неча %s олдин",s:"фурсат",m:"бир дакика",mm:"%d дакика",h:"бир соат",hh:"%d соат",d:"бир кун",dd:"%d кун",M:"бир ой",MM:"%d ой",y:"бир йил",yy:"%d йил"},week:{dow:1,doy:7}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("vi",{months:"tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),monthsShort:"Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12".split("_"),weekdays:"chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY LT",LLLL:"dddd, D MMMM [năm] YYYY LT",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY LT",llll:"ddd, D MMM YYYY LT"},calendar:{sameDay:"[Hôm nay lúc] LT",nextDay:"[Ngày mai lúc] LT",nextWeek:"dddd [tuần tới lúc] LT",lastDay:"[Hôm qua lúc] LT",lastWeek:"dddd [tuần rồi lúc] LT",sameElse:"L"},relativeTime:{future:"%s tới",past:"%s trước",s:"vài giây",m:"một phút",mm:"%d phút",h:"một giờ",hh:"%d giờ",d:"một ngày",dd:"%d ngày",M:"một tháng",MM:"%d tháng",y:"một năm",yy:"%d năm"},ordinal:function(t){return t},week:{dow:1,doy:4}})})},function(t,e,i){var s,n;!function(e){s=[i(51)],n=e.apply(null,s),!(void 0!==n&&(t.exports=n))}(function(t){return t.lang("zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"Ah点mm",L:"YYYY-MM-DD",LL:"YYYY年MMMD日",LLL:"YYYY年MMMD日LT",LLLL:"YYYY年MMMD日ddddLT",l:"YYYY-MM-DD",ll:"YYYY年MMMD日",lll:"YYYY年MMMD日LT",llll:"YYYY年MMMD日ddddLT"},meridiem:function(t,e){var i=100*t+e;return 600>i?"凌晨":900>i?"早上":1130>i?"上午":1230>i?"中午":1800>i?"下午":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var e,i;return e=t().startOf("week"),i=this.unix()-e.unix()>=604800?"[下]":"[本]",0===this.minutes()?i+"dddAh点整":i+"dddAh点mm"},lastWeek:function(){var e,i;return e=t().startOf("week"),i=this.unix()i?"早上":1130>i?"上午":1230>i?"中午":1800>i?"下午":"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},ordinal:function(t,e){switch(e){case"d":case"D":case"DDD":return t+"日";case"M":return t+"月";case"w":case"W":return t+"週";default:return t}},relativeTime:{future:"%s內",past:"%s前",s:"幾秒",m:"一分鐘",mm:"%d分鐘",h:"一小時",hh:"%d小時",d:"一天",dd:"%d天",M:"一個月",MM:"%d個月",y:"一年",yy:"%d年"}})})},function(t){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}}])}); +//# sourceMappingURL=vis.map \ No newline at end of file diff --git a/examples/network/07_selections.html b/examples/network/07_selections.html index 8b4ba05d..32d84e16 100644 --- a/examples/network/07_selections.html +++ b/examples/network/07_selections.html @@ -56,7 +56,7 @@ }); // set initial selection (id's of some nodes) - network.setSelection([3, 4, 5]); + network.selectNodes([3, 4, 5]); diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..252d81fa --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,138 @@ +var fs = require('fs'); +var gulp = require('gulp'); +var gutil = require('gulp-util'); +var concat = require('gulp-concat'); +var minifyCSS = require('gulp-minify-css'); +var rename = require("gulp-rename"); +var webpack = require('webpack'); +var uglify = require('uglify-js'); +var rimraf = require('rimraf'); +var merge = require('merge-stream'); + +var ENTRY = './index.js'; +var HEADER = './lib/header.js'; +var DIST = './dist'; +var VIS_JS = 'vis.js'; +var VIS_MAP = 'vis.map'; +var VIS_CSS = 'vis.css'; +var VIS_MIN_CSS = 'vis.min.css'; +var DIST_VIS_MIN_JS = DIST + '/vis.min.js'; +var DIST_VIS_MAP = DIST + '/' + VIS_MAP; + +// generate banner with today's date and correct version +function createBanner() { + var today = gutil.date(new Date(), 'yyyy-mm-dd'); // today, formatted as yyyy-mm-dd + var version = require('./package.json').version; + + return String(fs.readFileSync(HEADER)) + .replace('@@date', today) + .replace('@@version', version); +} + +var bannerPlugin = new webpack.BannerPlugin(createBanner(), { + entryOnly: true, + raw: true +}); + +var webpackConfig = { + entry: ENTRY, + output: { + library: 'vis', + libraryTarget: 'umd', + path: DIST, + filename: VIS_JS, + sourcePrefix: ' ' + }, + plugins: [ bannerPlugin ], + cache: true +}; + +var uglifyConfig = { + outSourceMap: VIS_MAP, + output: { + comments: /@license/ + } +}; + +// create a single instance of the compiler to allow caching +var compiler = webpack(webpackConfig); + +// clean the dist directory +gulp.task('clean', function (cb) { + rimraf(DIST, cb); +}); + +gulp.task('bundle-js', ['clean'], function (cb) { + // update the banner contents (has a date in it which should stay up to date) + bannerPlugin.banner = createBanner(); + + compiler.run(function (err, stats) { + if (err) gutil.log(err); + cb(); + }); +}); + +// bundle and minify css +gulp.task('bundle-css', ['clean'], function () { + var files = [ + './lib/timeline/component/css/timeline.css', + './lib/timeline/component/css/panel.css', + './lib/timeline/component/css/labelset.css', + './lib/timeline/component/css/itemset.css', + './lib/timeline/component/css/item.css', + './lib/timeline/component/css/timeaxis.css', + './lib/timeline/component/css/currenttime.css', + './lib/timeline/component/css/customtime.css', + './lib/timeline/component/css/animation.css', + + './lib/timeline/component/css/dataaxis.css', + './lib/timeline/component/css/pathStyles.css', + + './lib/network/css/network-manipulation.css', + './lib/network/css/network-navigation.css' + ]; + + return gulp.src(files) + .pipe(concat(VIS_CSS)) + .pipe(gulp.dest(DIST)) + + // TODO: nicer to put minifying css in a separate task? + .pipe(minifyCSS()) + .pipe(rename(VIS_MIN_CSS)) + .pipe(gulp.dest(DIST)); +}); + +gulp.task('copy-img', ['clean'], function () { + var network = gulp.src('./lib/network/img/**/*') + .pipe(gulp.dest(DIST + '/img/network')); + + var timeline = gulp.src('./lib/timeline/img/**/*') + .pipe(gulp.dest(DIST + '/img/timeline')); + + return merge(network, timeline); +}); + +gulp.task('minify', ['bundle-js'], function (cb) { + var result = uglify.minify([DIST + '/' + VIS_JS], uglifyConfig); + + fs.writeFileSync(DIST_VIS_MIN_JS, result.code); + fs.writeFileSync(DIST_VIS_MAP, result.map); + + cb(); +}); + +gulp.task('bundle', ['bundle-js', 'bundle-css', 'copy-img']); + +// The watch task (to automatically rebuild when the source code changes) +gulp.task('watch', ['bundle', 'minify'], function () { + gulp.watch(['index.js', 'lib/**/*.js'], ['bundle', 'minify']); +}); + +// The watch task (to automatically rebuild when the source code changes) +// this watch only rebuilds vis.js, not vis.min.js +gulp.task('watch-dev', ['bundle'], function () { + gulp.watch(['index.js', 'lib/**/*.js'], ['bundle']); +}); + +// The default task (called when you run `gulp`) +gulp.task('default', ['clean', 'bundle', 'minify']); diff --git a/index.js b/index.js new file mode 100644 index 00000000..0b60ac87 --- /dev/null +++ b/index.js @@ -0,0 +1,56 @@ +// utils +exports.util = require('./lib/util'); +exports.DOMutil = require('./lib/DOMutil'); + +// data +exports.DataSet = require('./lib/DataSet'); +exports.DataView = require('./lib/DataView'); + +// Graph3d +exports.Graph3d = require('./lib/graph3d/Graph3d'); + +// Timeline +exports.Timeline = require('./lib/timeline/Timeline'); +exports.Graph2d = require('./lib/timeline/Graph2d'); +exports.timeline= { + DataStep: require('./lib/timeline/DataStep'), + Range: require('./lib/timeline/Range'), + stack: require('./lib/timeline/Stack'), + TimeStep: require('./lib/timeline/TimeStep'), + + components: { + items: { + Item: require('./lib/timeline/component/item/Item'), + ItemBox: require('./lib/timeline/component/item/ItemBox'), + ItemPoint: require('./lib/timeline/component/item/ItemPoint'), + ItemRange: require('./lib/timeline/component/item/ItemRange') + }, + + Component: require('./lib/timeline/component/Component'), + CurrentTime: require('./lib/timeline/component/CurrentTime'), + CustomTime: require('./lib/timeline/component/CustomTime'), + DataAxis: require('./lib/timeline/component/DataAxis'), + GraphGroup: require('./lib/timeline/component/GraphGroup'), + Group: require('./lib/timeline/component/Group'), + ItemSet: require('./lib/timeline/component/ItemSet'), + Legend: require('./lib/timeline/component/Legend'), + LineGraph: require('./lib/timeline/component/LineGraph'), + TimeAxis: require('./lib/timeline/component/TimeAxis') + } +}; + +// Network +exports.Network = require('./lib/network/Network'); +exports.network = { + Edge: require('./lib/network/Edge'), + Groups: require('./lib/network/Groups'), + Images: require('./lib/network/Images'), + Node: require('./lib/network/Node'), + Popup: require('./lib/network/Popup'), + dotparser: require('./lib/network/dotparser') +}; + +// Deprecated since v3.0.0 +exports.Graph = function () { + throw new Error('Graph is renamed to Network. Please create a graph as new vis.Network(...)'); +}; diff --git a/lib/DOMutil.js b/lib/DOMutil.js index a7bf81e1..9fc06c9a 100644 --- a/lib/DOMutil.js +++ b/lib/DOMutil.js @@ -1,15 +1,11 @@ -/** - * Created by Alex on 6/20/14. - */ - -var DOMutil = {}; +// DOM utility methods /** * this prepares the JSON container for allocating SVG elements * @param JSONcontainer * @private */ -DOMutil.prepareElements = function(JSONcontainer) { +exports.prepareElements = function(JSONcontainer) { // cleanup the redundant svgElements; for (var elementType in JSONcontainer) { if (JSONcontainer.hasOwnProperty(elementType)) { @@ -26,7 +22,7 @@ DOMutil.prepareElements = function(JSONcontainer) { * @param JSONcontainer * @private */ -DOMutil.cleanupElements = function(JSONcontainer) { +exports.cleanupElements = function(JSONcontainer) { // cleanup the redundant svgElements; for (var elementType in JSONcontainer) { if (JSONcontainer.hasOwnProperty(elementType)) { @@ -50,7 +46,7 @@ DOMutil.cleanupElements = function(JSONcontainer) { * @returns {*} * @private */ -DOMutil.getSVGElement = function (elementType, JSONcontainer, svgContainer) { +exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { var element; // allocate SVG element, if it doesnt yet exist, create one. if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before @@ -86,7 +82,7 @@ DOMutil.getSVGElement = function (elementType, JSONcontainer, svgContainer) { * @returns {*} * @private */ -DOMutil.getDOMElement = function (elementType, JSONcontainer, DOMContainer) { +exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer) { var element; // allocate SVG element, if it doesnt yet exist, create one. if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before @@ -126,17 +122,17 @@ DOMutil.getDOMElement = function (elementType, JSONcontainer, DOMContainer) { * @param svgContainer * @returns {*} */ -DOMutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { +exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { var point; if (group.options.drawPoints.style == 'circle') { - point = DOMutil.getSVGElement('circle',JSONcontainer,svgContainer); + point = exports.getSVGElement('circle',JSONcontainer,svgContainer); point.setAttributeNS(null, "cx", x); point.setAttributeNS(null, "cy", y); point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); point.setAttributeNS(null, "class", group.className + " point"); } else { - point = DOMutil.getSVGElement('rect',JSONcontainer,svgContainer); + point = exports.getSVGElement('rect',JSONcontainer,svgContainer); point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); point.setAttributeNS(null, "width", group.options.drawPoints.size); @@ -153,8 +149,8 @@ DOMutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { * @param y * @param className */ -DOMutil.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { - var rect = DOMutil.getSVGElement('rect',JSONcontainer, svgContainer); +exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { + var rect = exports.getSVGElement('rect',JSONcontainer, svgContainer); rect.setAttributeNS(null, "x", x - 0.5 * width); rect.setAttributeNS(null, "y", y); rect.setAttributeNS(null, "width", width); diff --git a/lib/DataSet.js b/lib/DataSet.js index 1ce20953..b23e8f89 100644 --- a/lib/DataSet.js +++ b/lib/DataSet.js @@ -1,3 +1,5 @@ +var util = require('./util'); + /** * DataSet * @@ -932,3 +934,5 @@ DataSet.prototype._appendRow = function (dataTable, columns, item) { dataTable.setValue(row, col, item[field]); } }; + +module.exports = DataSet; diff --git a/lib/DataView.js b/lib/DataView.js index 3a934f89..f56e151e 100644 --- a/lib/DataView.js +++ b/lib/DataView.js @@ -1,3 +1,6 @@ +var util = require('./util'); +var DataSet = require('./DataSet'); + /** * DataView * @@ -294,3 +297,5 @@ DataView.prototype._trigger = DataSet.prototype._trigger; // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) DataView.prototype.subscribe = DataView.prototype.on; DataView.prototype.unsubscribe = DataView.prototype.off; + +module.exports = DataView; \ No newline at end of file diff --git a/lib/graph3d/Filter.js b/lib/graph3d/Filter.js new file mode 100644 index 00000000..2315104c --- /dev/null +++ b/lib/graph3d/Filter.js @@ -0,0 +1,218 @@ +var DataView = require('../DataView'); + +/** + * @class Filter + * + * @param {DataSet} data The google data table + * @param {Number} column The index of the column to be filtered + * @param {Graph} graph The graph + */ +function Filter (data, column, graph) { + this.data = data; + this.column = column; + this.graph = graph; // the parent graph + + this.index = undefined; + this.value = undefined; + + // read all distinct values and select the first one + this.values = graph.getDistinctValues(data.get(), this.column); + + // sort both numeric and string values correctly + this.values.sort(function (a, b) { + return a > b ? 1 : a < b ? -1 : 0; + }); + + if (this.values.length > 0) { + this.selectValue(0); + } + + // create an array with the filtered datapoints. this will be loaded afterwards + this.dataPoints = []; + + this.loaded = false; + this.onLoadCallback = undefined; + + if (graph.animationPreload) { + this.loaded = false; + this.loadInBackground(); + } + else { + this.loaded = true; + } +}; + + +/** + * Return the label + * @return {string} label + */ +Filter.prototype.isLoaded = function() { + return this.loaded; +}; + + +/** + * Return the loaded progress + * @return {Number} percentage between 0 and 100 + */ +Filter.prototype.getLoadedProgress = function() { + var len = this.values.length; + + var i = 0; + while (this.dataPoints[i]) { + i++; + } + + return Math.round(i / len * 100); +}; + + +/** + * Return the label + * @return {string} label + */ +Filter.prototype.getLabel = function() { + return this.graph.filterLabel; +}; + + +/** + * Return the columnIndex of the filter + * @return {Number} columnIndex + */ +Filter.prototype.getColumn = function() { + return this.column; +}; + +/** + * Return the currently selected value. Returns undefined if there is no selection + * @return {*} value + */ +Filter.prototype.getSelectedValue = function() { + if (this.index === undefined) + return undefined; + + return this.values[this.index]; +}; + +/** + * Retrieve all values of the filter + * @return {Array} values + */ +Filter.prototype.getValues = function() { + return this.values; +}; + +/** + * Retrieve one value of the filter + * @param {Number} index + * @return {*} value + */ +Filter.prototype.getValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; + + return this.values[index]; +}; + + +/** + * Retrieve the (filtered) dataPoints for the currently selected filter index + * @param {Number} [index] (optional) + * @return {Array} dataPoints + */ +Filter.prototype._getDataPoints = function(index) { + if (index === undefined) + index = this.index; + + if (index === undefined) + return []; + + var dataPoints; + if (this.dataPoints[index]) { + dataPoints = this.dataPoints[index]; + } + else { + var f = {}; + f.column = this.column; + f.value = this.values[index]; + + var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); + dataPoints = this.graph._getDataPoints(dataView); + + this.dataPoints[index] = dataPoints; + } + + return dataPoints; +}; + + + +/** + * Set a callback function when the filter is fully loaded. + */ +Filter.prototype.setOnLoadCallback = function(callback) { + this.onLoadCallback = callback; +}; + + +/** + * Add a value to the list with available values for this filter + * No double entries will be created. + * @param {Number} index + */ +Filter.prototype.selectValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; + + this.index = index; + this.value = this.values[index]; +}; + +/** + * Load all filtered rows in the background one by one + * Start this method without providing an index! + */ +Filter.prototype.loadInBackground = function(index) { + if (index === undefined) + index = 0; + + var frame = this.graph.frame; + + if (index < this.values.length) { + var dataPointsTemp = this._getDataPoints(index); + //this.graph.redrawInfo(); // TODO: not neat + + // create a progress box + if (frame.progress === undefined) { + frame.progress = document.createElement('DIV'); + frame.progress.style.position = 'absolute'; + frame.progress.style.color = 'gray'; + frame.appendChild(frame.progress); + } + var progress = this.getLoadedProgress(); + frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; + // TODO: this is no nice solution... + frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider + frame.progress.style.left = 10 + 'px'; + + var me = this; + setTimeout(function() {me.loadInBackground(index+1);}, 10); + this.loaded = false; + } + else { + this.loaded = true; + + // remove the progress box + if (frame.progress !== undefined) { + frame.removeChild(frame.progress); + frame.progress = undefined; + } + + if (this.onLoadCallback) + this.onLoadCallback(); + } +}; + +module.exports = Filter; diff --git a/lib/graph3d/Graph3d.js b/lib/graph3d/Graph3d.js index 038efe8b..96e4cb69 100644 --- a/lib/graph3d/Graph3d.js +++ b/lib/graph3d/Graph3d.js @@ -1,3 +1,11 @@ +var Emitter = require('emitter-component'); +var DataSet = require('../DataSet'); +var DataView = require('../DataView'); +var Point3d = require('./Point3d'); +var Point2d = require('./Point2d'); +var Filter = require('./Filter'); +var StepNumber = require('./StepNumber'); + /** * @constructor Graph3d * Graph3d displays data in 3d. @@ -715,19 +723,6 @@ Graph3d.prototype._getDataPoints = function (data) { return dataPoints; }; - - - -/** - * Append suffix 'px' to provided value x - * @param {int} x An integer value - * @return {string} the string value of x, followed by the suffix 'px' - */ -Graph3d.px = function(x) { - return x + 'px'; -}; - - /** * Create the main frame for the Graph3d. * This function is executed once when a Graph3d object is created. The frame @@ -2455,458 +2450,6 @@ G3DpreventDefault = function (event) { } }; - - -/** - * @prototype Point3d - * @param {Number} x - * @param {Number} y - * @param {Number} z - */ -function Point3d(x, y, z) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - this.z = z !== undefined ? z : 0; -}; - -/** - * Subtract the two provided points, returns a-b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a-b - */ -Point3d.subtract = function(a, b) { - var sub = new Point3d(); - sub.x = a.x - b.x; - sub.y = a.y - b.y; - sub.z = a.z - b.z; - return sub; -}; - -/** - * Add the two provided points, returns a+b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a+b - */ -Point3d.add = function(a, b) { - var sum = new Point3d(); - sum.x = a.x + b.x; - sum.y = a.y + b.y; - sum.z = a.z + b.z; - return sum; -}; - -/** - * Calculate the average of two 3d points - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} The average, (a+b)/2 - */ -Point3d.avg = function(a, b) { - return new Point3d( - (a.x + b.x) / 2, - (a.y + b.y) / 2, - (a.z + b.z) / 2 - ); -}; - -/** - * Calculate the cross product of the two provided points, returns axb - * Documentation: http://en.wikipedia.org/wiki/Cross_product - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} cross product axb - */ -Point3d.crossProduct = function(a, b) { - var crossproduct = new Point3d(); - - crossproduct.x = a.y * b.z - a.z * b.y; - crossproduct.y = a.z * b.x - a.x * b.z; - crossproduct.z = a.x * b.y - a.y * b.x; - - return crossproduct; -}; - - -/** - * Rtrieve the length of the vector (or the distance from this point to the origin - * @return {Number} length - */ -Point3d.prototype.length = function() { - return Math.sqrt( - this.x * this.x + - this.y * this.y + - this.z * this.z - ); -}; - -/** - * @prototype Point2d - */ -Point2d = function (x, y) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; -}; - - -/** - * @class Filter - * - * @param {DataSet} data The google data table - * @param {Number} column The index of the column to be filtered - * @param {Graph} graph The graph - */ -function Filter (data, column, graph) { - this.data = data; - this.column = column; - this.graph = graph; // the parent graph - - this.index = undefined; - this.value = undefined; - - // read all distinct values and select the first one - this.values = graph.getDistinctValues(data.get(), this.column); - - // sort both numeric and string values correctly - this.values.sort(function (a, b) { - return a > b ? 1 : a < b ? -1 : 0; - }); - - if (this.values.length > 0) { - this.selectValue(0); - } - - // create an array with the filtered datapoints. this will be loaded afterwards - this.dataPoints = []; - - this.loaded = false; - this.onLoadCallback = undefined; - - if (graph.animationPreload) { - this.loaded = false; - this.loadInBackground(); - } - else { - this.loaded = true; - } -}; - - -/** - * Return the label - * @return {string} label - */ -Filter.prototype.isLoaded = function() { - return this.loaded; -}; - - -/** - * Return the loaded progress - * @return {Number} percentage between 0 and 100 - */ -Filter.prototype.getLoadedProgress = function() { - var len = this.values.length; - - var i = 0; - while (this.dataPoints[i]) { - i++; - } - - return Math.round(i / len * 100); -}; - - -/** - * Return the label - * @return {string} label - */ -Filter.prototype.getLabel = function() { - return this.graph.filterLabel; -}; - - -/** - * Return the columnIndex of the filter - * @return {Number} columnIndex - */ -Filter.prototype.getColumn = function() { - return this.column; -}; - -/** - * Return the currently selected value. Returns undefined if there is no selection - * @return {*} value - */ -Filter.prototype.getSelectedValue = function() { - if (this.index === undefined) - return undefined; - - return this.values[this.index]; -}; - -/** - * Retrieve all values of the filter - * @return {Array} values - */ -Filter.prototype.getValues = function() { - return this.values; -}; - -/** - * Retrieve one value of the filter - * @param {Number} index - * @return {*} value - */ -Filter.prototype.getValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; - - return this.values[index]; -}; - - -/** - * Retrieve the (filtered) dataPoints for the currently selected filter index - * @param {Number} [index] (optional) - * @return {Array} dataPoints - */ -Filter.prototype._getDataPoints = function(index) { - if (index === undefined) - index = this.index; - - if (index === undefined) - return []; - - var dataPoints; - if (this.dataPoints[index]) { - dataPoints = this.dataPoints[index]; - } - else { - var f = {}; - f.column = this.column; - f.value = this.values[index]; - - var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); - dataPoints = this.graph._getDataPoints(dataView); - - this.dataPoints[index] = dataPoints; - } - - return dataPoints; -}; - - - -/** - * Set a callback function when the filter is fully loaded. - */ -Filter.prototype.setOnLoadCallback = function(callback) { - this.onLoadCallback = callback; -}; - - -/** - * Add a value to the list with available values for this filter - * No double entries will be created. - * @param {Number} index - */ -Filter.prototype.selectValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; - - this.index = index; - this.value = this.values[index]; -}; - -/** - * Load all filtered rows in the background one by one - * Start this method without providing an index! - */ -Filter.prototype.loadInBackground = function(index) { - if (index === undefined) - index = 0; - - var frame = this.graph.frame; - - if (index < this.values.length) { - var dataPointsTemp = this._getDataPoints(index); - //this.graph.redrawInfo(); // TODO: not neat - - // create a progress box - if (frame.progress === undefined) { - frame.progress = document.createElement('DIV'); - frame.progress.style.position = 'absolute'; - frame.progress.style.color = 'gray'; - frame.appendChild(frame.progress); - } - var progress = this.getLoadedProgress(); - frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; - // TODO: this is no nice solution... - frame.progress.style.bottom = Graph3d.px(60); // TODO: use height of slider - frame.progress.style.left = Graph3d.px(10); - - var me = this; - setTimeout(function() {me.loadInBackground(index+1);}, 10); - this.loaded = false; - } - else { - this.loaded = true; - - // remove the progress box - if (frame.progress !== undefined) { - frame.removeChild(frame.progress); - frame.progress = undefined; - } - - if (this.onLoadCallback) - this.onLoadCallback(); - } -}; - - - -/** - * @prototype StepNumber - * The class StepNumber is an iterator for Numbers. You provide a start and end - * value, and a best step size. StepNumber itself rounds to fixed values and - * a finds the step that best fits the provided step. - * - * If prettyStep is true, the step size is chosen as close as possible to the - * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... - * - * Example usage: - * var step = new StepNumber(0, 10, 2.5, true); - * step.start(); - * while (!step.end()) { - * alert(step.getCurrent()); - * step.next(); - * } - * - * Version: 1.0 - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) - */ -StepNumber = function (start, end, step, prettyStep) { - // set default values - this._start = 0; - this._end = 0; - this._step = 1; - this.prettyStep = true; - this.precision = 5; - - this._current = 0; - this.setRange(start, end, step, prettyStep); -}; - -/** - * Set a new range: start, end and step. - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) - */ -StepNumber.prototype.setRange = function(start, end, step, prettyStep) { - this._start = start ? start : 0; - this._end = end ? end : 0; - - this.setStep(step, prettyStep); -}; - -/** - * Set a new step size - * @param {Number} step New step size. Must be a positive value - * @param {boolean} prettyStep Optional. If true, the provided step is rounded - * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) - */ -StepNumber.prototype.setStep = function(step, prettyStep) { - if (step === undefined || step <= 0) - return; - - if (prettyStep !== undefined) - this.prettyStep = prettyStep; - - if (this.prettyStep === true) - this._step = StepNumber.calculatePrettyStep(step); - else - this._step = step; -}; - -/** - * Calculate a nice step size, closest to the desired step size. - * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an - * integer Number. For example 1, 2, 5, 10, 20, 50, etc... - * @param {Number} step Desired step size - * @return {Number} Nice step size - */ -StepNumber.calculatePrettyStep = function (step) { - var log10 = function (x) {return Math.log(x) / Math.LN10;}; - - // try three steps (multiple of 1, 2, or 5 - var step1 = Math.pow(10, Math.round(log10(step))), - step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), - step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); - - // choose the best step (closest to minimum step) - var prettyStep = step1; - if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; - if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; - - // for safety - if (prettyStep <= 0) { - prettyStep = 1; - } - - return prettyStep; -}; - -/** - * returns the current value of the step - * @return {Number} current value - */ -StepNumber.prototype.getCurrent = function () { - return parseFloat(this._current.toPrecision(this.precision)); -}; - -/** - * returns the current step size - * @return {Number} current step size - */ -StepNumber.prototype.getStep = function () { - return this._step; -}; - -/** - * Set the current value to the largest value smaller than start, which - * is a multiple of the step size - */ -StepNumber.prototype.start = function() { - this._current = this._start - this._start % this._step; -}; - -/** - * Do a step, add the step size to the current value - */ -StepNumber.prototype.next = function () { - this._current += this._step; -}; - -/** - * Returns true whether the end is reached - * @return {boolean} True if the current value has passed the end value. - */ -StepNumber.prototype.end = function () { - return (this._current > this._end); -}; - - /** * @constructor Slider * @@ -2981,7 +2524,7 @@ function Slider(container, options) { this.playTimeout = undefined; this.playInterval = 1000; // milliseconds this.playLoop = true; -}; +} /** * Select the previous index @@ -3308,3 +2851,4 @@ getMouseY = function(event) { return event.targetTouches[0] && event.targetTouches[0].clientY || 0; }; +module.exports = Graph3d; diff --git a/lib/graph3d/Point2d.js b/lib/graph3d/Point2d.js new file mode 100644 index 00000000..feec858e --- /dev/null +++ b/lib/graph3d/Point2d.js @@ -0,0 +1,11 @@ +/** + * @prototype Point2d + * @param {Number} [x] + * @param {Number} [y] + */ +Point2d = function (x, y) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; +}; + +module.exports = Point2d; diff --git a/lib/graph3d/Point3d.js b/lib/graph3d/Point3d.js new file mode 100644 index 00000000..0892e693 --- /dev/null +++ b/lib/graph3d/Point3d.js @@ -0,0 +1,85 @@ +/** + * @prototype Point3d + * @param {Number} [x] + * @param {Number} [y] + * @param {Number} [z] + */ +function Point3d(x, y, z) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + this.z = z !== undefined ? z : 0; +}; + +/** + * Subtract the two provided points, returns a-b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a-b + */ +Point3d.subtract = function(a, b) { + var sub = new Point3d(); + sub.x = a.x - b.x; + sub.y = a.y - b.y; + sub.z = a.z - b.z; + return sub; +}; + +/** + * Add the two provided points, returns a+b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a+b + */ +Point3d.add = function(a, b) { + var sum = new Point3d(); + sum.x = a.x + b.x; + sum.y = a.y + b.y; + sum.z = a.z + b.z; + return sum; +}; + +/** + * Calculate the average of two 3d points + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} The average, (a+b)/2 + */ +Point3d.avg = function(a, b) { + return new Point3d( + (a.x + b.x) / 2, + (a.y + b.y) / 2, + (a.z + b.z) / 2 + ); +}; + +/** + * Calculate the cross product of the two provided points, returns axb + * Documentation: http://en.wikipedia.org/wiki/Cross_product + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} cross product axb + */ +Point3d.crossProduct = function(a, b) { + var crossproduct = new Point3d(); + + crossproduct.x = a.y * b.z - a.z * b.y; + crossproduct.y = a.z * b.x - a.x * b.z; + crossproduct.z = a.x * b.y - a.y * b.x; + + return crossproduct; +}; + + +/** + * Rtrieve the length of the vector (or the distance from this point to the origin + * @return {Number} length + */ +Point3d.prototype.length = function() { + return Math.sqrt( + this.x * this.x + + this.y * this.y + + this.z * this.z + ); +}; + +module.exports = Point3d; diff --git a/lib/graph3d/StepNumber.js b/lib/graph3d/StepNumber.js new file mode 100644 index 00000000..6c11a37c --- /dev/null +++ b/lib/graph3d/StepNumber.js @@ -0,0 +1,140 @@ +/** + * @prototype StepNumber + * The class StepNumber is an iterator for Numbers. You provide a start and end + * value, and a best step size. StepNumber itself rounds to fixed values and + * a finds the step that best fits the provided step. + * + * If prettyStep is true, the step size is chosen as close as possible to the + * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... + * + * Example usage: + * var step = new StepNumber(0, 10, 2.5, true); + * step.start(); + * while (!step.end()) { + * alert(step.getCurrent()); + * step.next(); + * } + * + * Version: 1.0 + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ +function StepNumber(start, end, step, prettyStep) { + // set default values + this._start = 0; + this._end = 0; + this._step = 1; + this.prettyStep = true; + this.precision = 5; + + this._current = 0; + this.setRange(start, end, step, prettyStep); +}; + +/** + * Set a new range: start, end and step. + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ +StepNumber.prototype.setRange = function(start, end, step, prettyStep) { + this._start = start ? start : 0; + this._end = end ? end : 0; + + this.setStep(step, prettyStep); +}; + +/** + * Set a new step size + * @param {Number} step New step size. Must be a positive value + * @param {boolean} prettyStep Optional. If true, the provided step is rounded + * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ +StepNumber.prototype.setStep = function(step, prettyStep) { + if (step === undefined || step <= 0) + return; + + if (prettyStep !== undefined) + this.prettyStep = prettyStep; + + if (this.prettyStep === true) + this._step = StepNumber.calculatePrettyStep(step); + else + this._step = step; +}; + +/** + * Calculate a nice step size, closest to the desired step size. + * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an + * integer Number. For example 1, 2, 5, 10, 20, 50, etc... + * @param {Number} step Desired step size + * @return {Number} Nice step size + */ +StepNumber.calculatePrettyStep = function (step) { + var log10 = function (x) {return Math.log(x) / Math.LN10;}; + + // try three steps (multiple of 1, 2, or 5 + var step1 = Math.pow(10, Math.round(log10(step))), + step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), + step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); + + // choose the best step (closest to minimum step) + var prettyStep = step1; + if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; + if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; + + // for safety + if (prettyStep <= 0) { + prettyStep = 1; + } + + return prettyStep; +}; + +/** + * returns the current value of the step + * @return {Number} current value + */ +StepNumber.prototype.getCurrent = function () { + return parseFloat(this._current.toPrecision(this.precision)); +}; + +/** + * returns the current step size + * @return {Number} current step size + */ +StepNumber.prototype.getStep = function () { + return this._step; +}; + +/** + * Set the current value to the largest value smaller than start, which + * is a multiple of the step size + */ +StepNumber.prototype.start = function() { + this._current = this._start - this._start % this._step; +}; + +/** + * Do a step, add the step size to the current value + */ +StepNumber.prototype.next = function () { + this._current += this._step; +}; + +/** + * Returns true whether the end is reached + * @return {boolean} True if the current value has passed the end value. + */ +StepNumber.prototype.end = function () { + return (this._current > this._end); +}; + +module.exports = StepNumber; diff --git a/lib/module/header.js b/lib/header.js similarity index 100% rename from lib/module/header.js rename to lib/header.js diff --git a/lib/module/exports.js b/lib/module/exports.js deleted file mode 100644 index 076e04cf..00000000 --- a/lib/module/exports.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * vis.js module exports - */ -var vis = { - moment: moment, - - util: util, - DOMutil: DOMutil, - - DataSet: DataSet, - DataView: DataView, - - Timeline: Timeline, - Graph2d: Graph2d, - timeline: { - DataStep: DataStep, - Range: Range, - stack: stack, - TimeStep: TimeStep, - - components: { - items: { - Item: Item, - ItemBox: ItemBox, - ItemPoint: ItemPoint, - ItemRange: ItemRange - }, - - Component: Component, - CurrentTime: CurrentTime, - CustomTime: CustomTime, - DataAxis: DataAxis, - GraphGroup: GraphGroup, - Group: Group, - ItemSet: ItemSet, - Legend: Legend, - LineGraph: LineGraph, - TimeAxis: TimeAxis - } - }, - - Network: Network, - network: { - Edge: Edge, - Groups: Groups, - Images: Images, - Node: Node, - Popup: Popup - }, - - // Deprecated since v3.0.0 - Graph: function () { - throw new Error('Graph is renamed to Network. Please create a graph as new vis.Network(...)'); - }, - - Graph3d: Graph3d -}; - -/** - * CommonJS module exports - */ -if (typeof exports !== 'undefined') { - exports = vis; -} -if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { - module.exports = vis; -} - -/** - * AMD module exports - */ -if (typeof(define) === 'function') { - define(function () { - return vis; - }); -} - -/** - * Window exports - */ -if (typeof window !== 'undefined') { - // attach the module to the window, load as a regular javascript file - window['vis'] = vis; -} diff --git a/lib/module/hammer.js b/lib/module/hammer.js new file mode 100644 index 00000000..fbea30d3 --- /dev/null +++ b/lib/module/hammer.js @@ -0,0 +1,10 @@ +// Only load hammer.js when in a browser environment +// (loading hammer.js in a node.js environment gives errors) +if (typeof window !== 'undefined') { + module.exports = require('hammerjs'); +} +else { + module.exports = function () { + throw Error('hammer.js is only available in a browser, not in node.js.'); + } +} diff --git a/lib/module/imports.js b/lib/module/imports.js deleted file mode 100644 index bda6f792..00000000 --- a/lib/module/imports.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * vis.js module imports - */ - -// Try to load dependencies from the global window object. -// If not available there, load via require. - -var moment = (typeof window !== 'undefined') && window['moment'] || require('moment'); -var Emitter = require('emitter-component'); - -var Hammer; -if (typeof window !== 'undefined') { - // load hammer.js only when running in a browser (where window is available) - Hammer = window['Hammer'] || require('hammerjs'); -} -else { - Hammer = function () { - throw Error('hammer.js is only available in a browser, not in node.js.'); - } -} - -var mousetrap; -if (typeof window !== 'undefined') { - // load mousetrap.js only when running in a browser (where window is available) - mousetrap = window['mousetrap'] || require('mousetrap'); -} -else { - mousetrap = function () { - throw Error('mouseTrap is only available in a browser, not in node.js.'); - } -} diff --git a/lib/module/moment.js b/lib/module/moment.js new file mode 100644 index 00000000..7a65f58c --- /dev/null +++ b/lib/module/moment.js @@ -0,0 +1,3 @@ +// first check if moment.js is already loaded in the browser window, if so, +// use this instance. Else, load via commonjs. +module.exports = (typeof window !== 'undefined') && window['moment'] || require('moment'); diff --git a/lib/network/Edge.js b/lib/network/Edge.js index 305a7fe8..5a32d1ee 100644 --- a/lib/network/Edge.js +++ b/lib/network/Edge.js @@ -1,3 +1,5 @@ +var util = require('../util'); + /** * @class Edge * @@ -861,7 +863,7 @@ Edge.prototype._drawControlNodes = function(ctx) { else { this.controlNodes = {from:null, to:null, positions:{}}; } -} +}; /** * Enable control nodes. @@ -869,7 +871,7 @@ Edge.prototype._drawControlNodes = function(ctx) { */ Edge.prototype._enableControlNodes = function() { this.controlNodesEnabled = true; -} +}; /** * disable control nodes @@ -877,7 +879,7 @@ Edge.prototype._enableControlNodes = function() { */ 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. @@ -904,7 +906,7 @@ Edge.prototype._getSelectedControlNode = function(x,y) { else { return null; } -} +}; /** @@ -922,7 +924,7 @@ Edge.prototype._restoreControlNodes = function() { this.connectedNode = null; this.controlNodes.to.unselect(); } -} +}; /** * this calculates the position of the control nodes on the edges of the parent nodes. @@ -961,4 +963,6 @@ Edge.prototype.getControlNodePositions = function(ctx) { } return {from:{x:xFrom,y:yFrom},to:{x:xTo,y:yTo}}; -} +}; + +module.exports = Edge; \ No newline at end of file diff --git a/lib/network/Groups.js b/lib/network/Groups.js index ee8fc3ae..90e21d3c 100644 --- a/lib/network/Groups.js +++ b/lib/network/Groups.js @@ -1,3 +1,5 @@ +var util = require('../util'); + /** * @class Groups * This class can store groups and properties specific for groups. @@ -78,3 +80,5 @@ Groups.prototype.add = function (groupname, style) { } return style; }; + +module.exports = Groups; diff --git a/lib/network/Images.js b/lib/network/Images.js index 0c661b65..266eb491 100644 --- a/lib/network/Images.js +++ b/lib/network/Images.js @@ -39,3 +39,5 @@ Images.prototype.load = function(url) { return img; }; + +module.exports = Images; diff --git a/lib/network/Network.js b/lib/network/Network.js index 462ee806..f61ed05e 100644 --- a/lib/network/Network.js +++ b/lib/network/Network.js @@ -1,3 +1,20 @@ +var Emitter = require('emitter-component'); +var Hammer = require('hammerjs'); +var mousetrap = require('mousetrap'); +var util = require('../util'); +var DataSet = require('../DataSet'); +var DataView = require('../DataView'); +var dotparser = require('./dotparser'); +var Groups = require('./Groups'); +var Images = require('./Images'); +var Node = require('./Node'); +var Edge = require('./Edge'); +var Popup = require('./Popup'); +var MixinLoader = require('./mixins/MixinLoader'); + +// Load custom shapes into CanvasRenderingContext2D +require('./shapes'); + /** * @constructor Network * Create a network visualization, displaying nodes and edges. @@ -495,7 +512,7 @@ Network.prototype.setData = function(data, disableStart) { if (data && data.dot) { // parse DOT file if(data && data.dot) { - var dotData = vis.util.DOTToGraph(data.dot); + var dotData = dotparser.DOTToGraph(data.dot); this.setData(dotData); return; } @@ -874,8 +891,8 @@ Network.prototype._createKeyBinds = function() { */ Network.prototype._getPointer = function (touch) { return { - x: touch.pageX - vis.util.getAbsoluteLeft(this.frame.canvas), - y: touch.pageY - vis.util.getAbsoluteTop(this.frame.canvas) + x: touch.pageX - util.getAbsoluteLeft(this.frame.canvas), + y: touch.pageY - util.getAbsoluteTop(this.frame.canvas) }; }; @@ -2232,9 +2249,9 @@ Network.prototype._createBezierNodes = function() { * @private */ Network.prototype._initializeMixinLoaders = function () { - for (var mixinFunction in networkMixinLoaders) { - if (networkMixinLoaders.hasOwnProperty(mixinFunction)) { - Network.prototype[mixinFunction] = networkMixinLoaders[mixinFunction]; + for (var mixin in MixinLoader) { + if (MixinLoader.hasOwnProperty(mixin)) { + Network.prototype[mixin] = MixinLoader[mixin]; } } }; @@ -2289,11 +2306,4 @@ Network.prototype.focusOnNode = function (nodeId, zoomLevel) { } }; - - - - - - - - +module.exports = Network; diff --git a/lib/network/Node.js b/lib/network/Node.js index 1de217d0..76beec71 100644 --- a/lib/network/Node.js +++ b/lib/network/Node.js @@ -1,3 +1,5 @@ +var util = require('../util'); + /** * @class Node * A node. A node can be connected to other nodes via one or multiple edges. @@ -967,3 +969,4 @@ Node.prototype.updateVelocity = function(massBeforeClustering) { this.vy = Math.sqrt(energyBefore/this.mass); }; +module.exports = Node; diff --git a/lib/network/Popup.js b/lib/network/Popup.js index 2dd20404..072d90d8 100644 --- a/lib/network/Popup.js +++ b/lib/network/Popup.js @@ -130,3 +130,5 @@ Popup.prototype.show = function (show) { Popup.prototype.hide = function () { this.frame.style.visibility = "hidden"; }; + +module.exports = Popup; diff --git a/lib/network/dotparser.js b/lib/network/dotparser.js index 71576079..d395a36c 100644 --- a/lib/network/dotparser.js +++ b/lib/network/dotparser.js @@ -1,407 +1,519 @@ -(function(exports) { - /** - * Parse a text source containing data in DOT language into a JSON object. - * The object contains two lists: one with nodes and one with edges. - * - * DOT language reference: http://www.graphviz.org/doc/info/lang.html - * - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graph An object containing two parameters: - * {Object[]} nodes - * {Object[]} edges - */ - function parseDOT (data) { - dot = data; - return parseGraph(); - } - - // token types enumeration - var TOKENTYPE = { - NULL : 0, - DELIMITER : 1, - IDENTIFIER: 2, - UNKNOWN : 3 - }; - - // map with all delimiters - var DELIMITERS = { - '{': true, - '}': true, - '[': true, - ']': true, - ';': true, - '=': true, - ',': true, - - '->': true, - '--': true - }; - - var dot = ''; // current dot file - var index = 0; // current index in dot file - var c = ''; // current token character in expr - var token = ''; // current token - var tokenType = TOKENTYPE.NULL; // type of the token - - /** - * Get the first character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function first() { - index = 0; - c = dot.charAt(0); - } - - /** - * Get the next character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function next() { - index++; - c = dot.charAt(index); - } - - /** - * Preview the next character from the dot file. - * @return {String} cNext - */ - function nextPreview() { - return dot.charAt(index + 1); - } - - /** - * Test whether given character is alphabetic or numeric - * @param {String} c - * @return {Boolean} isAlphaNumeric - */ - var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; - function isAlphaNumeric(c) { - return regexAlphaNumeric.test(c); - } - - /** - * Merge all properties of object b into object b - * @param {Object} a - * @param {Object} b - * @return {Object} a - */ - function merge (a, b) { - if (!a) { - a = {}; - } +/** + * Parse a text source containing data in DOT language into a JSON object. + * The object contains two lists: one with nodes and one with edges. + * + * DOT language reference: http://www.graphviz.org/doc/info/lang.html + * + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graph An object containing two parameters: + * {Object[]} nodes + * {Object[]} edges + */ +function parseDOT (data) { + dot = data; + return parseGraph(); +} + +// token types enumeration +var TOKENTYPE = { + NULL : 0, + DELIMITER : 1, + IDENTIFIER: 2, + UNKNOWN : 3 +}; + +// map with all delimiters +var DELIMITERS = { + '{': true, + '}': true, + '[': true, + ']': true, + ';': true, + '=': true, + ',': true, + + '->': true, + '--': true +}; + +var dot = ''; // current dot file +var index = 0; // current index in dot file +var c = ''; // current token character in expr +var token = ''; // current token +var tokenType = TOKENTYPE.NULL; // type of the token + +/** + * Get the first character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. + */ +function first() { + index = 0; + c = dot.charAt(0); +} + +/** + * Get the next character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. + */ +function next() { + index++; + c = dot.charAt(index); +} + +/** + * Preview the next character from the dot file. + * @return {String} cNext + */ +function nextPreview() { + return dot.charAt(index + 1); +} + +/** + * Test whether given character is alphabetic or numeric + * @param {String} c + * @return {Boolean} isAlphaNumeric + */ +var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; +function isAlphaNumeric(c) { + return regexAlphaNumeric.test(c); +} + +/** + * Merge all properties of object b into object b + * @param {Object} a + * @param {Object} b + * @return {Object} a + */ +function merge (a, b) { + if (!a) { + a = {}; + } - if (b) { - for (var name in b) { - if (b.hasOwnProperty(name)) { - a[name] = b[name]; - } - } - } - return a; - } - - /** - * Set a value in an object, where the provided parameter name can be a - * path with nested parameters. For example: - * - * var obj = {a: 2}; - * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} - * - * @param {Object} obj - * @param {String} path A parameter name or dot-separated parameter path, - * like "color.highlight.border". - * @param {*} value - */ - function setValue(obj, path, value) { - var keys = path.split('.'); - var o = obj; - while (keys.length) { - var key = keys.shift(); - if (keys.length) { - // this isn't the end point - if (!o[key]) { - o[key] = {}; - } - o = o[key]; - } - else { - // this is the end point - o[key] = value; + if (b) { + for (var name in b) { + if (b.hasOwnProperty(name)) { + a[name] = b[name]; } } } - - /** - * Add a node to a graph object. If there is already a node with - * the same id, their attributes will be merged. - * @param {Object} graph - * @param {Object} node - */ - function addNode(graph, node) { - var i, len; - var current = null; - - // find root graph (in case of subgraph) - var graphs = [graph]; // list with all graphs from current graph to root graph - var root = graph; - while (root.parent) { - graphs.push(root.parent); - root = root.parent; - } - - // find existing node (at root level) by its id - if (root.nodes) { - for (i = 0, len = root.nodes.length; i < len; i++) { - if (node.id === root.nodes[i].id) { - current = root.nodes[i]; - break; - } + return a; +} + +/** + * Set a value in an object, where the provided parameter name can be a + * path with nested parameters. For example: + * + * var obj = {a: 2}; + * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} + * + * @param {Object} obj + * @param {String} path A parameter name or dot-separated parameter path, + * like "color.highlight.border". + * @param {*} value + */ +function setValue(obj, path, value) { + var keys = path.split('.'); + var o = obj; + while (keys.length) { + var key = keys.shift(); + if (keys.length) { + // this isn't the end point + if (!o[key]) { + o[key] = {}; } + o = o[key]; } - - if (!current) { - // this is a new node - current = { - id: node.id - }; - if (graph.node) { - // clone default attributes - current.attr = merge(current.attr, graph.node); - } + else { + // this is the end point + o[key] = value; } + } +} + +/** + * Add a node to a graph object. If there is already a node with + * the same id, their attributes will be merged. + * @param {Object} graph + * @param {Object} node + */ +function addNode(graph, node) { + var i, len; + var current = null; + + // find root graph (in case of subgraph) + var graphs = [graph]; // list with all graphs from current graph to root graph + var root = graph; + while (root.parent) { + graphs.push(root.parent); + root = root.parent; + } - // add node to this (sub)graph and all its parent graphs - for (i = graphs.length - 1; i >= 0; i--) { - var g = graphs[i]; - - if (!g.nodes) { - g.nodes = []; + // find existing node (at root level) by its id + if (root.nodes) { + for (i = 0, len = root.nodes.length; i < len; i++) { + if (node.id === root.nodes[i].id) { + current = root.nodes[i]; + break; } - if (g.nodes.indexOf(current) == -1) { - g.nodes.push(current); - } - } - - // merge attributes - if (node.attr) { - current.attr = merge(current.attr, node.attr); } } - /** - * Add an edge to a graph object - * @param {Object} graph - * @param {Object} edge - */ - function addEdge(graph, edge) { - if (!graph.edges) { - graph.edges = []; - } - graph.edges.push(edge); - if (graph.edge) { - var attr = merge({}, graph.edge); // clone default attributes - edge.attr = merge(attr, edge.attr); // merge attributes + if (!current) { + // this is a new node + current = { + id: node.id + }; + if (graph.node) { + // clone default attributes + current.attr = merge(current.attr, graph.node); } } - /** - * Create an edge to a graph object - * @param {Object} graph - * @param {String | Number | Object} from - * @param {String | Number | Object} to - * @param {String} type - * @param {Object | null} attr - * @return {Object} edge - */ - function createEdge(graph, from, to, type, attr) { - var edge = { - from: from, - to: to, - type: type - }; + // add node to this (sub)graph and all its parent graphs + for (i = graphs.length - 1; i >= 0; i--) { + var g = graphs[i]; - if (graph.edge) { - edge.attr = merge({}, graph.edge); // clone default attributes + if (!g.nodes) { + g.nodes = []; + } + if (g.nodes.indexOf(current) == -1) { + g.nodes.push(current); } - edge.attr = merge(edge.attr || {}, attr); // merge attributes - - return edge; } - /** - * Get next token in the current dot file. - * The token and token type are available as token and tokenType - */ - function getToken() { - tokenType = TOKENTYPE.NULL; - token = ''; + // merge attributes + if (node.attr) { + current.attr = merge(current.attr, node.attr); + } +} + +/** + * Add an edge to a graph object + * @param {Object} graph + * @param {Object} edge + */ +function addEdge(graph, edge) { + if (!graph.edges) { + graph.edges = []; + } + graph.edges.push(edge); + if (graph.edge) { + var attr = merge({}, graph.edge); // clone default attributes + edge.attr = merge(attr, edge.attr); // merge attributes + } +} + +/** + * Create an edge to a graph object + * @param {Object} graph + * @param {String | Number | Object} from + * @param {String | Number | Object} to + * @param {String} type + * @param {Object | null} attr + * @return {Object} edge + */ +function createEdge(graph, from, to, type, attr) { + var edge = { + from: from, + to: to, + type: type + }; - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } + if (graph.edge) { + edge.attr = merge({}, graph.edge); // clone default attributes + } + edge.attr = merge(edge.attr || {}, attr); // merge attributes + + return edge; +} + +/** + * Get next token in the current dot file. + * The token and token type are available as token and tokenType + */ +function getToken() { + tokenType = TOKENTYPE.NULL; + token = ''; + + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); + } - do { - var isComment = false; + do { + var isComment = false; - // skip comment - if (c == '#') { - // find the previous non-space character - var i = index - 1; - while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { - i--; - } - if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { - // the # is at the start of a line, this is indeed a line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } + // skip comment + if (c == '#') { + // find the previous non-space character + var i = index - 1; + while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { + i--; } - if (c == '/' && nextPreview() == '/') { - // skip line comment + if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { + // the # is at the start of a line, this is indeed a line comment while (c != '' && c != '\n') { next(); } isComment = true; } - if (c == '/' && nextPreview() == '*') { - // skip block comment - while (c != '') { - if (c == '*' && nextPreview() == '/') { - // end of block comment found. skip these last two characters - next(); - next(); - break; - } - else { - next(); - } - } - isComment = true; - } - - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + } + if (c == '/' && nextPreview() == '/') { + // skip line comment + while (c != '' && c != '\n') { next(); } + isComment = true; } - while (isComment); - - // check for end of dot file - if (c == '') { - // token is still empty - tokenType = TOKENTYPE.DELIMITER; - return; + if (c == '/' && nextPreview() == '*') { + // skip block comment + while (c != '') { + if (c == '*' && nextPreview() == '/') { + // end of block comment found. skip these last two characters + next(); + next(); + break; + } + else { + next(); + } + } + isComment = true; } - // check for delimiters consisting of 2 characters - var c2 = c + nextPreview(); - if (DELIMITERS[c2]) { - tokenType = TOKENTYPE.DELIMITER; - token = c2; - next(); + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter next(); - return; } + } + while (isComment); - // check for delimiters consisting of 1 character - if (DELIMITERS[c]) { - tokenType = TOKENTYPE.DELIMITER; - token = c; - next(); - return; - } + // check for end of dot file + if (c == '') { + // token is still empty + tokenType = TOKENTYPE.DELIMITER; + return; + } + + // check for delimiters consisting of 2 characters + var c2 = c + nextPreview(); + if (DELIMITERS[c2]) { + tokenType = TOKENTYPE.DELIMITER; + token = c2; + next(); + next(); + return; + } - // check for an identifier (number or string) - // TODO: more precise parsing of numbers/strings (and the port separator ':') - if (isAlphaNumeric(c) || c == '-') { + // check for delimiters consisting of 1 character + if (DELIMITERS[c]) { + tokenType = TOKENTYPE.DELIMITER; + token = c; + next(); + return; + } + + // check for an identifier (number or string) + // TODO: more precise parsing of numbers/strings (and the port separator ':') + if (isAlphaNumeric(c) || c == '-') { + token += c; + next(); + + while (isAlphaNumeric(c)) { token += c; next(); - - while (isAlphaNumeric(c)) { - token += c; - next(); - } - if (token == 'false') { - token = false; // convert to boolean - } - else if (token == 'true') { - token = true; // convert to boolean - } - else if (!isNaN(Number(token))) { - token = Number(token); // convert to number - } - tokenType = TOKENTYPE.IDENTIFIER; - return; } + if (token == 'false') { + token = false; // convert to boolean + } + else if (token == 'true') { + token = true; // convert to boolean + } + else if (!isNaN(Number(token))) { + token = Number(token); // convert to number + } + tokenType = TOKENTYPE.IDENTIFIER; + return; + } - // check for a string enclosed by double quotes - if (c == '"') { - next(); - while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { - token += c; - if (c == '"') { // skip the escape character - next(); - } + // check for a string enclosed by double quotes + if (c == '"') { + next(); + while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { + token += c; + if (c == '"') { // skip the escape character next(); } - if (c != '"') { - throw newSyntaxError('End of string " expected'); - } next(); - tokenType = TOKENTYPE.IDENTIFIER; - return; } - - // something unknown is found, wrong characters, a syntax error - tokenType = TOKENTYPE.UNKNOWN; - while (c != '') { - token += c; - next(); + if (c != '"') { + throw newSyntaxError('End of string " expected'); } - throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); + next(); + tokenType = TOKENTYPE.IDENTIFIER; + return; } - /** - * Parse a graph. - * @returns {Object} graph - */ - function parseGraph() { - var graph = {}; + // something unknown is found, wrong characters, a syntax error + tokenType = TOKENTYPE.UNKNOWN; + while (c != '') { + token += c; + next(); + } + throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); +} + +/** + * Parse a graph. + * @returns {Object} graph + */ +function parseGraph() { + var graph = {}; + + first(); + getToken(); + + // optional strict keyword + if (token == 'strict') { + graph.strict = true; + getToken(); + } + + // graph or digraph keyword + if (token == 'graph' || token == 'digraph') { + graph.type = token; + getToken(); + } - first(); + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + graph.id = token; getToken(); + } + + // open angle bracket + if (token != '{') { + throw newSyntaxError('Angle bracket { expected'); + } + getToken(); + + // statements + parseStatements(graph); - // optional strict keyword - if (token == 'strict') { - graph.strict = true; + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); + } + getToken(); + + // end of file + if (token !== '') { + throw newSyntaxError('End of file expected'); + } + getToken(); + + // remove temporary default properties + delete graph.node; + delete graph.edge; + delete graph.graph; + + return graph; +} + +/** + * Parse a list with statements. + * @param {Object} graph + */ +function parseStatements (graph) { + while (token !== '' && token != '}') { + parseStatement(graph); + if (token == ';') { getToken(); } + } +} + +/** + * Parse a single statement. Can be a an attribute statement, node + * statement, a series of node statements and edge statements, or a + * parameter. + * @param {Object} graph + */ +function parseStatement(graph) { + // parse subgraph + var subgraph = parseSubgraph(graph); + if (subgraph) { + // edge statements + parseEdge(graph, subgraph); - // graph or digraph keyword - if (token == 'graph' || token == 'digraph') { - graph.type = token; - getToken(); + return; + } + + // parse an attribute statement + var attr = parseAttributeStatement(graph); + if (attr) { + return; + } + + // parse node + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); + } + var id = token; // id can be a string or a number + getToken(); + + if (token == '=') { + // id statement + getToken(); + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); } + graph[id] = token; + getToken(); + // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " + } + else { + parseNodeStatement(graph, id); + } +} + +/** + * Parse a subgraph + * @param {Object} graph parent graph object + * @return {Object | null} subgraph + */ +function parseSubgraph (graph) { + var subgraph = null; + + // optional subgraph keyword + if (token == 'subgraph') { + subgraph = {}; + subgraph.type = 'subgraph'; + getToken(); // optional graph id if (tokenType == TOKENTYPE.IDENTIFIER) { - graph.id = token; + subgraph.id = token; getToken(); } + } - // open angle bracket - if (token != '{') { - throw newSyntaxError('Angle bracket { expected'); - } + // open angle bracket + if (token == '{') { getToken(); + if (!subgraph) { + subgraph = {}; + } + subgraph.parent = graph; + subgraph.node = graph.node; + subgraph.edge = graph.edge; + subgraph.graph = graph.graph; + // statements - parseStatements(graph); + parseStatements(subgraph); // close angle bracket if (token != '}') { @@ -409,421 +521,306 @@ } getToken(); - // end of file - if (token !== '') { - throw newSyntaxError('End of file expected'); + // remove temporary default properties + delete subgraph.node; + delete subgraph.edge; + delete subgraph.graph; + delete subgraph.parent; + + // register at the parent graph + if (!graph.subgraphs) { + graph.subgraphs = []; } + graph.subgraphs.push(subgraph); + } + + return subgraph; +} + +/** + * parse an attribute statement like "node [shape=circle fontSize=16]". + * Available keywords are 'node', 'edge', 'graph'. + * The previous list with default attributes will be replaced + * @param {Object} graph + * @returns {String | null} keyword Returns the name of the parsed attribute + * (node, edge, graph), or null if nothing + * is parsed. + */ +function parseAttributeStatement (graph) { + // attribute statements + if (token == 'node') { getToken(); - // remove temporary default properties - delete graph.node; - delete graph.edge; - delete graph.graph; + // node attributes + graph.node = parseAttributeList(); + return 'node'; + } + else if (token == 'edge') { + getToken(); - return graph; + // edge attributes + graph.edge = parseAttributeList(); + return 'edge'; } + else if (token == 'graph') { + getToken(); - /** - * Parse a list with statements. - * @param {Object} graph - */ - function parseStatements (graph) { - while (token !== '' && token != '}') { - parseStatement(graph); - if (token == ';') { - getToken(); - } - } + // graph attributes + graph.graph = parseAttributeList(); + return 'graph'; } - /** - * Parse a single statement. Can be a an attribute statement, node - * statement, a series of node statements and edge statements, or a - * parameter. - * @param {Object} graph - */ - function parseStatement(graph) { - // parse subgraph + return null; +} + +/** + * parse a node statement + * @param {Object} graph + * @param {String | Number} id + */ +function parseNodeStatement(graph, id) { + // node statement + var node = { + id: id + }; + var attr = parseAttributeList(); + if (attr) { + node.attr = attr; + } + addNode(graph, node); + + // edge statements + parseEdge(graph, id); +} + +/** + * Parse an edge or a series of edges + * @param {Object} graph + * @param {String | Number} from Id of the from node + */ +function parseEdge(graph, from) { + while (token == '->' || token == '--') { + var to; + var type = token; + getToken(); + var subgraph = parseSubgraph(graph); if (subgraph) { - // edge statements - parseEdge(graph, subgraph); - - return; - } - - // parse an attribute statement - var attr = parseAttributeStatement(graph); - if (attr) { - return; - } - - // parse node - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); + to = subgraph; } - var id = token; // id can be a string or a number - getToken(); - - if (token == '=') { - // id statement - getToken(); + else { if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); + throw newSyntaxError('Identifier or subgraph expected'); } - graph[id] = token; + to = token; + addNode(graph, { + id: to + }); getToken(); - // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " } - else { - parseNodeStatement(graph, id); - } - } - /** - * Parse a subgraph - * @param {Object} graph parent graph object - * @return {Object | null} subgraph - */ - function parseSubgraph (graph) { - var subgraph = null; + // parse edge attributes + var attr = parseAttributeList(); - // optional subgraph keyword - if (token == 'subgraph') { - subgraph = {}; - subgraph.type = 'subgraph'; - getToken(); + // create edge + var edge = createEdge(graph, from, to, type, attr); + addEdge(graph, edge); - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - subgraph.id = token; - getToken(); - } - } + from = to; + } +} - // open angle bracket - if (token == '{') { - getToken(); +/** + * Parse a set with attributes, + * for example [label="1.000", shape=solid] + * @return {Object | null} attr + */ +function parseAttributeList() { + var attr = null; - if (!subgraph) { - subgraph = {}; + while (token == '[') { + getToken(); + attr = {}; + while (token !== '' && token != ']') { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute name expected'); } - subgraph.parent = graph; - subgraph.node = graph.node; - subgraph.edge = graph.edge; - subgraph.graph = graph.graph; - - // statements - parseStatements(subgraph); + var name = token; - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); - } getToken(); - - // remove temporary default properties - delete subgraph.node; - delete subgraph.edge; - delete subgraph.graph; - delete subgraph.parent; - - // register at the parent graph - if (!graph.subgraphs) { - graph.subgraphs = []; + if (token != '=') { + throw newSyntaxError('Equal sign = expected'); } - graph.subgraphs.push(subgraph); - } - - return subgraph; - } - - /** - * parse an attribute statement like "node [shape=circle fontSize=16]". - * Available keywords are 'node', 'edge', 'graph'. - * The previous list with default attributes will be replaced - * @param {Object} graph - * @returns {String | null} keyword Returns the name of the parsed attribute - * (node, edge, graph), or null if nothing - * is parsed. - */ - function parseAttributeStatement (graph) { - // attribute statements - if (token == 'node') { - getToken(); - - // node attributes - graph.node = parseAttributeList(); - return 'node'; - } - else if (token == 'edge') { getToken(); - // edge attributes - graph.edge = parseAttributeList(); - return 'edge'; - } - else if (token == 'graph') { - getToken(); - - // graph attributes - graph.graph = parseAttributeList(); - return 'graph'; - } - - return null; - } - - /** - * parse a node statement - * @param {Object} graph - * @param {String | Number} id - */ - function parseNodeStatement(graph, id) { - // node statement - var node = { - id: id - }; - var attr = parseAttributeList(); - if (attr) { - node.attr = attr; - } - addNode(graph, node); - - // edge statements - parseEdge(graph, id); - } - - /** - * Parse an edge or a series of edges - * @param {Object} graph - * @param {String | Number} from Id of the from node - */ - function parseEdge(graph, from) { - while (token == '->' || token == '--') { - var to; - var type = token; - getToken(); - - var subgraph = parseSubgraph(graph); - if (subgraph) { - to = subgraph; - } - else { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier or subgraph expected'); - } - to = token; - addNode(graph, { - id: to - }); - getToken(); + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute value expected'); } + var value = token; + setValue(attr, name, value); // name can be a path - // parse edge attributes - var attr = parseAttributeList(); - - // create edge - var edge = createEdge(graph, from, to, type, attr); - addEdge(graph, edge); - - from = to; - } - } - - /** - * Parse a set with attributes, - * for example [label="1.000", shape=solid] - * @return {Object | null} attr - */ - function parseAttributeList() { - var attr = null; - - while (token == '[') { getToken(); - attr = {}; - while (token !== '' && token != ']') { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute name expected'); - } - var name = token; - - getToken(); - if (token != '=') { - throw newSyntaxError('Equal sign = expected'); - } + if (token ==',') { getToken(); - - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute value expected'); - } - var value = token; - setValue(attr, name, value); // name can be a path - - getToken(); - if (token ==',') { - getToken(); - } - } - - if (token != ']') { - throw newSyntaxError('Bracket ] expected'); } - getToken(); } - return attr; - } - - /** - * Create a syntax error with extra information on current token and index. - * @param {String} message - * @returns {SyntaxError} err - */ - function newSyntaxError(message) { - return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); - } - - /** - * Chop off text after a maximum length - * @param {String} text - * @param {Number} maxLength - * @returns {String} - */ - function chop (text, maxLength) { - return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); - } - - /** - * Execute a function fn for each pair of elements in two arrays - * @param {Array | *} array1 - * @param {Array | *} array2 - * @param {function} fn - */ - function forEach2(array1, array2, fn) { - if (array1 instanceof Array) { - array1.forEach(function (elem1) { - if (array2 instanceof Array) { - array2.forEach(function (elem2) { - fn(elem1, elem2); - }); - } - else { - fn(elem1, array2); - } - }); + if (token != ']') { + throw newSyntaxError('Bracket ] expected'); } - else { + getToken(); + } + + return attr; +} + +/** + * Create a syntax error with extra information on current token and index. + * @param {String} message + * @returns {SyntaxError} err + */ +function newSyntaxError(message) { + return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); +} + +/** + * Chop off text after a maximum length + * @param {String} text + * @param {Number} maxLength + * @returns {String} + */ +function chop (text, maxLength) { + return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); +} + +/** + * Execute a function fn for each pair of elements in two arrays + * @param {Array | *} array1 + * @param {Array | *} array2 + * @param {function} fn + */ +function forEach2(array1, array2, fn) { + if (array1 instanceof Array) { + array1.forEach(function (elem1) { if (array2 instanceof Array) { array2.forEach(function (elem2) { - fn(array1, elem2); + fn(elem1, elem2); }); } else { - fn(array1, array2); + fn(elem1, array2); } - } + }); } - - /** - * Convert a string containing a graph in DOT language into a map containing - * with nodes and edges in the format of graph. - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graphData - */ - function DOTToGraph (data) { - // parse the DOT file - var dotData = parseDOT(data); - var graphData = { - nodes: [], - edges: [], - options: {} - }; - - // copy the nodes - if (dotData.nodes) { - dotData.nodes.forEach(function (dotNode) { - var graphNode = { - id: dotNode.id, - label: String(dotNode.label || dotNode.id) - }; - merge(graphNode, dotNode.attr); - if (graphNode.image) { - graphNode.shape = 'image'; - } - graphData.nodes.push(graphNode); + else { + if (array2 instanceof Array) { + array2.forEach(function (elem2) { + fn(array1, elem2); }); } + else { + fn(array1, array2); + } + } +} + +/** + * Convert a string containing a graph in DOT language into a map containing + * with nodes and edges in the format of graph. + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graphData + */ +function DOTToGraph (data) { + // parse the DOT file + var dotData = parseDOT(data); + var graphData = { + nodes: [], + edges: [], + options: {} + }; - // copy the edges - if (dotData.edges) { - /** - * Convert an edge in DOT format to an edge with VisGraph format - * @param {Object} dotEdge - * @returns {Object} graphEdge - */ - function convertEdge(dotEdge) { - var graphEdge = { - from: dotEdge.from, - to: dotEdge.to - }; - merge(graphEdge, dotEdge.attr); - graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; - return graphEdge; + // copy the nodes + if (dotData.nodes) { + dotData.nodes.forEach(function (dotNode) { + var graphNode = { + id: dotNode.id, + label: String(dotNode.label || dotNode.id) + }; + merge(graphNode, dotNode.attr); + if (graphNode.image) { + graphNode.shape = 'image'; } + graphData.nodes.push(graphNode); + }); + } - dotData.edges.forEach(function (dotEdge) { - var from, to; - if (dotEdge.from instanceof Object) { - from = dotEdge.from.nodes; - } - else { - from = { - id: dotEdge.from - } - } + // copy the edges + if (dotData.edges) { + /** + * Convert an edge in DOT format to an edge with VisGraph format + * @param {Object} dotEdge + * @returns {Object} graphEdge + */ + function convertEdge(dotEdge) { + var graphEdge = { + from: dotEdge.from, + to: dotEdge.to + }; + merge(graphEdge, dotEdge.attr); + graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; + return graphEdge; + } - if (dotEdge.to instanceof Object) { - to = dotEdge.to.nodes; - } - else { - to = { - id: dotEdge.to - } + dotData.edges.forEach(function (dotEdge) { + var from, to; + if (dotEdge.from instanceof Object) { + from = dotEdge.from.nodes; + } + else { + from = { + id: dotEdge.from } + } - if (dotEdge.from instanceof Object && dotEdge.from.edges) { - dotEdge.from.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); + if (dotEdge.to instanceof Object) { + to = dotEdge.to.nodes; + } + else { + to = { + id: dotEdge.to } + } - forEach2(from, to, function (from, to) { - var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); + if (dotEdge.from instanceof Object && dotEdge.from.edges) { + dotEdge.from.edges.forEach(function (subEdge) { var graphEdge = convertEdge(subEdge); graphData.edges.push(graphEdge); }); + } - if (dotEdge.to instanceof Object && dotEdge.to.edges) { - dotEdge.to.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } + forEach2(from, to, function (from, to) { + var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); }); - } - // copy the options - if (dotData.attr) { - graphData.options = dotData.attr; - } + if (dotEdge.to instanceof Object && dotEdge.to.edges) { + dotEdge.to.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } + }); + } - return graphData; + // copy the options + if (dotData.attr) { + graphData.options = dotData.attr; } - // exports - exports.parseDOT = parseDOT; - exports.DOTToGraph = DOTToGraph; + return graphData; +} -})(typeof util !== 'undefined' ? util : exports); +// exports +exports.parseDOT = parseDOT; +exports.DOTToGraph = DOTToGraph; diff --git a/lib/network/mixins/ClusterMixin.js b/lib/network/mixins/ClusterMixin.js new file mode 100644 index 00000000..f9db6e86 --- /dev/null +++ b/lib/network/mixins/ClusterMixin.js @@ -0,0 +1,1137 @@ +/** + * Creation of the ClusterMixin var. + * + * This contains all the functions the Network object can use to employ clustering + */ + +/** +* This is only called in the constructor of the network object +* +*/ +exports.startWithClustering = function() { + // cluster if the data set is big + this.clusterToFit(this.constants.clustering.initialMaxNodes, true); + + // updates the lables after clustering + this.updateLabels(); + + // this is called here because if clusterin is disabled, the start and stabilize are called in + // the setData function. + if (this.stabilize) { + this._stabilize(); + } + this.start(); +}; + +/** + * This function clusters until the initialMaxNodes has been reached + * + * @param {Number} maxNumberOfNodes + * @param {Boolean} reposition + */ +exports.clusterToFit = function(maxNumberOfNodes, reposition) { + var numberOfNodes = this.nodeIndices.length; + + var maxLevels = 50; + var level = 0; + + // we first cluster the hubs, then we pull in the outliers, repeat + while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { + if (level % 3 == 0) { + this.forceAggregateHubs(true); + this.normalizeClusterLevels(); + } + else { + this.increaseClusterLevel(); // this also includes a cluster normalization + } + + numberOfNodes = this.nodeIndices.length; + level += 1; + } + + // after the clustering we reposition the nodes to reduce the initial chaos + if (level > 0 && reposition == true) { + this.repositionNodes(); + } + this._updateCalculationNodes(); +}; + +/** + * This function can be called to open up a specific cluster. It is only called by + * It will unpack the cluster back one level. + * + * @param node | Node object: cluster to open. + */ +exports.openCluster = function(node) { + var isMovingBeforeClustering = this.moving; + if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && + !(this._sector() == "default" && this.nodeIndices.length == 1)) { + // this loads a new sector, loads the nodes and edges and nodeIndices of it. + this._addSector(node); + var level = 0; + + // we decluster until we reach a decent number of nodes + while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { + this.decreaseClusterLevel(); + level += 1; + } + + } + else { + this._expandClusterNode(node,false,true); + + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this._updateCalculationNodes(); + this.updateLabels(); + } + + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } +}; + + +/** + * This calls the updateClustes with default arguments + */ +exports.updateClustersDefault = function() { + if (this.constants.clustering.enabled == true) { + this.updateClusters(0,false,false); + } +}; + + +/** + * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will + * be clustered with their connected node. This can be repeated as many times as needed. + * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. + */ +exports.increaseClusterLevel = function() { + this.updateClusters(-1,false,true); +}; + + +/** + * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will + * be unpacked if they are a cluster. This can be repeated as many times as needed. + * This can be called externally (by a key-bind for instance) to look into clusters without zooming. + */ +exports.decreaseClusterLevel = function() { + this.updateClusters(1,false,true); +}; + + +/** + * This is the main clustering function. It clusters and declusters on zoom or forced + * This function clusters on zoom, it can be called with a predefined zoom direction + * If out, check if we can form clusters, if in, check if we can open clusters. + * This function is only called from _zoom() + * + * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn + * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} doNotStart | if true do not call start + * + */ +exports.updateClusters = function(zoomDirection,recursive,force,doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; + + // on zoom out collapse the sector if the scale is at the level the sector was made + if (this.previousScale > this.scale && zoomDirection == 0) { + this._collapseSector(); + } + + // check if we zoom in or out + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + // forming clusters when forced pulls outliers in. When not forced, the edge length of the + // outer nodes determines if it is being clustered + this._formClusters(force); + } + else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in + if (force == true) { + // _openClusters checks for each node if the formationScale of the cluster is smaller than + // the current scale and if so, declusters. When forced, all clusters are reduced by one step + this._openClusters(recursive,force); + } + else { + // if a cluster takes up a set percentage of the active window + this._openClustersBySize(); + } + } + this._updateNodeIndexList(); + + // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs + if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { + this._aggregateHubs(force); + this._updateNodeIndexList(); + } + + // we now reduce chains. + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + this.handleChains(); + this._updateNodeIndexList(); + } + + this.previousScale = this.scale; + + // rest of the update the index list, dynamic edges and labels + this._updateDynamicEdges(); + this.updateLabels(); + + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place + this.clusterSession += 1; + // if clusters have been made, we normalize the cluster level + this.normalizeClusterLevels(); + } + + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + } + + this._updateCalculationNodes(); +}; + +/** + * This function handles the chains. It is called on every updateClusters(). + */ +exports.handleChains = function() { + // after clustering we check how many chains there are + var chainPercentage = this._getChainFraction(); + if (chainPercentage > this.constants.clustering.chainThreshold) { + this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) + + } +}; + +/** + * this functions starts clustering by hubs + * The minimum hub threshold is set globally + * + * @private + */ +exports._aggregateHubs = function(force) { + this._getHubSize(); + this._formClustersByHub(force,false); +}; + + +/** + * This function is fired by keypress. It forces hubs to form. + * + */ +exports.forceAggregateHubs = function(doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; + + this._aggregateHubs(true); + + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this.updateLabels(); + + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; + } + + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + } +}; + +/** + * If a cluster takes up more than a set percentage of the screen, open the cluster + * + * @private + */ +exports._openClustersBySize = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.inView() == true) { + if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + this.openCluster(node); + } + } + } + } +}; + + +/** + * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it + * has to be opened based on the current zoom level. + * + * @private + */ +exports._openClusters = function(recursive,force) { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + this._expandClusterNode(node,recursive,force); + this._updateCalculationNodes(); + } +}; + +/** + * This function checks if a node has to be opened. This is done by checking the zoom level. + * If the node contains child nodes, this function is recursively called on the child nodes as well. + * This recursive behaviour is optional and can be set by the recursive argument. + * + * @param {Node} parentNode | to check for cluster and expand + * @param {Boolean} recursive | enabled or disable recursive calling + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released + * @private + */ +exports._expandClusterNode = function(parentNode, recursive, force, openAll) { + // first check if node is a cluster + if (parentNode.clusterSize > 1) { + // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 + if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { + openAll = true; + } + recursive = openAll ? true : recursive; + + // if the last child has been added on a smaller scale than current scale decluster + if (parentNode.formationScale < this.scale || force == true) { + // we will check if any of the contained child nodes should be removed from the cluster + for (var containedNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { + var childNode = parentNode.containedNodes[containedNodeId]; + + // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that + // the largest cluster is the one that comes from outside + if (force == true) { + if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] + || openAll) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + else { + if (this._nodeInActiveArea(parentNode)) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + } + } + } + } +}; + +/** + * ONLY CALLED FROM _expandClusterNode + * + * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove + * the child node from the parent contained_node object and put it back into the global nodes object. + * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. + * + * @param {Node} parentNode | the parent node + * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node + * @param {Boolean} recursive | This will also check if the child needs to be expanded. + * With force and recursive both true, the entire cluster is unpacked + * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent + * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released + * @private + */ +exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { + var childNode = parentNode.containedNodes[containedNodeId]; + + // if child node has been added on smaller scale than current, kick out + if (childNode.formationScale < this.scale || force == true) { + // unselect all selected items + this._unselectAll(); + + // put the child node back in the global nodes object + this.nodes[containedNodeId] = childNode; + + // release the contained edges from this childNode back into the global edges + this._releaseContainedEdges(parentNode,childNode); + + // reconnect rerouted edges to the childNode + this._connectEdgeBackToChild(parentNode,childNode); + + // validate all edges in dynamicEdges + this._validateEdges(parentNode); + + // undo the changes from the clustering operation on the parent node + parentNode.mass -= childNode.mass; + parentNode.clusterSize -= childNode.clusterSize; + parentNode.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); + parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; + + // place the child node near the parent, not at the exact same location to avoid chaos in the system + childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); + childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); + + // remove node from the list + delete parentNode.containedNodes[containedNodeId]; + + // check if there are other childs with this clusterSession in the parent. + var othersPresent = false; + for (var childNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { + if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { + othersPresent = true; + break; + } + } + } + // if there are no others, remove the cluster session from the list + if (othersPresent == false) { + parentNode.clusterSessions.pop(); + } + + this._repositionBezierNodes(childNode); +// this._repositionBezierNodes(parentNode); + + // remove the clusterSession from the child node + childNode.clusterSession = 0; + + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); + + // restart the simulation to reorganise all nodes + this.moving = true; + } + + // check if a further expansion step is possible if recursivity is enabled + if (recursive == true) { + this._expandClusterNode(childNode,recursive,force,openAll); + } +}; + + +/** + * position the bezier nodes at the center of the edges + * + * @param node + * @private + */ +exports._repositionBezierNodes = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + node.dynamicEdges[i].positionBezierNode(); + } +}; + + +/** + * This function checks if any nodes at the end of their trees have edges below a threshold length + * This function is called only from updateClusters() + * forceLevelCollapse ignores the length of the edge and collapses one level + * This means that a node with only one edge will be clustered with its connected node + * + * @private + * @param {Boolean} force + */ +exports._formClusters = function(force) { + if (force == false) { + this._formClustersByZoom(); + } + else { + this._forceClustersByZoom(); + } +}; + + +/** + * This function handles the clustering by zooming out, this is based on a minimum edge distance + * + * @private + */ +exports._formClustersByZoom = function() { + var dx,dy,length, + minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + + // check if any edges are shorter than minLength and start the clustering + // the clustering favours the node with the larger mass + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); + + + if (length < minLength) { + // first check which node is larger + var parentNode = edge.from; + var childNode = edge.to; + if (edge.to.mass > edge.from.mass) { + parentNode = edge.to; + childNode = edge.from; + } + + if (childNode.dynamicEdgesLength == 1) { + this._addToCluster(parentNode,childNode,false); + } + else if (parentNode.dynamicEdgesLength == 1) { + this._addToCluster(childNode,parentNode,false); + } + } + } + } + } + } +}; + +/** + * This function forces the network to cluster all nodes with only one connecting edge to their + * connected node. + * + * @private + */ +exports._forceClustersByZoom = function() { + for (var nodeId in this.nodes) { + // another node could have absorbed this child. + if (this.nodes.hasOwnProperty(nodeId)) { + var childNode = this.nodes[nodeId]; + + // the edges can be swallowed by another decrease + if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { + var edge = childNode.dynamicEdges[0]; + var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; + + // group to the largest node + if (childNode.id != parentNode.id) { + if (parentNode.mass > childNode.mass) { + this._addToCluster(parentNode,childNode,true); + } + else { + this._addToCluster(childNode,parentNode,true); + } + } + } + } + } +}; + + +/** + * To keep the nodes of roughly equal size we normalize the cluster levels. + * This function clusters a node to its smallest connected neighbour. + * + * @param node + * @private + */ +exports._clusterToSmallestNeighbour = function(node) { + var smallestNeighbour = -1; + var smallestNeighbourNode = null; + for (var i = 0; i < node.dynamicEdges.length; i++) { + if (node.dynamicEdges[i] !== undefined) { + var neighbour = null; + if (node.dynamicEdges[i].fromId != node.id) { + neighbour = node.dynamicEdges[i].from; + } + else if (node.dynamicEdges[i].toId != node.id) { + neighbour = node.dynamicEdges[i].to; + } + + + if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { + smallestNeighbour = neighbour.clusterSessions.length; + smallestNeighbourNode = neighbour; + } + } + } + + if (neighbour != null && this.nodes[neighbour.id] !== undefined) { + this._addToCluster(neighbour, node, true); + } +}; + + +/** + * This function forms clusters from hubs, it loops over all nodes + * + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @private + */ +exports._formClustersByHub = function(force, onlyEqual) { + // we loop over all nodes in the list + for (var nodeId in this.nodes) { + // we check if it is still available since it can be used by the clustering in this loop + if (this.nodes.hasOwnProperty(nodeId)) { + this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); + } + } +}; + +/** + * This function forms a cluster from a specific preselected hub node + * + * @param {Node} hubNode | the node we will cluster as a hub + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @param {Number} [absorptionSizeOffset] | + * @private + */ +exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSizeOffset) { + if (absorptionSizeOffset === undefined) { + absorptionSizeOffset = 0; + } + // we decide if the node is a hub + if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || + (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { + // initialize variables + var dx,dy,length; + var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + var allowCluster = false; + + // we create a list of edges because the dynamicEdges change over the course of this loop + var edgesIdarray = []; + var amountOfInitialEdges = hubNode.dynamicEdges.length; + for (var j = 0; j < amountOfInitialEdges; j++) { + edgesIdarray.push(hubNode.dynamicEdges[j].id); + } + + // if the hub clustering is not forces, we check if one of the edges connected + // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold + if (force == false) { + allowCluster = false; + for (j = 0; j < amountOfInitialEdges; j++) { + var edge = this.edges[edgesIdarray[j]]; + if (edge !== undefined) { + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); + + if (length < minLength) { + allowCluster = true; + break; + } + } + } + } + } + } + + // start the clustering if allowed + if ((!force && allowCluster) || force) { + // we loop over all edges INITIALLY connected to this hub + for (j = 0; j < amountOfInitialEdges; j++) { + edge = this.edges[edgesIdarray[j]]; + // the edge can be clustered by this function in a previous loop + if (edge !== undefined) { + var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; + // we do not want hubs to merge with other hubs nor do we want to cluster itself. + if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && + (childNode.id != hubNode.id)) { + this._addToCluster(hubNode,childNode,force); + } + } + } + } + } +}; + + + +/** + * This function adds the child node to the parent node, creating a cluster if it is not already. + * + * @param {Node} parentNode | this is the node that will house the child node + * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node + * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse + * @private + */ +exports._addToCluster = function(parentNode, childNode, force) { + // join child node in the parent node + parentNode.containedNodes[childNode.id] = childNode; + + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < childNode.dynamicEdges.length; i++) { + var edge = childNode.dynamicEdges[i]; + if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode + this._addToContainedEdges(parentNode,childNode,edge); + } + else { + this._connectEdgeToCluster(parentNode,childNode,edge); + } + } + // a contained node has no dynamic edges. + childNode.dynamicEdges = []; + + // remove circular edges from clusters + this._containCircularEdgesFromNode(parentNode,childNode); + + + // remove the childNode from the global nodes object + delete this.nodes[childNode.id]; + + // update the properties of the child and parent + var massBefore = parentNode.mass; + childNode.clusterSession = this.clusterSession; + parentNode.mass += childNode.mass; + parentNode.clusterSize += childNode.clusterSize; + parentNode.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); + + // keep track of the clustersessions so we can open the cluster up as it has been formed. + if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { + parentNode.clusterSessions.push(this.clusterSession); + } + + // forced clusters only open from screen size and double tap + if (force == true) { + // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); + parentNode.formationScale = 0; + } + else { + parentNode.formationScale = this.scale; // The latest child has been added on this scale + } + + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); + + // set the pop-out scale for the childnode + parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; + + // nullify the movement velocity of the child, this is to avoid hectic behaviour + childNode.clearVelocity(); + + // the mass has altered, preservation of energy dictates the velocity to be updated + parentNode.updateVelocity(massBefore); + + // restart the simulation to reorganise all nodes + this.moving = true; +}; + + +/** + * This function will apply the changes made to the remainingEdges during the formation of the clusters. + * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. + * It has to be called if a level is collapsed. It is called by _formClusters(). + * @private + */ +exports._updateDynamicEdges = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + node.dynamicEdgesLength = node.dynamicEdges.length; + + // this corrects for multiple edges pointing at the same other node + var correction = 0; + if (node.dynamicEdgesLength > 1) { + for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { + var edgeToId = node.dynamicEdges[j].toId; + var edgeFromId = node.dynamicEdges[j].fromId; + for (var k = j+1; k < node.dynamicEdgesLength; k++) { + if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || + (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { + correction += 1; + } + } + } + } + node.dynamicEdgesLength -= correction; + } +}; + + +/** + * This adds an edge from the childNode to the contained edges of the parent node + * + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private + */ +exports._addToContainedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { + parentNode.containedEdges[childNode.id] = [] + } + // add this edge to the list + parentNode.containedEdges[childNode.id].push(edge); + + // remove the edge from the global edges object + delete this.edges[edge.id]; + + // remove the edge from the parent object + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + if (parentNode.dynamicEdges[i].id == edge.id) { + parentNode.dynamicEdges.splice(i,1); + break; + } + } +}; + +/** + * This function connects an edge that was connected to a child node to the parent node. + * It keeps track of which nodes it has been connected to with the originalId array. + * + * @param {Node} parentNode | Node object + * @param {Node} childNode | Node object + * @param {Edge} edge | Edge object + * @private + */ +exports._connectEdgeToCluster = function(parentNode, childNode, edge) { + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); + } + else { + if (edge.toId == childNode.id) { // edge connected to other node on the "to" side + edge.originalToId.push(childNode.id); + edge.to = parentNode; + edge.toId = parentNode.id; + } + else { // edge connected to other node with the "from" side + + edge.originalFromId.push(childNode.id); + edge.from = parentNode; + edge.fromId = parentNode.id; + } + + this._addToReroutedEdges(parentNode,childNode,edge); + } +}; + + +/** + * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain + * these edges inside of the cluster. + * + * @param parentNode + * @param childNode + * @private + */ +exports._containCircularEdgesFromNode = function(parentNode, childNode) { + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); + } + } +}; + + +/** + * This adds an edge from the childNode to the rerouted edges of the parent node + * + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private + */ +exports._addToReroutedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + // we store the edge in the rerouted edges so we can restore it when the cluster pops open + if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { + parentNode.reroutedEdges[childNode.id] = []; + } + parentNode.reroutedEdges[childNode.id].push(edge); + + // this edge becomes part of the dynamicEdges of the cluster node + parentNode.dynamicEdges.push(edge); + }; + + + +/** + * This function connects an edge that was connected to a cluster node back to the child node. + * + * @param parentNode | Node object + * @param childNode | Node object + * @private + */ +exports._connectEdgeBackToChild = function(parentNode, childNode) { + if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { + for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { + var edge = parentNode.reroutedEdges[childNode.id][i]; + if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { + edge.originalFromId.pop(); + edge.fromId = childNode.id; + edge.from = childNode; + } + else { + edge.originalToId.pop(); + edge.toId = childNode.id; + edge.to = childNode; + } + + // append this edge to the list of edges connecting to the childnode + childNode.dynamicEdges.push(edge); + + // remove the edge from the parent object + for (var j = 0; j < parentNode.dynamicEdges.length; j++) { + if (parentNode.dynamicEdges[j].id == edge.id) { + parentNode.dynamicEdges.splice(j,1); + break; + } + } + } + // remove the entry from the rerouted edges + delete parentNode.reroutedEdges[childNode.id]; + } +}; + + +/** + * When loops are clustered, an edge can be both in the rerouted array and the contained array. + * This function is called last to verify that all edges in dynamicEdges are in fact connected to the + * parentNode + * + * @param parentNode | Node object + * @private + */ +exports._validateEdges = function(parentNode) { + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { + parentNode.dynamicEdges.splice(i,1); + } + } +}; + + +/** + * This function released the contained edges back into the global domain and puts them back into the + * dynamic edges of both parent and child. + * + * @param {Node} parentNode | + * @param {Node} childNode | + * @private + */ +exports._releaseContainedEdges = function(parentNode, childNode) { + for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { + var edge = parentNode.containedEdges[childNode.id][i]; + + // put the edge back in the global edges object + this.edges[edge.id] = edge; + + // put the edge back in the dynamic edges of the child and parent + childNode.dynamicEdges.push(edge); + parentNode.dynamicEdges.push(edge); + } + // remove the entry from the contained edges + delete parentNode.containedEdges[childNode.id]; + +}; + + + + +// ------------------- UTILITY FUNCTIONS ---------------------------- // + + +/** + * This updates the node labels for all nodes (for debugging purposes) + */ +exports.updateLabels = function() { + var nodeId; + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.clusterSize > 1) { + node.label = "[".concat(String(node.clusterSize),"]"); + } + } + } + + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.clusterSize == 1) { + if (node.originalLabel !== undefined) { + node.label = node.originalLabel; + } + else { + node.label = String(node.id); + } + } + } + } + +// /* Debug Override */ +// for (nodeId in this.nodes) { +// if (this.nodes.hasOwnProperty(nodeId)) { +// node = this.nodes[nodeId]; +// node.label = String(node.level); +// } +// } + +}; + + +/** + * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes + * if the rest of the nodes are already a few cluster levels in. + * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not + * clustered enough to the clusterToSmallestNeighbours function. + */ +exports.normalizeClusterLevels = function() { + var maxLevel = 0; + var minLevel = 1e9; + var clusterLevel = 0; + var nodeId; + + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + clusterLevel = this.nodes[nodeId].clusterSessions.length; + if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} + if (minLevel > clusterLevel) {minLevel = clusterLevel;} + } + } + + if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { + var amountOfNodes = this.nodeIndices.length; + var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].clusterSessions.length < targetLevel) { + this._clusterToSmallestNeighbour(this.nodes[nodeId]); + } + } + } + this._updateNodeIndexList(); + this._updateDynamicEdges(); + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; + } + } +}; + + + +/** + * This function determines if the cluster we want to decluster is in the active area + * this means around the zoom center + * + * @param {Node} node + * @returns {boolean} + * @private + */ +exports._nodeInActiveArea = function(node) { + return ( + Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale + && + Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale + ) +}; + + +/** + * This is an adaptation of the original repositioning function. This is called if the system is clustered initially + * It puts large clusters away from the center and randomizes the order. + * + */ +exports.repositionNodes = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + if ((node.xFixed == false || node.yFixed == false)) { + var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.mass); + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} + this._repositionBezierNodes(node); + } + } +}; + + +/** + * We determine how many connections denote an important hub. + * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) + * + * @private + */ +exports._getHubSize = function() { + var average = 0; + var averageSquared = 0; + var hubCounter = 0; + var largestHub = 0; + + for (var i = 0; i < this.nodeIndices.length; i++) { + + var node = this.nodes[this.nodeIndices[i]]; + if (node.dynamicEdgesLength > largestHub) { + largestHub = node.dynamicEdgesLength; + } + average += node.dynamicEdgesLength; + averageSquared += Math.pow(node.dynamicEdgesLength,2); + hubCounter += 1; + } + average = average / hubCounter; + averageSquared = averageSquared / hubCounter; + + var variance = averageSquared - Math.pow(average,2); + + var standardDeviation = Math.sqrt(variance); + + this.hubThreshold = Math.floor(average + 2*standardDeviation); + + // always have at least one to cluster + if (this.hubThreshold > largestHub) { + this.hubThreshold = largestHub; + } + +// console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); +// console.log("hubThreshold:",this.hubThreshold); +}; + + +/** + * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. + * + * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce + * @private + */ +exports._reduceAmountOfChains = function(fraction) { + this.hubThreshold = 2; + var reduceAmount = Math.floor(this.nodeIndices.length * fraction); + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + if (reduceAmount > 0) { + this._formClusterFromHub(this.nodes[nodeId],true,true,1); + reduceAmount -= 1; + } + } + } + } +}; + +/** + * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. + * + * @private + */ +exports._getChainFraction = function() { + var chains = 0; + var total = 0; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + chains += 1; + } + total += 1; + } + } + return chains/total; +}; diff --git a/lib/network/mixins/HierarchicalLayoutMixin.js b/lib/network/mixins/HierarchicalLayoutMixin.js new file mode 100644 index 00000000..4be8aab5 --- /dev/null +++ b/lib/network/mixins/HierarchicalLayoutMixin.js @@ -0,0 +1,304 @@ +exports._resetLevels = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.preassignedLevel == false) { + node.level = -1; + } + } + } +}; + +/** + * This is the main function to layout the nodes in a hierarchical way. + * It checks if the node details are supplied correctly + * + * @private + */ +exports._setupHierarchicalLayout = function() { + if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { + if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "DU") { + this.constants.hierarchicalLayout.levelSeparation *= -1; + } + else { + this.constants.hierarchicalLayout.levelSeparation = Math.abs(this.constants.hierarchicalLayout.levelSeparation); + } + // get the size of the largest hubs and check if the user has defined a level for a node. + var hubsize = 0; + var node, nodeId; + var definedLevel = false; + var undefinedLevel = false; + + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level != -1) { + definedLevel = true; + } + else { + undefinedLevel = true; + } + if (hubsize < node.edges.length) { + hubsize = node.edges.length; + } + } + } + + // if the user defined some levels but not all, alert and run without hierarchical layout + if (undefinedLevel == true && definedLevel == true) { + alert("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); + this.zoomExtent(true,this.constants.clustering.enabled); + if (!this.constants.clustering.enabled) { + this.start(); + } + } + else { + // setup the system to use hierarchical method. + this._changeConstants(); + + // define levels if undefined by the users. Based on hubsize + if (undefinedLevel == true) { + this._determineLevels(hubsize); + } + // check the distribution of the nodes per level. + var distribution = this._getDistribution(); + + // place the nodes on the canvas. This also stablilizes the system. + this._placeNodesByHierarchy(distribution); + + // start the simulation. + this.start(); + } + } +}; + + +/** + * This function places the nodes on the canvas based on the hierarchial distribution. + * + * @param {Object} distribution | obtained by the function this._getDistribution() + * @private + */ +exports._placeNodesByHierarchy = function(distribution) { + var nodeId, node; + + // start placing all the level 0 nodes first. Then recursively position their branches. + for (nodeId in distribution[0].nodes) { + if (distribution[0].nodes.hasOwnProperty(nodeId)) { + node = distribution[0].nodes[nodeId]; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (node.xFixed) { + node.x = distribution[0].minPos; + node.xFixed = false; + + distribution[0].minPos += distribution[0].nodeSpacing; + } + } + else { + if (node.yFixed) { + node.y = distribution[0].minPos; + node.yFixed = false; + + distribution[0].minPos += distribution[0].nodeSpacing; + } + } + this._placeBranchNodes(node.edges,node.id,distribution,node.level); + } + } + + // stabilize the system after positioning. This function calls zoomExtent. + this._stabilize(); +}; + + +/** + * This function get the distribution of levels based on hubsize + * + * @returns {Object} + * @private + */ +exports._getDistribution = function() { + var distribution = {}; + var nodeId, node, level; + + // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. + // the fix of X is removed after the x value has been set. + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.xFixed = true; + node.yFixed = true; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + else { + node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + if (!distribution.hasOwnProperty(node.level)) { + distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; + } + distribution[node.level].amount += 1; + distribution[node.level].nodes[node.id] = node; + } + } + + // determine the largest amount of nodes of all levels + var maxCount = 0; + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + if (maxCount < distribution[level].amount) { + maxCount = distribution[level].amount; + } + } + } + + // set the initial position and spacing of each nodes accordingly + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; + distribution[level].nodeSpacing /= (distribution[level].amount + 1); + distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); + } + } + + return distribution; +}; + + +/** + * this function allocates nodes in levels based on the recursive branching from the largest hubs. + * + * @param hubsize + * @private + */ +exports._determineLevels = function(hubsize) { + var nodeId, node; + + // determine hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.edges.length == hubsize) { + node.level = 0; + } + } + } + + // branch from hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level == 0) { + this._setLevel(1,node.edges,node.id); + } + } + } +}; + + +/** + * Since hierarchical layout does not support: + * - smooth curves (based on the physics), + * - clustering (based on dynamic node counts) + * + * We disable both features so there will be no problems. + * + * @private + */ +exports._changeConstants = function() { + this.constants.clustering.enabled = false; + this.constants.physics.barnesHut.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this._loadSelectedForceSolver(); + this.constants.smoothCurves = false; + this._configureSmoothCurves(); +}; + + +/** + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. + * + * @param edges + * @param parentId + * @param distribution + * @param parentLevel + * @private + */ +exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + + // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. + var nodeMoved = false; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (childNode.xFixed && childNode.level > parentLevel) { + childNode.xFixed = false; + childNode.x = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + else { + if (childNode.yFixed && childNode.level > parentLevel) { + childNode.yFixed = false; + childNode.y = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + + if (nodeMoved == true) { + distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; + if (childNode.edges.length > 1) { + this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); + } + } + } +}; + + +/** + * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. + * + * @param level + * @param edges + * @param parentId + * @private + */ +exports._setLevel = function(level, edges, parentId) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1 || childNode.level > level) { + childNode.level = level; + if (edges.length > 1) { + this._setLevel(level+1, childNode.edges, childNode.id); + } + } + } +}; + + +/** + * Unfix nodes + * + * @private + */ +exports._restoreNodes = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.nodes[nodeId].xFixed = false; + this.nodes[nodeId].yFixed = false; + } + } +}; diff --git a/lib/network/mixins/ManipulationMixin.js b/lib/network/mixins/ManipulationMixin.js new file mode 100644 index 00000000..5348c777 --- /dev/null +++ b/lib/network/mixins/ManipulationMixin.js @@ -0,0 +1,571 @@ +var util = require('../../util'); + +/** + * clears the toolbar div element of children + * + * @private + */ +exports._clearManipulatorBar = function() { + while (this.manipulationDiv.hasChildNodes()) { + this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + } +}; + +/** + * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore + * these functions to their original functionality, we saved them in this.cachedFunctions. + * This function restores these functions to their original function. + * + * @private + */ +exports._restoreOverloadedFunctions = function() { + for (var functionName in this.cachedFunctions) { + if (this.cachedFunctions.hasOwnProperty(functionName)) { + this[functionName] = this.cachedFunctions[functionName]; + } + } +}; + +/** + * Enable or disable edit-mode. + * + * @private + */ +exports._toggleEditMode = function() { + this.editMode = !this.editMode; + var toolbar = document.getElementById("network-manipulationDiv"); + var closeDiv = document.getElementById("network-manipulation-closeDiv"); + var editModeDiv = document.getElementById("network-manipulation-editMode"); + if (this.editMode == true) { + toolbar.style.display="block"; + closeDiv.style.display="block"; + editModeDiv.style.display="none"; + closeDiv.onclick = this._toggleEditMode.bind(this); + } + else { + toolbar.style.display="none"; + closeDiv.style.display="none"; + editModeDiv.style.display="block"; + closeDiv.onclick = null; + } + this._createManipulatorBar() +}; + +/** + * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. + * + * @private + */ +exports._createManipulatorBar = function() { + // remove bound functions + if (this.boundFunction) { + this.off('select', this.boundFunction); + } + if (this.edgeBeingEdited !== undefined) { + this.edgeBeingEdited._disableControlNodes(); + this.edgeBeingEdited = undefined; + this.selectedControlNode = null; + } + + // restore overloaded functions + this._restoreOverloadedFunctions(); + + // resume calculation + this.freezeSimulation = false; + + // reset global variables + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; + + if (this.editMode == true) { + while (this.manipulationDiv.hasChildNodes()) { + this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + } + // add the icons to the manipulator div + this.manipulationDiv.innerHTML = "" + + "" + + ""+this.constants.labels['add'] +"" + + "
" + + "" + + ""+this.constants.labels['link'] +""; + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + this.manipulationDiv.innerHTML += "" + + "
" + + "" + + ""+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 += "" + + "
" + + "" + + ""+this.constants.labels['del'] +""; + } + + + // bind the icons + var addNodeButton = document.getElementById("network-manipulate-addNode"); + addNodeButton.onclick = this._createAddNodeToolbar.bind(this); + var addEdgeButton = document.getElementById("network-manipulate-connectNode"); + addEdgeButton.onclick = this._createAddEdgeToolbar.bind(this); + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + var editButton = document.getElementById("network-manipulate-editNode"); + editButton.onclick = this._editNode.bind(this); + } + else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { + var editButton = document.getElementById("network-manipulate-editEdge"); + editButton.onclick = this._createEditEdgeToolbar.bind(this); + } + if (this._selectionIsEmpty() == false) { + var deleteButton = document.getElementById("network-manipulate-delete"); + deleteButton.onclick = this._deleteSelected.bind(this); + } + var closeDiv = document.getElementById("network-manipulation-closeDiv"); + closeDiv.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 editModeButton = document.getElementById("network-manipulate-editModeButton"); + editModeButton.onclick = this._toggleEditMode.bind(this); + } +}; + + + +/** + * Create the toolbar for adding Nodes + * + * @private + */ +exports._createAddNodeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + if (this.boundFunction) { + this.off('select', this.boundFunction); + } + + // create the toolbar contents + this.manipulationDiv.innerHTML = "" + + "" + + "" + this.constants.labels['back'] + " " + + "
" + + "" + + "" + this.constants.labels['addDescription'] + ""; + + // bind the icon + var backButton = document.getElementById("network-manipulate-back"); + backButton.onclick = this._createManipulatorBar.bind(this); + + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + this.boundFunction = this._addNode.bind(this); + this.on('select', this.boundFunction); +}; + + +/** + * create the toolbar to connect nodes + * + * @private + */ +exports._createAddEdgeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + this._unselectAll(true); + this.freezeSimulation = true; + + if (this.boundFunction) { + this.off('select', this.boundFunction); + } + + this._unselectAll(); + this.forceAppendSelection = false; + this.blockConnectingEdgeSelection = true; + + this.manipulationDiv.innerHTML = "" + + "" + + "" + this.constants.labels['back'] + " " + + "
" + + "" + + "" + this.constants.labels['linkDescription'] + ""; + + // bind the icon + var backButton = document.getElementById("network-manipulate-back"); + backButton.onclick = this._createManipulatorBar.bind(this); + + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + this.boundFunction = this._handleConnect.bind(this); + this.on('select', this.boundFunction); + + // temporarily overload functions + this.cachedFunctions["_handleTouch"] = this._handleTouch; + this.cachedFunctions["_handleOnRelease"] = this._handleOnRelease; + this._handleTouch = this._handleConnect; + this._handleOnRelease = this._finishConnect; + + // redraw to show the unselect + this._redraw(); +}; + +/** + * create the toolbar to edit edges + * + * @private + */ +exports._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("network-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 + */ +exports._selectControlNode = function(pointer) { + this.edgeBeingEdited.controlNodes.from.unselect(); + this.edgeBeingEdited.controlNodes.to.unselect(); + this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y)); + if (this.selectedControlNode !== null) { + this.selectedControlNode.select(); + this.freezeSimulation = true; + } + this._redraw(); +}; + +/** + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. + * + * @private + */ +exports._controlNodeDrag = function(event) { + var pointer = this._getPointer(event.gesture.center); + if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) { + this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x); + this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y); + } + this._redraw(); +}; + +exports._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 + * to walk the user through the process. + * + * @private + */ +exports._handleConnect = function(pointer) { + if (this._getSelectedNodeCount() == 0) { + var node = this._getNodeAt(pointer); + if (node != null) { + if (node.clusterSize > 1) { + alert("Cannot create edges to a cluster.") + } + else { + this._selectObject(node,false); + // create a node the temporary line can look at + this.sectors['support']['nodes']['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); + this.sectors['support']['nodes']['targetNode'].x = node.x; + this.sectors['support']['nodes']['targetNode'].y = node.y; + this.sectors['support']['nodes']['targetViaNode'] = new Node({id:'targetViaNode'},{},{},this.constants); + this.sectors['support']['nodes']['targetViaNode'].x = node.x; + this.sectors['support']['nodes']['targetViaNode'].y = node.y; + this.sectors['support']['nodes']['targetViaNode'].parentEdgeId = "connectionEdge"; + + // create a temporary edge + this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:this.sectors['support']['nodes']['targetNode'].id}, this, this.constants); + this.edges['connectionEdge'].from = node; + this.edges['connectionEdge'].connected = true; + this.edges['connectionEdge'].smooth = true; + this.edges['connectionEdge'].selected = true; + 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(event) { + var pointer = this._getPointer(event.gesture.center); + this.sectors['support']['nodes']['targetNode'].x = this._XconvertDOMtoCanvas(pointer.x); + this.sectors['support']['nodes']['targetNode'].y = this._YconvertDOMtoCanvas(pointer.y); + this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._XconvertDOMtoCanvas(pointer.x) + this.edges['connectionEdge'].from.x); + this.sectors['support']['nodes']['targetViaNode'].y = this._YconvertDOMtoCanvas(pointer.y); + }; + + this.moving = true; + this.start(); + } + } + } +}; + +exports._finishConnect = function(pointer) { + if (this._getSelectedNodeCount() == 1) { + + // restore the drag function + this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; + delete this.cachedFunctions["_handleOnDrag"]; + + // remember the edge id + var connectFromId = this.edges['connectionEdge'].fromId; + + // remove the temporary nodes and edge + delete this.edges['connectionEdge']; + delete this.sectors['support']['nodes']['targetNode']; + delete this.sectors['support']['nodes']['targetViaNode']; + + var node = this._getNodeAt(pointer); + if (node != null) { + if (node.clusterSize > 1) { + alert("Cannot create edges to a cluster.") + } + else { + this._createEdge(connectFromId,node.id); + this._createManipulatorBar(); + } + } + this._unselectAll(); + } +}; + + +/** + * Adds a node on the specified location + */ +exports._addNode = function() { + if (this._selectionIsEmpty() && this.editMode == true) { + var positionObject = this._pointerToPositionObject(this.pointerPosition); + var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; + if (this.triggerFunctions.add) { + if (this.triggerFunctions.add.length == 2) { + var me = this; + this.triggerFunctions.add(defaultData, function(finalizedData) { + me.nodesData.add(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + alert(this.constants.labels['addError']); + this._createManipulatorBar(); + this.moving = true; + this.start(); + } + } + else { + this.nodesData.add(defaultData); + this._createManipulatorBar(); + this.moving = true; + this.start(); + } + } +}; + + +/** + * connect two nodes with a new edge. + * + * @private + */ +exports._createEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.connect) { + if (this.triggerFunctions.connect.length == 2) { + var me = this; + this.triggerFunctions.connect(defaultData, function(finalizedData) { + me.edgesData.add(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + alert(this.constants.labels["linkError"]); + this.moving = true; + this.start(); + } + } + else { + this.edgesData.add(defaultData); + this.moving = true; + this.start(); + } + } +}; + +/** + * connect two nodes with a new edge. + * + * @private + */ +exports._editEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.editEdge) { + if (this.triggerFunctions.editEdge.length == 2) { + var me = this; + this.triggerFunctions.editEdge(defaultData, function(finalizedData) { + me.edgesData.update(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + 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. + * + * @private + */ +exports._editNode = function() { + if (this.triggerFunctions.edit && this.editMode == true) { + var node = this._getSelectedNode(); + var data = {id:node.id, + label: node.label, + group: node.group, + shape: node.shape, + color: { + background:node.color.background, + border:node.color.border, + highlight: { + background:node.color.highlight.background, + border:node.color.highlight.border + } + }}; + if (this.triggerFunctions.edit.length == 2) { + var me = this; + this.triggerFunctions.edit(data, function (finalizedData) { + me.nodesData.update(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + alert(this.constants.labels["editError"]); + } + } + else { + alert(this.constants.labels["editBoundError"]); + } +}; + + + + +/** + * delete everything in the selection + * + * @private + */ +exports._deleteSelected = function() { + if (!this._selectionIsEmpty() && this.editMode == true) { + if (!this._clusterInSelection()) { + var selectedNodes = this.getSelectedNodes(); + var selectedEdges = this.getSelectedEdges(); + if (this.triggerFunctions.del) { + var me = this; + var data = {nodes: selectedNodes, edges: selectedEdges}; + if (this.triggerFunctions.del.length = 2) { + this.triggerFunctions.del(data, function (finalizedData) { + me.edgesData.remove(finalizedData.edges); + me.nodesData.remove(finalizedData.nodes); + me._unselectAll(); + me.moving = true; + me.start(); + }); + } + else { + alert(this.constants.labels["deleteError"]) + } + } + else { + this.edgesData.remove(selectedEdges); + this.nodesData.remove(selectedNodes); + this._unselectAll(); + this.moving = true; + this.start(); + } + } + else { + alert(this.constants.labels["deleteClusterError"]); + } + } +}; diff --git a/lib/network/mixins/MixinLoader.js b/lib/network/mixins/MixinLoader.js new file mode 100644 index 00000000..f6970030 --- /dev/null +++ b/lib/network/mixins/MixinLoader.js @@ -0,0 +1,198 @@ +var PhysicsMixin = require('./physics/PhysicsMixin'); +var ClusterMixin = require('./ClusterMixin'); +var SectorsMixin = require('./SectorsMixin'); +var SelectionMixin = require('./SelectionMixin'); +var ManipulationMixin = require('./ManipulationMixin'); +var NavigationMixin = require('./NavigationMixin'); +var HierarchicalLayoutMixin = require('./HierarchicalLayoutMixin'); + +/** + * Load a mixin into the network object + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private + */ +exports._loadMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = sourceVariable[mixinFunction]; + } + } +}; + + +/** + * removes a mixin from the network object. + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private + */ +exports._clearMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = undefined; + } + } +}; + + +/** + * Mixin the physics system and initialize the parameters required. + * + * @private + */ +exports._loadPhysicsSystem = function () { + this._loadMixin(PhysicsMixin); + this._loadSelectedForceSolver(); + if (this.constants.configurePhysics == true) { + this._loadPhysicsConfiguration(); + } +}; + + +/** + * Mixin the cluster system and initialize the parameters required. + * + * @private + */ +exports._loadClusterSystem = function () { + this.clusterSession = 0; + this.hubThreshold = 5; + this._loadMixin(ClusterMixin); +}; + + +/** + * Mixin the sector system and initialize the parameters required + * + * @private + */ +exports._loadSectorSystem = function () { + this.sectors = {}; + this.activeSector = ["default"]; + this.sectors["active"] = {}; + this.sectors["active"]["default"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; + this.sectors["frozen"] = {}; + this.sectors["support"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; + + this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields + + this._loadMixin(SectorsMixin); +}; + + +/** + * Mixin the selection system and initialize the parameters required + * + * @private + */ +exports._loadSelectionSystem = function () { + this.selectionObj = {nodes: {}, edges: {}}; + + this._loadMixin(SelectionMixin); +}; + + +/** + * Mixin the navigationUI (User Interface) system and initialize the parameters required + * + * @private + */ +exports._loadManipulationSystem = function () { + // reset global variables -- these are used by the selection of nodes and edges. + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; + + if (this.constants.dataManipulation.enabled == true) { + // load the manipulator HTML elements. All styling done in css. + if (this.manipulationDiv === undefined) { + this.manipulationDiv = document.createElement('div'); + this.manipulationDiv.className = 'network-manipulationDiv'; + this.manipulationDiv.id = 'network-manipulationDiv'; + if (this.editMode == true) { + this.manipulationDiv.style.display = "block"; + } + else { + this.manipulationDiv.style.display = "none"; + } + this.containerElement.insertBefore(this.manipulationDiv, this.frame); + } + + if (this.editModeDiv === undefined) { + this.editModeDiv = document.createElement('div'); + this.editModeDiv.className = 'network-manipulation-editMode'; + this.editModeDiv.id = 'network-manipulation-editMode'; + if (this.editMode == true) { + this.editModeDiv.style.display = "none"; + } + else { + this.editModeDiv.style.display = "block"; + } + this.containerElement.insertBefore(this.editModeDiv, this.frame); + } + + if (this.closeDiv === undefined) { + this.closeDiv = document.createElement('div'); + this.closeDiv.className = 'network-manipulation-closeDiv'; + this.closeDiv.id = 'network-manipulation-closeDiv'; + this.closeDiv.style.display = this.manipulationDiv.style.display; + this.containerElement.insertBefore(this.closeDiv, this.frame); + } + + // load the manipulation functions + this._loadMixin(ManipulationMixin); + + // create the manipulator toolbar + this._createManipulatorBar(); + } + else { + if (this.manipulationDiv !== undefined) { + // removes all the bindings and overloads + this._createManipulatorBar(); + // remove the manipulation divs + this.containerElement.removeChild(this.manipulationDiv); + this.containerElement.removeChild(this.editModeDiv); + this.containerElement.removeChild(this.closeDiv); + + this.manipulationDiv = undefined; + this.editModeDiv = undefined; + this.closeDiv = undefined; + // remove the mixin functions + this._clearMixin(ManipulationMixin); + } + } +}; + + +/** + * Mixin the navigation (User Interface) system and initialize the parameters required + * + * @private + */ +exports._loadNavigationControls = function () { + this._loadMixin(NavigationMixin); + + // the clean function removes the button divs, this is done to remove the bindings. + this._cleanNavigation(); + if (this.constants.navigation.enabled == true) { + this._loadNavigationElements(); + } +}; + + +/** + * Mixin the hierarchical layout system. + * + * @private + */ +exports._loadHierarchySystem = function () { + this._loadMixin(HierarchicalLayoutMixin); +}; diff --git a/lib/network/mixins/NavigationMixin.js b/lib/network/mixins/NavigationMixin.js new file mode 100644 index 00000000..557f33ee --- /dev/null +++ b/lib/network/mixins/NavigationMixin.js @@ -0,0 +1,196 @@ +exports._cleanNavigation = function() { + // clean up previous navigation items + var wrapper = document.getElementById('network-navigation_wrapper'); + if (wrapper != null) { + this.containerElement.removeChild(wrapper); + } + document.onmouseup = null; +}; + +/** + * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation + * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent + * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. + * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. + * + * @private + */ +exports._loadNavigationElements = function() { + this._cleanNavigation(); + + this.navigationDivs = {}; + var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; + var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','zoomExtent']; + + this.navigationDivs['wrapper'] = document.createElement('div'); + this.navigationDivs['wrapper'].id = "network-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; i < navigationDivs.length; i++) { + this.navigationDivs[navigationDivs[i]] = document.createElement('div'); + this.navigationDivs[navigationDivs[i]].id = "network-navigation_" + navigationDivs[i]; + this.navigationDivs[navigationDivs[i]].className = "network-navigation " + navigationDivs[i]; + this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); + this.navigationDivs[navigationDivs[i]].onmousedown = this[navigationDivActions[i]].bind(this); + } + + document.onmouseup = this._stopMovement.bind(this); +}; + +/** + * this stops all movement induced by the navigation buttons + * + * @private + */ +exports._stopMovement = function() { + this._xStopMoving(); + this._yStopMoving(); + this._stopZoom(); +}; + + +/** + * stops the actions performed by page up and down etc. + * + * @param event + * @private + */ +exports._preventDefault = function(event) { + if (event !== undefined) { + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } + } +}; + + +/** + * move the screen up + * By using the increments, instead of adding a fixed number to the translation, we keep fluent and + * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently + * To avoid this behaviour, we do the translation in the start loop. + * + * @private + */ +exports._moveUp = function(event) { + this.yIncrement = this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['up'].className += " active"; + } +}; + + +/** + * move the screen down + * @private + */ +exports._moveDown = function(event) { + this.yIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['down'].className += " active"; + } +}; + + +/** + * move the screen left + * @private + */ +exports._moveLeft = function(event) { + this.xIncrement = this.constants.keyboard.speed.x; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['left'].className += " active"; + } +}; + + +/** + * move the screen right + * @private + */ +exports._moveRight = function(event) { + this.xIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['right'].className += " active"; + } +}; + + +/** + * Zoom in, using the same method as the movement. + * @private + */ +exports._zoomIn = function(event) { + this.zoomIncrement = this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['zoomIn'].className += " active"; + } +}; + + +/** + * Zoom out + * @private + */ +exports._zoomOut = function() { + this.zoomIncrement = -this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + this._preventDefault(event); + if (this.navigationDivs) { + this.navigationDivs['zoomOut'].className += " active"; + } +}; + + +/** + * Stop zooming and unhighlight the zoom controls + * @private + */ +exports._stopZoom = function() { + this.zoomIncrement = 0; + if (this.navigationDivs) { + this.navigationDivs['zoomIn'].className = this.navigationDivs['zoomIn'].className.replace(" active",""); + this.navigationDivs['zoomOut'].className = this.navigationDivs['zoomOut'].className.replace(" active",""); + } +}; + + +/** + * Stop moving in the Y direction and unHighlight the up and down + * @private + */ +exports._yStopMoving = function() { + this.yIncrement = 0; + if (this.navigationDivs) { + this.navigationDivs['up'].className = this.navigationDivs['up'].className.replace(" active",""); + this.navigationDivs['down'].className = this.navigationDivs['down'].className.replace(" active",""); + } +}; + + +/** + * Stop moving in the X direction and unHighlight left and right. + * @private + */ +exports._xStopMoving = function() { + this.xIncrement = 0; + if (this.navigationDivs) { + this.navigationDivs['left'].className = this.navigationDivs['left'].className.replace(" active",""); + this.navigationDivs['right'].className = this.navigationDivs['right'].className.replace(" active",""); + } +}; diff --git a/lib/network/mixins/SectorsMixin.js b/lib/network/mixins/SectorsMixin.js new file mode 100644 index 00000000..03e0075a --- /dev/null +++ b/lib/network/mixins/SectorsMixin.js @@ -0,0 +1,548 @@ +var util = require('../../util'); + +/** + * Creation of the SectorMixin var. + * + * This contains all the functions the Network object can use to employ the sector system. + * The sector system is always used by Network, though the benefits only apply to the use of clustering. + * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. + */ + +/** + * This function is only called by the setData function of the Network object. + * This loads the global references into the active sector. This initializes the sector. + * + * @private + */ +exports._putDataInSector = function() { + this.sectors["active"][this._sector()].nodes = this.nodes; + this.sectors["active"][this._sector()].edges = this.edges; + this.sectors["active"][this._sector()].nodeIndices = this.nodeIndices; +}; + + +/** + * /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied (active) sector. If a type is defined, do the specific type + * + * @param {String} sectorId + * @param {String} [sectorType] | "active" or "frozen" + * @private + */ +exports._switchToSector = function(sectorId, sectorType) { + if (sectorType === undefined || sectorType == "active") { + this._switchToActiveSector(sectorId); + } + else { + this._switchToFrozenSector(sectorId); + } +}; + + +/** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @param sectorId + * @private + */ +exports._switchToActiveSector = function(sectorId) { + this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["active"][sectorId]["nodes"]; + this.edges = this.sectors["active"][sectorId]["edges"]; +}; + + +/** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @private + */ +exports._switchToSupportSector = function() { + this.nodeIndices = this.sectors["support"]["nodeIndices"]; + this.nodes = this.sectors["support"]["nodes"]; + this.edges = this.sectors["support"]["edges"]; +}; + + +/** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied frozen sector. + * + * @param sectorId + * @private + */ +exports._switchToFrozenSector = function(sectorId) { + this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["frozen"][sectorId]["nodes"]; + this.edges = this.sectors["frozen"][sectorId]["edges"]; +}; + + +/** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the currently active sector. + * + * @private + */ +exports._loadLatestSector = function() { + this._switchToSector(this._sector()); +}; + + +/** + * This function returns the currently active sector Id + * + * @returns {String} + * @private + */ +exports._sector = function() { + return this.activeSector[this.activeSector.length-1]; +}; + + +/** + * This function returns the previously active sector Id + * + * @returns {String} + * @private + */ +exports._previousSector = function() { + if (this.activeSector.length > 1) { + return this.activeSector[this.activeSector.length-2]; + } + else { + throw new TypeError('there are not enough sectors in the this.activeSector array.'); + } +}; + + +/** + * We add the active sector at the end of the this.activeSector array + * This ensures it is the currently active sector returned by _sector() and it reaches the top + * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. + * + * @param newId + * @private + */ +exports._setActiveSector = function(newId) { + this.activeSector.push(newId); +}; + + +/** + * We remove the currently active sector id from the active sector stack. This happens when + * we reactivate the previously active sector + * + * @private + */ +exports._forgetLastSector = function() { + this.activeSector.pop(); +}; + + +/** + * This function creates a new active sector with the supplied newId. This newId + * is the expanding node id. + * + * @param {String} newId | Id of the new active sector + * @private + */ +exports._createNewSector = function(newId) { + // create the new sector + this.sectors["active"][newId] = {"nodes":{}, + "edges":{}, + "nodeIndices":[], + "formationScale": this.scale, + "drawingNode": undefined}; + + // create the new sector render node. This gives visual feedback that you are in a new sector. + this.sectors["active"][newId]['drawingNode'] = new Node( + {id:newId, + color: { + background: "#eaefef", + border: "495c5e" + } + },{},{},this.constants); + this.sectors["active"][newId]['drawingNode'].clusterSize = 2; +}; + + +/** + * This function removes the currently active sector. This is called when we create a new + * active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ +exports._deleteActiveSector = function(sectorId) { + delete this.sectors["active"][sectorId]; +}; + + +/** + * This function removes the currently active sector. This is called when we reactivate + * the previously active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ +exports._deleteFrozenSector = function(sectorId) { + delete this.sectors["frozen"][sectorId]; +}; + + +/** + * Freezing an active sector means moving it from the "active" object to the "frozen" object. + * We copy the references, then delete the active entree. + * + * @param sectorId + * @private + */ +exports._freezeSector = function(sectorId) { + // we move the set references from the active to the frozen stack. + this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; + + // we have moved the sector data into the frozen set, we now remove it from the active set + this._deleteActiveSector(sectorId); +}; + + +/** + * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" + * object to the "active" object. + * + * @param sectorId + * @private + */ +exports._activateSector = function(sectorId) { + // we move the set references from the frozen to the active stack. + this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; + + // we have moved the sector data into the active set, we now remove it from the frozen stack + this._deleteFrozenSector(sectorId); +}; + + +/** + * This function merges the data from the currently active sector with a frozen sector. This is used + * in the process of reverting back to the previously active sector. + * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it + * upon the creation of a new active sector. + * + * @param sectorId + * @private + */ +exports._mergeThisWithFrozen = function(sectorId) { + // copy all nodes + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; + } + } + + // copy all edges (if not fully clustered, else there are no edges) + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; + } + } + + // merge the nodeIndices + for (var i = 0; i < this.nodeIndices.length; i++) { + this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); + } +}; + + +/** + * This clusters the sector to one cluster. It was a single cluster before this process started so + * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. + * + * @private + */ +exports._collapseThisToSingleCluster = function() { + this.clusterToFit(1,false); +}; + + +/** + * We create a new active sector from the node that we want to open. + * + * @param node + * @private + */ +exports._addSector = function(node) { + // this is the currently active sector + var sector = this._sector(); + +// // this should allow me to select nodes from a frozen set. +// if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { +// console.log("the node is part of the active sector"); +// } +// else { +// console.log("I dont know what the fuck happened!!"); +// } + + // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. + delete this.nodes[node.id]; + + var unqiueIdentifier = util.randomUUID(); + + // we fully freeze the currently active sector + this._freezeSector(sector); + + // we create a new active sector. This sector has the Id of the node to ensure uniqueness + this._createNewSector(unqiueIdentifier); + + // we add the active sector to the sectors array to be able to revert these steps later on + this._setActiveSector(unqiueIdentifier); + + // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier + this._switchToSector(this._sector()); + + // finally we add the node we removed from our previous active sector to the new active sector + this.nodes[node.id] = node; +}; + + +/** + * We close the sector that is currently open and revert back to the one before. + * If the active sector is the "default" sector, nothing happens. + * + * @private + */ +exports._collapseSector = function() { + // the currently active sector + var sector = this._sector(); + + // we cannot collapse the default sector + if (sector != "default") { + if ((this.nodeIndices.length == 1) || + (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + var previousSector = this._previousSector(); + + // we collapse the sector back to a single cluster + this._collapseThisToSingleCluster(); + + // we move the remaining nodes, edges and nodeIndices to the previous sector. + // This previous sector is the one we will reactivate + this._mergeThisWithFrozen(previousSector); + + // the previously active (frozen) sector now has all the data from the currently active sector. + // we can now delete the active sector. + this._deleteActiveSector(sector); + + // we activate the previously active (and currently frozen) sector. + this._activateSector(previousSector); + + // we load the references from the newly active sector into the global references + this._switchToSector(previousSector); + + // we forget the previously active sector because we reverted to the one before + this._forgetLastSector(); + + // finally, we update the node index list. + this._updateNodeIndexList(); + + // we refresh the list with calulation nodes and calculation node indices. + this._updateCalculationNodes(); + } + } +}; + + +/** + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ +exports._doInAllActiveSectors = function(runFunction,argument) { + if (argument === undefined) { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + this[runFunction](); + } + } + } + else { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); + } + else { + this[runFunction](argument); + } + } + } + } + // we revert the global references back to our active sector + this._loadLatestSector(); +}; + + +/** + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ +exports._doInSupportSector = function(runFunction,argument) { + if (argument === undefined) { + this._switchToSupportSector(); + this[runFunction](); + } + else { + this._switchToSupportSector(); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); + } + else { + this[runFunction](argument); + } + } + // we revert the global references back to our active sector + this._loadLatestSector(); +}; + + +/** + * This runs a function in all frozen sectors. This is used in the _redraw(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ +exports._doInAllFrozenSectors = function(runFunction,argument) { + if (argument === undefined) { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + this[runFunction](); + } + } + } + else { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); + } + else { + this[runFunction](argument); + } + } + } + } + this._loadLatestSector(); +}; + + +/** + * This runs a function in all sectors. This is used in the _redraw(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ +exports._doInAllSectors = function(runFunction,argument) { + var args = Array.prototype.splice.call(arguments, 1); + if (argument === undefined) { + this._doInAllActiveSectors(runFunction); + this._doInAllFrozenSectors(runFunction); + } + else { + if (args.length > 1) { + this._doInAllActiveSectors(runFunction,args[0],args[1]); + this._doInAllFrozenSectors(runFunction,args[0],args[1]); + } + else { + this._doInAllActiveSectors(runFunction,argument); + this._doInAllFrozenSectors(runFunction,argument); + } + } +}; + + +/** + * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the + * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. + * + * @private + */ +exports._clearNodeIndexList = function() { + var sector = this._sector(); + this.sectors["active"][sector]["nodeIndices"] = []; + this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; +}; + + +/** + * Draw the encompassing sector node + * + * @param ctx + * @param sectorType + * @private + */ +exports._drawSectorNodes = function(ctx,sectorType) { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var sector in this.sectors[sectorType]) { + if (this.sectors[sectorType].hasOwnProperty(sector)) { + if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { + + this._switchToSector(sector,sectorType); + + minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.resize(ctx); + if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} + if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} + if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} + if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} + } + } + node = this.sectors[sectorType][sector]["drawingNode"]; + node.x = 0.5 * (maxX + minX); + node.y = 0.5 * (maxY + minY); + node.width = 2 * (node.x - minX); + node.height = 2 * (node.y - minY); + node.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); + node.setScale(this.scale); + node._drawCircle(ctx); + } + } + } +}; + +exports._drawAllSectorNodes = function(ctx) { + this._drawSectorNodes(ctx,"frozen"); + this._drawSectorNodes(ctx,"active"); + this._loadLatestSector(); +}; diff --git a/lib/network/mixins/SelectionMixin.js b/lib/network/mixins/SelectionMixin.js new file mode 100644 index 00000000..0a02d238 --- /dev/null +++ b/lib/network/mixins/SelectionMixin.js @@ -0,0 +1,705 @@ +var Node = require('../Node'); + +/** + * This function can be called from the _doInAllSectors function + * + * @param object + * @param overlappingNodes + * @private + */ +exports._getNodesOverlappingWith = function(object, overlappingNodes) { + var nodes = this.nodes; + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].isOverlappingWith(object)) { + overlappingNodes.push(nodeId); + } + } + } +}; + +/** + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ +exports._getAllNodesOverlappingWith = function (object) { + var overlappingNodes = []; + this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); + return overlappingNodes; +}; + + +/** + * Return a position object in canvasspace from a single point in screenspace + * + * @param pointer + * @returns {{left: number, top: number, right: number, bottom: number}} + * @private + */ +exports._pointerToPositionObject = function(pointer) { + var x = this._XconvertDOMtoCanvas(pointer.x); + var y = this._YconvertDOMtoCanvas(pointer.y); + + return { + left: x, + top: y, + right: x, + bottom: y + }; +}; + + +/** + * Get the top node at the a specific point (like a click) + * + * @param {{x: Number, y: Number}} pointer + * @return {Node | null} node + * @private + */ +exports._getNodeAt = function (pointer) { + // we first check if this is an navigation controls element + var positionObject = this._pointerToPositionObject(pointer); + var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); + + // if there are overlapping nodes, select the last one, this is the + // one which is drawn on top of the others + if (overlappingNodes.length > 0) { + return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + } + else { + return null; + } +}; + + +/** + * retrieve all edges overlapping with given object, selector is around center + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ +exports._getEdgesOverlappingWith = function (object, overlappingEdges) { + var edges = this.edges; + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].isOverlappingWith(object)) { + overlappingEdges.push(edgeId); + } + } + } +}; + + +/** + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ +exports._getAllEdgesOverlappingWith = function (object) { + var overlappingEdges = []; + this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); + return overlappingEdges; +}; + +/** + * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call + * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. + * + * @param pointer + * @returns {null} + * @private + */ +exports._getEdgeAt = function(pointer) { + var positionObject = this._pointerToPositionObject(pointer); + var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); + + if (overlappingEdges.length > 0) { + return this.edges[overlappingEdges[overlappingEdges.length - 1]]; + } + else { + return null; + } +}; + + +/** + * Add object to the selection array. + * + * @param obj + * @private + */ +exports._addToSelection = function(obj) { + if (obj instanceof Node) { + this.selectionObj.nodes[obj.id] = obj; + } + else { + this.selectionObj.edges[obj.id] = obj; + } +}; + +/** + * Add object to the selection array. + * + * @param obj + * @private + */ +exports._addToHover = function(obj) { + if (obj instanceof Node) { + this.hoverObj.nodes[obj.id] = obj; + } + else { + this.hoverObj.edges[obj.id] = obj; + } +}; + + +/** + * Remove a single option from selection. + * + * @param {Object} obj + * @private + */ +exports._removeFromSelection = function(obj) { + if (obj instanceof Node) { + delete this.selectionObj.nodes[obj.id]; + } + else { + delete this.selectionObj.edges[obj.id]; + } +}; + +/** + * Unselect all. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ +exports._unselectAll = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + this.selectionObj.nodes[nodeId].unselect(); + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + this.selectionObj.edges[edgeId].unselect(); + } + } + + this.selectionObj = {nodes:{},edges:{}}; + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } +}; + +/** + * Unselect all clusters. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ +exports._unselectClusters = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + this.selectionObj.nodes[nodeId].unselect(); + this._removeFromSelection(this.selectionObj.nodes[nodeId]); + } + } + } + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } +}; + + +/** + * return the number of selected nodes + * + * @returns {number} + * @private + */ +exports._getSelectedNodeCount = function() { + var count = 0; + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; + } + } + return count; +}; + +/** + * return the selected node + * + * @returns {number} + * @private + */ +exports._getSelectedNode = function() { + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return this.selectionObj.nodes[nodeId]; + } + } + return null; +}; + +/** + * return the selected edge + * + * @returns {number} + * @private + */ +exports._getSelectedEdge = function() { + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + return this.selectionObj.edges[edgeId]; + } + } + return null; +}; + + +/** + * return the number of selected edges + * + * @returns {number} + * @private + */ +exports._getSelectedEdgeCount = function() { + var count = 0; + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; + } + } + return count; +}; + + +/** + * return the number of selected objects. + * + * @returns {number} + * @private + */ +exports._getSelectedObjectCount = function() { + var count = 0; + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; + } + } + return count; +}; + +/** + * Check if anything is selected + * + * @returns {boolean} + * @private + */ +exports._selectionIsEmpty = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return false; + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + return false; + } + } + return true; +}; + + +/** + * check if one of the selected nodes is a cluster. + * + * @returns {boolean} + * @private + */ +exports._clusterInSelection = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + return true; + } + } + } + return false; +}; + +/** + * select the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ +exports._selectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.select(); + this._addToSelection(edge); + } +}; + +/** + * select the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ +exports._hoverConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.hover = true; + this._addToHover(edge); + } +}; + + +/** + * unselect the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ +exports._unselectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.unselect(); + this._removeFromSelection(edge); + } +}; + + + + +/** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @param {Boolean} append + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ +exports._selectObject = function(object, append, doNotTrigger, highlightEdges) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + if (highlightEdges === undefined) { + highlightEdges = true; + } + + if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { + this._unselectAll(true); + } + + if (object.selected == false) { + object.select(); + this._addToSelection(object); + if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { + this._selectConnectedEdges(object); + } + } + else { + object.unselect(); + this._removeFromSelection(object); + } + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } +}; + + +/** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @private + */ +exports._blurObject = function(object) { + if (object.hover == true) { + object.hover = false; + this.emit("blurNode",{node:object.id}); + } +}; + +/** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @private + */ +exports._hoverObject = function(object) { + if (object.hover == false) { + object.hover = true; + this._addToHover(object); + if (object instanceof Node) { + this.emit("hoverNode",{node:object.id}); + } + } + if (object instanceof Node) { + this._hoverConnectedEdges(object); + } +}; + + +/** + * handles the selection part of the touch, only for navigation controls elements; + * Touch is triggered before tap, also before hold. Hold triggers after a while. + * This is the most responsive solution + * + * @param {Object} pointer + * @private + */ +exports._handleTouch = function(pointer) { +}; + + +/** + * handles the selection part of the tap; + * + * @param {Object} pointer + * @private + */ +exports._handleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node,false); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge,false); + } + else { + this._unselectAll(); + } + } + this.emit("click", this.getSelection()); + this._redraw(); +}; + + +/** + * handles the selection part of the double tap and opens a cluster if needed + * + * @param {Object} pointer + * @private + */ +exports._handleDoubleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null && node !== undefined) { + // we reset the areaCenter here so the opening of the node will occur + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; + this.openCluster(node); + } + this.emit("doubleClick", this.getSelection()); +}; + + +/** + * Handle the onHold selection part + * + * @param pointer + * @private + */ +exports._handleOnHold = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node,true); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge,true); + } + } + this._redraw(); +}; + + +/** + * handle the onRelease event. These functions are here for the navigation controls module. + * + * @private + */ +exports._handleOnRelease = function(pointer) { + +}; + + + +/** + * + * retrieve the currently selected objects + * @return {{nodes: Array., edges: Array.}} selection + */ +exports.getSelection = function() { + var nodeIds = this.getSelectedNodes(); + var edgeIds = this.getSelectedEdges(); + return {nodes:nodeIds, edges:edgeIds}; +}; + +/** + * + * retrieve the currently selected nodes + * @return {String[]} selection An array with the ids of the + * selected nodes. + */ +exports.getSelectedNodes = function() { + var idArray = []; + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + idArray.push(nodeId); + } + } + return idArray +}; + +/** + * + * retrieve the currently selected edges + * @return {Array} selection An array with the ids of the + * selected nodes. + */ +exports.getSelectedEdges = function() { + var idArray = []; + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + idArray.push(edgeId); + } + } + return idArray; +}; + + +/** + * select zero or more nodes + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + */ +exports.setSelection = function(selection) { + var i, iMax, id; + + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; + + // first unselect any selected node + this._unselectAll(true); + + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; + + var node = this.nodes[id]; + if (!node) { + throw new RangeError('Node with id "' + id + '" not found'); + } + this._selectObject(node,true,true); + } + + console.log("setSelection is deprecated. Please use selectNodes instead.") + + this.redraw(); +}; + + +/** + * select zero or more nodes with the option to highlight edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + * @param {boolean} [highlightEdges] + */ +exports.selectNodes = function(selection, highlightEdges) { + var i, iMax, id; + + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; + + // first unselect any selected node + this._unselectAll(true); + + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; + + var node = this.nodes[id]; + if (!node) { + throw new RangeError('Node with id "' + id + '" not found'); + } + this._selectObject(node,true,true,highlightEdges); + } + this.redraw(); +}; + + +/** + * select zero or more edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + */ +exports.selectEdges = function(selection) { + var i, iMax, id; + + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; + + // first unselect any selected node + this._unselectAll(true); + + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; + + var edge = this.edges[id]; + if (!edge) { + throw new RangeError('Edge with id "' + id + '" not found'); + } + this._selectObject(edge,true,true,highlightEdges); + } + this.redraw(); +}; + +/** + * Validate the selection: remove ids of nodes which no longer exist + * @private + */ +exports._updateSelection = function () { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (!this.nodes.hasOwnProperty(nodeId)) { + delete this.selectionObj.nodes[nodeId]; + } + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + if (!this.edges.hasOwnProperty(edgeId)) { + delete this.selectionObj.edges[edgeId]; + } + } + } +}; diff --git a/lib/network/mixins/physics/BarnesHutMixin.js b/lib/network/mixins/physics/BarnesHutMixin.js new file mode 100644 index 00000000..f1e11a9a --- /dev/null +++ b/lib/network/mixins/physics/BarnesHutMixin.js @@ -0,0 +1,393 @@ +/** + * This function calculates the forces the nodes apply on eachother based on a gravitational model. + * The Barnes Hut method is used to speed up this N-body simulation. + * + * @private + */ +exports._calculateNodeForces = function() { + if (this.constants.physics.barnesHut.gravitationalConstant != 0) { + var node; + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + var nodeCount = nodeIndices.length; + + this._formBarnesHutTree(nodes,nodeIndices); + + var barnesHutTree = this.barnesHutTree; + + // place the nodes one by one recursively + for (var i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + // starting with root is irrelevant, it never passes the BarnesHut condition + this._getForceContribution(barnesHutTree.root.children.NW,node); + this._getForceContribution(barnesHutTree.root.children.NE,node); + this._getForceContribution(barnesHutTree.root.children.SW,node); + this._getForceContribution(barnesHutTree.root.children.SE,node); + } + } +}; + + +/** + * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. + * If a region contains a single node, we check if it is not itself, then we apply the force. + * + * @param parentBranch + * @param node + * @private + */ +exports._getForceContribution = function(parentBranch,node) { + // we get no force contribution from an empty region + if (parentBranch.childrenCount > 0) { + var dx,dy,distance; + + // get the distance from the center of mass to the node. + dx = parentBranch.centerOfMass.x - node.x; + dy = parentBranch.centerOfMass.y - node.y; + distance = Math.sqrt(dx * dx + dy * dy); + + // BarnesHut condition + // original condition : s/d < theta = passed === d/s > 1/theta = passed + // calcSize = 1/s --> d * 1/s > 1/theta = passed + if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) { + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.1*Math.random(); + dx = distance; + } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; + } + else { + // Did not pass the condition, go into children if available + if (parentBranch.childrenCount == 4) { + this._getForceContribution(parentBranch.children.NW,node); + this._getForceContribution(parentBranch.children.NE,node); + this._getForceContribution(parentBranch.children.SW,node); + this._getForceContribution(parentBranch.children.SE,node); + } + else { // parentBranch must have only one node, if it was empty we wouldnt be here + if (parentBranch.children.data.id != node.id) { // if it is not self + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.5*Math.random(); + dx = distance; + } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; + } + } + } + } +}; + +/** + * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * + * @param nodes + * @param nodeIndices + * @private + */ +exports._formBarnesHutTree = function(nodes,nodeIndices) { + var node; + var nodeCount = nodeIndices.length; + + var minX = Number.MAX_VALUE, + minY = Number.MAX_VALUE, + maxX =-Number.MAX_VALUE, + maxY =-Number.MAX_VALUE; + + // get the range of the nodes + for (var i = 0; i < nodeCount; i++) { + var x = nodes[nodeIndices[i]].x; + var y = nodes[nodeIndices[i]].y; + if (x < minX) { minX = x; } + if (x > maxX) { maxX = x; } + if (y < minY) { minY = y; } + if (y > maxY) { maxY = y; } + } + // make the range a square + var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y + if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize + else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize + + + var minimumTreeSize = 1e-5; + var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); + var halfRootSize = 0.5 * rootSize; + var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); + + // construct the barnesHutTree + var barnesHutTree = { + root:{ + centerOfMass: {x:0, y:0}, + mass:0, + range: { + minX: centerX-halfRootSize,maxX:centerX+halfRootSize, + minY: centerY-halfRootSize,maxY:centerY+halfRootSize + }, + size: rootSize, + calcSize: 1 / rootSize, + children: { data:null}, + maxWidth: 0, + level: 0, + childrenCount: 4 + } + }; + this._splitBranch(barnesHutTree.root); + + // place the nodes one by one recursively + for (i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + this._placeInTree(barnesHutTree.root,node); + } + + // make global + this.barnesHutTree = barnesHutTree +}; + + +/** + * this updates the mass of a branch. this is increased by adding a node. + * + * @param parentBranch + * @param node + * @private + */ +exports._updateBranchMass = function(parentBranch, node) { + var totalMass = parentBranch.mass + node.mass; + var totalMassInv = 1/totalMass; + + parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.mass; + parentBranch.centerOfMass.x *= totalMassInv; + + parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.mass; + parentBranch.centerOfMass.y *= totalMassInv; + + parentBranch.mass = totalMass; + var biggestSize = Math.max(Math.max(node.height,node.radius),node.width); + parentBranch.maxWidth = (parentBranch.maxWidth < biggestSize) ? biggestSize : parentBranch.maxWidth; + +}; + + +/** + * determine in which branch the node will be placed. + * + * @param parentBranch + * @param node + * @param skipMassUpdate + * @private + */ +exports._placeInTree = function(parentBranch,node,skipMassUpdate) { + if (skipMassUpdate != true || skipMassUpdate === undefined) { + // update the mass of the branch. + this._updateBranchMass(parentBranch,node); + } + + if (parentBranch.children.NW.range.maxX > node.x) { // in NW or SW + if (parentBranch.children.NW.range.maxY > node.y) { // in NW + this._placeInRegion(parentBranch,node,"NW"); + } + else { // in SW + this._placeInRegion(parentBranch,node,"SW"); + } + } + else { // in NE or SE + if (parentBranch.children.NW.range.maxY > node.y) { // in NE + this._placeInRegion(parentBranch,node,"NE"); + } + else { // in SE + this._placeInRegion(parentBranch,node,"SE"); + } + } +}; + + +/** + * actually place the node in a region (or branch) + * + * @param parentBranch + * @param node + * @param region + * @private + */ +exports._placeInRegion = function(parentBranch,node,region) { + switch (parentBranch.children[region].childrenCount) { + case 0: // place node here + parentBranch.children[region].children.data = node; + parentBranch.children[region].childrenCount = 1; + this._updateBranchMass(parentBranch.children[region],node); + break; + case 1: // convert into children + // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) + // we move one node a pixel and we do not put it in the tree. + if (parentBranch.children[region].children.data.x == node.x && + parentBranch.children[region].children.data.y == node.y) { + node.x += Math.random(); + node.y += Math.random(); + } + else { + this._splitBranch(parentBranch.children[region]); + this._placeInTree(parentBranch.children[region],node); + } + break; + case 4: // place in branch + this._placeInTree(parentBranch.children[region],node); + break; + } +}; + + +/** + * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch + * after the split is complete. + * + * @param parentBranch + * @private + */ +exports._splitBranch = function(parentBranch) { + // if the branch is shaded with a node, replace the node in the new subset. + var containedNode = null; + if (parentBranch.childrenCount == 1) { + containedNode = parentBranch.children.data; + parentBranch.mass = 0; parentBranch.centerOfMass.x = 0; parentBranch.centerOfMass.y = 0; + } + parentBranch.childrenCount = 4; + parentBranch.children.data = null; + this._insertRegion(parentBranch,"NW"); + this._insertRegion(parentBranch,"NE"); + this._insertRegion(parentBranch,"SW"); + this._insertRegion(parentBranch,"SE"); + + if (containedNode != null) { + this._placeInTree(parentBranch,containedNode); + } +}; + + +/** + * This function subdivides the region into four new segments. + * Specifically, this inserts a single new segment. + * It fills the children section of the parentBranch + * + * @param parentBranch + * @param region + * @param parentRange + * @private + */ +exports._insertRegion = function(parentBranch, region) { + var minX,maxX,minY,maxY; + var childSize = 0.5 * parentBranch.size; + switch (region) { + case "NW": + minX = parentBranch.range.minX; + maxX = parentBranch.range.minX + childSize; + minY = parentBranch.range.minY; + maxY = parentBranch.range.minY + childSize; + break; + case "NE": + minX = parentBranch.range.minX + childSize; + maxX = parentBranch.range.maxX; + minY = parentBranch.range.minY; + maxY = parentBranch.range.minY + childSize; + break; + case "SW": + minX = parentBranch.range.minX; + maxX = parentBranch.range.minX + childSize; + minY = parentBranch.range.minY + childSize; + maxY = parentBranch.range.maxY; + break; + case "SE": + minX = parentBranch.range.minX + childSize; + maxX = parentBranch.range.maxX; + minY = parentBranch.range.minY + childSize; + maxY = parentBranch.range.maxY; + break; + } + + + parentBranch.children[region] = { + centerOfMass:{x:0,y:0}, + mass:0, + range:{minX:minX,maxX:maxX,minY:minY,maxY:maxY}, + size: 0.5 * parentBranch.size, + calcSize: 2 * parentBranch.calcSize, + children: {data:null}, + maxWidth: 0, + level: parentBranch.level+1, + childrenCount: 0 + }; +}; + + +/** + * This function is for debugging purposed, it draws the tree. + * + * @param ctx + * @param color + * @private + */ +exports._drawTree = function(ctx,color) { + if (this.barnesHutTree !== undefined) { + + ctx.lineWidth = 1; + + this._drawBranch(this.barnesHutTree.root,ctx,color); + } +}; + + +/** + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private + */ +exports._drawBranch = function(branch,ctx,color) { + if (color === undefined) { + color = "#FF0000"; + } + + if (branch.childrenCount == 4) { + this._drawBranch(branch.children.NW,ctx); + this._drawBranch(branch.children.NE,ctx); + this._drawBranch(branch.children.SE,ctx); + this._drawBranch(branch.children.SW,ctx); + } + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.moveTo(branch.range.minX,branch.range.minY); + ctx.lineTo(branch.range.maxX,branch.range.minY); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(branch.range.maxX,branch.range.minY); + ctx.lineTo(branch.range.maxX,branch.range.maxY); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(branch.range.maxX,branch.range.maxY); + ctx.lineTo(branch.range.minX,branch.range.maxY); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(branch.range.minX,branch.range.maxY); + ctx.lineTo(branch.range.minX,branch.range.minY); + ctx.stroke(); + + /* + if (branch.mass > 0) { + ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); + ctx.stroke(); + } + */ +}; diff --git a/lib/network/mixins/physics/HierarchialRepulsionMixin.js b/lib/network/mixins/physics/HierarchialRepulsionMixin.js new file mode 100644 index 00000000..9ece24c6 --- /dev/null +++ b/lib/network/mixins/physics/HierarchialRepulsionMixin.js @@ -0,0 +1,125 @@ +/** + * Calculate the forces the nodes apply on eachother based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ +exports._calculateNodeForces = function () { + var dx, dy, distance, fx, fy, combinedClusterSize, + repulsingForce, node1, node2, i, j; + + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + + // approximation constants + var b = 5; + var a_base = 0.5 * -b; + + + // repulsing forces between nodes + var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; + var minimumDistance = nodeDistance; + var a = a_base / minimumDistance; + + // we loop from i over all but the last entree in the array + // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j + for (i = 0; i < nodeIndices.length - 1; i++) { + + node1 = nodes[nodeIndices[i]]; + for (j = i + 1; j < nodeIndices.length; j++) { + node2 = nodes[nodeIndices[j]]; + if (node1.level == node2.level) { + + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); + + + if (distance < 2 * minimumDistance) { + repulsingForce = a * distance + b; + var c = 0.05; + var d = 2 * minimumDistance * 2 * c; + repulsingForce = c * Math.pow(distance,2) - d * distance + d*d/(4*c); + + // normalize force with + if (distance == 0) { + distance = 0.01; + } + else { + repulsingForce = repulsingForce / distance; + } + fx = dx * repulsingForce; + fy = dy * repulsingForce; + + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; + } + } + } + } +}; + + +/** + * this function calculates the effects of the springs in the case of unsmooth curves. + * + * @private + */ +exports._calculateHierarchicalSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + distance = Math.max(0.8*edgeLength,Math.min(5*edgeLength, distance)); + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + edge.to.fx -= fx; + edge.to.fy -= fy; + edge.from.fx += fx; + edge.from.fy += fy; + + + var factor = 5; + if (distance > edgeLength) { + factor = 25; + } + + if (edge.from.level > edge.to.level) { + edge.to.fx -= factor*fx; + edge.to.fy -= factor*fy; + } + else if (edge.from.level < edge.to.level) { + edge.from.fx += factor*fx; + edge.from.fy += factor*fy; + } + } + } + } + } +}; \ No newline at end of file diff --git a/lib/network/mixins/physics/PhysicsMixin.js b/lib/network/mixins/physics/PhysicsMixin.js new file mode 100644 index 00000000..1ab07620 --- /dev/null +++ b/lib/network/mixins/physics/PhysicsMixin.js @@ -0,0 +1,700 @@ +var util = require('../../../util'); +var RepulsionMixin = require('./RepulsionMixin'); +var HierarchialRepulsionMixin = require('./HierarchialRepulsionMixin'); +var BarnesHutMixin = require('./BarnesHutMixin'); + +/** + * Toggling barnes Hut calculation on and off. + * + * @private + */ +exports._toggleBarnesHut = function () { + this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; + this._loadSelectedForceSolver(); + this.moving = true; + this.start(); +}; + + +/** + * This loads the node force solver based on the barnes hut or repulsion algorithm + * + * @private + */ +exports._loadSelectedForceSolver = function () { + // this overloads the this._calculateNodeForces + if (this.constants.physics.barnesHut.enabled == true) { + this._clearMixin(RepulsionMixin); + this._clearMixin(HierarchialRepulsionMixin); + + this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; + this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; + this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; + this.constants.physics.damping = this.constants.physics.barnesHut.damping; + + this._loadMixin(BarnesHutMixin); + } + else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._clearMixin(BarnesHutMixin); + this._clearMixin(RepulsionMixin); + + this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; + this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; + + this._loadMixin(HierarchialRepulsionMixin); + } + else { + this._clearMixin(BarnesHutMixin); + this._clearMixin(HierarchialRepulsionMixin); + this.barnesHutTree = undefined; + + this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.repulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; + this.constants.physics.damping = this.constants.physics.repulsion.damping; + + this._loadMixin(RepulsionMixin); + } +}; + +/** + * Before calculating the forces, we check if we need to cluster to keep up performance and we check + * if there is more than one node. If it is just one node, we dont calculate anything. + * + * @private + */ +exports._initializeForceCalculation = function () { + // stop calculation if there is only one node + if (this.nodeIndices.length == 1) { + this.nodes[this.nodeIndices[0]]._setForce(0, 0); + } + else { + // if there are too many nodes on screen, we cluster without repositioning + if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { + this.clusterToFit(this.constants.clustering.reduceToNodes, false); + } + + // we now start the force calculation + this._calculateForces(); + } +}; + + +/** + * Calculate the external forces acting on the nodes + * Forces are caused by: edges, repulsing forces between nodes, gravity + * @private + */ +exports._calculateForces = function () { + // Gravity is required to keep separated groups from floating off + // the forces are reset to zero in this loop by using _setForce instead + // of _addForce + + this._calculateGravitationalForces(); + this._calculateNodeForces(); + + if (this.constants.smoothCurves == true) { + this._calculateSpringForcesWithSupport(); + } + else { + if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._calculateHierarchicalSpringForces(); + } + else { + this._calculateSpringForces(); + } + } +}; + + +/** + * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also + * handled in the calculateForces function. We then use a quadratic curve with the center node as control. + * This function joins the datanodes and invisible (called support) nodes into one object. + * We do this so we do not contaminate this.nodes with the support nodes. + * + * @private + */ +exports._updateCalculationNodes = function () { + if (this.constants.smoothCurves == true) { + this.calculationNodes = {}; + this.calculationNodeIndices = []; + + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId] = this.nodes[nodeId]; + } + } + var supportNodes = this.sectors['support']['nodes']; + for (var supportNodeId in supportNodes) { + if (supportNodes.hasOwnProperty(supportNodeId)) { + if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { + this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; + } + else { + supportNodes[supportNodeId]._setForce(0, 0); + } + } + } + + for (var idx in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(idx)) { + this.calculationNodeIndices.push(idx); + } + } + } + else { + this.calculationNodes = this.nodes; + this.calculationNodeIndices = this.nodeIndices; + } +}; + + +/** + * this function applies the central gravity effect to keep groups from floating off + * + * @private + */ +exports._calculateGravitationalForces = function () { + var dx, dy, distance, node, i; + var nodes = this.calculationNodes; + var gravity = this.constants.physics.centralGravity; + var gravityForce = 0; + + for (i = 0; i < this.calculationNodeIndices.length; i++) { + node = nodes[this.calculationNodeIndices[i]]; + node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. + // gravity does not apply when we are in a pocket sector + if (this._sector() == "default" && gravity != 0) { + dx = -node.x; + dy = -node.y; + distance = Math.sqrt(dx * dx + dy * dy); + + gravityForce = (distance == 0) ? 0 : (gravity / distance); + node.fx = dx * gravityForce; + node.fy = dy * gravityForce; + } + else { + node.fx = 0; + node.fy = 0; + } + } +}; + + + + +/** + * this function calculates the effects of the springs in the case of unsmooth curves. + * + * @private + */ +exports._calculateSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + edge.from.fx += fx; + edge.from.fy += fy; + edge.to.fx -= fx; + edge.to.fy -= fy; + } + } + } + } +}; + + + + +/** + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private + */ +exports._calculateSpringForcesWithSupport = function () { + var edgeLength, edge, edgeId, combinedClusterSize; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + if (edge.via != null) { + var node1 = edge.to; + var node2 = edge.via; + var node3 = edge.from; + + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; + + combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + + // this implies that the edges between big clusters are longer + edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; + this._calculateSpringForce(node1, node2, 0.5 * edgeLength); + this._calculateSpringForce(node2, node3, 0.5 * edgeLength); + } + } + } + } + } +}; + + +/** + * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. + * + * @param node1 + * @param node2 + * @param edgeLength + * @private + */ +exports._calculateSpringForce = function (node1, node2, edgeLength) { + var dx, dy, fx, fy, springForce, distance; + + dx = (node1.x - node2.x); + dy = (node1.y - node2.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + node1.fx += fx; + node1.fy += fy; + node2.fx -= fx; + node2.fy -= fy; +}; + + +/** + * Load the HTML for the physics config and bind it + * @private + */ +exports._loadPhysicsConfiguration = function () { + if (this.physicsConfiguration === undefined) { + this.backupConstants = {}; + util.deepExtend(this.backupConstants,this.constants); + + var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; + this.physicsConfiguration = document.createElement('div'); + this.physicsConfiguration.className = "PhysicsConfiguration"; + this.physicsConfiguration.innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Options:
' + this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); + this.optionsDiv = document.createElement("div"); + this.optionsDiv.style.fontSize = "14px"; + this.optionsDiv.style.fontFamily = "verdana"; + this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); + + var rangeElement; + rangeElement = document.getElementById('graph_BH_gc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); + rangeElement = document.getElementById('graph_BH_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_BH_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_BH_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_BH_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_R_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); + rangeElement = document.getElementById('graph_R_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_R_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_R_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_R_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_H_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); + rangeElement = document.getElementById('graph_H_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_H_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_H_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_H_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); + rangeElement = document.getElementById('graph_H_direction'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); + rangeElement = document.getElementById('graph_H_levsep'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); + rangeElement = document.getElementById('graph_H_nspac'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + var radioButton3 = document.getElementById("graph_physicsMethod3"); + radioButton2.checked = true; + if (this.constants.physics.barnesHut.enabled) { + radioButton1.checked = true; + } + if (this.constants.hierarchicalLayout.enabled) { + radioButton3.checked = true; + } + + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + var graph_repositionNodes = document.getElementById("graph_repositionNodes"); + var graph_generateOptions = document.getElementById("graph_generateOptions"); + + graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); + graph_repositionNodes.onclick = graphRepositionNodes.bind(this); + graph_generateOptions.onclick = graphGenerateOptions.bind(this); + if (this.constants.smoothCurves == true) { + graph_toggleSmooth.style.background = "#A4FF56"; + } + else { + graph_toggleSmooth.style.background = "#FF8532"; + } + + + switchConfigurations.apply(this); + + radioButton1.onchange = switchConfigurations.bind(this); + radioButton2.onchange = switchConfigurations.bind(this); + radioButton3.onchange = switchConfigurations.bind(this); + } +}; + +/** + * This overwrites the this.constants. + * + * @param constantsVariableName + * @param value + * @private + */ +exports._overWriteGraphConstants = function (constantsVariableName, value) { + var nameArray = constantsVariableName.split("_"); + if (nameArray.length == 1) { + this.constants[nameArray[0]] = value; + } + else if (nameArray.length == 2) { + this.constants[nameArray[0]][nameArray[1]] = value; + } + else if (nameArray.length == 3) { + this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; + } +}; + + +/** + * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. + */ +function graphToggleSmoothCurves () { + this.constants.smoothCurves = !this.constants.smoothCurves; + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + + this._configureSmoothCurves(false); +} + +/** + * this function is used to scramble the nodes + * + */ +function graphRepositionNodes () { + for (var nodeId in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; + this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; + } + } + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + } + else { + this.repositionNodes(); + } + this.moving = true; + this.start(); +} + +/** + * this is used to generate an options file from the playing with physics system. + */ +function graphGenerateOptions () { + var options = "No options are required, default values used."; + var optionsSpecific = []; + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + if (radioButton1.checked == true) { + if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options = "var options = {"; + options += "physics: {barnesHut: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}}' + } + if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { + if (optionsSpecific.length == 0) {options = "var options = {";} + else {options += ", "} + options += "smoothCurves: " + this.constants.smoothCurves; + } + if (options != "No options are required, default values used.") { + options += '};' + } + } + else if (radioButton2.checked == true) { + options = "var options = {"; + options += "physics: {barnesHut: {enabled: false}"; + if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += ", repulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}}' + } + if (optionsSpecific.length == 0) {options += "}"} + if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { + options += ", smoothCurves: " + this.constants.smoothCurves; + } + options += '};' + } + else { + options = "var options = {"; + if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += "physics: {hierarchicalRepulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", "; + } + } + options += '}},'; + } + options += 'hierarchicalLayout: {'; + optionsSpecific = []; + if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} + if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} + if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} + if (optionsSpecific.length != 0) { + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}' + } + else { + options += "enabled:true}"; + } + options += '};' + } + + + this.optionsDiv.innerHTML = options; +} + +/** + * this is used to switch between barnesHut, repulsion and hierarchical. + * + */ +function switchConfigurations () { + var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; + var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; + var tableId = "graph_" + radioButton + "_table"; + var table = document.getElementById(tableId); + table.style.display = "block"; + for (var i = 0; i < ids.length; i++) { + if (ids[i] != tableId) { + table = document.getElementById(ids[i]); + table.style.display = "none"; + } + } + this._restoreNodes(); + if (radioButton == "R") { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = false; + } + else if (radioButton == "H") { + if (this.constants.hierarchicalLayout.enabled == false) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + this._setupHierarchicalLayout(); + } + } + else { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = true; + } + this._loadSelectedForceSolver(); + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + this.moving = true; + this.start(); +} + + +/** + * this generates the ranges depending on the iniital values. + * + * @param id + * @param map + * @param constantsVariableName + */ +function showValueOfRange (id,map,constantsVariableName) { + var valueId = id + "_value"; + var rangeValue = document.getElementById(id).value; + + if (map instanceof Array) { + document.getElementById(valueId).value = map[parseInt(rangeValue)]; + this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); + } + else { + document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); + this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); + } + + if (constantsVariableName == "hierarchicalLayout_direction" || + constantsVariableName == "hierarchicalLayout_levelSeparation" || + constantsVariableName == "hierarchicalLayout_nodeSpacing") { + this._setupHierarchicalLayout(); + } + this.moving = true; + this.start(); +} diff --git a/lib/network/mixins/physics/RepulsionMixin.js b/lib/network/mixins/physics/RepulsionMixin.js new file mode 100644 index 00000000..06b5ff22 --- /dev/null +++ b/lib/network/mixins/physics/RepulsionMixin.js @@ -0,0 +1,58 @@ +/** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ +exports._calculateNodeForces = function () { + var dx, dy, angle, distance, fx, fy, combinedClusterSize, + repulsingForce, node1, node2, i, j; + + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + + // approximation constants + var a_base = -2 / 3; + var b = 4 / 3; + + // repulsing forces between nodes + var nodeDistance = this.constants.physics.repulsion.nodeDistance; + var minimumDistance = nodeDistance; + + // we loop from i over all but the last entree in the array + // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j + for (i = 0; i < nodeIndices.length - 1; i++) { + node1 = nodes[nodeIndices[i]]; + for (j = i + 1; j < nodeIndices.length; j++) { + node2 = nodes[nodeIndices[j]]; + combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; + + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); + + minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); + var a = a_base / minimumDistance; + if (distance < 2 * minimumDistance) { + if (distance < 0.5 * minimumDistance) { + repulsingForce = 1.0; + } + else { + repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) + } + + // amplify the repulsion for clusters. + repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; + repulsingForce = repulsingForce / distance; + + fx = dx * repulsingForce; + fy = dy * repulsingForce; + + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; + } + } + } +}; diff --git a/lib/network/networkMixins/ClusterMixin.js b/lib/network/networkMixins/ClusterMixin.js deleted file mode 100644 index 512ceb2a..00000000 --- a/lib/network/networkMixins/ClusterMixin.js +++ /dev/null @@ -1,1143 +0,0 @@ -/** - * Creation of the ClusterMixin var. - * - * This contains all the functions the Network object can use to employ clustering - * - * Alex de Mulder - * 21-01-2013 - */ -var ClusterMixin = { - - /** - * This is only called in the constructor of the network object - * - */ - startWithClustering : function() { - // cluster if the data set is big - this.clusterToFit(this.constants.clustering.initialMaxNodes, true); - - // updates the lables after clustering - this.updateLabels(); - - // this is called here because if clusterin is disabled, the start and stabilize are called in - // the setData function. - if (this.stabilize) { - this._stabilize(); - } - this.start(); - }, - - /** - * This function clusters until the initialMaxNodes has been reached - * - * @param {Number} maxNumberOfNodes - * @param {Boolean} reposition - */ - clusterToFit : function(maxNumberOfNodes, reposition) { - var numberOfNodes = this.nodeIndices.length; - - var maxLevels = 50; - var level = 0; - - // we first cluster the hubs, then we pull in the outliers, repeat - while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { - if (level % 3 == 0) { - this.forceAggregateHubs(true); - this.normalizeClusterLevels(); - } - else { - this.increaseClusterLevel(); // this also includes a cluster normalization - } - - numberOfNodes = this.nodeIndices.length; - level += 1; - } - - // after the clustering we reposition the nodes to reduce the initial chaos - if (level > 0 && reposition == true) { - this.repositionNodes(); - } - this._updateCalculationNodes(); - }, - - /** - * This function can be called to open up a specific cluster. It is only called by - * It will unpack the cluster back one level. - * - * @param node | Node object: cluster to open. - */ - openCluster : function(node) { - var isMovingBeforeClustering = this.moving; - if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && - !(this._sector() == "default" && this.nodeIndices.length == 1)) { - // this loads a new sector, loads the nodes and edges and nodeIndices of it. - this._addSector(node); - var level = 0; - - // we decluster until we reach a decent number of nodes - while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { - this.decreaseClusterLevel(); - level += 1; - } - - } - else { - this._expandClusterNode(node,false,true); - - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this._updateCalculationNodes(); - this.updateLabels(); - } - - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); - } - }, - - - /** - * This calls the updateClustes with default arguments - */ - updateClustersDefault : function() { - if (this.constants.clustering.enabled == true) { - this.updateClusters(0,false,false); - } - }, - - - /** - * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will - * be clustered with their connected node. This can be repeated as many times as needed. - * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. - */ - increaseClusterLevel : function() { - this.updateClusters(-1,false,true); - }, - - - /** - * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will - * be unpacked if they are a cluster. This can be repeated as many times as needed. - * This can be called externally (by a key-bind for instance) to look into clusters without zooming. - */ - decreaseClusterLevel : function() { - this.updateClusters(1,false,true); - }, - - - /** - * This is the main clustering function. It clusters and declusters on zoom or forced - * This function clusters on zoom, it can be called with a predefined zoom direction - * If out, check if we can form clusters, if in, check if we can open clusters. - * This function is only called from _zoom() - * - * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn - * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} doNotStart | if true do not call start - * - */ - updateClusters : function(zoomDirection,recursive,force,doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; - - // on zoom out collapse the sector if the scale is at the level the sector was made - if (this.previousScale > this.scale && zoomDirection == 0) { - this._collapseSector(); - } - - // check if we zoom in or out - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - // forming clusters when forced pulls outliers in. When not forced, the edge length of the - // outer nodes determines if it is being clustered - this._formClusters(force); - } - else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in - if (force == true) { - // _openClusters checks for each node if the formationScale of the cluster is smaller than - // the current scale and if so, declusters. When forced, all clusters are reduced by one step - this._openClusters(recursive,force); - } - else { - // if a cluster takes up a set percentage of the active window - this._openClustersBySize(); - } - } - this._updateNodeIndexList(); - - // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs - if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { - this._aggregateHubs(force); - this._updateNodeIndexList(); - } - - // we now reduce chains. - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - this.handleChains(); - this._updateNodeIndexList(); - } - - this.previousScale = this.scale; - - // rest of the update the index list, dynamic edges and labels - this._updateDynamicEdges(); - this.updateLabels(); - - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place - this.clusterSession += 1; - // if clusters have been made, we normalize the cluster level - this.normalizeClusterLevels(); - } - - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); - } - } - - this._updateCalculationNodes(); - }, - - /** - * This function handles the chains. It is called on every updateClusters(). - */ - handleChains : function() { - // after clustering we check how many chains there are - var chainPercentage = this._getChainFraction(); - if (chainPercentage > this.constants.clustering.chainThreshold) { - this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) - - } - }, - - /** - * this functions starts clustering by hubs - * The minimum hub threshold is set globally - * - * @private - */ - _aggregateHubs : function(force) { - this._getHubSize(); - this._formClustersByHub(force,false); - }, - - - /** - * This function is fired by keypress. It forces hubs to form. - * - */ - forceAggregateHubs : function(doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; - - this._aggregateHubs(true); - - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this.updateLabels(); - - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; - } - - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); - } - } - }, - - /** - * If a cluster takes up more than a set percentage of the screen, open the cluster - * - * @private - */ - _openClustersBySize : function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.inView() == true) { - if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - this.openCluster(node); - } - } - } - } - }, - - - /** - * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it - * has to be opened based on the current zoom level. - * - * @private - */ - _openClusters : function(recursive,force) { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - this._expandClusterNode(node,recursive,force); - this._updateCalculationNodes(); - } - }, - - /** - * This function checks if a node has to be opened. This is done by checking the zoom level. - * If the node contains child nodes, this function is recursively called on the child nodes as well. - * This recursive behaviour is optional and can be set by the recursive argument. - * - * @param {Node} parentNode | to check for cluster and expand - * @param {Boolean} recursive | enabled or disable recursive calling - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released - * @private - */ - _expandClusterNode : function(parentNode, recursive, force, openAll) { - // first check if node is a cluster - if (parentNode.clusterSize > 1) { - // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 - if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { - openAll = true; - } - recursive = openAll ? true : recursive; - - // if the last child has been added on a smaller scale than current scale decluster - if (parentNode.formationScale < this.scale || force == true) { - // we will check if any of the contained child nodes should be removed from the cluster - for (var containedNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { - var childNode = parentNode.containedNodes[containedNodeId]; - - // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that - // the largest cluster is the one that comes from outside - if (force == true) { - if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] - || openAll) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } - else { - if (this._nodeInActiveArea(parentNode)) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } - } - } - } - } - }, - - /** - * ONLY CALLED FROM _expandClusterNode - * - * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove - * the child node from the parent contained_node object and put it back into the global nodes object. - * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. - * - * @param {Node} parentNode | the parent node - * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node - * @param {Boolean} recursive | This will also check if the child needs to be expanded. - * With force and recursive both true, the entire cluster is unpacked - * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent - * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released - * @private - */ - _expelChildFromParent : function(parentNode, containedNodeId, recursive, force, openAll) { - var childNode = parentNode.containedNodes[containedNodeId]; - - // if child node has been added on smaller scale than current, kick out - if (childNode.formationScale < this.scale || force == true) { - // unselect all selected items - this._unselectAll(); - - // put the child node back in the global nodes object - this.nodes[containedNodeId] = childNode; - - // release the contained edges from this childNode back into the global edges - this._releaseContainedEdges(parentNode,childNode); - - // reconnect rerouted edges to the childNode - this._connectEdgeBackToChild(parentNode,childNode); - - // validate all edges in dynamicEdges - this._validateEdges(parentNode); - - // undo the changes from the clustering operation on the parent node - parentNode.mass -= childNode.mass; - parentNode.clusterSize -= childNode.clusterSize; - parentNode.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); - parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; - - // place the child node near the parent, not at the exact same location to avoid chaos in the system - childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); - childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); - - // remove node from the list - delete parentNode.containedNodes[containedNodeId]; - - // check if there are other childs with this clusterSession in the parent. - var othersPresent = false; - for (var childNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { - if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { - othersPresent = true; - break; - } - } - } - // if there are no others, remove the cluster session from the list - if (othersPresent == false) { - parentNode.clusterSessions.pop(); - } - - this._repositionBezierNodes(childNode); -// this._repositionBezierNodes(parentNode); - - // remove the clusterSession from the child node - childNode.clusterSession = 0; - - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); - - // restart the simulation to reorganise all nodes - this.moving = true; - } - - // check if a further expansion step is possible if recursivity is enabled - if (recursive == true) { - this._expandClusterNode(childNode,recursive,force,openAll); - } - }, - - - /** - * position the bezier nodes at the center of the edges - * - * @param node - * @private - */ - _repositionBezierNodes : function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - node.dynamicEdges[i].positionBezierNode(); - } - }, - - - /** - * This function checks if any nodes at the end of their trees have edges below a threshold length - * This function is called only from updateClusters() - * forceLevelCollapse ignores the length of the edge and collapses one level - * This means that a node with only one edge will be clustered with its connected node - * - * @private - * @param {Boolean} force - */ - _formClusters : function(force) { - if (force == false) { - this._formClustersByZoom(); - } - else { - this._forceClustersByZoom(); - } - }, - - - /** - * This function handles the clustering by zooming out, this is based on a minimum edge distance - * - * @private - */ - _formClustersByZoom : function() { - var dx,dy,length, - minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - - // check if any edges are shorter than minLength and start the clustering - // the clustering favours the node with the larger mass - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); - - - if (length < minLength) { - // first check which node is larger - var parentNode = edge.from; - var childNode = edge.to; - if (edge.to.mass > edge.from.mass) { - parentNode = edge.to; - childNode = edge.from; - } - - if (childNode.dynamicEdgesLength == 1) { - this._addToCluster(parentNode,childNode,false); - } - else if (parentNode.dynamicEdgesLength == 1) { - this._addToCluster(childNode,parentNode,false); - } - } - } - } - } - } - }, - - /** - * This function forces the network to cluster all nodes with only one connecting edge to their - * connected node. - * - * @private - */ - _forceClustersByZoom : function() { - for (var nodeId in this.nodes) { - // another node could have absorbed this child. - if (this.nodes.hasOwnProperty(nodeId)) { - var childNode = this.nodes[nodeId]; - - // the edges can be swallowed by another decrease - if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { - var edge = childNode.dynamicEdges[0]; - var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; - - // group to the largest node - if (childNode.id != parentNode.id) { - if (parentNode.mass > childNode.mass) { - this._addToCluster(parentNode,childNode,true); - } - else { - this._addToCluster(childNode,parentNode,true); - } - } - } - } - } - }, - - - /** - * To keep the nodes of roughly equal size we normalize the cluster levels. - * This function clusters a node to its smallest connected neighbour. - * - * @param node - * @private - */ - _clusterToSmallestNeighbour : function(node) { - var smallestNeighbour = -1; - var smallestNeighbourNode = null; - for (var i = 0; i < node.dynamicEdges.length; i++) { - if (node.dynamicEdges[i] !== undefined) { - var neighbour = null; - if (node.dynamicEdges[i].fromId != node.id) { - neighbour = node.dynamicEdges[i].from; - } - else if (node.dynamicEdges[i].toId != node.id) { - neighbour = node.dynamicEdges[i].to; - } - - - if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { - smallestNeighbour = neighbour.clusterSessions.length; - smallestNeighbourNode = neighbour; - } - } - } - - if (neighbour != null && this.nodes[neighbour.id] !== undefined) { - this._addToCluster(neighbour, node, true); - } - }, - - - /** - * This function forms clusters from hubs, it loops over all nodes - * - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges - * @private - */ - _formClustersByHub : function(force, onlyEqual) { - // we loop over all nodes in the list - for (var nodeId in this.nodes) { - // we check if it is still available since it can be used by the clustering in this loop - if (this.nodes.hasOwnProperty(nodeId)) { - this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); - } - } - }, - - /** - * This function forms a cluster from a specific preselected hub node - * - * @param {Node} hubNode | the node we will cluster as a hub - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges - * @param {Number} [absorptionSizeOffset] | - * @private - */ - _formClusterFromHub : function(hubNode, force, onlyEqual, absorptionSizeOffset) { - if (absorptionSizeOffset === undefined) { - absorptionSizeOffset = 0; - } - // we decide if the node is a hub - if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || - (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { - // initialize variables - var dx,dy,length; - var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - var allowCluster = false; - - // we create a list of edges because the dynamicEdges change over the course of this loop - var edgesIdarray = []; - var amountOfInitialEdges = hubNode.dynamicEdges.length; - for (var j = 0; j < amountOfInitialEdges; j++) { - edgesIdarray.push(hubNode.dynamicEdges[j].id); - } - - // if the hub clustering is not forces, we check if one of the edges connected - // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold - if (force == false) { - allowCluster = false; - for (j = 0; j < amountOfInitialEdges; j++) { - var edge = this.edges[edgesIdarray[j]]; - if (edge !== undefined) { - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); - - if (length < minLength) { - allowCluster = true; - break; - } - } - } - } - } - } - - // start the clustering if allowed - if ((!force && allowCluster) || force) { - // we loop over all edges INITIALLY connected to this hub - for (j = 0; j < amountOfInitialEdges; j++) { - edge = this.edges[edgesIdarray[j]]; - // the edge can be clustered by this function in a previous loop - if (edge !== undefined) { - var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; - // we do not want hubs to merge with other hubs nor do we want to cluster itself. - if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && - (childNode.id != hubNode.id)) { - this._addToCluster(hubNode,childNode,force); - } - } - } - } - } - }, - - - - /** - * This function adds the child node to the parent node, creating a cluster if it is not already. - * - * @param {Node} parentNode | this is the node that will house the child node - * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node - * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse - * @private - */ - _addToCluster : function(parentNode, childNode, force) { - // join child node in the parent node - parentNode.containedNodes[childNode.id] = childNode; - - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < childNode.dynamicEdges.length; i++) { - var edge = childNode.dynamicEdges[i]; - if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode - this._addToContainedEdges(parentNode,childNode,edge); - } - else { - this._connectEdgeToCluster(parentNode,childNode,edge); - } - } - // a contained node has no dynamic edges. - childNode.dynamicEdges = []; - - // remove circular edges from clusters - this._containCircularEdgesFromNode(parentNode,childNode); - - - // remove the childNode from the global nodes object - delete this.nodes[childNode.id]; - - // update the properties of the child and parent - var massBefore = parentNode.mass; - childNode.clusterSession = this.clusterSession; - parentNode.mass += childNode.mass; - parentNode.clusterSize += childNode.clusterSize; - parentNode.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); - - // keep track of the clustersessions so we can open the cluster up as it has been formed. - if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { - parentNode.clusterSessions.push(this.clusterSession); - } - - // forced clusters only open from screen size and double tap - if (force == true) { - // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); - parentNode.formationScale = 0; - } - else { - parentNode.formationScale = this.scale; // The latest child has been added on this scale - } - - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); - - // set the pop-out scale for the childnode - parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; - - // nullify the movement velocity of the child, this is to avoid hectic behaviour - childNode.clearVelocity(); - - // the mass has altered, preservation of energy dictates the velocity to be updated - parentNode.updateVelocity(massBefore); - - // restart the simulation to reorganise all nodes - this.moving = true; - }, - - - /** - * This function will apply the changes made to the remainingEdges during the formation of the clusters. - * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. - * It has to be called if a level is collapsed. It is called by _formClusters(). - * @private - */ - _updateDynamicEdges : function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - node.dynamicEdgesLength = node.dynamicEdges.length; - - // this corrects for multiple edges pointing at the same other node - var correction = 0; - if (node.dynamicEdgesLength > 1) { - for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { - var edgeToId = node.dynamicEdges[j].toId; - var edgeFromId = node.dynamicEdges[j].fromId; - for (var k = j+1; k < node.dynamicEdgesLength; k++) { - if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || - (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { - correction += 1; - } - } - } - } - node.dynamicEdgesLength -= correction; - } - }, - - - /** - * This adds an edge from the childNode to the contained edges of the parent node - * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object - * @private - */ - _addToContainedEdges : function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { - parentNode.containedEdges[childNode.id] = [] - } - // add this edge to the list - parentNode.containedEdges[childNode.id].push(edge); - - // remove the edge from the global edges object - delete this.edges[edge.id]; - - // remove the edge from the parent object - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - if (parentNode.dynamicEdges[i].id == edge.id) { - parentNode.dynamicEdges.splice(i,1); - break; - } - } - }, - - /** - * This function connects an edge that was connected to a child node to the parent node. - * It keeps track of which nodes it has been connected to with the originalId array. - * - * @param {Node} parentNode | Node object - * @param {Node} childNode | Node object - * @param {Edge} edge | Edge object - * @private - */ - _connectEdgeToCluster : function(parentNode, childNode, edge) { - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); - } - else { - if (edge.toId == childNode.id) { // edge connected to other node on the "to" side - edge.originalToId.push(childNode.id); - edge.to = parentNode; - edge.toId = parentNode.id; - } - else { // edge connected to other node with the "from" side - - edge.originalFromId.push(childNode.id); - edge.from = parentNode; - edge.fromId = parentNode.id; - } - - this._addToReroutedEdges(parentNode,childNode,edge); - } - }, - - - /** - * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain - * these edges inside of the cluster. - * - * @param parentNode - * @param childNode - * @private - */ - _containCircularEdgesFromNode : function(parentNode, childNode) { - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); - } - } - }, - - - /** - * This adds an edge from the childNode to the rerouted edges of the parent node - * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object - * @private - */ - _addToReroutedEdges : function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - // we store the edge in the rerouted edges so we can restore it when the cluster pops open - if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { - parentNode.reroutedEdges[childNode.id] = []; - } - parentNode.reroutedEdges[childNode.id].push(edge); - - // this edge becomes part of the dynamicEdges of the cluster node - parentNode.dynamicEdges.push(edge); - }, - - - - /** - * This function connects an edge that was connected to a cluster node back to the child node. - * - * @param parentNode | Node object - * @param childNode | Node object - * @private - */ - _connectEdgeBackToChild : function(parentNode, childNode) { - if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { - for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { - var edge = parentNode.reroutedEdges[childNode.id][i]; - if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { - edge.originalFromId.pop(); - edge.fromId = childNode.id; - edge.from = childNode; - } - else { - edge.originalToId.pop(); - edge.toId = childNode.id; - edge.to = childNode; - } - - // append this edge to the list of edges connecting to the childnode - childNode.dynamicEdges.push(edge); - - // remove the edge from the parent object - for (var j = 0; j < parentNode.dynamicEdges.length; j++) { - if (parentNode.dynamicEdges[j].id == edge.id) { - parentNode.dynamicEdges.splice(j,1); - break; - } - } - } - // remove the entry from the rerouted edges - delete parentNode.reroutedEdges[childNode.id]; - } - }, - - - /** - * When loops are clustered, an edge can be both in the rerouted array and the contained array. - * This function is called last to verify that all edges in dynamicEdges are in fact connected to the - * parentNode - * - * @param parentNode | Node object - * @private - */ - _validateEdges : function(parentNode) { - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { - parentNode.dynamicEdges.splice(i,1); - } - } - }, - - - /** - * This function released the contained edges back into the global domain and puts them back into the - * dynamic edges of both parent and child. - * - * @param {Node} parentNode | - * @param {Node} childNode | - * @private - */ - _releaseContainedEdges : function(parentNode, childNode) { - for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { - var edge = parentNode.containedEdges[childNode.id][i]; - - // put the edge back in the global edges object - this.edges[edge.id] = edge; - - // put the edge back in the dynamic edges of the child and parent - childNode.dynamicEdges.push(edge); - parentNode.dynamicEdges.push(edge); - } - // remove the entry from the contained edges - delete parentNode.containedEdges[childNode.id]; - - }, - - - - - // ------------------- UTILITY FUNCTIONS ---------------------------- // - - - /** - * This updates the node labels for all nodes (for debugging purposes) - */ - updateLabels : function() { - var nodeId; - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.clusterSize > 1) { - node.label = "[".concat(String(node.clusterSize),"]"); - } - } - } - - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.clusterSize == 1) { - if (node.originalLabel !== undefined) { - node.label = node.originalLabel; - } - else { - node.label = String(node.id); - } - } - } - } - -// /* Debug Override */ -// for (nodeId in this.nodes) { -// if (this.nodes.hasOwnProperty(nodeId)) { -// node = this.nodes[nodeId]; -// node.label = String(node.level); -// } -// } - - }, - - - /** - * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes - * if the rest of the nodes are already a few cluster levels in. - * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not - * clustered enough to the clusterToSmallestNeighbours function. - */ - normalizeClusterLevels : function() { - var maxLevel = 0; - var minLevel = 1e9; - var clusterLevel = 0; - var nodeId; - - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - clusterLevel = this.nodes[nodeId].clusterSessions.length; - if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} - if (minLevel > clusterLevel) {minLevel = clusterLevel;} - } - } - - if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { - var amountOfNodes = this.nodeIndices.length; - var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].clusterSessions.length < targetLevel) { - this._clusterToSmallestNeighbour(this.nodes[nodeId]); - } - } - } - this._updateNodeIndexList(); - this._updateDynamicEdges(); - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; - } - } - }, - - - - /** - * This function determines if the cluster we want to decluster is in the active area - * this means around the zoom center - * - * @param {Node} node - * @returns {boolean} - * @private - */ - _nodeInActiveArea : function(node) { - return ( - Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale - && - Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale - ) - }, - - - /** - * This is an adaptation of the original repositioning function. This is called if the system is clustered initially - * It puts large clusters away from the center and randomizes the order. - * - */ - repositionNodes : function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - if ((node.xFixed == false || node.yFixed == false)) { - var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.mass); - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - this._repositionBezierNodes(node); - } - } - }, - - - /** - * We determine how many connections denote an important hub. - * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) - * - * @private - */ - _getHubSize : function() { - var average = 0; - var averageSquared = 0; - var hubCounter = 0; - var largestHub = 0; - - for (var i = 0; i < this.nodeIndices.length; i++) { - - var node = this.nodes[this.nodeIndices[i]]; - if (node.dynamicEdgesLength > largestHub) { - largestHub = node.dynamicEdgesLength; - } - average += node.dynamicEdgesLength; - averageSquared += Math.pow(node.dynamicEdgesLength,2); - hubCounter += 1; - } - average = average / hubCounter; - averageSquared = averageSquared / hubCounter; - - var variance = averageSquared - Math.pow(average,2); - - var standardDeviation = Math.sqrt(variance); - - this.hubThreshold = Math.floor(average + 2*standardDeviation); - - // always have at least one to cluster - if (this.hubThreshold > largestHub) { - this.hubThreshold = largestHub; - } - - // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); - // console.log("hubThreshold:",this.hubThreshold); - }, - - - /** - * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. - * - * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce - * @private - */ - _reduceAmountOfChains : function(fraction) { - this.hubThreshold = 2; - var reduceAmount = Math.floor(this.nodeIndices.length * fraction); - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - if (reduceAmount > 0) { - this._formClusterFromHub(this.nodes[nodeId],true,true,1); - reduceAmount -= 1; - } - } - } - } - }, - - /** - * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. - * - * @private - */ - _getChainFraction : function() { - var chains = 0; - var total = 0; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - chains += 1; - } - total += 1; - } - } - return chains/total; - } - -}; diff --git a/lib/network/networkMixins/HierarchicalLayoutMixin.js b/lib/network/networkMixins/HierarchicalLayoutMixin.js deleted file mode 100644 index 6a7c4706..00000000 --- a/lib/network/networkMixins/HierarchicalLayoutMixin.js +++ /dev/null @@ -1,311 +0,0 @@ -var HierarchicalLayoutMixin = { - - - - _resetLevels : function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.preassignedLevel == false) { - node.level = -1; - } - } - } - }, - - /** - * This is the main function to layout the nodes in a hierarchical way. - * It checks if the node details are supplied correctly - * - * @private - */ - _setupHierarchicalLayout : function() { - if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { - if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "DU") { - this.constants.hierarchicalLayout.levelSeparation *= -1; - } - else { - this.constants.hierarchicalLayout.levelSeparation = Math.abs(this.constants.hierarchicalLayout.levelSeparation); - } - // get the size of the largest hubs and check if the user has defined a level for a node. - var hubsize = 0; - var node, nodeId; - var definedLevel = false; - var undefinedLevel = false; - - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level != -1) { - definedLevel = true; - } - else { - undefinedLevel = true; - } - if (hubsize < node.edges.length) { - hubsize = node.edges.length; - } - } - } - - // if the user defined some levels but not all, alert and run without hierarchical layout - if (undefinedLevel == true && definedLevel == true) { - alert("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); - this.zoomExtent(true,this.constants.clustering.enabled); - if (!this.constants.clustering.enabled) { - this.start(); - } - } - else { - // setup the system to use hierarchical method. - this._changeConstants(); - - // define levels if undefined by the users. Based on hubsize - if (undefinedLevel == true) { - this._determineLevels(hubsize); - } - // check the distribution of the nodes per level. - var distribution = this._getDistribution(); - - // place the nodes on the canvas. This also stablilizes the system. - this._placeNodesByHierarchy(distribution); - - // start the simulation. - this.start(); - } - } - }, - - - /** - * This function places the nodes on the canvas based on the hierarchial distribution. - * - * @param {Object} distribution | obtained by the function this._getDistribution() - * @private - */ - _placeNodesByHierarchy : function(distribution) { - var nodeId, node; - - // start placing all the level 0 nodes first. Then recursively position their branches. - for (nodeId in distribution[0].nodes) { - if (distribution[0].nodes.hasOwnProperty(nodeId)) { - node = distribution[0].nodes[nodeId]; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (node.xFixed) { - node.x = distribution[0].minPos; - node.xFixed = false; - - distribution[0].minPos += distribution[0].nodeSpacing; - } - } - else { - if (node.yFixed) { - node.y = distribution[0].minPos; - node.yFixed = false; - - distribution[0].minPos += distribution[0].nodeSpacing; - } - } - this._placeBranchNodes(node.edges,node.id,distribution,node.level); - } - } - - // stabilize the system after positioning. This function calls zoomExtent. - this._stabilize(); - }, - - - /** - * This function get the distribution of levels based on hubsize - * - * @returns {Object} - * @private - */ - _getDistribution : function() { - var distribution = {}; - var nodeId, node, level; - - // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. - // the fix of X is removed after the x value has been set. - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.xFixed = true; - node.yFixed = true; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - else { - node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - if (!distribution.hasOwnProperty(node.level)) { - distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; - } - distribution[node.level].amount += 1; - distribution[node.level].nodes[node.id] = node; - } - } - - // determine the largest amount of nodes of all levels - var maxCount = 0; - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - if (maxCount < distribution[level].amount) { - maxCount = distribution[level].amount; - } - } - } - - // set the initial position and spacing of each nodes accordingly - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; - distribution[level].nodeSpacing /= (distribution[level].amount + 1); - distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); - } - } - - return distribution; - }, - - - /** - * this function allocates nodes in levels based on the recursive branching from the largest hubs. - * - * @param hubsize - * @private - */ - _determineLevels : function(hubsize) { - var nodeId, node; - - // determine hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.edges.length == hubsize) { - node.level = 0; - } - } - } - - // branch from hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level == 0) { - this._setLevel(1,node.edges,node.id); - } - } - } - }, - - - /** - * Since hierarchical layout does not support: - * - smooth curves (based on the physics), - * - clustering (based on dynamic node counts) - * - * We disable both features so there will be no problems. - * - * @private - */ - _changeConstants : function() { - this.constants.clustering.enabled = false; - this.constants.physics.barnesHut.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this._loadSelectedForceSolver(); - this.constants.smoothCurves = false; - this._configureSmoothCurves(); - }, - - - /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. - * - * @param edges - * @param parentId - * @param distribution - * @param parentLevel - * @private - */ - _placeBranchNodes : function(edges, parentId, distribution, parentLevel) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } - - // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. - var nodeMoved = false; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (childNode.xFixed && childNode.level > parentLevel) { - childNode.xFixed = false; - childNode.x = distribution[childNode.level].minPos; - nodeMoved = true; - } - } - else { - if (childNode.yFixed && childNode.level > parentLevel) { - childNode.yFixed = false; - childNode.y = distribution[childNode.level].minPos; - nodeMoved = true; - } - } - - if (nodeMoved == true) { - distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; - if (childNode.edges.length > 1) { - this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); - } - } - } - }, - - - /** - * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. - * - * @param level - * @param edges - * @param parentId - * @private - */ - _setLevel : function(level, edges, parentId) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1 || childNode.level > level) { - childNode.level = level; - if (edges.length > 1) { - this._setLevel(level+1, childNode.edges, childNode.id); - } - } - } - }, - - - /** - * Unfix nodes - * - * @private - */ - _restoreNodes : function() { - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.nodes[nodeId].xFixed = false; - this.nodes[nodeId].yFixed = false; - } - } - } - - -}; \ No newline at end of file diff --git a/lib/network/networkMixins/ManipulationMixin.js b/lib/network/networkMixins/ManipulationMixin.js deleted file mode 100644 index 83947515..00000000 --- a/lib/network/networkMixins/ManipulationMixin.js +++ /dev/null @@ -1,576 +0,0 @@ -/** - * Created by Alex on 2/4/14. - */ - -var manipulationMixin = { - - /** - * clears the toolbar div element of children - * - * @private - */ - _clearManipulatorBar : function() { - while (this.manipulationDiv.hasChildNodes()) { - this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); - } - }, - - /** - * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore - * these functions to their original functionality, we saved them in this.cachedFunctions. - * This function restores these functions to their original function. - * - * @private - */ - _restoreOverloadedFunctions : function() { - for (var functionName in this.cachedFunctions) { - if (this.cachedFunctions.hasOwnProperty(functionName)) { - this[functionName] = this.cachedFunctions[functionName]; - } - } - }, - - /** - * Enable or disable edit-mode. - * - * @private - */ - _toggleEditMode : function() { - this.editMode = !this.editMode; - var toolbar = document.getElementById("network-manipulationDiv"); - var closeDiv = document.getElementById("network-manipulation-closeDiv"); - var editModeDiv = document.getElementById("network-manipulation-editMode"); - if (this.editMode == true) { - toolbar.style.display="block"; - closeDiv.style.display="block"; - editModeDiv.style.display="none"; - closeDiv.onclick = this._toggleEditMode.bind(this); - } - else { - toolbar.style.display="none"; - closeDiv.style.display="none"; - editModeDiv.style.display="block"; - closeDiv.onclick = null; - } - this._createManipulatorBar() - }, - - /** - * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. - * - * @private - */ - _createManipulatorBar : function() { - // remove bound functions - 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(); - - // resume calculation - this.freezeSimulation = false; - - // reset global variables - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; - - if (this.editMode == true) { - while (this.manipulationDiv.hasChildNodes()) { - this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); - } - // add the icons to the manipulator div - this.manipulationDiv.innerHTML = "" + - "" + - ""+this.constants.labels['add'] +"" + - "
" + - "" + - ""+this.constants.labels['link'] +""; - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - this.manipulationDiv.innerHTML += "" + - "
" + - "" + - ""+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 += "" + - "
" + - "" + - ""+this.constants.labels['del'] +""; - } - - - // bind the icons - var addNodeButton = document.getElementById("network-manipulate-addNode"); - addNodeButton.onclick = this._createAddNodeToolbar.bind(this); - var addEdgeButton = document.getElementById("network-manipulate-connectNode"); - addEdgeButton.onclick = this._createAddEdgeToolbar.bind(this); - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - var editButton = document.getElementById("network-manipulate-editNode"); - editButton.onclick = this._editNode.bind(this); - } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - var editButton = document.getElementById("network-manipulate-editEdge"); - editButton.onclick = this._createEditEdgeToolbar.bind(this); - } - if (this._selectionIsEmpty() == false) { - var deleteButton = document.getElementById("network-manipulate-delete"); - deleteButton.onclick = this._deleteSelected.bind(this); - } - var closeDiv = document.getElementById("network-manipulation-closeDiv"); - closeDiv.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 editModeButton = document.getElementById("network-manipulate-editModeButton"); - editModeButton.onclick = this._toggleEditMode.bind(this); - } - }, - - - - /** - * Create the toolbar for adding Nodes - * - * @private - */ - _createAddNodeToolbar : function() { - // clear the toolbar - this._clearManipulatorBar(); - if (this.boundFunction) { - this.off('select', this.boundFunction); - } - - // create the toolbar contents - this.manipulationDiv.innerHTML = "" + - "" + - "" + this.constants.labels['back'] + " " + - "
" + - "" + - "" + this.constants.labels['addDescription'] + ""; - - // bind the icon - var backButton = document.getElementById("network-manipulate-back"); - backButton.onclick = this._createManipulatorBar.bind(this); - - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - this.boundFunction = this._addNode.bind(this); - this.on('select', this.boundFunction); - }, - - - /** - * create the toolbar to connect nodes - * - * @private - */ - _createAddEdgeToolbar : function() { - // clear the toolbar - this._clearManipulatorBar(); - this._unselectAll(true); - this.freezeSimulation = true; - - if (this.boundFunction) { - this.off('select', this.boundFunction); - } - - this._unselectAll(); - this.forceAppendSelection = false; - this.blockConnectingEdgeSelection = true; - - this.manipulationDiv.innerHTML = "" + - "" + - "" + this.constants.labels['back'] + " " + - "
" + - "" + - "" + this.constants.labels['linkDescription'] + ""; - - // bind the icon - var backButton = document.getElementById("network-manipulate-back"); - backButton.onclick = this._createManipulatorBar.bind(this); - - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - this.boundFunction = this._handleConnect.bind(this); - this.on('select', this.boundFunction); - - // temporarily overload functions - this.cachedFunctions["_handleTouch"] = this._handleTouch; - this.cachedFunctions["_handleOnRelease"] = this._handleOnRelease; - this._handleTouch = this._handleConnect; - this._handleOnRelease = this._finishConnect; - - // 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("network-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 - * to walk the user through the process. - * - * @private - */ - _handleConnect : function(pointer) { - if (this._getSelectedNodeCount() == 0) { - var node = this._getNodeAt(pointer); - if (node != null) { - if (node.clusterSize > 1) { - alert("Cannot create edges to a cluster.") - } - else { - this._selectObject(node,false); - // create a node the temporary line can look at - this.sectors['support']['nodes']['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); - this.sectors['support']['nodes']['targetNode'].x = node.x; - this.sectors['support']['nodes']['targetNode'].y = node.y; - this.sectors['support']['nodes']['targetViaNode'] = new Node({id:'targetViaNode'},{},{},this.constants); - this.sectors['support']['nodes']['targetViaNode'].x = node.x; - this.sectors['support']['nodes']['targetViaNode'].y = node.y; - this.sectors['support']['nodes']['targetViaNode'].parentEdgeId = "connectionEdge"; - - // create a temporary edge - this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:this.sectors['support']['nodes']['targetNode'].id}, this, this.constants); - this.edges['connectionEdge'].from = node; - this.edges['connectionEdge'].connected = true; - this.edges['connectionEdge'].smooth = true; - this.edges['connectionEdge'].selected = true; - 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(event) { - var pointer = this._getPointer(event.gesture.center); - this.sectors['support']['nodes']['targetNode'].x = this._XconvertDOMtoCanvas(pointer.x); - this.sectors['support']['nodes']['targetNode'].y = this._YconvertDOMtoCanvas(pointer.y); - this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._XconvertDOMtoCanvas(pointer.x) + this.edges['connectionEdge'].from.x); - this.sectors['support']['nodes']['targetViaNode'].y = this._YconvertDOMtoCanvas(pointer.y); - }; - - this.moving = true; - this.start(); - } - } - } - }, - - _finishConnect : function(pointer) { - if (this._getSelectedNodeCount() == 1) { - - // restore the drag function - this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; - delete this.cachedFunctions["_handleOnDrag"]; - - // remember the edge id - var connectFromId = this.edges['connectionEdge'].fromId; - - // remove the temporary nodes and edge - delete this.edges['connectionEdge']; - delete this.sectors['support']['nodes']['targetNode']; - delete this.sectors['support']['nodes']['targetViaNode']; - - var node = this._getNodeAt(pointer); - if (node != null) { - if (node.clusterSize > 1) { - alert("Cannot create edges to a cluster.") - } - else { - this._createEdge(connectFromId,node.id); - this._createManipulatorBar(); - } - } - this._unselectAll(); - } - }, - - - /** - * Adds a node on the specified location - */ - _addNode : function() { - if (this._selectionIsEmpty() && this.editMode == true) { - var positionObject = this._pointerToPositionObject(this.pointerPosition); - var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; - if (this.triggerFunctions.add) { - if (this.triggerFunctions.add.length == 2) { - var me = this; - this.triggerFunctions.add(defaultData, function(finalizedData) { - me.nodesData.add(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - alert(this.constants.labels['addError']); - this._createManipulatorBar(); - this.moving = true; - this.start(); - } - } - else { - this.nodesData.add(defaultData); - this._createManipulatorBar(); - this.moving = true; - this.start(); - } - } - }, - - - /** - * connect two nodes with a new edge. - * - * @private - */ - _createEdge : function(sourceNodeId,targetNodeId) { - if (this.editMode == true) { - var defaultData = {from:sourceNodeId, to:targetNodeId}; - if (this.triggerFunctions.connect) { - if (this.triggerFunctions.connect.length == 2) { - var me = this; - this.triggerFunctions.connect(defaultData, function(finalizedData) { - me.edgesData.add(finalizedData); - me.moving = true; - me.start(); - }); - } - else { - alert(this.constants.labels["linkError"]); - this.moving = true; - this.start(); - } - } - else { - this.edgesData.add(defaultData); - this.moving = true; - this.start(); - } - } - }, - - /** - * 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. - * - * @private - */ - _editNode : function() { - if (this.triggerFunctions.edit && this.editMode == true) { - var node = this._getSelectedNode(); - var data = {id:node.id, - label: node.label, - group: node.group, - shape: node.shape, - color: { - background:node.color.background, - border:node.color.border, - highlight: { - background:node.color.highlight.background, - border:node.color.highlight.border - } - }}; - if (this.triggerFunctions.edit.length == 2) { - var me = this; - this.triggerFunctions.edit(data, function (finalizedData) { - me.nodesData.update(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - alert(this.constants.labels["editError"]); - } - } - else { - alert(this.constants.labels["editBoundError"]); - } - }, - - - - - /** - * delete everything in the selection - * - * @private - */ - _deleteSelected : function() { - if (!this._selectionIsEmpty() && this.editMode == true) { - if (!this._clusterInSelection()) { - var selectedNodes = this.getSelectedNodes(); - var selectedEdges = this.getSelectedEdges(); - if (this.triggerFunctions.del) { - var me = this; - var data = {nodes: selectedNodes, edges: selectedEdges}; - if (this.triggerFunctions.del.length = 2) { - this.triggerFunctions.del(data, function (finalizedData) { - me.edgesData.remove(finalizedData.edges); - me.nodesData.remove(finalizedData.nodes); - me._unselectAll(); - me.moving = true; - me.start(); - }); - } - else { - alert(this.constants.labels["deleteError"]) - } - } - else { - this.edgesData.remove(selectedEdges); - this.nodesData.remove(selectedNodes); - this._unselectAll(); - this.moving = true; - this.start(); - } - } - else { - alert(this.constants.labels["deleteClusterError"]); - } - } - } -}; \ No newline at end of file diff --git a/lib/network/networkMixins/MixinLoader.js b/lib/network/networkMixins/MixinLoader.js deleted file mode 100644 index d3b31f4e..00000000 --- a/lib/network/networkMixins/MixinLoader.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Created by Alex on 2/10/14. - */ - - -var networkMixinLoaders = { - - /** - * Load a mixin into the network object - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private - */ - _loadMixin: function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - Network.prototype[mixinFunction] = sourceVariable[mixinFunction]; - } - } - }, - - - /** - * removes a mixin from the network object. - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private - */ - _clearMixin: function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - Network.prototype[mixinFunction] = undefined; - } - } - }, - - - /** - * Mixin the physics system and initialize the parameters required. - * - * @private - */ - _loadPhysicsSystem: function () { - this._loadMixin(physicsMixin); - this._loadSelectedForceSolver(); - if (this.constants.configurePhysics == true) { - this._loadPhysicsConfiguration(); - } - }, - - - /** - * Mixin the cluster system and initialize the parameters required. - * - * @private - */ - _loadClusterSystem: function () { - this.clusterSession = 0; - this.hubThreshold = 5; - this._loadMixin(ClusterMixin); - }, - - - /** - * Mixin the sector system and initialize the parameters required - * - * @private - */ - _loadSectorSystem: function () { - this.sectors = {}; - this.activeSector = ["default"]; - this.sectors["active"] = {}; - this.sectors["active"]["default"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; - this.sectors["frozen"] = {}; - this.sectors["support"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; - - this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields - - this._loadMixin(SectorMixin); - }, - - - /** - * Mixin the selection system and initialize the parameters required - * - * @private - */ - _loadSelectionSystem: function () { - this.selectionObj = {nodes: {}, edges: {}}; - - this._loadMixin(SelectionMixin); - }, - - - /** - * Mixin the navigationUI (User Interface) system and initialize the parameters required - * - * @private - */ - _loadManipulationSystem: function () { - // reset global variables -- these are used by the selection of nodes and edges. - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; - - if (this.constants.dataManipulation.enabled == true) { - // load the manipulator HTML elements. All styling done in css. - if (this.manipulationDiv === undefined) { - this.manipulationDiv = document.createElement('div'); - this.manipulationDiv.className = 'network-manipulationDiv'; - this.manipulationDiv.id = 'network-manipulationDiv'; - if (this.editMode == true) { - this.manipulationDiv.style.display = "block"; - } - else { - this.manipulationDiv.style.display = "none"; - } - this.containerElement.insertBefore(this.manipulationDiv, this.frame); - } - - if (this.editModeDiv === undefined) { - this.editModeDiv = document.createElement('div'); - this.editModeDiv.className = 'network-manipulation-editMode'; - this.editModeDiv.id = 'network-manipulation-editMode'; - if (this.editMode == true) { - this.editModeDiv.style.display = "none"; - } - else { - this.editModeDiv.style.display = "block"; - } - this.containerElement.insertBefore(this.editModeDiv, this.frame); - } - - if (this.closeDiv === undefined) { - this.closeDiv = document.createElement('div'); - this.closeDiv.className = 'network-manipulation-closeDiv'; - this.closeDiv.id = 'network-manipulation-closeDiv'; - this.closeDiv.style.display = this.manipulationDiv.style.display; - this.containerElement.insertBefore(this.closeDiv, this.frame); - } - - // load the manipulation functions - this._loadMixin(manipulationMixin); - - // create the manipulator toolbar - this._createManipulatorBar(); - } - else { - if (this.manipulationDiv !== undefined) { - // removes all the bindings and overloads - this._createManipulatorBar(); - // remove the manipulation divs - this.containerElement.removeChild(this.manipulationDiv); - this.containerElement.removeChild(this.editModeDiv); - this.containerElement.removeChild(this.closeDiv); - - this.manipulationDiv = undefined; - this.editModeDiv = undefined; - this.closeDiv = undefined; - // remove the mixin functions - this._clearMixin(manipulationMixin); - } - } - }, - - - /** - * Mixin the navigation (User Interface) system and initialize the parameters required - * - * @private - */ - _loadNavigationControls: function () { - this._loadMixin(NavigationMixin); - - // the clean function removes the button divs, this is done to remove the bindings. - this._cleanNavigation(); - if (this.constants.navigation.enabled == true) { - this._loadNavigationElements(); - } - }, - - - /** - * Mixin the hierarchical layout system. - * - * @private - */ - _loadHierarchySystem: function () { - this._loadMixin(HierarchicalLayoutMixin); - } - -}; diff --git a/lib/network/networkMixins/NavigationMixin.js b/lib/network/networkMixins/NavigationMixin.js deleted file mode 100644 index 375daf5e..00000000 --- a/lib/network/networkMixins/NavigationMixin.js +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Created by Alex on 1/22/14. - */ - -var NavigationMixin = { - - _cleanNavigation : function() { - // clean up previosu navigation items - var wrapper = document.getElementById('network-navigation_wrapper'); - if (wrapper != null) { - this.containerElement.removeChild(wrapper); - } - document.onmouseup = null; - }, - - /** - * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation - * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent - * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. - * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. - * - * @private - */ - _loadNavigationElements : function() { - this._cleanNavigation(); - - this.navigationDivs = {}; - var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; - var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','zoomExtent']; - - this.navigationDivs['wrapper'] = document.createElement('div'); - this.navigationDivs['wrapper'].id = "network-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; i < navigationDivs.length; i++) { - this.navigationDivs[navigationDivs[i]] = document.createElement('div'); - this.navigationDivs[navigationDivs[i]].id = "network-navigation_" + navigationDivs[i]; - this.navigationDivs[navigationDivs[i]].className = "network-navigation " + navigationDivs[i]; - this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); - this.navigationDivs[navigationDivs[i]].onmousedown = this[navigationDivActions[i]].bind(this); - } - - document.onmouseup = this._stopMovement.bind(this); - }, - - /** - * this stops all movement induced by the navigation buttons - * - * @private - */ - _stopMovement : function() { - this._xStopMoving(); - this._yStopMoving(); - this._stopZoom(); - }, - - - /** - * stops the actions performed by page up and down etc. - * - * @param event - * @private - */ - _preventDefault : function(event) { - if (event !== undefined) { - if (event.preventDefault) { - event.preventDefault(); - } else { - event.returnValue = false; - } - } - }, - - - /** - * move the screen up - * By using the increments, instead of adding a fixed number to the translation, we keep fluent and - * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently - * To avoid this behaviour, we do the translation in the start loop. - * - * @private - */ - _moveUp : function(event) { - this.yIncrement = this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['up'].className += " active"; - } - }, - - - /** - * move the screen down - * @private - */ - _moveDown : function(event) { - this.yIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['down'].className += " active"; - } - }, - - - /** - * move the screen left - * @private - */ - _moveLeft : function(event) { - this.xIncrement = this.constants.keyboard.speed.x; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['left'].className += " active"; - } - }, - - - /** - * move the screen right - * @private - */ - _moveRight : function(event) { - this.xIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['right'].className += " active"; - } - }, - - - /** - * Zoom in, using the same method as the movement. - * @private - */ - _zoomIn : function(event) { - this.zoomIncrement = this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['zoomIn'].className += " active"; - } - }, - - - /** - * Zoom out - * @private - */ - _zoomOut : function() { - this.zoomIncrement = -this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - this._preventDefault(event); - if (this.navigationDivs) { - this.navigationDivs['zoomOut'].className += " active"; - } - }, - - - /** - * Stop zooming and unhighlight the zoom controls - * @private - */ - _stopZoom : function() { - this.zoomIncrement = 0; - if (this.navigationDivs) { - this.navigationDivs['zoomIn'].className = this.navigationDivs['zoomIn'].className.replace(" active",""); - this.navigationDivs['zoomOut'].className = this.navigationDivs['zoomOut'].className.replace(" active",""); - } - }, - - - /** - * Stop moving in the Y direction and unHighlight the up and down - * @private - */ - _yStopMoving : function() { - this.yIncrement = 0; - if (this.navigationDivs) { - this.navigationDivs['up'].className = this.navigationDivs['up'].className.replace(" active",""); - this.navigationDivs['down'].className = this.navigationDivs['down'].className.replace(" active",""); - } - }, - - - /** - * Stop moving in the X direction and unHighlight left and right. - * @private - */ - _xStopMoving : function() { - this.xIncrement = 0; - if (this.navigationDivs) { - this.navigationDivs['left'].className = this.navigationDivs['left'].className.replace(" active",""); - this.navigationDivs['right'].className = this.navigationDivs['right'].className.replace(" active",""); - } - } - - -}; diff --git a/lib/network/networkMixins/SectorsMixin.js b/lib/network/networkMixins/SectorsMixin.js deleted file mode 100644 index 51642d68..00000000 --- a/lib/network/networkMixins/SectorsMixin.js +++ /dev/null @@ -1,552 +0,0 @@ -/** - * Creation of the SectorMixin var. - * - * This contains all the functions the Network object can use to employ the sector system. - * The sector system is always used by Network, though the benefits only apply to the use of clustering. - * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. - * - * Alex de Mulder - * 21-01-2013 - */ -var SectorMixin = { - - /** - * This function is only called by the setData function of the Network object. - * This loads the global references into the active sector. This initializes the sector. - * - * @private - */ - _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; - }, - - - /** - * /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied (active) sector. If a type is defined, do the specific type - * - * @param {String} sectorId - * @param {String} [sectorType] | "active" or "frozen" - * @private - */ - _switchToSector : function(sectorId, sectorType) { - if (sectorType === undefined || sectorType == "active") { - this._switchToActiveSector(sectorId); - } - else { - this._switchToFrozenSector(sectorId); - } - }, - - - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. - * - * @param sectorId - * @private - */ - _switchToActiveSector : function(sectorId) { - this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["active"][sectorId]["nodes"]; - this.edges = this.sectors["active"][sectorId]["edges"]; - }, - - - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. - * - * @param sectorId - * @private - */ - _switchToSupportSector : function() { - this.nodeIndices = this.sectors["support"]["nodeIndices"]; - this.nodes = this.sectors["support"]["nodes"]; - this.edges = this.sectors["support"]["edges"]; - }, - - - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied frozen sector. - * - * @param sectorId - * @private - */ - _switchToFrozenSector : function(sectorId) { - this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["frozen"][sectorId]["nodes"]; - this.edges = this.sectors["frozen"][sectorId]["edges"]; - }, - - - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the currently active sector. - * - * @private - */ - _loadLatestSector : function() { - this._switchToSector(this._sector()); - }, - - - /** - * This function returns the currently active sector Id - * - * @returns {String} - * @private - */ - _sector : function() { - return this.activeSector[this.activeSector.length-1]; - }, - - - /** - * This function returns the previously active sector Id - * - * @returns {String} - * @private - */ - _previousSector : function() { - if (this.activeSector.length > 1) { - return this.activeSector[this.activeSector.length-2]; - } - else { - throw new TypeError('there are not enough sectors in the this.activeSector array.'); - } - }, - - - /** - * We add the active sector at the end of the this.activeSector array - * This ensures it is the currently active sector returned by _sector() and it reaches the top - * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. - * - * @param newId - * @private - */ - _setActiveSector : function(newId) { - this.activeSector.push(newId); - }, - - - /** - * We remove the currently active sector id from the active sector stack. This happens when - * we reactivate the previously active sector - * - * @private - */ - _forgetLastSector : function() { - this.activeSector.pop(); - }, - - - /** - * This function creates a new active sector with the supplied newId. This newId - * is the expanding node id. - * - * @param {String} newId | Id of the new active sector - * @private - */ - _createNewSector : function(newId) { - // create the new sector - this.sectors["active"][newId] = {"nodes":{}, - "edges":{}, - "nodeIndices":[], - "formationScale": this.scale, - "drawingNode": undefined}; - - // create the new sector render node. This gives visual feedback that you are in a new sector. - this.sectors["active"][newId]['drawingNode'] = new Node( - {id:newId, - color: { - background: "#eaefef", - border: "495c5e" - } - },{},{},this.constants); - this.sectors["active"][newId]['drawingNode'].clusterSize = 2; - }, - - - /** - * This function removes the currently active sector. This is called when we create a new - * active sector. - * - * @param {String} sectorId | Id of the active sector that will be removed - * @private - */ - _deleteActiveSector : function(sectorId) { - delete this.sectors["active"][sectorId]; - }, - - - /** - * This function removes the currently active sector. This is called when we reactivate - * the previously active sector. - * - * @param {String} sectorId | Id of the active sector that will be removed - * @private - */ - _deleteFrozenSector : function(sectorId) { - delete this.sectors["frozen"][sectorId]; - }, - - - /** - * Freezing an active sector means moving it from the "active" object to the "frozen" object. - * We copy the references, then delete the active entree. - * - * @param sectorId - * @private - */ - _freezeSector : function(sectorId) { - // we move the set references from the active to the frozen stack. - this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; - - // we have moved the sector data into the frozen set, we now remove it from the active set - this._deleteActiveSector(sectorId); - }, - - - /** - * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" - * object to the "active" object. - * - * @param sectorId - * @private - */ - _activateSector : function(sectorId) { - // we move the set references from the frozen to the active stack. - this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; - - // we have moved the sector data into the active set, we now remove it from the frozen stack - this._deleteFrozenSector(sectorId); - }, - - - /** - * This function merges the data from the currently active sector with a frozen sector. This is used - * in the process of reverting back to the previously active sector. - * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it - * upon the creation of a new active sector. - * - * @param sectorId - * @private - */ - _mergeThisWithFrozen : function(sectorId) { - // copy all nodes - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; - } - } - - // copy all edges (if not fully clustered, else there are no edges) - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; - } - } - - // merge the nodeIndices - for (var i = 0; i < this.nodeIndices.length; i++) { - this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); - } - }, - - - /** - * This clusters the sector to one cluster. It was a single cluster before this process started so - * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. - * - * @private - */ - _collapseThisToSingleCluster : function() { - this.clusterToFit(1,false); - }, - - - /** - * We create a new active sector from the node that we want to open. - * - * @param node - * @private - */ - _addSector : function(node) { - // this is the currently active sector - var sector = this._sector(); - -// // this should allow me to select nodes from a frozen set. -// if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { -// console.log("the node is part of the active sector"); -// } -// else { -// console.log("I dont know what the fuck happened!!"); -// } - - // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. - delete this.nodes[node.id]; - - var unqiueIdentifier = util.randomUUID(); - - // we fully freeze the currently active sector - this._freezeSector(sector); - - // we create a new active sector. This sector has the Id of the node to ensure uniqueness - this._createNewSector(unqiueIdentifier); - - // we add the active sector to the sectors array to be able to revert these steps later on - this._setActiveSector(unqiueIdentifier); - - // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier - this._switchToSector(this._sector()); - - // finally we add the node we removed from our previous active sector to the new active sector - this.nodes[node.id] = node; - }, - - - /** - * We close the sector that is currently open and revert back to the one before. - * If the active sector is the "default" sector, nothing happens. - * - * @private - */ - _collapseSector : function() { - // the currently active sector - var sector = this._sector(); - - // we cannot collapse the default sector - if (sector != "default") { - if ((this.nodeIndices.length == 1) || - (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - var previousSector = this._previousSector(); - - // we collapse the sector back to a single cluster - this._collapseThisToSingleCluster(); - - // we move the remaining nodes, edges and nodeIndices to the previous sector. - // This previous sector is the one we will reactivate - this._mergeThisWithFrozen(previousSector); - - // the previously active (frozen) sector now has all the data from the currently active sector. - // we can now delete the active sector. - this._deleteActiveSector(sector); - - // we activate the previously active (and currently frozen) sector. - this._activateSector(previousSector); - - // we load the references from the newly active sector into the global references - this._switchToSector(previousSector); - - // we forget the previously active sector because we reverted to the one before - this._forgetLastSector(); - - // finally, we update the node index list. - this._updateNodeIndexList(); - - // we refresh the list with calulation nodes and calculation node indices. - this._updateCalculationNodes(); - } - } - }, - - - /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private - */ - _doInAllActiveSectors : function(runFunction,argument) { - if (argument === undefined) { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - this[runFunction](); - } - } - } - else { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); - } - } - } - } - // we revert the global references back to our active sector - this._loadLatestSector(); - }, - - - /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private - */ - _doInSupportSector : function(runFunction,argument) { - if (argument === undefined) { - this._switchToSupportSector(); - this[runFunction](); - } - else { - this._switchToSupportSector(); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); - } - } - // we revert the global references back to our active sector - this._loadLatestSector(); - }, - - - /** - * This runs a function in all frozen sectors. This is used in the _redraw(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private - */ - _doInAllFrozenSectors : function(runFunction,argument) { - if (argument === undefined) { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - this[runFunction](); - } - } - } - else { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); - } - } - } - } - this._loadLatestSector(); - }, - - - /** - * This runs a function in all sectors. This is used in the _redraw(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private - */ - _doInAllSectors : function(runFunction,argument) { - var args = Array.prototype.splice.call(arguments, 1); - if (argument === undefined) { - this._doInAllActiveSectors(runFunction); - this._doInAllFrozenSectors(runFunction); - } - else { - if (args.length > 1) { - this._doInAllActiveSectors(runFunction,args[0],args[1]); - this._doInAllFrozenSectors(runFunction,args[0],args[1]); - } - else { - this._doInAllActiveSectors(runFunction,argument); - this._doInAllFrozenSectors(runFunction,argument); - } - } - }, - - - /** - * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the - * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. - * - * @private - */ - _clearNodeIndexList : function() { - var sector = this._sector(); - this.sectors["active"][sector]["nodeIndices"] = []; - this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; - }, - - - /** - * Draw the encompassing sector node - * - * @param ctx - * @param sectorType - * @private - */ - _drawSectorNodes : function(ctx,sectorType) { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var sector in this.sectors[sectorType]) { - if (this.sectors[sectorType].hasOwnProperty(sector)) { - if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { - - this._switchToSector(sector,sectorType); - - minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.resize(ctx); - if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} - if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} - if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} - if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} - } - } - node = this.sectors[sectorType][sector]["drawingNode"]; - node.x = 0.5 * (maxX + minX); - node.y = 0.5 * (maxY + minY); - node.width = 2 * (node.x - minX); - node.height = 2 * (node.y - minY); - node.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); - node.setScale(this.scale); - node._drawCircle(ctx); - } - } - } - }, - - _drawAllSectorNodes : function(ctx) { - this._drawSectorNodes(ctx,"frozen"); - this._drawSectorNodes(ctx,"active"); - this._loadLatestSector(); - } -}; \ No newline at end of file diff --git a/lib/network/networkMixins/SelectionMixin.js b/lib/network/networkMixins/SelectionMixin.js deleted file mode 100644 index 8acb29c6..00000000 --- a/lib/network/networkMixins/SelectionMixin.js +++ /dev/null @@ -1,708 +0,0 @@ - -var SelectionMixin = { - - /** - * This function can be called from the _doInAllSectors function - * - * @param object - * @param overlappingNodes - * @private - */ - _getNodesOverlappingWith : function(object, overlappingNodes) { - var nodes = this.nodes; - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].isOverlappingWith(object)) { - overlappingNodes.push(nodeId); - } - } - } - }, - - /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - _getAllNodesOverlappingWith : function (object) { - var overlappingNodes = []; - this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); - return overlappingNodes; - }, - - - /** - * Return a position object in canvasspace from a single point in screenspace - * - * @param pointer - * @returns {{left: number, top: number, right: number, bottom: number}} - * @private - */ - _pointerToPositionObject : function(pointer) { - var x = this._XconvertDOMtoCanvas(pointer.x); - var y = this._YconvertDOMtoCanvas(pointer.y); - - return {left: x, - top: y, - right: x, - bottom: y}; - }, - - - /** - * Get the top node at the a specific point (like a click) - * - * @param {{x: Number, y: Number}} pointer - * @return {Node | null} node - * @private - */ - _getNodeAt : function (pointer) { - // we first check if this is an navigation controls element - var positionObject = this._pointerToPositionObject(pointer); - var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); - - // if there are overlapping nodes, select the last one, this is the - // one which is drawn on top of the others - if (overlappingNodes.length > 0) { - return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; - } - else { - return null; - } - }, - - - /** - * retrieve all edges overlapping with given object, selector is around center - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - _getEdgesOverlappingWith : function (object, overlappingEdges) { - var edges = this.edges; - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].isOverlappingWith(object)) { - overlappingEdges.push(edgeId); - } - } - } - }, - - - /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - _getAllEdgesOverlappingWith : function (object) { - var overlappingEdges = []; - this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); - return overlappingEdges; - }, - - /** - * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call - * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. - * - * @param pointer - * @returns {null} - * @private - */ - _getEdgeAt : function(pointer) { - var positionObject = this._pointerToPositionObject(pointer); - var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); - - if (overlappingEdges.length > 0) { - return this.edges[overlappingEdges[overlappingEdges.length - 1]]; - } - else { - return null; - } - }, - - - /** - * Add object to the selection array. - * - * @param obj - * @private - */ - _addToSelection : function(obj) { - if (obj instanceof Node) { - this.selectionObj.nodes[obj.id] = obj; - } - else { - this.selectionObj.edges[obj.id] = obj; - } - }, - - /** - * Add object to the selection array. - * - * @param obj - * @private - */ - _addToHover : function(obj) { - if (obj instanceof Node) { - this.hoverObj.nodes[obj.id] = obj; - } - else { - this.hoverObj.edges[obj.id] = obj; - } - }, - - - /** - * Remove a single option from selection. - * - * @param {Object} obj - * @private - */ - _removeFromSelection : function(obj) { - if (obj instanceof Node) { - delete this.selectionObj.nodes[obj.id]; - } - else { - delete this.selectionObj.edges[obj.id]; - } - }, - - /** - * Unselect all. The selectionObj is useful for this. - * - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - _unselectAll : function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - this.selectionObj.nodes[nodeId].unselect(); - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - this.selectionObj.edges[edgeId].unselect(); - } - } - - this.selectionObj = {nodes:{},edges:{}}; - - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } - }, - - /** - * Unselect all clusters. The selectionObj is useful for this. - * - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - _unselectClusters : function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - this.selectionObj.nodes[nodeId].unselect(); - this._removeFromSelection(this.selectionObj.nodes[nodeId]); - } - } - } - - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } - }, - - - /** - * return the number of selected nodes - * - * @returns {number} - * @private - */ - _getSelectedNodeCount : function() { - var count = 0; - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; - } - } - return count; - }, - - /** - * return the selected node - * - * @returns {number} - * @private - */ - _getSelectedNode : function() { - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return this.selectionObj.nodes[nodeId]; - } - } - return null; - }, - - /** - * return the selected edge - * - * @returns {number} - * @private - */ - _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 - * - * @returns {number} - * @private - */ - _getSelectedEdgeCount : function() { - var count = 0; - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; - } - } - return count; - }, - - - /** - * return the number of selected objects. - * - * @returns {number} - * @private - */ - _getSelectedObjectCount : function() { - var count = 0; - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; - } - } - return count; - }, - - /** - * Check if anything is selected - * - * @returns {boolean} - * @private - */ - _selectionIsEmpty : function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return false; - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - return false; - } - } - return true; - }, - - - /** - * check if one of the selected nodes is a cluster. - * - * @returns {boolean} - * @private - */ - _clusterInSelection : function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - return true; - } - } - } - return false; - }, - - /** - * select the edges connected to the node that is being selected - * - * @param {Node} node - * @private - */ - _selectConnectedEdges : function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.select(); - this._addToSelection(edge); - } - }, - - /** - * select the edges connected to the node that is being selected - * - * @param {Node} node - * @private - */ - _hoverConnectedEdges : function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.hover = true; - this._addToHover(edge); - } - }, - - - /** - * unselect the edges connected to the node that is being selected - * - * @param {Node} node - * @private - */ - _unselectConnectedEdges : function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.unselect(); - this._removeFromSelection(edge); - } - }, - - - - - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @param {Boolean} append - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - _selectObject : function(object, append, doNotTrigger, highlightEdges) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - if (highlightEdges === undefined) { - highlightEdges = true; - } - - if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { - this._unselectAll(true); - } - - if (object.selected == false) { - object.select(); - this._addToSelection(object); - if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { - this._selectConnectedEdges(object); - } - } - else { - object.unselect(); - this._removeFromSelection(object); - } - - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } - }, - - - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @private - */ - _blurObject : function(object) { - if (object.hover == true) { - object.hover = false; - this.emit("blurNode",{node:object.id}); - } - }, - - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @private - */ - _hoverObject : function(object) { - if (object.hover == false) { - object.hover = true; - this._addToHover(object); - if (object instanceof Node) { - this.emit("hoverNode",{node:object.id}); - } - } - if (object instanceof Node) { - this._hoverConnectedEdges(object); - } - }, - - - /** - * handles the selection part of the touch, only for navigation controls elements; - * Touch is triggered before tap, also before hold. Hold triggers after a while. - * This is the most responsive solution - * - * @param {Object} pointer - * @private - */ - _handleTouch : function(pointer) { - }, - - - /** - * handles the selection part of the tap; - * - * @param {Object} pointer - * @private - */ - _handleTap : function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node,false); - } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge,false); - } - else { - this._unselectAll(); - } - } - this.emit("click", this.getSelection()); - this._redraw(); - }, - - - /** - * handles the selection part of the double tap and opens a cluster if needed - * - * @param {Object} pointer - * @private - */ - _handleDoubleTap : function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null && node !== undefined) { - // we reset the areaCenter here so the opening of the node will occur - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; - this.openCluster(node); - } - this.emit("doubleClick", this.getSelection()); - }, - - - /** - * Handle the onHold selection part - * - * @param pointer - * @private - */ - _handleOnHold : function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node,true); - } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge,true); - } - } - this._redraw(); - }, - - - /** - * handle the onRelease event. These functions are here for the navigation controls module. - * - * @private - */ - _handleOnRelease : function(pointer) { - - }, - - - - /** - * - * retrieve the currently selected objects - * @return {Number[] | String[]} selection An array with the ids of the - * selected nodes. - */ - getSelection : function() { - var nodeIds = this.getSelectedNodes(); - var edgeIds = this.getSelectedEdges(); - return {nodes:nodeIds, edges:edgeIds}; - }, - - /** - * - * retrieve the currently selected nodes - * @return {String} selection An array with the ids of the - * selected nodes. - */ - getSelectedNodes : function() { - var idArray = []; - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - idArray.push(nodeId); - } - } - return idArray - }, - - /** - * - * retrieve the currently selected edges - * @return {Array} selection An array with the ids of the - * selected nodes. - */ - getSelectedEdges : function() { - var idArray = []; - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - idArray.push(edgeId); - } - } - return idArray; - }, - - - /** - * select zero or more nodes - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - */ - setSelection : function(selection) { - var i, iMax, id; - - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; - - // first unselect any selected node - this._unselectAll(true); - - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; - - var node = this.nodes[id]; - if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); - } - this._selectObject(node,true,true); - } - - console.log("setSelection is deprecated. Please use selectNodes instead.") - - this.redraw(); - }, - - - /** - * select zero or more nodes with the option to highlight edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - * @param {boolean} [highlightEdges] - */ - selectNodes : function(selection, highlightEdges) { - var i, iMax, id; - - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; - - // first unselect any selected node - this._unselectAll(true); - - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; - - var node = this.nodes[id]; - if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); - } - this._selectObject(node,true,true,highlightEdges); - } - this.redraw(); - }, - - - /** - * select zero or more edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - */ - selectEdges : function(selection) { - var i, iMax, id; - - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; - - // first unselect any selected node - this._unselectAll(true); - - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; - - var edge = this.edges[id]; - if (!edge) { - throw new RangeError('Edge with id "' + id + '" not found'); - } - this._selectObject(edge,true,true,highlightEdges); - } - this.redraw(); - }, - - /** - * Validate the selection: remove ids of nodes which no longer exist - * @private - */ - _updateSelection : function () { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (!this.nodes.hasOwnProperty(nodeId)) { - delete this.selectionObj.nodes[nodeId]; - } - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - if (!this.edges.hasOwnProperty(edgeId)) { - delete this.selectionObj.edges[edgeId]; - } - } - } - } -}; - - diff --git a/lib/network/networkMixins/physics/BarnesHut.js b/lib/network/networkMixins/physics/BarnesHut.js deleted file mode 100644 index d0c5f8da..00000000 --- a/lib/network/networkMixins/physics/BarnesHut.js +++ /dev/null @@ -1,398 +0,0 @@ -/** - * Created by Alex on 2/10/14. - */ - -var barnesHutMixin = { - - /** - * This function calculates the forces the nodes apply on eachother based on a gravitational model. - * The Barnes Hut method is used to speed up this N-body simulation. - * - * @private - */ - _calculateNodeForces : function() { - if (this.constants.physics.barnesHut.gravitationalConstant != 0) { - var node; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - var nodeCount = nodeIndices.length; - - this._formBarnesHutTree(nodes,nodeIndices); - - var barnesHutTree = this.barnesHutTree; - - // place the nodes one by one recursively - for (var i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - // starting with root is irrelevant, it never passes the BarnesHut condition - this._getForceContribution(barnesHutTree.root.children.NW,node); - this._getForceContribution(barnesHutTree.root.children.NE,node); - this._getForceContribution(barnesHutTree.root.children.SW,node); - this._getForceContribution(barnesHutTree.root.children.SE,node); - } - } - }, - - - /** - * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. - * If a region contains a single node, we check if it is not itself, then we apply the force. - * - * @param parentBranch - * @param node - * @private - */ - _getForceContribution : function(parentBranch,node) { - // we get no force contribution from an empty region - if (parentBranch.childrenCount > 0) { - var dx,dy,distance; - - // get the distance from the center of mass to the node. - dx = parentBranch.centerOfMass.x - node.x; - dy = parentBranch.centerOfMass.y - node.y; - distance = Math.sqrt(dx * dx + dy * dy); - - // BarnesHut condition - // original condition : s/d < theta = passed === d/s > 1/theta = passed - // calcSize = 1/s --> d * 1/s > 1/theta = passed - if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) { - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.1*Math.random(); - dx = distance; - } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; - } - else { - // Did not pass the condition, go into children if available - if (parentBranch.childrenCount == 4) { - this._getForceContribution(parentBranch.children.NW,node); - this._getForceContribution(parentBranch.children.NE,node); - this._getForceContribution(parentBranch.children.SW,node); - this._getForceContribution(parentBranch.children.SE,node); - } - else { // parentBranch must have only one node, if it was empty we wouldnt be here - if (parentBranch.children.data.id != node.id) { // if it is not self - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.5*Math.random(); - dx = distance; - } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; - } - } - } - } - }, - - /** - * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. - * - * @param nodes - * @param nodeIndices - * @private - */ - _formBarnesHutTree : function(nodes,nodeIndices) { - var node; - var nodeCount = nodeIndices.length; - - var minX = Number.MAX_VALUE, - minY = Number.MAX_VALUE, - maxX =-Number.MAX_VALUE, - maxY =-Number.MAX_VALUE; - - // get the range of the nodes - for (var i = 0; i < nodeCount; i++) { - var x = nodes[nodeIndices[i]].x; - var y = nodes[nodeIndices[i]].y; - if (x < minX) { minX = x; } - if (x > maxX) { maxX = x; } - if (y < minY) { minY = y; } - if (y > maxY) { maxY = y; } - } - // make the range a square - var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y - if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize - else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize - - - var minimumTreeSize = 1e-5; - var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); - var halfRootSize = 0.5 * rootSize; - var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); - - // construct the barnesHutTree - var barnesHutTree = {root:{ - centerOfMass:{x:0,y:0}, // Center of Mass - mass:0, - range: {minX:centerX-halfRootSize,maxX:centerX+halfRootSize, - minY:centerY-halfRootSize,maxY:centerY+halfRootSize}, - - size: rootSize, - calcSize: 1 / rootSize, - children: {data:null}, - maxWidth: 0, - level: 0, - childrenCount: 4 - }}; - this._splitBranch(barnesHutTree.root); - - // place the nodes one by one recursively - for (i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - this._placeInTree(barnesHutTree.root,node); - } - - // make global - this.barnesHutTree = barnesHutTree - }, - - - /** - * this updates the mass of a branch. this is increased by adding a node. - * - * @param parentBranch - * @param node - * @private - */ - _updateBranchMass : function(parentBranch, node) { - var totalMass = parentBranch.mass + node.mass; - var totalMassInv = 1/totalMass; - - parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.mass; - parentBranch.centerOfMass.x *= totalMassInv; - - parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.mass; - parentBranch.centerOfMass.y *= totalMassInv; - - parentBranch.mass = totalMass; - var biggestSize = Math.max(Math.max(node.height,node.radius),node.width); - parentBranch.maxWidth = (parentBranch.maxWidth < biggestSize) ? biggestSize : parentBranch.maxWidth; - - }, - - - /** - * determine in which branch the node will be placed. - * - * @param parentBranch - * @param node - * @param skipMassUpdate - * @private - */ - _placeInTree : function(parentBranch,node,skipMassUpdate) { - if (skipMassUpdate != true || skipMassUpdate === undefined) { - // update the mass of the branch. - this._updateBranchMass(parentBranch,node); - } - - if (parentBranch.children.NW.range.maxX > node.x) { // in NW or SW - if (parentBranch.children.NW.range.maxY > node.y) { // in NW - this._placeInRegion(parentBranch,node,"NW"); - } - else { // in SW - this._placeInRegion(parentBranch,node,"SW"); - } - } - else { // in NE or SE - if (parentBranch.children.NW.range.maxY > node.y) { // in NE - this._placeInRegion(parentBranch,node,"NE"); - } - else { // in SE - this._placeInRegion(parentBranch,node,"SE"); - } - } - }, - - - /** - * actually place the node in a region (or branch) - * - * @param parentBranch - * @param node - * @param region - * @private - */ - _placeInRegion : function(parentBranch,node,region) { - switch (parentBranch.children[region].childrenCount) { - case 0: // place node here - parentBranch.children[region].children.data = node; - parentBranch.children[region].childrenCount = 1; - this._updateBranchMass(parentBranch.children[region],node); - break; - case 1: // convert into children - // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) - // we move one node a pixel and we do not put it in the tree. - if (parentBranch.children[region].children.data.x == node.x && - parentBranch.children[region].children.data.y == node.y) { - node.x += Math.random(); - node.y += Math.random(); - } - else { - this._splitBranch(parentBranch.children[region]); - this._placeInTree(parentBranch.children[region],node); - } - break; - case 4: // place in branch - this._placeInTree(parentBranch.children[region],node); - break; - } - }, - - - /** - * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch - * after the split is complete. - * - * @param parentBranch - * @private - */ - _splitBranch : function(parentBranch) { - // if the branch is shaded with a node, replace the node in the new subset. - var containedNode = null; - if (parentBranch.childrenCount == 1) { - containedNode = parentBranch.children.data; - parentBranch.mass = 0; parentBranch.centerOfMass.x = 0; parentBranch.centerOfMass.y = 0; - } - parentBranch.childrenCount = 4; - parentBranch.children.data = null; - this._insertRegion(parentBranch,"NW"); - this._insertRegion(parentBranch,"NE"); - this._insertRegion(parentBranch,"SW"); - this._insertRegion(parentBranch,"SE"); - - if (containedNode != null) { - this._placeInTree(parentBranch,containedNode); - } - }, - - - /** - * This function subdivides the region into four new segments. - * Specifically, this inserts a single new segment. - * It fills the children section of the parentBranch - * - * @param parentBranch - * @param region - * @param parentRange - * @private - */ - _insertRegion : function(parentBranch, region) { - var minX,maxX,minY,maxY; - var childSize = 0.5 * parentBranch.size; - switch (region) { - case "NW": - minX = parentBranch.range.minX; - maxX = parentBranch.range.minX + childSize; - minY = parentBranch.range.minY; - maxY = parentBranch.range.minY + childSize; - break; - case "NE": - minX = parentBranch.range.minX + childSize; - maxX = parentBranch.range.maxX; - minY = parentBranch.range.minY; - maxY = parentBranch.range.minY + childSize; - break; - case "SW": - minX = parentBranch.range.minX; - maxX = parentBranch.range.minX + childSize; - minY = parentBranch.range.minY + childSize; - maxY = parentBranch.range.maxY; - break; - case "SE": - minX = parentBranch.range.minX + childSize; - maxX = parentBranch.range.maxX; - minY = parentBranch.range.minY + childSize; - maxY = parentBranch.range.maxY; - break; - } - - - parentBranch.children[region] = { - centerOfMass:{x:0,y:0}, - mass:0, - range:{minX:minX,maxX:maxX,minY:minY,maxY:maxY}, - size: 0.5 * parentBranch.size, - calcSize: 2 * parentBranch.calcSize, - children: {data:null}, - maxWidth: 0, - level: parentBranch.level+1, - childrenCount: 0 - }; - }, - - - /** - * This function is for debugging purposed, it draws the tree. - * - * @param ctx - * @param color - * @private - */ - _drawTree : function(ctx,color) { - if (this.barnesHutTree !== undefined) { - - ctx.lineWidth = 1; - - this._drawBranch(this.barnesHutTree.root,ctx,color); - } - }, - - - /** - * This function is for debugging purposes. It draws the branches recursively. - * - * @param branch - * @param ctx - * @param color - * @private - */ - _drawBranch : function(branch,ctx,color) { - if (color === undefined) { - color = "#FF0000"; - } - - if (branch.childrenCount == 4) { - this._drawBranch(branch.children.NW,ctx); - this._drawBranch(branch.children.NE,ctx); - this._drawBranch(branch.children.SE,ctx); - this._drawBranch(branch.children.SW,ctx); - } - ctx.strokeStyle = color; - ctx.beginPath(); - ctx.moveTo(branch.range.minX,branch.range.minY); - ctx.lineTo(branch.range.maxX,branch.range.minY); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(branch.range.maxX,branch.range.minY); - ctx.lineTo(branch.range.maxX,branch.range.maxY); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(branch.range.maxX,branch.range.maxY); - ctx.lineTo(branch.range.minX,branch.range.maxY); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(branch.range.minX,branch.range.maxY); - ctx.lineTo(branch.range.minX,branch.range.minY); - ctx.stroke(); - - /* - if (branch.mass > 0) { - ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); - ctx.stroke(); - } - */ - } - -}; \ No newline at end of file diff --git a/lib/network/networkMixins/physics/HierarchialRepulsion.js b/lib/network/networkMixins/physics/HierarchialRepulsion.js deleted file mode 100644 index 6cc39b55..00000000 --- a/lib/network/networkMixins/physics/HierarchialRepulsion.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Created by Alex on 2/10/14. - */ - -var hierarchalRepulsionMixin = { - - - /** - * Calculate the forces the nodes apply on eachother based on a repulsion field. - * This field is linearly approximated. - * - * @private - */ - _calculateNodeForces: function () { - var dx, dy, distance, fx, fy, combinedClusterSize, - repulsingForce, node1, node2, i, j; - - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - - // approximation constants - var b = 5; - var a_base = 0.5 * -b; - - - // repulsing forces between nodes - var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; - var minimumDistance = nodeDistance; - var a = a_base / minimumDistance; - - // we loop from i over all but the last entree in the array - // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j - for (i = 0; i < nodeIndices.length - 1; i++) { - - node1 = nodes[nodeIndices[i]]; - for (j = i + 1; j < nodeIndices.length; j++) { - node2 = nodes[nodeIndices[j]]; - if (node1.level == node2.level) { - - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); - - - if (distance < 2 * minimumDistance) { - repulsingForce = a * distance + b; - var c = 0.05; - var d = 2 * minimumDistance * 2 * c; - repulsingForce = c * Math.pow(distance,2) - d * distance + d*d/(4*c); - - // normalize force with - if (distance == 0) { - distance = 0.01; - } - else { - repulsingForce = repulsingForce / distance; - } - fx = dx * repulsingForce; - fy = dy * repulsingForce; - - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; - } - } - } - } - }, - - - /** - * this function calculates the effects of the springs in the case of unsmooth curves. - * - * @private - */ - _calculateHierarchicalSpringForces: function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; - - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; - - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); - - if (distance == 0) { - distance = 0.01; - } - - distance = Math.max(0.8*edgeLength,Math.min(5*edgeLength, distance)); - - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; - - fx = dx * springForce; - fy = dy * springForce; - - edge.to.fx -= fx; - edge.to.fy -= fy; - edge.from.fx += fx; - edge.from.fy += fy; - - - var factor = 5; - if (distance > edgeLength) { - factor = 25; - } - - if (edge.from.level > edge.to.level) { - edge.to.fx -= factor*fx; - edge.to.fy -= factor*fy; - } - else if (edge.from.level < edge.to.level) { - edge.from.fx += factor*fx; - edge.from.fy += factor*fy; - } - } - } - } - } - } -}; \ No newline at end of file diff --git a/lib/network/networkMixins/physics/PhysicsMixin.js b/lib/network/networkMixins/physics/PhysicsMixin.js deleted file mode 100644 index b492b462..00000000 --- a/lib/network/networkMixins/physics/PhysicsMixin.js +++ /dev/null @@ -1,706 +0,0 @@ -/** - * Created by Alex on 2/6/14. - */ - - -var physicsMixin = { - - /** - * Toggling barnes Hut calculation on and off. - * - * @private - */ - _toggleBarnesHut: function () { - this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; - this._loadSelectedForceSolver(); - this.moving = true; - this.start(); - }, - - - /** - * This loads the node force solver based on the barnes hut or repulsion algorithm - * - * @private - */ - _loadSelectedForceSolver: function () { - // this overloads the this._calculateNodeForces - if (this.constants.physics.barnesHut.enabled == true) { - this._clearMixin(repulsionMixin); - this._clearMixin(hierarchalRepulsionMixin); - - this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; - this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; - this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; - this.constants.physics.damping = this.constants.physics.barnesHut.damping; - - this._loadMixin(barnesHutMixin); - } - else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._clearMixin(barnesHutMixin); - this._clearMixin(repulsionMixin); - - this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; - this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; - - this._loadMixin(hierarchalRepulsionMixin); - } - else { - this._clearMixin(barnesHutMixin); - this._clearMixin(hierarchalRepulsionMixin); - this.barnesHutTree = undefined; - - this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.repulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; - this.constants.physics.damping = this.constants.physics.repulsion.damping; - - this._loadMixin(repulsionMixin); - } - }, - - /** - * Before calculating the forces, we check if we need to cluster to keep up performance and we check - * if there is more than one node. If it is just one node, we dont calculate anything. - * - * @private - */ - _initializeForceCalculation: function () { - // stop calculation if there is only one node - if (this.nodeIndices.length == 1) { - this.nodes[this.nodeIndices[0]]._setForce(0, 0); - } - else { - // if there are too many nodes on screen, we cluster without repositioning - if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { - this.clusterToFit(this.constants.clustering.reduceToNodes, false); - } - - // we now start the force calculation - this._calculateForces(); - } - }, - - - /** - * Calculate the external forces acting on the nodes - * Forces are caused by: edges, repulsing forces between nodes, gravity - * @private - */ - _calculateForces: function () { - // Gravity is required to keep separated groups from floating off - // the forces are reset to zero in this loop by using _setForce instead - // of _addForce - - this._calculateGravitationalForces(); - this._calculateNodeForces(); - - if (this.constants.smoothCurves == true) { - this._calculateSpringForcesWithSupport(); - } - else { - if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._calculateHierarchicalSpringForces(); - } - else { - this._calculateSpringForces(); - } - } - }, - - - /** - * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also - * handled in the calculateForces function. We then use a quadratic curve with the center node as control. - * This function joins the datanodes and invisible (called support) nodes into one object. - * We do this so we do not contaminate this.nodes with the support nodes. - * - * @private - */ - _updateCalculationNodes: function () { - if (this.constants.smoothCurves == true) { - this.calculationNodes = {}; - this.calculationNodeIndices = []; - - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId] = this.nodes[nodeId]; - } - } - var supportNodes = this.sectors['support']['nodes']; - for (var supportNodeId in supportNodes) { - if (supportNodes.hasOwnProperty(supportNodeId)) { - if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { - this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; - } - else { - supportNodes[supportNodeId]._setForce(0, 0); - } - } - } - - for (var idx in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(idx)) { - this.calculationNodeIndices.push(idx); - } - } - } - else { - this.calculationNodes = this.nodes; - this.calculationNodeIndices = this.nodeIndices; - } - }, - - - /** - * this function applies the central gravity effect to keep groups from floating off - * - * @private - */ - _calculateGravitationalForces: function () { - var dx, dy, distance, node, i; - var nodes = this.calculationNodes; - var gravity = this.constants.physics.centralGravity; - var gravityForce = 0; - - for (i = 0; i < this.calculationNodeIndices.length; i++) { - node = nodes[this.calculationNodeIndices[i]]; - node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. - // gravity does not apply when we are in a pocket sector - if (this._sector() == "default" && gravity != 0) { - dx = -node.x; - dy = -node.y; - distance = Math.sqrt(dx * dx + dy * dy); - - gravityForce = (distance == 0) ? 0 : (gravity / distance); - node.fx = dx * gravityForce; - node.fy = dy * gravityForce; - } - else { - node.fx = 0; - node.fy = 0; - } - } - }, - - - - - /** - * this function calculates the effects of the springs in the case of unsmooth curves. - * - * @private - */ - _calculateSpringForces: function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; - - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; - - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); - - if (distance == 0) { - distance = 0.01; - } - - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; - - fx = dx * springForce; - fy = dy * springForce; - - edge.from.fx += fx; - edge.from.fy += fy; - edge.to.fx -= fx; - edge.to.fy -= fy; - } - } - } - } - }, - - - - - /** - * This function calculates the springforces on the nodes, accounting for the support nodes. - * - * @private - */ - _calculateSpringForcesWithSupport: function () { - var edgeLength, edge, edgeId, combinedClusterSize; - var edges = this.edges; - - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - if (edge.via != null) { - var node1 = edge.to; - var node2 = edge.via; - var node3 = edge.from; - - edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; - - combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; - - // this implies that the edges between big clusters are longer - edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; - this._calculateSpringForce(node1, node2, 0.5 * edgeLength); - this._calculateSpringForce(node2, node3, 0.5 * edgeLength); - } - } - } - } - } - }, - - - /** - * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. - * - * @param node1 - * @param node2 - * @param edgeLength - * @private - */ - _calculateSpringForce: function (node1, node2, edgeLength) { - var dx, dy, fx, fy, springForce, distance; - - dx = (node1.x - node2.x); - dy = (node1.y - node2.y); - distance = Math.sqrt(dx * dx + dy * dy); - - if (distance == 0) { - distance = 0.01; - } - - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; - - fx = dx * springForce; - fy = dy * springForce; - - node1.fx += fx; - node1.fy += fy; - node2.fx -= fx; - node2.fy -= fy; - }, - - - /** - * Load the HTML for the physics config and bind it - * @private - */ - _loadPhysicsConfiguration: function () { - if (this.physicsConfiguration === undefined) { - this.backupConstants = {}; - util.deepExtend(this.backupConstants,this.constants); - - var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; - this.physicsConfiguration = document.createElement('div'); - this.physicsConfiguration.className = "PhysicsConfiguration"; - this.physicsConfiguration.innerHTML = '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Options:
' - this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); - this.optionsDiv = document.createElement("div"); - this.optionsDiv.style.fontSize = "14px"; - this.optionsDiv.style.fontFamily = "verdana"; - this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); - - var rangeElement; - rangeElement = document.getElementById('graph_BH_gc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); - rangeElement = document.getElementById('graph_BH_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_BH_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_BH_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_BH_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); - - rangeElement = document.getElementById('graph_R_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); - rangeElement = document.getElementById('graph_R_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_R_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_R_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_R_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); - - rangeElement = document.getElementById('graph_H_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); - rangeElement = document.getElementById('graph_H_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_H_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_H_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_H_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); - rangeElement = document.getElementById('graph_H_direction'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); - rangeElement = document.getElementById('graph_H_levsep'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); - rangeElement = document.getElementById('graph_H_nspac'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); - - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - var radioButton3 = document.getElementById("graph_physicsMethod3"); - radioButton2.checked = true; - if (this.constants.physics.barnesHut.enabled) { - radioButton1.checked = true; - } - if (this.constants.hierarchicalLayout.enabled) { - radioButton3.checked = true; - } - - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - var graph_repositionNodes = document.getElementById("graph_repositionNodes"); - var graph_generateOptions = document.getElementById("graph_generateOptions"); - - graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); - graph_repositionNodes.onclick = graphRepositionNodes.bind(this); - graph_generateOptions.onclick = graphGenerateOptions.bind(this); - if (this.constants.smoothCurves == true) { - graph_toggleSmooth.style.background = "#A4FF56"; - } - else { - graph_toggleSmooth.style.background = "#FF8532"; - } - - - switchConfigurations.apply(this); - - radioButton1.onchange = switchConfigurations.bind(this); - radioButton2.onchange = switchConfigurations.bind(this); - radioButton3.onchange = switchConfigurations.bind(this); - } - }, - - /** - * This overwrites the this.constants. - * - * @param constantsVariableName - * @param value - * @private - */ - _overWriteGraphConstants: function (constantsVariableName, value) { - var nameArray = constantsVariableName.split("_"); - if (nameArray.length == 1) { - this.constants[nameArray[0]] = value; - } - else if (nameArray.length == 2) { - this.constants[nameArray[0]][nameArray[1]] = value; - } - else if (nameArray.length == 3) { - this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; - } - } -}; - -/** - * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. - */ -function graphToggleSmoothCurves () { - this.constants.smoothCurves = !this.constants.smoothCurves; - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} - - this._configureSmoothCurves(false); -}; - -/** - * this function is used to scramble the nodes - * - */ -function graphRepositionNodes () { - for (var nodeId in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; - this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; - } - } - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); - } - else { - this.repositionNodes(); - } - this.moving = true; - this.start(); -}; - -/** - * this is used to generate an options file from the playing with physics system. - */ -function graphGenerateOptions () { - var options = "No options are required, default values used."; - var optionsSpecific = []; - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - if (radioButton1.checked == true) { - if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options = "var options = {"; - options += "physics: {barnesHut: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}}' - } - if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { - if (optionsSpecific.length == 0) {options = "var options = {";} - else {options += ", "} - options += "smoothCurves: " + this.constants.smoothCurves; - } - if (options != "No options are required, default values used.") { - options += '};' - } - } - else if (radioButton2.checked == true) { - options = "var options = {"; - options += "physics: {barnesHut: {enabled: false}"; - if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += ", repulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}}' - } - if (optionsSpecific.length == 0) {options += "}"} - if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { - options += ", smoothCurves: " + this.constants.smoothCurves; - } - options += '};' - } - else { - options = "var options = {"; - if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += "physics: {hierarchicalRepulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", "; - } - } - options += '}},'; - } - options += 'hierarchicalLayout: {'; - optionsSpecific = []; - if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} - if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} - if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} - if (optionsSpecific.length != 0) { - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}' - } - else { - options += "enabled:true}"; - } - options += '};' - } - - - this.optionsDiv.innerHTML = options; - -}; - -/** - * this is used to switch between barnesHut, repulsion and hierarchical. - * - */ -function switchConfigurations () { - var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; - var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; - var tableId = "graph_" + radioButton + "_table"; - var table = document.getElementById(tableId); - table.style.display = "block"; - for (var i = 0; i < ids.length; i++) { - if (ids[i] != tableId) { - table = document.getElementById(ids[i]); - table.style.display = "none"; - } - } - this._restoreNodes(); - if (radioButton == "R") { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = false; - } - else if (radioButton == "H") { - if (this.constants.hierarchicalLayout.enabled == false) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - this._setupHierarchicalLayout(); - } - } - else { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = true; - } - this._loadSelectedForceSolver(); - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} - this.moving = true; - this.start(); - -} - - -/** - * this generates the ranges depending on the iniital values. - * - * @param id - * @param map - * @param constantsVariableName - */ -function showValueOfRange (id,map,constantsVariableName) { - var valueId = id + "_value"; - var rangeValue = document.getElementById(id).value; - - if (map instanceof Array) { - document.getElementById(valueId).value = map[parseInt(rangeValue)]; - this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); - } - else { - document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); - this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); - } - - if (constantsVariableName == "hierarchicalLayout_direction" || - constantsVariableName == "hierarchicalLayout_levelSeparation" || - constantsVariableName == "hierarchicalLayout_nodeSpacing") { - this._setupHierarchicalLayout(); - } - this.moving = true; - this.start(); -}; - - diff --git a/lib/network/networkMixins/physics/Repulsion.js b/lib/network/networkMixins/physics/Repulsion.js deleted file mode 100644 index 71a9994b..00000000 --- a/lib/network/networkMixins/physics/Repulsion.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Created by Alex on 2/10/14. - */ - -var repulsionMixin = { - - - /** - * Calculate the forces the nodes apply on eachother based on a repulsion field. - * This field is linearly approximated. - * - * @private - */ - _calculateNodeForces: function () { - var dx, dy, angle, distance, fx, fy, combinedClusterSize, - repulsingForce, node1, node2, i, j; - - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - - // approximation constants - var a_base = -2 / 3; - var b = 4 / 3; - - // repulsing forces between nodes - var nodeDistance = this.constants.physics.repulsion.nodeDistance; - var minimumDistance = nodeDistance; - - // we loop from i over all but the last entree in the array - // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j - for (i = 0; i < nodeIndices.length - 1; i++) { - node1 = nodes[nodeIndices[i]]; - for (j = i + 1; j < nodeIndices.length; j++) { - node2 = nodes[nodeIndices[j]]; - combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; - - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); - - minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); - var a = a_base / minimumDistance; - if (distance < 2 * minimumDistance) { - if (distance < 0.5 * minimumDistance) { - repulsingForce = 1.0; - } - else { - repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) - } - - // amplify the repulsion for clusters. - repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; - repulsingForce = repulsingForce / distance; - - fx = dx * repulsingForce; - fy = dy * repulsingForce; - - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; - } - } - } - } -}; \ No newline at end of file diff --git a/lib/shim.js b/lib/shim.js deleted file mode 100644 index b7b71691..00000000 --- a/lib/shim.js +++ /dev/null @@ -1,252 +0,0 @@ - -// Internet Explorer 8 and older does not support Array.indexOf, so we define -// it here in that case. -// http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/ -if(!Array.prototype.indexOf) { - Array.prototype.indexOf = function(obj){ - for(var i = 0; i < this.length; i++){ - if(this[i] == obj){ - return i; - } - } - return -1; - }; - - try { - console.log("Warning: Ancient browser detected. Please update your browser"); - } - catch (err) { - } -} - -// Internet Explorer 8 and older does not support Array.forEach, so we define -// it here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach -if (!Array.prototype.forEach) { - Array.prototype.forEach = function(fn, scope) { - for(var i = 0, len = this.length; i < len; ++i) { - fn.call(scope || this, this[i], i, this); - } - } -} - -// Internet Explorer 8 and older does not support Array.map, so we define it -// here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map -// Production steps of ECMA-262, Edition 5, 15.4.4.19 -// Reference: http://es5.github.com/#x15.4.4.19 -if (!Array.prototype.map) { - Array.prototype.map = function(callback, thisArg) { - - var T, A, k; - - if (this == null) { - throw new TypeError(" this is null or not defined"); - } - - // 1. Let O be the result of calling ToObject passing the |this| value as the argument. - var O = Object(this); - - // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". - // 3. Let len be ToUint32(lenValue). - var len = O.length >>> 0; - - // 4. If IsCallable(callback) is false, throw a TypeError exception. - // See: http://es5.github.com/#x9.11 - if (typeof callback !== "function") { - throw new TypeError(callback + " is not a function"); - } - - // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. - if (thisArg) { - T = thisArg; - } - - // 6. Let A be a new array created as if by the expression new Array(len) where Array is - // the standard built-in constructor with that name and len is the value of len. - A = new Array(len); - - // 7. Let k be 0 - k = 0; - - // 8. Repeat, while k < len - while(k < len) { - - var kValue, mappedValue; - - // a. Let Pk be ToString(k). - // This is implicit for LHS operands of the in operator - // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. - // This step can be combined with c - // c. If kPresent is true, then - if (k in O) { - - // i. Let kValue be the result of calling the Get internal method of O with argument Pk. - kValue = O[ k ]; - - // ii. Let mappedValue be the result of calling the Call internal method of callback - // with T as the this value and argument list containing kValue, k, and O. - mappedValue = callback.call(T, kValue, k, O); - - // iii. Call the DefineOwnProperty internal method of A with arguments - // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true}, - // and false. - - // In browsers that support Object.defineProperty, use the following: - // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true }); - - // For best browser support, use the following: - A[ k ] = mappedValue; - } - // d. Increase k by 1. - k++; - } - - // 9. return A - return A; - }; -} - -// Internet Explorer 8 and older does not support Array.filter, so we define it -// here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter -if (!Array.prototype.filter) { - Array.prototype.filter = function(fun /*, thisp */) { - "use strict"; - - if (this == null) { - throw new TypeError(); - } - - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun != "function") { - throw new TypeError(); - } - - var res = []; - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in t) { - var val = t[i]; // in case fun mutates this - if (fun.call(thisp, val, i, t)) - res.push(val); - } - } - - return res; - }; -} - - -// Internet Explorer 8 and older does not support Object.keys, so we define it -// here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys -if (!Object.keys) { - Object.keys = (function () { - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ], - dontEnumsLength = dontEnums.length; - - return function (obj) { - if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) { - throw new TypeError('Object.keys called on non-object'); - } - - var result = []; - - for (var prop in obj) { - if (hasOwnProperty.call(obj, prop)) result.push(prop); - } - - if (hasDontEnumBug) { - for (var i=0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]); - } - } - return result; - } - })() -} - -// Internet Explorer 8 and older does not support Array.isArray, -// so we define it here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray -if(!Array.isArray) { - Array.isArray = function (vArg) { - return Object.prototype.toString.call(vArg) === "[object Array]"; - }; -} - -// Internet Explorer 8 and older does not support Function.bind, -// so we define it here in that case. -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; -} - -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create -if (!Object.create) { - Object.create = function (o) { - if (arguments.length > 1) { - throw new Error('Object.create implementation only accepts the first parameter.'); - } - function F() {} - F.prototype = o; - return new F(); - }; -} - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; -} diff --git a/lib/timeline/DataStep.js b/lib/timeline/DataStep.js index e57062cd..b51f2e32 100644 --- a/lib/timeline/DataStep.js +++ b/lib/timeline/DataStep.js @@ -172,7 +172,7 @@ DataStep.prototype.previous = function() { /** * Get the current datetime - * @return {Number} current The current date + * @return {String} current The current date */ DataStep.prototype.getCurrent = function() { var toPrecision = '' + Number(this.current).toPrecision(5); @@ -189,7 +189,6 @@ DataStep.prototype.getCurrent = function() { } } - return toPrecision; }; @@ -213,3 +212,5 @@ DataStep.prototype.snap = function(date) { DataStep.prototype.isMajor = function() { return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); }; + +module.exports = DataStep; diff --git a/lib/timeline/Graph2d.js b/lib/timeline/Graph2d.js index 39ffbb07..cc6de2cf 100644 --- a/lib/timeline/Graph2d.js +++ b/lib/timeline/Graph2d.js @@ -1,3 +1,14 @@ +var Emitter = require('emitter-component'); +var Hammer = require('hammerjs'); +var util = require('../util'); +var DataSet = require('../DataSet'); +var DataView = require('../DataView'); +var Range = require('./Range'); +var TimeAxis = require('./component/TimeAxis'); +var CurrentTime = require('./component/CurrentTime'); +var CustomTime = require('./component/CustomTime'); +var LineGraph = require('./component/LineGraph'); + /** * Create a timeline visualization * @param {HTMLElement} container @@ -868,3 +879,5 @@ Graph2d.prototype._updateScrollTop = function () { Graph2d.prototype._getScrollTop = function () { return this.props.scrollTop; }; + +module.exports = Graph2d; diff --git a/lib/timeline/Range.js b/lib/timeline/Range.js index 8f878541..f2aa9477 100644 --- a/lib/timeline/Range.js +++ b/lib/timeline/Range.js @@ -1,3 +1,7 @@ +var util = require('../util'); +var moment = require('../module/moment'); +var Component = require('./component/Component'); + /** * @constructor Range * A Range controls a numeric range with a start and end value. @@ -525,3 +529,5 @@ Range.prototype.moveTo = function(moveTo) { this.setRange(newStart, newEnd); }; + +module.exports = Range; diff --git a/lib/timeline/Stack.js b/lib/timeline/Stack.js index b3aa5d1a..0918369d 100644 --- a/lib/timeline/Stack.js +++ b/lib/timeline/Stack.js @@ -1,13 +1,10 @@ -/** - * Utility functions for ordering and stacking of items - */ -var stack = {}; +// Utility functions for ordering and stacking of items /** * Order items by their start data * @param {Item[]} items */ -stack.orderByStart = function(items) { +exports.orderByStart = function(items) { items.sort(function (a, b) { return a.data.start - b.data.start; }); @@ -18,7 +15,7 @@ stack.orderByStart = function(items) { * is used. * @param {Item[]} items */ -stack.orderByEnd = function(items) { +exports.orderByEnd = function(items) { items.sort(function (a, b) { var aTime = ('end' in a.data) ? a.data.end : a.data.start, bTime = ('end' in b.data) ? b.data.end : b.data.start; @@ -38,7 +35,7 @@ stack.orderByEnd = function(items) { * If true, all items will be repositioned. If false (default), only * items having a top===null will be re-stacked */ -stack.stack = function(items, margin, force) { +exports.stack = function(items, margin, force) { var i, iMax; if (force) { @@ -61,7 +58,7 @@ stack.stack = function(items, margin, force) { var collidingItem = null; for (var j = 0, jj = items.length; j < jj; j++) { var other = items[j]; - if (other.top !== null && other !== item && stack.collision(item, other, margin.item)) { + if (other.top !== null && other !== item && exports.collision(item, other, margin.item)) { collidingItem = other; break; } @@ -83,7 +80,7 @@ stack.stack = function(items, margin, force) { * @param {{item: number, axis: number}} margin * Margins between items and between items and the axis. */ -stack.nostack = function(items, margin) { +exports.nostack = function(items, margin) { var i, iMax; // reset top position of all items @@ -104,7 +101,7 @@ stack.nostack = function(items, margin) { * the requested margin. * @return {boolean} true if a and b collide, else false */ -stack.collision = function(a, b, margin) { +exports.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) && diff --git a/lib/timeline/TimeStep.js b/lib/timeline/TimeStep.js index febe04fc..e477e6a3 100644 --- a/lib/timeline/TimeStep.js +++ b/lib/timeline/TimeStep.js @@ -1,3 +1,5 @@ +var moment = require('../module/moment'); + /** * @constructor TimeStep * The class TimeStep is an iterator for dates. You provide a start date and an @@ -464,3 +466,5 @@ TimeStep.prototype.getLabelMajor = function(date) { default: return ''; } }; + +module.exports = TimeStep; diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js index 64b1652d..23c3dc22 100644 --- a/lib/timeline/Timeline.js +++ b/lib/timeline/Timeline.js @@ -1,3 +1,14 @@ +var Emitter = require('emitter-component'); +var Hammer = require('hammerjs'); +var util = require('../util'); +var DataSet = require('../DataSet'); +var DataView = require('../DataView'); +var Range = require('./Range'); +var TimeAxis = require('./component/TimeAxis'); +var CurrentTime = require('./component/CurrentTime'); +var CustomTime = require('./component/CustomTime'); +var ItemSet = require('./component/ItemSet'); + /** * Create a timeline visualization * @param {HTMLElement} container @@ -885,3 +896,5 @@ Timeline.prototype._updateScrollTop = function () { Timeline.prototype._getScrollTop = function () { return this.props.scrollTop; }; + +module.exports = Timeline; diff --git a/lib/timeline/component/Component.js b/lib/timeline/component/Component.js index bddccf0d..5f211b8d 100644 --- a/lib/timeline/component/Component.js +++ b/lib/timeline/component/Component.js @@ -50,3 +50,5 @@ Component.prototype._isResized = function() { return resized; }; + +module.exports = Component; diff --git a/lib/timeline/component/CurrentTime.js b/lib/timeline/component/CurrentTime.js index 3b067280..e3735b7f 100644 --- a/lib/timeline/component/CurrentTime.js +++ b/lib/timeline/component/CurrentTime.js @@ -1,3 +1,6 @@ +var util = require('../../util'); +var Component = require('./Component'); + /** * A current time bar * @param {{range: Range, dom: Object, domProps: Object}} body @@ -6,7 +9,6 @@ * @constructor CurrentTime * @extends Component */ - function CurrentTime (body, options) { this.body = body; @@ -126,3 +128,5 @@ CurrentTime.prototype.stop = function() { delete this.currentTimeTimer; } }; + +module.exports = CurrentTime; diff --git a/lib/timeline/component/CustomTime.js b/lib/timeline/component/CustomTime.js index 21c86e8b..407350c3 100644 --- a/lib/timeline/component/CustomTime.js +++ b/lib/timeline/component/CustomTime.js @@ -1,3 +1,7 @@ +var Hammer = require('hammerjs'); +var util = require('../../util'); +var Component = require('./Component'); + /** * A custom time bar * @param {{range: Range, dom: Object}} body @@ -180,3 +184,5 @@ CustomTime.prototype._onDragEnd = function (event) { event.stopPropagation(); event.preventDefault(); }; + +module.exports = CustomTime; diff --git a/lib/timeline/component/DataAxis.js b/lib/timeline/component/DataAxis.js index 81ea5cb6..e5af2c9c 100644 --- a/lib/timeline/component/DataAxis.js +++ b/lib/timeline/component/DataAxis.js @@ -1,3 +1,8 @@ +var util = require('../../util'); +var DOMutil = require('../../DOMutil'); +var Component = require('./Component'); +var DataStep = require('../DataStep'); + /** * A horizontal time axis * @param {Object} [options] See DataAxis.setOptions for the available @@ -466,3 +471,5 @@ DataAxis.prototype._calculateCharSize = function () { DataAxis.prototype.snap = function(date) { return this.step.snap(date); }; + +module.exports = DataAxis; diff --git a/lib/timeline/component/GraphGroup.js b/lib/timeline/component/GraphGroup.js index 0da3d235..e15d113b 100644 --- a/lib/timeline/component/GraphGroup.js +++ b/lib/timeline/component/GraphGroup.js @@ -1,3 +1,6 @@ +var util = require('../../util'); +var DOMutil = require('../../DOMutil'); + /** * @constructor Group * @param {Number | String} groupId @@ -28,11 +31,11 @@ GraphGroup.prototype.setItems = function(items) { else { this.itemsData = []; } -} +}; GraphGroup.prototype.setZeroPosition = function(pos) { this.zeroPosition = pos; -} +}; GraphGroup.prototype.setOptions = function(options) { if (options !== undefined) { @@ -113,4 +116,6 @@ GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, icon DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); } -} +}; + +module.exports = GraphGroup; diff --git a/lib/timeline/component/Group.js b/lib/timeline/component/Group.js index bfc63e71..a9582c25 100644 --- a/lib/timeline/component/Group.js +++ b/lib/timeline/component/Group.js @@ -1,3 +1,7 @@ +var util = require('../../util'); +var stack = require('../Stack'); +var ItemRange = require('./item/ItemRange'); + /** * @constructor Group * @param {Number | String} groupId @@ -415,3 +419,5 @@ Group.prototype._checkIfVisible = function(item, visibleItems, range) { if (item.displayed) item.hide(); } }; + +module.exports = Group; diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js index 1a5171cd..37dbc496 100644 --- a/lib/timeline/component/ItemSet.js +++ b/lib/timeline/component/ItemSet.js @@ -1,3 +1,14 @@ +var Hammer = require('hammerjs'); +var util = require('../../util'); +var DataSet = require('../../DataSet'); +var DataView = require('../../DataView'); +var Component = require('./Component'); +var Group = require('./Group'); +var ItemBox = require('./item/ItemBox'); +var ItemPoint = require('./item/ItemPoint'); +var ItemRange = require('./item/ItemRange'); + + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items /** @@ -1318,3 +1329,4 @@ ItemSet.itemSetFromTarget = function(event) { return null; }; +module.exports = ItemSet; diff --git a/lib/timeline/component/Legend.js b/lib/timeline/component/Legend.js index d879d0de..6cd32fed 100644 --- a/lib/timeline/component/Legend.js +++ b/lib/timeline/component/Legend.js @@ -1,5 +1,9 @@ +var util = require('../../util'); +var DOMutil = require('../../DOMutil'); +var Component = require('./Component'); + /** - * Created by Alex on 6/17/14. + * Legend for Graph2d */ function Legend(body, options, side) { this.body = body; @@ -27,7 +31,7 @@ function Legend(body, options, side) { this._create(); this.setOptions(options); -}; +} Legend.prototype = new Component(); @@ -69,7 +73,7 @@ Legend.prototype._create = function() { this.dom.frame.appendChild(this.svg); this.dom.frame.appendChild(this.dom.textArea); -} +}; /** * Hide the component from the DOM @@ -95,7 +99,7 @@ Legend.prototype.show = function() { Legend.prototype.setOptions = function(options) { var fields = ['enabled','orientation','icons','left','right']; util.selectiveDeepExtend(fields, this.options, options); -} +}; Legend.prototype.redraw = function() { if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false) { @@ -142,7 +146,7 @@ Legend.prototype.redraw = function() { this.drawLegendIcons(); } - var content = ""; + var content = ''; for (var groupId in this.groups) { if (this.groups.hasOwnProperty(groupId)) { content += this.groups[groupId].content + '
'; @@ -151,13 +155,13 @@ Legend.prototype.redraw = function() { this.dom.textArea.innerHTML = content; this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; } -} +}; Legend.prototype.drawLegendIcons = function() { if (this.dom.frame.parentNode) { DOMutil.prepareElements(this.svgElements); var padding = window.getComputedStyle(this.dom.frame).paddingTop; - var iconOffset = Number(padding.replace("px",'')); + var iconOffset = Number(padding.replace('px','')); var x = iconOffset; var iconWidth = this.options.iconSize; var iconHeight = 0.75 * this.options.iconSize; @@ -174,4 +178,6 @@ Legend.prototype.drawLegendIcons = function() { DOMutil.cleanupElements(this.svgElements); } -} \ No newline at end of file +}; + +module.exports = Legend; diff --git a/lib/timeline/component/LineGraph.js b/lib/timeline/component/LineGraph.js index 6ffed716..07b0f973 100644 --- a/lib/timeline/component/LineGraph.js +++ b/lib/timeline/component/LineGraph.js @@ -1,3 +1,12 @@ +var util = require('../../util'); +var DOMutil = require('../../DOMutil'); +var DataSet = require('../../DataSet'); +var DataView = require('../../DataView'); +var Component = require('./Component'); +var DataAxis = require('./DataAxis'); +var GraphGroup = require('./GraphGroup'); +var Legend = require('./Legend'); + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items /** @@ -1059,7 +1068,4 @@ LineGraph.prototype._linear = function(data) { return d; }; - - - - +module.exports = LineGraph; diff --git a/lib/timeline/component/TimeAxis.js b/lib/timeline/component/TimeAxis.js index c244c0ce..e26259a2 100644 --- a/lib/timeline/component/TimeAxis.js +++ b/lib/timeline/component/TimeAxis.js @@ -1,3 +1,7 @@ +var util = require('../../util'); +var Component = require('./Component'); +var TimeStep = require('../TimeStep'); + /** * A horizontal time axis * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body @@ -387,3 +391,5 @@ TimeAxis.prototype._calculateCharSize = function () { TimeAxis.prototype.snap = function(date) { return this.step.snap(date); }; + +module.exports = TimeAxis; diff --git a/lib/timeline/component/item/Item.js b/lib/timeline/component/item/Item.js index 998f3bf8..13d3f8ce 100644 --- a/lib/timeline/component/item/Item.js +++ b/lib/timeline/component/item/Item.js @@ -1,3 +1,5 @@ +var Hammer = require('hammerjs'); + /** * @constructor Item * @param {Object} data Object containing (optional) parameters type, @@ -137,3 +139,5 @@ Item.prototype._repaintDeleteButton = function (anchor) { this.dom.deleteButton = null; } }; + +module.exports = Item; diff --git a/lib/timeline/component/item/ItemBox.js b/lib/timeline/component/item/ItemBox.js index 04c94439..64abbbd2 100644 --- a/lib/timeline/component/item/ItemBox.js +++ b/lib/timeline/component/item/ItemBox.js @@ -1,3 +1,5 @@ +var Item = require('./Item'); + /** * @constructor ItemBox * @extends Item @@ -234,3 +236,5 @@ ItemBox.prototype.repositionY = function() { dot.style.top = (-this.props.dot.height / 2) + 'px'; }; + +module.exports = ItemBox; diff --git a/lib/timeline/component/item/ItemPoint.js b/lib/timeline/component/item/ItemPoint.js index bd6fd09f..7f87665e 100644 --- a/lib/timeline/component/item/ItemPoint.js +++ b/lib/timeline/component/item/ItemPoint.js @@ -1,3 +1,5 @@ +var Item = require('./Item'); + /** * @constructor ItemPoint * @extends Item @@ -194,3 +196,5 @@ ItemPoint.prototype.repositionY = function() { point.style.top = (this.parent.height - this.top - this.height) + 'px'; } }; + +module.exports = ItemPoint; diff --git a/lib/timeline/component/item/ItemRange.js b/lib/timeline/component/item/ItemRange.js index b6bc2572..3a7f270c 100644 --- a/lib/timeline/component/item/ItemRange.js +++ b/lib/timeline/component/item/ItemRange.js @@ -1,3 +1,6 @@ +var Hammer = require('hammerjs'); +var Item = require('./Item'); + /** * @constructor ItemRange * @extends Item @@ -284,3 +287,5 @@ ItemRange.prototype._repaintDragRight = function () { this.dom.dragRight = null; } }; + +module.exports = ItemRange; diff --git a/lib/util.js b/lib/util.js index abf577f2..46fcdb14 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,14 +1,16 @@ -/** - * utility functions - */ -var util = {}; +// utility functions + +// first check if moment.js is already loaded in the browser window, if so, +// use this instance. Else, load via commonjs. +var Hammer = require('./module/hammer'); +var moment = require('./module/moment'); /** * Test whether given object is a number * @param {*} object * @return {Boolean} isNumber */ -util.isNumber = function(object) { +exports.isNumber = function(object) { return (object instanceof Number || typeof object == 'number'); }; @@ -17,7 +19,7 @@ util.isNumber = function(object) { * @param {*} object * @return {Boolean} isString */ -util.isString = function(object) { +exports.isString = function(object) { return (object instanceof String || typeof object == 'string'); }; @@ -26,11 +28,11 @@ util.isString = function(object) { * @param {Date | String} object * @return {Boolean} isDate */ -util.isDate = function(object) { +exports.isDate = function(object) { if (object instanceof Date) { return true; } - else if (util.isString(object)) { + else if (exports.isString(object)) { // test whether this string contains a date var match = ASPDateRegex.exec(object); if (match) { @@ -49,7 +51,7 @@ util.isDate = function(object) { * @param {*} object * @return {Boolean} isDataTable */ -util.isDataTable = function(object) { +exports.isDataTable = function(object) { return (typeof (google) !== 'undefined') && (google.visualization) && (google.visualization.DataTable) && @@ -61,7 +63,7 @@ util.isDataTable = function(object) { * source: http://stackoverflow.com/a/105074/1262753 * @return {String} uuid */ -util.randomUUID = function() { +exports.randomUUID = function() { var S4 = function () { return Math.floor( Math.random() * 0x10000 /* 65536 */ @@ -84,7 +86,7 @@ util.randomUUID = function() { * @param {... Object} b * @return {Object} a */ -util.extend = function (a, b) { +exports.extend = function (a, b) { for (var i = 1, len = arguments.length; i < len; i++) { var other = arguments[i]; for (var prop in other) { @@ -105,7 +107,7 @@ util.extend = function (a, b) { * @param {... Object} b * @return {Object} a */ -util.selectiveExtend = function (props, a, b) { +exports.selectiveExtend = function (props, a, b) { if (!Array.isArray(props)) { throw new Error('Array with property names expected as first argument'); } @@ -131,7 +133,7 @@ util.selectiveExtend = function (props, a, b) { * @param {... Object} b * @return {Object} a */ -util.selectiveDeepExtend = function (props, a, b) { +exports.selectiveDeepExtend = function (props, a, b) { // TODO: add support for Arrays to deepExtend if (Array.isArray(b)) { throw new TypeError('Arrays are not supported by deepExtend'); @@ -146,7 +148,7 @@ util.selectiveDeepExtend = function (props, a, b) { a[prop] = {}; } if (a[prop].constructor === Object) { - util.deepExtend(a[prop], b[prop]); + exports.deepExtend(a[prop], b[prop]); } else { a[prop] = b[prop]; @@ -169,7 +171,7 @@ util.selectiveDeepExtend = function (props, a, b) { * @param {Object} b * @returns {Object} */ -util.deepExtend = function(a, b) { +exports.deepExtend = function(a, b) { // TODO: add support for Arrays to deepExtend if (Array.isArray(b)) { throw new TypeError('Arrays are not supported by deepExtend'); @@ -182,7 +184,7 @@ util.deepExtend = function(a, b) { a[prop] = {}; } if (a[prop].constructor === Object) { - util.deepExtend(a[prop], b[prop]); + exports.deepExtend(a[prop], b[prop]); } else { a[prop] = b[prop]; @@ -204,7 +206,7 @@ util.deepExtend = function(a, b) { * @return {boolean} Returns true if both arrays have the same length and same * elements. */ -util.equalArray = function (a, b) { +exports.equalArray = function (a, b) { if (a.length != b.length) return false; for (var i = 0, len = a.length; i < len; i++) { @@ -223,7 +225,7 @@ util.equalArray = function (a, b) { * @return {*} object * @throws Error */ -util.convert = function(object, type) { +exports.convert = function(object, type) { var match; if (object === undefined) { @@ -255,7 +257,7 @@ util.convert = function(object, type) { return String(object); case 'Date': - if (util.isNumber(object)) { + if (exports.isNumber(object)) { return new Date(object); } if (object instanceof Date) { @@ -264,7 +266,7 @@ util.convert = function(object, type) { else if (moment.isMoment(object)) { return new Date(object.valueOf()); } - if (util.isString(object)) { + if (exports.isString(object)) { match = ASPDateRegex.exec(object); if (match) { // object is an ASP date @@ -276,12 +278,12 @@ util.convert = function(object, type) { } else { throw new Error( - 'Cannot convert object of type ' + util.getType(object) + + 'Cannot convert object of type ' + exports.getType(object) + ' to type Date'); } case 'Moment': - if (util.isNumber(object)) { + if (exports.isNumber(object)) { return moment(object); } if (object instanceof Date) { @@ -290,7 +292,7 @@ util.convert = function(object, type) { else if (moment.isMoment(object)) { return moment(object); } - if (util.isString(object)) { + if (exports.isString(object)) { match = ASPDateRegex.exec(object); if (match) { // object is an ASP date @@ -302,12 +304,12 @@ util.convert = function(object, type) { } else { throw new Error( - 'Cannot convert object of type ' + util.getType(object) + + 'Cannot convert object of type ' + exports.getType(object) + ' to type Date'); } case 'ISODate': - if (util.isNumber(object)) { + if (exports.isNumber(object)) { return new Date(object); } else if (object instanceof Date) { @@ -316,7 +318,7 @@ util.convert = function(object, type) { else if (moment.isMoment(object)) { return object.toDate().toISOString(); } - else if (util.isString(object)) { + else if (exports.isString(object)) { match = ASPDateRegex.exec(object); if (match) { // object is an ASP date @@ -328,18 +330,18 @@ util.convert = function(object, type) { } else { throw new Error( - 'Cannot convert object of type ' + util.getType(object) + + 'Cannot convert object of type ' + exports.getType(object) + ' to type ISODate'); } case 'ASPDate': - if (util.isNumber(object)) { + if (exports.isNumber(object)) { return '/Date(' + object + ')/'; } else if (object instanceof Date) { return '/Date(' + object.valueOf() + ')/'; } - else if (util.isString(object)) { + else if (exports.isString(object)) { match = ASPDateRegex.exec(object); var value; if (match) { @@ -353,7 +355,7 @@ util.convert = function(object, type) { } else { throw new Error( - 'Cannot convert object of type ' + util.getType(object) + + 'Cannot convert object of type ' + exports.getType(object) + ' to type ASPDate'); } @@ -368,11 +370,11 @@ util.convert = function(object, type) { var ASPDateRegex = /^\/?Date\((\-?\d+)/i; /** - * Get the type of an object, for example util.getType([]) returns 'Array' + * Get the type of an object, for example exports.getType([]) returns 'Array' * @param {*} object * @return {String} type */ -util.getType = function(object) { +exports.getType = function(object) { var type = typeof object; if (type == 'object') { @@ -415,7 +417,7 @@ util.getType = function(object) { * @return {number} left The absolute left position of this element * in the browser page. */ -util.getAbsoluteLeft = function(elem) { +exports.getAbsoluteLeft = function(elem) { var doc = document.documentElement; var body = document.body; @@ -435,7 +437,7 @@ util.getAbsoluteLeft = function(elem) { * @return {number} top The absolute top position of this element * in the browser page. */ -util.getAbsoluteTop = function(elem) { +exports.getAbsoluteTop = function(elem) { var doc = document.documentElement; var body = document.body; @@ -454,7 +456,7 @@ util.getAbsoluteTop = function(elem) { * @param {Event} event * @return {Number} pageY */ -util.getPageY = function(event) { +exports.getPageY = function(event) { if ('pageY' in event) { return event.pageY; } @@ -480,7 +482,7 @@ util.getPageY = function(event) { * @param {Event} event * @return {Number} pageX */ -util.getPageX = function(event) { +exports.getPageX = function(event) { if ('pageY' in event) { return event.pageX; } @@ -506,7 +508,7 @@ util.getPageX = function(event) { * @param {Element} elem * @param {String} className */ -util.addClassName = function(elem, className) { +exports.addClassName = function(elem, className) { var classes = elem.className.split(' '); if (classes.indexOf(className) == -1) { classes.push(className); // add the class to the array @@ -519,7 +521,7 @@ util.addClassName = function(elem, className) { * @param {Element} elem * @param {String} className */ -util.removeClassName = function(elem, className) { +exports.removeClassName = function(elem, className) { var classes = elem.className.split(' '); var index = classes.indexOf(className); if (index != -1) { @@ -537,7 +539,7 @@ util.removeClassName = function(elem, className) { * the object or array with three parameters: * callback(value, index, object) */ -util.forEach = function(object, callback) { +exports.forEach = function(object, callback) { var i, len; if (object instanceof Array) { @@ -562,7 +564,7 @@ util.forEach = function(object, callback) { * @param {Object} object * @param {Array} array */ -util.toArray = function(object) { +exports.toArray = function(object) { var array = []; for (var prop in object) { @@ -579,7 +581,7 @@ util.toArray = function(object) { * @param {*} value * @return {Boolean} changed */ -util.updateProperty = function(object, key, value) { +exports.updateProperty = function(object, key, value) { if (object[key] !== value) { object[key] = value; return true; @@ -597,7 +599,7 @@ util.updateProperty = function(object, key, value) { * @param {function} listener The callback function to be executed * @param {boolean} [useCapture] */ -util.addEventListener = function(element, action, listener, useCapture) { +exports.addEventListener = function(element, action, listener, useCapture) { if (element.addEventListener) { if (useCapture === undefined) useCapture = false; @@ -619,7 +621,7 @@ util.addEventListener = function(element, action, listener, useCapture) { * @param {function} listener The listener function * @param {boolean} [useCapture] */ -util.removeEventListener = function(element, action, listener, useCapture) { +exports.removeEventListener = function(element, action, listener, useCapture) { if (element.removeEventListener) { // non-IE browsers if (useCapture === undefined) @@ -642,7 +644,7 @@ util.removeEventListener = function(element, action, listener, useCapture) { * @param {Event} event * @return {Element} target element */ -util.getTarget = function(event) { +exports.getTarget = function(event) { // code from http://www.quirksmode.org/js/events_properties.html if (!event) { event = window.event; @@ -670,7 +672,7 @@ util.getTarget = function(event) { * @param {Element} element * @param {Event} event */ -util.fakeGesture = function(element, event) { +exports.fakeGesture = function(element, event) { var eventType = null; // for hammer.js 1.0.5 @@ -692,7 +694,7 @@ util.fakeGesture = function(element, event) { return gesture; }; -util.option = {}; +exports.option = {}; /** * Convert a value into a boolean @@ -700,7 +702,7 @@ util.option = {}; * @param {Boolean} [defaultValue] * @returns {Boolean} bool */ -util.option.asBoolean = function (value, defaultValue) { +exports.option.asBoolean = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } @@ -718,7 +720,7 @@ util.option.asBoolean = function (value, defaultValue) { * @param {Number} [defaultValue] * @returns {Number} number */ -util.option.asNumber = function (value, defaultValue) { +exports.option.asNumber = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } @@ -736,7 +738,7 @@ util.option.asNumber = function (value, defaultValue) { * @param {String} [defaultValue] * @returns {String} str */ -util.option.asString = function (value, defaultValue) { +exports.option.asString = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } @@ -754,15 +756,15 @@ util.option.asString = function (value, defaultValue) { * @param {String} [defaultValue] * @returns {String} size */ -util.option.asSize = function (value, defaultValue) { +exports.option.asSize = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } - if (util.isString(value)) { + if (exports.isString(value)) { return value; } - else if (util.isNumber(value)) { + else if (exports.isNumber(value)) { return value + 'px'; } else { @@ -776,7 +778,7 @@ util.option.asSize = function (value, defaultValue) { * @param {HTMLElement} [defaultValue] * @returns {HTMLElement | null} dom */ -util.option.asElement = function (value, defaultValue) { +exports.option.asElement = function (value, defaultValue) { if (typeof value == 'function') { value = value(); } @@ -786,7 +788,7 @@ util.option.asElement = function (value, defaultValue) { -util.GiveDec = function(Hex) { +exports.GiveDec = function(Hex) { var Value; if (Hex == "A") @@ -807,7 +809,7 @@ util.GiveDec = function(Hex) { return Value; }; -util.GiveHex = function(Dec) { +exports.GiveHex = function(Dec) { var Value; if(Dec == 10) @@ -834,15 +836,15 @@ util.GiveHex = function(Dec) { * @param {Object | String} color * @return {Object} colorObject */ -util.parseColor = function(color) { +exports.parseColor = function(color) { var c; - if (util.isString(color)) { - if (util.isValidHex(color)) { - var hsv = util.hexToHSV(color); + if (exports.isString(color)) { + if (exports.isValidHex(color)) { + var hsv = exports.hexToHSV(color); var lighterColorHSV = {h:hsv.h,s:hsv.s * 0.45,v:Math.min(1,hsv.v * 1.05)}; var darkerColorHSV = {h:hsv.h,s:Math.min(1,hsv.v * 1.25),v:hsv.v*0.6}; - var darkerColorHex = util.HSVToHex(darkerColorHSV.h ,darkerColorHSV.h ,darkerColorHSV.v); - var lighterColorHex = util.HSVToHex(lighterColorHSV.h,lighterColorHSV.s,lighterColorHSV.v); + var darkerColorHex = exports.HSVToHex(darkerColorHSV.h ,darkerColorHSV.h ,darkerColorHSV.v); + var lighterColorHex = exports.HSVToHex(lighterColorHSV.h,lighterColorHSV.s,lighterColorHSV.v); c = { background: color, @@ -877,7 +879,7 @@ util.parseColor = function(color) { c.background = color.background || 'white'; c.border = color.border || c.background; - if (util.isString(color.highlight)) { + if (exports.isString(color.highlight)) { c.highlight = { border: color.highlight, background: color.highlight @@ -889,7 +891,7 @@ util.parseColor = function(color) { c.highlight.border = color.highlight && color.highlight.border || c.border; } - if (util.isString(color.hover)) { + if (exports.isString(color.hover)) { c.hover = { border: color.hover, background: color.hover @@ -911,15 +913,15 @@ util.parseColor = function(color) { * @param {String} hex * @returns {{r: *, g: *, b: *}} */ -util.hexToRGB = function(hex) { +exports.hexToRGB = function(hex) { hex = hex.replace("#","").toUpperCase(); - var a = util.GiveDec(hex.substring(0, 1)); - var b = util.GiveDec(hex.substring(1, 2)); - var c = util.GiveDec(hex.substring(2, 3)); - var d = util.GiveDec(hex.substring(3, 4)); - var e = util.GiveDec(hex.substring(4, 5)); - var f = util.GiveDec(hex.substring(5, 6)); + var a = exports.GiveDec(hex.substring(0, 1)); + var b = exports.GiveDec(hex.substring(1, 2)); + var c = exports.GiveDec(hex.substring(2, 3)); + var d = exports.GiveDec(hex.substring(3, 4)); + var e = exports.GiveDec(hex.substring(4, 5)); + var f = exports.GiveDec(hex.substring(5, 6)); var r = (a * 16) + b; var g = (c * 16) + d; @@ -928,13 +930,13 @@ util.hexToRGB = function(hex) { return {r:r,g:g,b:b}; }; -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)); - var d = util.GiveHex(green % 16); - var e = util.GiveHex(Math.floor(blue / 16)); - var f = util.GiveHex(blue % 16); +exports.RGBToHex = function(red,green,blue) { + var a = exports.GiveHex(Math.floor(red / 16)); + var b = exports.GiveHex(red % 16); + var c = exports.GiveHex(Math.floor(green / 16)); + var d = exports.GiveHex(green % 16); + var e = exports.GiveHex(Math.floor(blue / 16)); + var f = exports.GiveHex(blue % 16); var hex = a + b + c + d + e + f; return "#" + hex; @@ -950,7 +952,7 @@ util.RGBToHex = function(red,green,blue) { * @returns {*} * @constructor */ -util.RGBToHSV = function(red,green,blue) { +exports.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)); @@ -978,7 +980,7 @@ util.RGBToHSV = function(red,green,blue) { * @returns {{r: number, g: number, b: number}} * @constructor */ -util.HSVToRGB = function(h, s, v) { +exports.HSVToRGB = function(h, s, v) { var r, g, b; var i = Math.floor(h * 6); @@ -999,17 +1001,17 @@ util.HSVToRGB = function(h, s, v) { return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) }; }; -util.HSVToHex = function(h, s, v) { - var rgb = util.HSVToRGB(h, s, v); - return util.RGBToHex(rgb.r, rgb.g, rgb.b); +exports.HSVToHex = function(h, s, v) { + var rgb = exports.HSVToRGB(h, s, v); + return exports.RGBToHex(rgb.r, rgb.g, rgb.b); }; -util.hexToHSV = function(hex) { - var rgb = util.hexToRGB(hex); - return util.RGBToHSV(rgb.r, rgb.g, rgb.b); +exports.hexToHSV = function(hex) { + var rgb = exports.hexToRGB(hex); + return exports.RGBToHSV(rgb.r, rgb.g, rgb.b); }; -util.isValidHex = function(hex) { +exports.isValidHex = function(hex) { var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex); return isOk; }; @@ -1022,13 +1024,13 @@ util.isValidHex = function(hex) { * @param referenceObject * @returns {*} */ -util.selectiveBridgeObject = function(fields, referenceObject) { +exports.selectiveBridgeObject = function(fields, referenceObject) { if (typeof referenceObject == "object") { var objectTo = Object.create(referenceObject); for (var i = 0; i < fields.length; i++) { if (referenceObject.hasOwnProperty(fields[i])) { if (typeof referenceObject[fields[i]] == "object") { - objectTo[fields[i]] = util.bridgeObject(referenceObject[fields[i]]); + objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]); } } } @@ -1046,13 +1048,13 @@ util.selectiveBridgeObject = function(fields, referenceObject) { * @param referenceObject * @returns {*} */ -util.bridgeObject = function(referenceObject) { +exports.bridgeObject = function(referenceObject) { if (typeof referenceObject == "object") { var objectTo = Object.create(referenceObject); for (var i in referenceObject) { if (referenceObject.hasOwnProperty(i)) { if (typeof referenceObject[i] == "object") { - objectTo[i] = util.bridgeObject(referenceObject[i]); + objectTo[i] = exports.bridgeObject(referenceObject[i]); } } } @@ -1073,7 +1075,7 @@ util.bridgeObject = function(referenceObject) { * @param [String] option | this is the option key in the options argument * @private */ -util.mergeOptions = function (mergeTarget, options, option) { +exports.mergeOptions = function (mergeTarget, options, option) { if (options[option] !== undefined) { if (typeof options[option] == 'boolean') { mergeTarget[option].enabled = options[option]; @@ -1099,7 +1101,7 @@ util.mergeOptions = function (mergeTarget, options, option) { * @param [String] option | this is the option key in the options argument * @private */ -util.mergeOptions = function (mergeTarget, options, option) { +exports.mergeOptions = function (mergeTarget, options, option) { if (options[option] !== undefined) { if (typeof options[option] == 'boolean') { mergeTarget[option].enabled = options[option]; @@ -1134,7 +1136,7 @@ util.mergeOptions = function (mergeTarget, options, option) { * @returns {number} * @private */ -util.binarySearch = function(orderedItems, range, field, field2) { +exports.binarySearch = function(orderedItems, range, field, field2) { var array = orderedItems; var interval = range.end - range.start; @@ -1200,7 +1202,7 @@ util.binarySearch = function(orderedItems, range, field, field2) { * @returns {number} * @private */ -util.binarySearchGeneric = function(orderedItems, target, field, sidePreference) { +exports.binarySearchGeneric = function(orderedItems, target, field, sidePreference) { var array = orderedItems; var found = false; var low = 0; diff --git a/package.json b/package.json index d35e6bd7..6a1cbcd3 100644 --- a/package.json +++ b/package.json @@ -20,21 +20,29 @@ "network", "browser" ], + "main": "./index", "scripts": { - "test": "jake test --trace", - "build": "jake --trace" + "test": "mocha", + "build": "build" + }, + "dependencies": { + "emitter-component": "^1.1.1", + "hammerjs": "1.0.5", + "moment": "^2.7.0", + "mousetrap": "0.0.1" }, - "dependencies": {}, "devDependencies": { - "jake": "latest", - "jake-utils": "latest", "clean-css": "latest", - "browserify": "3.22", - "wrench": "latest", - "moment": "latest", - "hammerjs": "1.0.5", - "mousetrap": "latest", - "emitter-component": "latest", - "node-watch": "latest" + "gulp": "^3.8.5", + "gulp-concat": "^2.2.0", + "gulp-minify-css": "^0.3.6", + "gulp-rename": "^1.2.0", + "gulp-util": "^2.2.19", + "merge-stream": "^0.1.5", + "mocha": "^1.20.1", + "rimraf": "^2.2.8", + "uglify-js": "^2.4.14", + "webpack": "^1.3.1-beta7", + "wrench": "latest" } } diff --git a/test/dataset.js b/test/dataset.js index 3e18923c..f7e67d95 100644 --- a/test/dataset.js +++ b/test/dataset.js @@ -1,7 +1,6 @@ -var assert = require('assert'), - moment = require('moment'), - vis = require('../dist/vis.js'), - DataSet = vis.DataSet; +var assert = require('assert'); +var moment = require('moment'); +var DataSet = require('../lib/DataSet'); var now = new Date(); @@ -103,7 +102,7 @@ assert.equal(data.get().length, 0); // test filtering and sorting -data = new vis.DataSet(); +data = new DataSet(); data.add([ {id:1, age: 30, group: 2}, {id:2, age: 25, group: 4}, diff --git a/test/dataview.js b/test/dataview.js index 1329454c..9b5de4e3 100644 --- a/test/dataview.js +++ b/test/dataview.js @@ -1,9 +1,7 @@ -var assert = require('assert'), - moment = require('moment'), - vis = require('../dist/vis.js'), - DataSet = vis.DataSet, - DataView = vis.DataView; - +var assert = require('assert'); +var moment = require('moment'); +var DataSet = require('../lib/DataSet'); +var DataView = require('../lib/DataView'); var groups = new DataSet();