diff --git a/dist/vis.js b/dist/vis.js index fcccaf87..42f3ee98 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -93,20 +93,20 @@ return /******/ (function(modules) { // webpackBootstrap // Graph3d exports.Graph3d = __webpack_require__(10); exports.graph3d = { - Camera: __webpack_require__(11), - Filter: __webpack_require__(13), - Point2d: __webpack_require__(14), + Camera: __webpack_require__(14), + Filter: __webpack_require__(15), + Point2d: __webpack_require__(13), Point3d: __webpack_require__(12), - Slider: __webpack_require__(15), - StepNumber: __webpack_require__(16) + Slider: __webpack_require__(16), + StepNumber: __webpack_require__(17) }; // Timeline - exports.Timeline = __webpack_require__(17); + exports.Timeline = __webpack_require__(18); exports.Graph2d = __webpack_require__(42); exports.timeline = { DateUtil: __webpack_require__(24), - DataStep: __webpack_require__(45), + DataStep: __webpack_require__(44), Range: __webpack_require__(21), stack: __webpack_require__(28), TimeStep: __webpack_require__(38), @@ -123,7 +123,7 @@ return /******/ (function(modules) { // webpackBootstrap Component: __webpack_require__(23), CurrentTime: __webpack_require__(39), CustomTime: __webpack_require__(41), - DataAxis: __webpack_require__(44), + DataAxis: __webpack_require__(45), GraphGroup: __webpack_require__(46), Group: __webpack_require__(27), BackgroundGroup: __webpack_require__(31), @@ -6115,16 +6115,16 @@ return /******/ (function(modules) { // webpackBootstrap /* 10 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(18); + var Emitter = __webpack_require__(11); var DataSet = __webpack_require__(7); var DataView = __webpack_require__(9); var util = __webpack_require__(1); var Point3d = __webpack_require__(12); - var Point2d = __webpack_require__(14); - var Camera = __webpack_require__(11); - var Filter = __webpack_require__(13); - var Slider = __webpack_require__(15); - var StepNumber = __webpack_require__(16); + var Point2d = __webpack_require__(13); + var Camera = __webpack_require__(14); + var Filter = __webpack_require__(15); + var Slider = __webpack_require__(16); + var StepNumber = __webpack_require__(17); /** * @constructor Graph3d @@ -8400,141 +8400,171 @@ return /******/ (function(modules) { // webpackBootstrap /* 11 */ /***/ function(module, exports, __webpack_require__) { - var Point3d = __webpack_require__(12); - + /** - * @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 + * Expose `Emitter`. */ - function Camera() { - this.armLocation = new Point3d(); - this.armRotation = {}; - this.armRotation.horizontal = 0; - this.armRotation.vertical = 0; - this.armLength = 1.7; - - this.cameraLocation = new Point3d(); - this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); - this.calculateCameraOrientation(); - } + module.exports = Emitter; /** - * 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 + * Initialize a new `Emitter`. + * + * @api public */ - Camera.prototype.setArmLocation = function(x, y, z) { - this.armLocation.x = x; - this.armLocation.y = y; - this.armLocation.z = z; - this.calculateCameraOrientation(); + function Emitter(obj) { + if (obj) return mixin(obj); }; /** - * 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. + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private */ - Camera.prototype.setArmRotation = function(horizontal, vertical) { - if (horizontal !== undefined) { - this.armRotation.horizontal = horizontal; - } - - if (vertical !== undefined) { - this.armRotation.vertical = vertical; - if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; - if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; - } - if (horizontal !== undefined || vertical !== undefined) { - this.calculateCameraOrientation(); + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; } - }; + return obj; + } /** - * Retrieve the current arm rotation - * @return {object} An object with parameters horizontal and vertical + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - Camera.prototype.getArmRotation = function() { - var rot = {}; - rot.horizontal = this.armRotation.horizontal; - rot.vertical = this.armRotation.vertical; - return rot; + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; }; /** - * Set the (normalized) length of the camera arm. - * @param {Number} length A length between 0.71 and 5.0 + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - Camera.prototype.setArmLength = function(length) { - if (length === undefined) - return; - this.armLength = length; + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; - // 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; + function on() { + self.off(event, on); + fn.apply(this, arguments); + } - this.calculateCameraOrientation(); + on.fn = fn; + this.on(event, on); + return this; }; /** - * Retrieve the arm length - * @return {Number} length + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - Camera.prototype.getArmLength = function() { - return this.armLength; + + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; }; /** - * Retrieve the camera location - * @return {Point3d} cameraLocation + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} */ - Camera.prototype.getCameraLocation = function() { - return this.cameraLocation; + + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks[event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; }; /** - * Retrieve the camera rotation - * @return {Point3d} cameraRotation + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public */ - Camera.prototype.getCameraRotation = function() { - return this.cameraRotation; + + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; }; /** - * Calculate the location and rotation of the camera based on the - * position and orientation of the camera arm + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public */ - 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; + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; }; - module.exports = Camera; /***/ }, /* 12 */ @@ -8631,43 +8661,200 @@ return /******/ (function(modules) { // webpackBootstrap /* 13 */ /***/ function(module, exports, __webpack_require__) { - var DataView = __webpack_require__(9); - /** - * @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 + * @prototype Point2d + * @param {Number} [x] + * @param {Number} [y] */ - 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); + function Point2d (x, y) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + } - // sort both numeric and string values correctly - this.values.sort(function (a, b) { - return a > b ? 1 : a < b ? -1 : 0; - }); + module.exports = Point2d; - if (this.values.length > 0) { - this.selectValue(0); - } - // create an array with the filtered datapoints. this will be loaded afterwards - this.dataPoints = []; +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { - this.loaded = false; - this.onLoadCallback = undefined; + var Point3d = __webpack_require__(12); - if (graph.animationPreload) { - this.loaded = false; + /** + * @class Camera + * The camera is mounted on a (virtual) camera arm. The camera arm can rotate + * The camera is always looking in the direction of the origin of the arm. + * This way, the camera always rotates around one fixed point, the location + * of the camera arm. + * + * Documentation: + * http://en.wikipedia.org/wiki/3D_projection + */ + function Camera() { + this.armLocation = new Point3d(); + this.armRotation = {}; + this.armRotation.horizontal = 0; + this.armRotation.vertical = 0; + this.armLength = 1.7; + + this.cameraLocation = new Point3d(); + this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); + + this.calculateCameraOrientation(); + } + + /** + * Set the location (origin) of the arm + * @param {Number} x Normalized value of x + * @param {Number} y Normalized value of y + * @param {Number} z Normalized value of z + */ + Camera.prototype.setArmLocation = function(x, y, z) { + this.armLocation.x = x; + this.armLocation.y = y; + this.armLocation.z = z; + + this.calculateCameraOrientation(); + }; + + /** + * Set the rotation of the camera arm + * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. + */ + Camera.prototype.setArmRotation = function(horizontal, vertical) { + if (horizontal !== undefined) { + this.armRotation.horizontal = horizontal; + } + + 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; + } + + if (horizontal !== undefined || vertical !== undefined) { + this.calculateCameraOrientation(); + } + }; + + /** + * Retrieve the current arm rotation + * @return {object} An object with parameters horizontal and vertical + */ + Camera.prototype.getArmRotation = function() { + var rot = {}; + rot.horizontal = this.armRotation.horizontal; + rot.vertical = this.armRotation.vertical; + + return rot; + }; + + /** + * Set the (normalized) length of the camera arm. + * @param {Number} length A length between 0.71 and 5.0 + */ + Camera.prototype.setArmLength = function(length) { + if (length === undefined) + return; + + this.armLength = length; + + // Radius must be larger than the corner of the graph, + // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the + // graph + if (this.armLength < 0.71) this.armLength = 0.71; + if (this.armLength > 5.0) this.armLength = 5.0; + + this.calculateCameraOrientation(); + }; + + /** + * Retrieve the arm length + * @return {Number} length + */ + Camera.prototype.getArmLength = function() { + return this.armLength; + }; + + /** + * Retrieve the camera location + * @return {Point3d} cameraLocation + */ + Camera.prototype.getCameraLocation = function() { + return this.cameraLocation; + }; + + /** + * Retrieve the camera rotation + * @return {Point3d} cameraRotation + */ + Camera.prototype.getCameraRotation = function() { + return this.cameraRotation; + }; + + /** + * Calculate the location and rotation of the camera based on the + * position and orientation of the camera arm + */ + Camera.prototype.calculateCameraOrientation = function() { + // calculate location of the camera + this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); + + // calculate rotation of the camera + this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; + this.cameraRotation.y = 0; + this.cameraRotation.z = -this.armRotation.horizontal; + }; + + module.exports = Camera; + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + var DataView = __webpack_require__(9); + + /** + * @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 { @@ -8852,24 +9039,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { - - /** - * @prototype Point2d - * @param {Number} [x] - * @param {Number} [y] - */ - function Point2d (x, y) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - } - - module.exports = Point2d; - - -/***/ }, -/* 15 */ +/* 16 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); @@ -9221,7 +9391,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 16 */ +/* 17 */ /***/ function(module, exports, __webpack_require__) { /** @@ -9367,10 +9537,10 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 17 */ +/* 18 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(18); + var Emitter = __webpack_require__(11); var Hammer = __webpack_require__(19); var util = __webpack_require__(1); var DataSet = __webpack_require__(7); @@ -9687,217 +9857,47 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 18 */ +/* 19 */ /***/ function(module, exports, __webpack_require__) { - - /** - * Expose `Emitter`. - */ + // Only load hammer.js when in a browser environment + // (loading hammer.js in a node.js environment gives errors) + if (typeof window !== 'undefined') { + module.exports = window['Hammer'] || __webpack_require__(20); + } + else { + module.exports = function () { + throw Error('hammer.js is only available in a browser, not in node.js.'); + } + } - module.exports = Emitter; - /** - * Initialize a new `Emitter`. +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 + * http://eightmedia.github.io/hammer.js * - * @api public - */ + * Copyright (c) 2014 Jorik Tangelder ; + * Licensed under the MIT license */ - function Emitter(obj) { - if (obj) return mixin(obj); - }; + (function(window, undefined) { + 'use strict'; /** - * Mixin the emitter properties. + * @main + * @module hammer * - * @param {Object} obj - * @return {Object} - * @api private + * @class Hammer + * @static */ - function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; - } - /** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.on = - Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks[event] = this._callbacks[event] || []) - .push(fn); - return this; - }; - - /** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; - - function on() { - self.off(event, on); - fn.apply(this, arguments); - } - - on.fn = fn; - this.on(event, on); - return this; - }; - - /** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - - Emitter.prototype.off = - Emitter.prototype.removeListener = - Emitter.prototype.removeAllListeners = - Emitter.prototype.removeEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } - - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; - - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; - } - - // remove specific handler - var cb; - for (var i = 0; i < callbacks.length; i++) { - cb = callbacks[i]; - if (cb === fn || cb.fn === fn) { - callbacks.splice(i, 1); - break; - } - } - return this; - }; - - /** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ - - Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks[event]; - - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } - - return this; - }; - - /** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ - - Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks[event] || []; - }; - - /** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ - - Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; - }; - - -/***/ }, -/* 19 */ -/***/ function(module, exports, __webpack_require__) { - - // Only load hammer.js when in a browser environment - // (loading hammer.js in a node.js environment gives errors) - if (typeof window !== 'undefined') { - module.exports = window['Hammer'] || __webpack_require__(20); - } - else { - module.exports = function () { - throw Error('hammer.js is only available in a browser, not in node.js.'); - } - } - - -/***/ }, -/* 20 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 - * http://eightmedia.github.io/hammer.js - * - * Copyright (c) 2014 Jorik Tangelder ; - * Licensed under the MIT license */ - - (function(window, undefined) { - 'use strict'; - - /** - * @main - * @module hammer - * - * @class Hammer - * @static - */ - - /** - * Hammer, use this to create instances - * ```` - * var hammertime = new Hammer(myElement); - * ```` + * Hammer, use this to create instances + * ```` + * var hammertime = new Hammer(myElement); + * ```` * * @method Hammer * @param {HTMLElement} element @@ -13289,7 +13289,7 @@ return /******/ (function(modules) { // webpackBootstrap /* 25 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(18); + var Emitter = __webpack_require__(11); var Hammer = __webpack_require__(19); var util = __webpack_require__(1); var DataSet = __webpack_require__(7); @@ -17675,7 +17675,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { var keycharm = __webpack_require__(36); - var Emitter = __webpack_require__(18); + var Emitter = __webpack_require__(11); var Hammer = __webpack_require__(19); var util = __webpack_require__(1); @@ -19383,7 +19383,7 @@ return /******/ (function(modules) { // webpackBootstrap /* 42 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(18); + var Emitter = __webpack_require__(11); var Hammer = __webpack_require__(19); var util = __webpack_require__(1); var DataSet = __webpack_require__(7); @@ -19638,7 +19638,7 @@ return /******/ (function(modules) { // webpackBootstrap var DataSet = __webpack_require__(7); var DataView = __webpack_require__(9); var Component = __webpack_require__(23); - var DataAxis = __webpack_require__(44); + var DataAxis = __webpack_require__(45); var GraphGroup = __webpack_require__(46); var Legend = __webpack_require__(50); var BarGraphFunctions = __webpack_require__(49); @@ -19778,7 +19778,7 @@ return /******/ (function(modules) { // webpackBootstrap this.COUNTER = 0; this.body.emitter.on('rangechanged', function() { me.lastStart = me.body.range.start; - me.svg.style.left = util.option.asSize(-me.width); + me.svg.style.left = util.option.asSize(-me.props.width); me.redraw.call(me,true); }); @@ -19827,7 +19827,7 @@ return /******/ (function(modules) { // webpackBootstrap */ LineGraph.prototype.setOptions = function(options) { if (options) { - var fields = ['sampling','defaultGroup','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; + var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { this.autoSizeSVG = true; } @@ -20178,44 +20178,61 @@ return /******/ (function(modules) { // webpackBootstrap LineGraph.prototype.redraw = function(forceGraphUpdate) { var resized = false; - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; - if (this.lastWidth === undefined && this.width || this.lastWidth != this.width) { - resized = true; + // calculate actual size and position + this.props.width = this.dom.frame.offsetWidth; + this.props.height = this.body.domProps.centerContainer.height; + + // update the graph if there is no lastWidth or with, used for the initial draw + if (this.lastWidth === undefined && this.props.width) { + forceGraphUpdate = true; } + // check if this component is resized resized = this._isResized() || resized; + // check whether zoomed (in that case we need to re-stack everything) var visibleInterval = this.body.range.end - this.body.range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth); // we get this from the range changed event + var zoomed = (visibleInterval != this.lastVisibleInterval); 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); + this.svg.style.width = util.option.asSize(3*this.props.width); + this.svg.style.left = util.option.asSize(-this.props.width); + if ((this.options.height + '').indexOf("%") != -1) { + this.autoSizeSVG = true; + } + } + + // update the height of the graph on each redraw of the graph. + if (this.autoSizeSVG == true) { + if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { + this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; + this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; + } + this.autoSizeSVG = false; + } + else { + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; } // zoomed is here to ensure that animations are shown correctly. - if (zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { - resized = resized || this._updateGraph(); + if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { + resized = this._updateGraph() || resized; } else { // move the whole svg while dragging if (this.lastStart != 0) { var offset = this.body.range.start - this.lastStart; var range = this.body.range.end - this.body.range.start; - if (this.width != 0) { - var rangePerPixelInv = this.width/range; + if (this.props.width != 0) { + var rangePerPixelInv = this.props.width/range; var xOffset = offset * rangePerPixelInv; - this.svg.style.left = (-this.width - xOffset) + 'px'; + this.svg.style.left = (-this.props.width - xOffset) + 'px'; } } - } this.legendLeft.redraw(); @@ -20232,22 +20249,13 @@ return /******/ (function(modules) { // webpackBootstrap LineGraph.prototype._updateGraph = function () { // reset the svg elements DOMutil.prepareElements(this.svgElements); - if (this.width != 0 && this.itemsData != null) { + if (this.props.width != 0 && this.itemsData != null) { var group, i; var preprocessedGroupData = {}; var processedGroupData = {}; var groupRanges = {}; var changeCalled = false; - // update the height of the graph on each redraw of the graph. - if (this.autoSizeSVG == true) { - if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { - this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; - this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; - } - this.autoSizeSVG = false; - } - // getting group Ids var groupIds = []; for (var groupId in this.groups) { @@ -20501,7 +20509,6 @@ return /******/ (function(modules) { // webpackBootstrap } changeCalled = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || changeCalled; changeCalled = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || changeCalled; - if (yAxisRightUsed == true && yAxisLeftUsed == true) { this.yAxisLeft.drawIcons = true; this.yAxisRight.drawIcons = true; @@ -20510,7 +20517,6 @@ return /******/ (function(modules) { // webpackBootstrap this.yAxisLeft.drawIcons = false; this.yAxisRight.drawIcons = false; } - this.yAxisRight.master = !yAxisLeftUsed; if (this.yAxisRight.master == false) { @@ -20579,7 +20585,7 @@ return /******/ (function(modules) { // webpackBootstrap var toScreen = this.body.util.toScreen; for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.width; + xValue = toScreen(datapoints[i].x) + this.props.width; yValue = datapoints[i].y; extractedData.push({x: xValue, y: yValue}); } @@ -20609,7 +20615,7 @@ return /******/ (function(modules) { // webpackBootstrap } for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.width; + xValue = toScreen(datapoints[i].x) + this.props.width; yValue = Math.round(axis.convertValue(datapoints[i].y)); extractedData.push({x: xValue, y: yValue}); } @@ -20627,50 +20633,331 @@ return /******/ (function(modules) { // webpackBootstrap /* 44 */ /***/ function(module, exports, __webpack_require__) { - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var Component = __webpack_require__(23); - var DataStep = __webpack_require__(45); - /** - * A horizontal time axis - * @param {Object} [options] See DataAxis.setOptions for the available - * options. - * @constructor DataAxis - * @extends Component - * @param body + * @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 DataAxis (body, options, svg, linegraphOptions) { - this.id = util.randomUUID(); - this.body = body; + function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { + // variables + this.current = 0; - this.defaultOptions = { - orientation: 'left', // supported: 'left', 'right' - showMinorLabels: true, - showMajorLabels: true, - showMinorLines: true, - showMajorLines: true, - icons: true, - majorLinesOffset: 7, - minorLinesOffset: 4, - labelOffsetX: 10, - labelOffsetY: 2, - iconWidth: 20, - width: '40px', - visible: true, - alignZeros: true, - customRange: { - left: {min:undefined, max:undefined}, - right: {min:undefined, max:undefined} - }, - title: { - left: {text:undefined}, - right: {text:undefined} - }, - format: { - left: {decimals: undefined}, - right: {decimals: undefined} - } + this.autoScale = true; + this.stepIndex = 0; + this.step = 1; + this.scale = 1; + + this.marginStart; + this.marginEnd; + this.deadSpace = 0; + + this.majorSteps = [1, 2, 5, 10]; + this.minorSteps = [0.25, 0.5, 1, 2]; + + this.alignZeros = alignZeros; + + this.setRange(start, end, minimumStep, containerHeight, customRange); + } + + + + /** + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Number} [start] The start date and time. + * @param {Number} [end] The end date and time. + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + */ + DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { + this._start = customRange.min === undefined ? start : customRange.min; + this._end = customRange.max === undefined ? end : customRange.max; + + if (this._start == this._end) { + this._start -= 0.75; + this._end += 1; + } + + if (this.autoScale == true) { + this.setMinimumStep(minimumStep, containerHeight); + } + + this.setFirst(customRange); + }; + + /** + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds + */ + DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { + // round to floor + var size = this._end - this._start; + var safeSize = size * 1.2; + var minimumStepValue = minimumStep * (safeSize / containerHeight); + var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); + + 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]; + }; + + + + /** + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date + */ + DataStep.prototype.setFirst = function(customRange) { + if (customRange === undefined) { + customRange = {}; + } + + var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; + var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; + + this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; + this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; + + // if we need to align the zero's we need to make sure that there is a zero to use. + if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { + this.marginEnd += this.marginEnd % this.step; + } + + this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; + this.marginRange = this.marginEnd - this.marginStart; + + + this.current = this.marginEnd; + }; + + 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; + } + } + + + /** + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date + */ + DataStep.prototype.hasNext = function () { + return (this.current >= this.marginStart); + }; + + /** + * Do the next step + */ + DataStep.prototype.next = function() { + var prev = this.current; + this.current -= this.step; + + // safety mechanism: if current time is still unchanged, move to the end + if (this.current == prev) { + this.current = this._end; + } + }; + + /** + * Do the next step + */ + DataStep.prototype.previous = function() { + this.current += this.step; + this.marginEnd += this.step; + this.marginRange = this.marginEnd - this.marginStart; + }; + + + + /** + * Get the current datetime + * @return {String} current The current date + */ + DataStep.prototype.getCurrent = function(decimals) { + // prevent round-off errors when close to zero + var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; + var toPrecision = '' + Number(current).toPrecision(5); + + // If decimals is specified, then limit or extend the string as required + if(decimals !== undefined && !isNaN(Number(decimals))) { + // If string includes exponent, then we need to add it to the end + var exp = ""; + var index = toPrecision.indexOf("e"); + if(index != -1) { + // Get the exponent + exp = toPrecision.slice(index); + // Remove the exponent in case we need to zero-extend + toPrecision = toPrecision.slice(0, index); + } + index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); + if(index === -1) { + // No decimal found - if we want decimals, then we need to add it + if(decimals !== 0) { + toPrecision += '.'; + } + // Calculate how long the string should be + index = toPrecision.length + decimals; + } + else if(decimals !== 0) { + // Calculate how long the string should be - accounting for the decimal place + index += decimals + 1; + } + if(index > toPrecision.length) { + // We need to add zeros! + for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { + toPrecision += '0'; + } + } + else { + // we need to remove characters + toPrecision = toPrecision.slice(0, index); + } + // Add the exponent if there is one + toPrecision += exp; + } + else { + if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { + // If no decimal is specified, and there are decimal places, remove trailing zeros + for (var i = toPrecision.length - 1; i > 0; i--) { + if (toPrecision[i] == "0") { + toPrecision = toPrecision.slice(0, i); + } + else if (toPrecision[i] == "." || toPrecision[i] == ",") { + toPrecision = toPrecision.slice(0, i); + break; + } + else { + break; + } + } + } + } + + return toPrecision; + }; + + + + /** + * 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 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); + }; + + module.exports = DataStep; + + +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(6); + var Component = __webpack_require__(23); + var DataStep = __webpack_require__(44); + + /** + * A horizontal time axis + * @param {Object} [options] See DataAxis.setOptions for the available + * options. + * @constructor DataAxis + * @extends Component + * @param body + */ + function DataAxis (body, options, svg, linegraphOptions) { + this.id = util.randomUUID(); + this.body = body; + + this.defaultOptions = { + orientation: 'left', // supported: 'left', 'right' + showMinorLabels: true, + showMajorLabels: true, + showMinorLines: true, + showMajorLines: true, + icons: true, + majorLinesOffset: 7, + minorLinesOffset: 4, + labelOffsetX: 10, + labelOffsetY: 2, + iconWidth: 20, + width: '40px', + visible: true, + alignZeros: true, + customRange: { + left: {min:undefined, max:undefined}, + right: {min:undefined, max:undefined} + }, + title: { + left: {text:undefined}, + right: {text:undefined} + }, + format: { + left: {decimals: undefined}, + right: {decimals: undefined} + } }; this.linegraphOptions = linegraphOptions; @@ -21126,403 +21413,131 @@ return /******/ (function(modules) { // webpackBootstrap label.innerHTML = text; if (orientation == 'left') { label.style.left = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "right"; - } - else { - label.style.right = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "left"; - } - - label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; - - text += ''; - - var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); - if (this.maxLabelSize < text.length * largestWidth) { - this.maxLabelSize = text.length * largestWidth; - } - }; - - /** - * Create a minor line for the axis at position y - * @param y - * @param orientation - * @param className - * @param offset - * @param width - */ - DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { - if (this.master == true) { - var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift(); - line.className = className; - line.innerHTML = ''; - - if (orientation == 'left') { - line.style.left = (this.width - offset) + 'px'; - } - else { - line.style.right = (this.width - offset) + 'px'; - } - - line.style.width = width + 'px'; - line.style.top = y + 'px'; - } - }; - - /** - * Create a title for the axis - * @private - * @param orientation - */ - DataAxis.prototype._redrawTitle = function (orientation) { - DOMutil.prepareElements(this.DOMelements.title); - - // Check if the title is defined for this axes - if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { - var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); - title.className = 'yAxis title ' + orientation; - title.innerHTML = this.options.title[orientation].text; - - // Add style - if provided - if (this.options.title[orientation].style !== undefined) { - util.addCssText(title, this.options.title[orientation].style); - } - - if (orientation == 'left') { - title.style.left = this.props.titleCharHeight + 'px'; - } - else { - title.style.right = this.props.titleCharHeight + 'px'; - } - - title.style.width = this.height + 'px'; - } - - // we need to clean up in case we did not use all elements. - DOMutil.cleanupElements(this.DOMelements.title); - }; - - - - - /** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. - * @private - */ - DataAxis.prototype._calculateCharSize = function () { - // determine the char width and height on the minor axis - if (!('minorCharHeight' in this.props)) { - var textMinor = document.createTextNode('0'); - var measureCharMinor = document.createElement('div'); - measureCharMinor.className = 'yAxis minor measure'; - measureCharMinor.appendChild(textMinor); - this.dom.frame.appendChild(measureCharMinor); - - this.props.minorCharHeight = measureCharMinor.clientHeight; - this.props.minorCharWidth = measureCharMinor.clientWidth; - - this.dom.frame.removeChild(measureCharMinor); - } - - if (!('majorCharHeight' in this.props)) { - var textMajor = document.createTextNode('0'); - var measureCharMajor = document.createElement('div'); - measureCharMajor.className = 'yAxis major measure'; - measureCharMajor.appendChild(textMajor); - this.dom.frame.appendChild(measureCharMajor); - - this.props.majorCharHeight = measureCharMajor.clientHeight; - this.props.majorCharWidth = measureCharMajor.clientWidth; - - this.dom.frame.removeChild(measureCharMajor); - } - - if (!('titleCharHeight' in this.props)) { - var textTitle = document.createTextNode('0'); - var measureCharTitle = document.createElement('div'); - measureCharTitle.className = 'yAxis title measure'; - measureCharTitle.appendChild(textTitle); - this.dom.frame.appendChild(measureCharTitle); - - this.props.titleCharHeight = measureCharTitle.clientHeight; - this.props.titleCharWidth = measureCharTitle.clientWidth; - - this.dom.frame.removeChild(measureCharTitle); - } - }; - - /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ - DataAxis.prototype.snap = function(date) { - return this.step.snap(date); - }; - - module.exports = DataAxis; - - -/***/ }, -/* 45 */ -/***/ function(module, exports, __webpack_require__) { - - /** - * @constructor DataStep - * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an - * end data point. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 - * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ - function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { - // variables - this.current = 0; - - this.autoScale = true; - this.stepIndex = 0; - this.step = 1; - this.scale = 1; - - this.marginStart; - this.marginEnd; - this.deadSpace = 0; - - this.majorSteps = [1, 2, 5, 10]; - this.minorSteps = [0.25, 0.5, 1, 2]; - - this.alignZeros = alignZeros; - - this.setRange(start, end, minimumStep, containerHeight, customRange); - } - - - - /** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Number} [start] The start date and time. - * @param {Number} [end] The end date and time. - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ - DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { - this._start = customRange.min === undefined ? start : customRange.min; - this._end = customRange.max === undefined ? end : customRange.max; - - if (this._start == this._end) { - this._start -= 0.75; - this._end += 1; + label.style.textAlign = "right"; } - - if (this.autoScale == true) { - this.setMinimumStep(minimumStep, containerHeight); + else { + label.style.right = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "left"; } - this.setFirst(customRange); + 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; + } }; /** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds + * Create a minor line for the axis at position y + * @param y + * @param orientation + * @param className + * @param offset + * @param width */ - DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { - // round to floor - var size = this._end - this._start; - var safeSize = size * 1.2; - var minimumStepValue = minimumStep * (safeSize / containerHeight); - var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); - - var minorStepIdx = -1; - var magnitudefactor = Math.pow(10,orderOfMagnitude); - - var start = 0; - if (orderOfMagnitude < 0) { - start = orderOfMagnitude; - } + DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { + if (this.master == true) { + var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift(); + line.className = className; + line.innerHTML = ''; - 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 (orientation == 'left') { + line.style.left = (this.width - offset) + 'px'; } - if (solutionFound == true) { - break; + else { + line.style.right = (this.width - offset) + 'px'; } + + line.style.width = width + 'px'; + line.style.top = y + 'px'; } - this.stepIndex = minorStepIdx; - this.scale = magnitudefactor; - this.step = magnitudefactor * this.minorSteps[minorStepIdx]; }; - - /** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date + * Create a title for the axis + * @private + * @param orientation */ - DataStep.prototype.setFirst = function(customRange) { - if (customRange === undefined) { - customRange = {}; - } - - var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; - var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; + DataAxis.prototype._redrawTitle = function (orientation) { + DOMutil.prepareElements(this.DOMelements.title); - this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; - this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; + // Check if the title is defined for this axes + if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { + var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); + title.className = 'yAxis title ' + orientation; + title.innerHTML = this.options.title[orientation].text; - // if we need to align the zero's we need to make sure that there is a zero to use. - if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { - this.marginEnd += this.marginEnd % this.step; - } + // Add style - if provided + if (this.options.title[orientation].style !== undefined) { + util.addCssText(title, this.options.title[orientation].style); + } - this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; - this.marginRange = this.marginEnd - this.marginStart; + if (orientation == 'left') { + title.style.left = this.props.titleCharHeight + 'px'; + } + else { + title.style.right = this.props.titleCharHeight + 'px'; + } + title.style.width = this.height + 'px'; + } - this.current = this.marginEnd; + // we need to clean up in case we did not use all elements. + DOMutil.cleanupElements(this.DOMelements.title); }; - 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; - } - } - /** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date - */ - DataStep.prototype.hasNext = function () { - return (this.current >= this.marginStart); - }; /** - * Do the next step + * 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 */ - DataStep.prototype.next = function() { - var prev = this.current; - this.current -= this.step; - - // safety mechanism: if current time is still unchanged, move to the end - if (this.current == prev) { - this.current = this._end; - } - }; + DataAxis.prototype._calculateCharSize = function () { + // determine the char width and height on the minor axis + if (!('minorCharHeight' in this.props)) { + var textMinor = document.createTextNode('0'); + var measureCharMinor = document.createElement('div'); + measureCharMinor.className = 'yAxis minor measure'; + measureCharMinor.appendChild(textMinor); + this.dom.frame.appendChild(measureCharMinor); - /** - * Do the next step - */ - DataStep.prototype.previous = function() { - this.current += this.step; - this.marginEnd += this.step; - this.marginRange = this.marginEnd - this.marginStart; - }; + this.props.minorCharHeight = measureCharMinor.clientHeight; + this.props.minorCharWidth = measureCharMinor.clientWidth; + this.dom.frame.removeChild(measureCharMinor); + } + if (!('majorCharHeight' in this.props)) { + var textMajor = document.createTextNode('0'); + var measureCharMajor = document.createElement('div'); + measureCharMajor.className = 'yAxis major measure'; + measureCharMajor.appendChild(textMajor); + this.dom.frame.appendChild(measureCharMajor); - /** - * Get the current datetime - * @return {String} current The current date - */ - DataStep.prototype.getCurrent = function(decimals) { - // prevent round-off errors when close to zero - var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; - var toPrecision = '' + Number(current).toPrecision(5); + this.props.majorCharHeight = measureCharMajor.clientHeight; + this.props.majorCharWidth = measureCharMajor.clientWidth; - // If decimals is specified, then limit or extend the string as required - if(decimals !== undefined && !isNaN(Number(decimals))) { - // If string includes exponent, then we need to add it to the end - var exp = ""; - var index = toPrecision.indexOf("e"); - if(index != -1) { - // Get the exponent - exp = toPrecision.slice(index); - // Remove the exponent in case we need to zero-extend - toPrecision = toPrecision.slice(0, index); - } - index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); - if(index === -1) { - // No decimal found - if we want decimals, then we need to add it - if(decimals !== 0) { - toPrecision += '.'; - } - // Calculate how long the string should be - index = toPrecision.length + decimals; - } - else if(decimals !== 0) { - // Calculate how long the string should be - accounting for the decimal place - index += decimals + 1; - } - if(index > toPrecision.length) { - // We need to add zeros! - for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { - toPrecision += '0'; - } - } - else { - // we need to remove characters - toPrecision = toPrecision.slice(0, index); - } - // Add the exponent if there is one - toPrecision += exp; - } - else { - if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { - // If no decimal is specified, and there are decimal places, remove trailing zeros - for (var i = toPrecision.length - 1; i > 0; i--) { - if (toPrecision[i] == "0") { - toPrecision = toPrecision.slice(0, i); - } - else if (toPrecision[i] == "." || toPrecision[i] == ",") { - toPrecision = toPrecision.slice(0, i); - break; - } - else { - break; - } - } - } + this.dom.frame.removeChild(measureCharMajor); } - return toPrecision; - }; + if (!('titleCharHeight' in this.props)) { + var textTitle = document.createTextNode('0'); + var measureCharTitle = document.createElement('div'); + measureCharTitle.className = 'yAxis title measure'; + measureCharTitle.appendChild(textTitle); + this.dom.frame.appendChild(measureCharTitle); + this.props.titleCharHeight = measureCharTitle.clientHeight; + this.props.titleCharWidth = measureCharTitle.clientWidth; + this.dom.frame.removeChild(measureCharTitle); + } + }; /** * Snap a date to a rounded value. @@ -21530,20 +21545,11 @@ return /******/ (function(modules) { // webpackBootstrap * @param {Date} date the date to be snapped. * @return {Date} snappedDate */ - DataStep.prototype.snap = function(date) { - - }; - - /** - * 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); + DataAxis.prototype.snap = function(date) { + return this.step.snap(date); }; - module.exports = DataStep; + module.exports = DataAxis; /***/ }, @@ -22471,7 +22477,7 @@ return /******/ (function(modules) { // webpackBootstrap /* 51 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(18); + var Emitter = __webpack_require__(11); var Hammer = __webpack_require__(19); var keycharm = __webpack_require__(36); var util = __webpack_require__(1); diff --git a/lib/timeline/component/LineGraph.js b/lib/timeline/component/LineGraph.js index 19d6f421..b60bf616 100644 --- a/lib/timeline/component/LineGraph.js +++ b/lib/timeline/component/LineGraph.js @@ -143,7 +143,7 @@ function LineGraph(body, options) { this.COUNTER = 0; this.body.emitter.on('rangechanged', function() { me.lastStart = me.body.range.start; - me.svg.style.left = util.option.asSize(-me.width); + me.svg.style.left = util.option.asSize(-me.props.width); me.redraw.call(me,true); }); @@ -192,7 +192,7 @@ LineGraph.prototype._create = function(){ */ LineGraph.prototype.setOptions = function(options) { if (options) { - var fields = ['sampling','defaultGroup','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; + var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { this.autoSizeSVG = true; } @@ -543,44 +543,61 @@ LineGraph.prototype._updateUngrouped = function() { LineGraph.prototype.redraw = function(forceGraphUpdate) { var resized = false; - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; - if (this.lastWidth === undefined && this.width || this.lastWidth != this.width) { - resized = true; + // calculate actual size and position + this.props.width = this.dom.frame.offsetWidth; + this.props.height = this.body.domProps.centerContainer.height; + + // update the graph if there is no lastWidth or with, used for the initial draw + if (this.lastWidth === undefined && this.props.width) { + forceGraphUpdate = true; } + // check if this component is resized resized = this._isResized() || resized; + // check whether zoomed (in that case we need to re-stack everything) var visibleInterval = this.body.range.end - this.body.range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth); // we get this from the range changed event + var zoomed = (visibleInterval != this.lastVisibleInterval); 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); + this.svg.style.width = util.option.asSize(3*this.props.width); + this.svg.style.left = util.option.asSize(-this.props.width); + if ((this.options.height + '').indexOf("%") != -1) { + this.autoSizeSVG = true; + } + } + + // update the height of the graph on each redraw of the graph. + if (this.autoSizeSVG == true) { + if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { + this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; + this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; + } + this.autoSizeSVG = false; + } + else { + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; } // zoomed is here to ensure that animations are shown correctly. - if (zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { - resized = resized || this._updateGraph(); + if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { + resized = this._updateGraph() || resized; } else { // move the whole svg while dragging if (this.lastStart != 0) { var offset = this.body.range.start - this.lastStart; var range = this.body.range.end - this.body.range.start; - if (this.width != 0) { - var rangePerPixelInv = this.width/range; + if (this.props.width != 0) { + var rangePerPixelInv = this.props.width/range; var xOffset = offset * rangePerPixelInv; - this.svg.style.left = (-this.width - xOffset) + 'px'; + this.svg.style.left = (-this.props.width - xOffset) + 'px'; } } - } this.legendLeft.redraw(); @@ -597,22 +614,13 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) { LineGraph.prototype._updateGraph = function () { // reset the svg elements DOMutil.prepareElements(this.svgElements); - if (this.width != 0 && this.itemsData != null) { + if (this.props.width != 0 && this.itemsData != null) { var group, i; var preprocessedGroupData = {}; var processedGroupData = {}; var groupRanges = {}; var changeCalled = false; - // update the height of the graph on each redraw of the graph. - if (this.autoSizeSVG == true) { - if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { - this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; - this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; - } - this.autoSizeSVG = false; - } - // getting group Ids var groupIds = []; for (var groupId in this.groups) { @@ -866,7 +874,6 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { } changeCalled = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || changeCalled; changeCalled = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || changeCalled; - if (yAxisRightUsed == true && yAxisLeftUsed == true) { this.yAxisLeft.drawIcons = true; this.yAxisRight.drawIcons = true; @@ -875,7 +882,6 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { this.yAxisLeft.drawIcons = false; this.yAxisRight.drawIcons = false; } - this.yAxisRight.master = !yAxisLeftUsed; if (this.yAxisRight.master == false) { @@ -944,7 +950,7 @@ LineGraph.prototype._convertXcoordinates = function (datapoints) { var toScreen = this.body.util.toScreen; for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.width; + xValue = toScreen(datapoints[i].x) + this.props.width; yValue = datapoints[i].y; extractedData.push({x: xValue, y: yValue}); } @@ -974,7 +980,7 @@ LineGraph.prototype._convertYcoordinates = function (datapoints, group) { } for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.width; + xValue = toScreen(datapoints[i].x) + this.props.width; yValue = Math.round(axis.convertValue(datapoints[i].y)); extractedData.push({x: xValue, y: yValue}); }