} path\n * @param {{}} optionsObj\n * @returns {{}}\n * @private\n */\n _constructOptions(value, path, optionsObj = {}) {\n let pointer = optionsObj;\n\n // when dropdown boxes can be string or boolean, we typecast it into correct types\n value = value === 'true' ? true : value;\n value = value === 'false' ? false : value;\n\n for (let i = 0; i < path.length; i++) {\n if (path[i] !== 'global') {\n if (pointer[path[i]] === undefined) {\n pointer[path[i]] = {};\n }\n if (i !== path.length - 1) {\n pointer = pointer[path[i]];\n }\n else {\n pointer[path[i]] = value;\n }\n }\n }\n return optionsObj;\n }\n\n /**\n * @private\n */\n _printOptions() {\n let options = this.getOptions();\n this.optionsContainer.innerHTML = 'var options = ' + JSON.stringify(options, null, 2) + '
';\n }\n\n /**\n *\n * @returns {{}} options\n */\n getOptions() {\n let options = {};\n for (var i = 0; i < this.changedOptions.length; i++) {\n this._constructOptions(this.changedOptions[i].value, this.changedOptions[i].path, options)\n }\n return options;\n }\n}\n\n\nexport default Configurator;","var moment = require('../module/moment');\nvar util = require('vis-util');\nvar { DataSet } = require('vis-data');\nvar { DataView } = require('vis-data');\nvar Range = require('./Range');\nvar Core = require('./Core');\nvar TimeAxis = require('./component/TimeAxis');\nvar CurrentTime = require('./component/CurrentTime');\nvar CustomTime = require('./component/CustomTime');\nvar ItemSet = require('./component/ItemSet');\n\nvar Validator = require('../shared/Validator').Validator;\nvar printStyle = require('../shared/Validator').printStyle;\nvar allOptions = require('./optionsTimeline').allOptions;\nvar configureOptions = require('./optionsTimeline').configureOptions;\n\nvar Configurator = require('../shared/Configurator').default;\n\n\n/**\n * Create a timeline visualization\n * @param {HTMLElement} container\n * @param {vis.DataSet | vis.DataView | Array} [items]\n * @param {vis.DataSet | vis.DataView | Array} [groups]\n * @param {Object} [options] See Timeline.setOptions for the available options.\n * @constructor Timeline\n * @extends Core\n */\nfunction Timeline (container, items, groups, options) {\n\n this.initTime = new Date();\n this.itemsDone = false;\n\n if (!(this instanceof Timeline)) {\n throw new SyntaxError('Constructor must be called with the new operator');\n }\n\n // if the third element is options, the forth is groups (optionally);\n if (!(Array.isArray(groups) || groups instanceof DataSet || groups instanceof DataView) && groups instanceof Object) {\n var forthArgument = options;\n options = groups;\n groups = forthArgument;\n }\n\n // TODO: REMOVE THIS in the next MAJOR release\n // see https://github.com/almende/vis/issues/2511\n if (options && options.throttleRedraw) {\n console.warn(\"Timeline option \\\"throttleRedraw\\\" is DEPRICATED and no longer supported. It will be removed in the next MAJOR release.\");\n }\n\n var me = this;\n this.defaultOptions = {\n start: null,\n end: null,\n autoResize: true,\n orientation: {\n axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'\n item: 'bottom' // not relevant\n },\n moment: moment,\n width: null,\n height: null,\n maxHeight: null,\n minHeight: null,\n };\n this.options = util.deepExtend({}, this.defaultOptions);\n\n // Create the DOM, props, and emitter\n this._create(container);\n if (!options || (options && typeof options.rtl == \"undefined\")) {\n this.dom.root.style.visibility = 'hidden';\n var directionFromDom, domNode = this.dom.root;\n while (!directionFromDom && domNode) {\n directionFromDom = window.getComputedStyle(domNode, null).direction;\n domNode = domNode.parentElement;\n }\n this.options.rtl = (directionFromDom && (directionFromDom.toLowerCase() == \"rtl\"));\n } else {\n this.options.rtl = options.rtl;\n }\n\n this.options.rollingMode = options && options.rollingMode;\n this.options.onInitialDrawComplete = options && options.onInitialDrawComplete;\n this.options.onTimeout = options && options.onTimeout;\n this.options.loadingScreenTemplate = options && options.loadingScreenTemplate;\n\n // Prepare loading screen\n var loadingScreenFragment = document.createElement('div');\n if (this.options.loadingScreenTemplate) {\n var templateFunction = this.options.loadingScreenTemplate.bind(this);\n var loadingScreen = templateFunction(this.dom.loadingScreen);\n if ((loadingScreen instanceof Object) && !(loadingScreen instanceof Element)) {\n templateFunction(loadingScreenFragment)\n } else {\n if (loadingScreen instanceof Element) {\n loadingScreenFragment.innerHTML = '';\n loadingScreenFragment.appendChild(loadingScreen);\n }\n else if (loadingScreen != undefined) {\n loadingScreenFragment.innerHTML = loadingScreen;\n }\n }\n }\n this.dom.loadingScreen.appendChild(loadingScreenFragment);\n\n // all components listed here will be repainted automatically\n this.components = [];\n\n this.body = {\n dom: this.dom,\n domProps: this.props,\n emitter: {\n on: this.on.bind(this),\n off: this.off.bind(this),\n emit: this.emit.bind(this)\n },\n hiddenDates: [],\n util: {\n getScale: function () {\n return me.timeAxis.step.scale;\n },\n getStep: function () {\n return me.timeAxis.step.step;\n },\n\n toScreen: me._toScreen.bind(me),\n toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width\n toTime: me._toTime.bind(me),\n toGlobalTime : me._toGlobalTime.bind(me)\n }\n };\n\n // range\n this.range = new Range(this.body, this.options);\n this.components.push(this.range);\n this.body.range = this.range;\n\n // time axis\n this.timeAxis = new TimeAxis(this.body, this.options);\n this.timeAxis2 = null; // used in case of orientation option 'both'\n this.components.push(this.timeAxis);\n\n // current time bar\n this.currentTime = new CurrentTime(this.body, this.options);\n this.components.push(this.currentTime);\n\n // item set\n this.itemSet = new ItemSet(this.body, this.options);\n this.components.push(this.itemSet);\n\n this.itemsData = null; // DataSet\n this.groupsData = null; // DataSet\n\n this.dom.root.onclick = function (event) {\n me.emit('click', me.getEventProperties(event))\n };\n this.dom.root.ondblclick = function (event) {\n me.emit('doubleClick', me.getEventProperties(event))\n };\n this.dom.root.oncontextmenu = function (event) {\n me.emit('contextmenu', me.getEventProperties(event))\n };\n this.dom.root.onmouseover = function (event) {\n me.emit('mouseOver', me.getEventProperties(event))\n };\n if(window.PointerEvent) {\n this.dom.root.onpointerdown = function (event) {\n me.emit('mouseDown', me.getEventProperties(event))\n };\n this.dom.root.onpointermove = function (event) {\n me.emit('mouseMove', me.getEventProperties(event))\n };\n this.dom.root.onpointerup = function (event) {\n me.emit('mouseUp', me.getEventProperties(event))\n };\n } else {\n this.dom.root.onmousemove = function (event) {\n me.emit('mouseMove', me.getEventProperties(event))\n };\n this.dom.root.onmousedown = function (event) {\n me.emit('mouseDown', me.getEventProperties(event))\n };\n this.dom.root.onmouseup = function (event) {\n me.emit('mouseUp', me.getEventProperties(event))\n };\n }\n\n //Single time autoscale/fit\n this.initialFitDone = false;\n this.on('changed', function (){\n if (me.itemsData == null) return;\n if (!me.initialFitDone && !me.options.rollingMode) {\n me.initialFitDone = true;\n if (me.options.start != undefined || me.options.end != undefined) {\n if (me.options.start == undefined || me.options.end == undefined) {\n var range = me.getItemRange();\n }\n\n var start = me.options.start != undefined ? me.options.start : range.min;\n var end = me.options.end != undefined ? me.options.end : range.max;\n me.setWindow(start, end, {animation: false});\n } else {\n me.fit({animation: false});\n }\n }\n\n if (!me.initialDrawDone && (me.initialRangeChangeDone || (!me.options.start && !me.options.end) \n || me.options.rollingMode)) {\n me.initialDrawDone = true;\n me.itemSet.initialDrawDone = true;\n me.dom.root.style.visibility = 'visible';\n me.dom.loadingScreen.parentNode.removeChild(me.dom.loadingScreen);\n if (me.options.onInitialDrawComplete) {\n setTimeout(() => {\n return me.options.onInitialDrawComplete();\n }, 0)\n }\n }\n });\n\n this.on('destroyTimeline', () => {\n me.destroy()\n });\n\n // apply options\n if (options) {\n this.setOptions(options);\n }\n\n // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!\n if (groups) {\n this.setGroups(groups);\n }\n\n // create itemset\n if (items) {\n this.setItems(items);\n }\n\n // draw for the first time\n this._redraw();\n}\n\n// Extend the functionality from Core\nTimeline.prototype = new Core();\n\n/**\n * Load a configurator\n * @return {Object}\n * @private\n */\nTimeline.prototype._createConfigurator = function () {\n return new Configurator(this, this.dom.container, configureOptions);\n};\n\n/**\n * Force a redraw. The size of all items will be recalculated.\n * Can be useful to manually redraw when option autoResize=false and the window\n * has been resized, or when the items CSS has been changed.\n *\n * Note: this function will be overridden on construction with a trottled version\n */\nTimeline.prototype.redraw = function() {\n this.itemSet && this.itemSet.markDirty({refreshItems: true});\n this._redraw();\n};\n\nTimeline.prototype.setOptions = function (options) {\n // validate options\n let errorFound = Validator.validate(options, allOptions);\n\n if (errorFound === true) {\n console.log('%cErrors have been found in the supplied options object.', printStyle);\n }\n Core.prototype.setOptions.call(this, options);\n\n if ('type' in options) {\n if (options.type !== this.options.type) {\n this.options.type = options.type;\n\n // force recreation of all items\n var itemsData = this.itemsData;\n if (itemsData) {\n var selection = this.getSelection();\n this.setItems(null); // remove all\n this.setItems(itemsData); // add all\n this.setSelection(selection); // restore selection\n }\n }\n }\n};\n\n/**\n * Set items\n * @param {vis.DataSet | Array | null} items\n */\nTimeline.prototype.setItems = function(items) {\n this.itemsDone = false;\n \n // convert to type DataSet when needed\n var newDataSet;\n if (!items) {\n newDataSet = null;\n }\n else if (items instanceof DataSet || items instanceof DataView) {\n newDataSet = items;\n }\n else {\n // turn an array into a dataset\n newDataSet = new DataSet(items, {\n type: {\n start: 'Date',\n end: 'Date'\n }\n });\n }\n\n // set items\n this.itemsData = newDataSet;\n this.itemSet && this.itemSet.setItems(newDataSet);\n};\n\n/**\n * Set groups\n * @param {vis.DataSet | Array} groups\n */\nTimeline.prototype.setGroups = function(groups) {\n // convert to type DataSet when needed\n var newDataSet;\n if (!groups) {\n newDataSet = null;\n }\n else {\n var filter = function(group) {\n return group.visible !== false;\n }\n if (groups instanceof DataSet || groups instanceof DataView) {\n newDataSet = new DataView(groups,{filter: filter});\n }\n else {\n // turn an array into a dataset\n newDataSet = new DataSet(groups.filter(filter));\n }\n }\n\n\n this.groupsData = newDataSet;\n this.itemSet.setGroups(newDataSet);\n};\n\n/**\n * Set both items and groups in one go\n * @param {{items: (Array | vis.DataSet), groups: (Array | vis.DataSet)}} data\n */\nTimeline.prototype.setData = function (data) {\n if (data && data.groups) {\n this.setGroups(data.groups);\n }\n\n if (data && data.items) {\n this.setItems(data.items);\n }\n};\n\n/**\n * Set selected items by their id. Replaces the current selection\n * Unknown id's are silently ignored.\n * @param {string[] | string} [ids] An array with zero or more id's of the items to be\n * selected. If ids is an empty array, all items will be\n * unselected.\n * @param {Object} [options] Available options:\n * `focus: boolean`\n * If true, focus will be set to the selected item(s)\n * `animation: boolean | {duration: number, easingFunction: string}`\n * If true (default), the range is animated\n * smoothly to the new window. An object can be\n * provided to specify duration and easing function.\n * Default duration is 500 ms, and default easing\n * function is 'easeInOutQuad'.\n * Only applicable when option focus is true.\n */\nTimeline.prototype.setSelection = function(ids, options) {\n this.itemSet && this.itemSet.setSelection(ids);\n\n if (options && options.focus) {\n this.focus(ids, options);\n }\n};\n\n/**\n * Get the selected items by their id\n * @return {Array} ids The ids of the selected items\n */\nTimeline.prototype.getSelection = function() {\n return this.itemSet && this.itemSet.getSelection() || [];\n};\n\n/**\n * Adjust the visible window such that the selected item (or multiple items)\n * are centered on screen.\n * @param {string | String[]} id An item id or array with item ids\n * @param {Object} [options] Available options:\n * `animation: boolean | {duration: number, easingFunction: string}`\n * If true (default), the range is animated\n * smoothly to the new window. An object can be\n * provided to specify duration and easing function.\n * Default duration is 500 ms, and default easing\n * function is 'easeInOutQuad'.\n */\nTimeline.prototype.focus = function(id, options) {\n if (!this.itemsData || id == undefined) return;\n\n var ids = Array.isArray(id) ? id : [id];\n\n // get the specified item(s)\n var itemsData = this.itemsData.getDataSet().get(ids, {\n type: {\n start: 'Date',\n end: 'Date'\n }\n });\n\n // calculate minimum start and maximum end of specified items\n var start = null;\n var end = null;\n itemsData.forEach(function (itemData) {\n var s = itemData.start.valueOf();\n var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf();\n\n if (start === null || s < start) {\n start = s;\n }\n\n if (end === null || e > end) {\n end = e;\n }\n });\n\n\n if (start !== null && end !== null) {\n var me = this;\n // Use the first item for the vertical focus\n var item = this.itemSet.items[ids[0]];\n var startPos = this._getScrollTop() * -1;\n var initialVerticalScroll = null;\n\n // Setup a handler for each frame of the vertical scroll\n var verticalAnimationFrame = function(ease, willDraw, done) {\n var verticalScroll = getItemVerticalScroll(me, item);\n\n if (verticalScroll === false) {\n return; // We don't need to scroll, so do nothing\n }\n\n if(!initialVerticalScroll) {\n initialVerticalScroll = verticalScroll;\n }\n\n if(initialVerticalScroll.itemTop == verticalScroll.itemTop && !initialVerticalScroll.shouldScroll) {\n return; // We don't need to scroll, so do nothing\n }\n else if(initialVerticalScroll.itemTop != verticalScroll.itemTop && verticalScroll.shouldScroll) {\n // The redraw shifted elements, so reset the animation to correct\n initialVerticalScroll = verticalScroll;\n startPos = me._getScrollTop() * -1;\n } \n\n var from = startPos;\n var to = initialVerticalScroll.scrollOffset;\n var scrollTop = done ? to : (from + (to - from) * ease);\n\n me._setScrollTop(-scrollTop);\n\n if(!willDraw) {\n me._redraw();\n }\n };\n\n // Enforces the final vertical scroll position\n var setFinalVerticalPosition = function() {\n var finalVerticalScroll = getItemVerticalScroll(me, item);\n\n if (finalVerticalScroll.shouldScroll && finalVerticalScroll.itemTop != initialVerticalScroll.itemTop) {\n me._setScrollTop(-finalVerticalScroll.scrollOffset);\n me._redraw();\n }\n };\n\n // Perform one last check at the end to make sure the final vertical\n // position is correct\n var finalVerticalCallback = function() {\n // Double check we ended at the proper scroll position\n setFinalVerticalPosition();\n\n // Let the redraw settle and finalize the position. \n setTimeout(setFinalVerticalPosition, 100);\n };\n\n // calculate the new middle and interval for the window\n var middle = (start + end) / 2;\n var interval = Math.max(this.range.end - this.range.start, (end - start) * 1.1);\n\n var animation = options && options.animation !== undefined ? options.animation : true;\n\n if (!animation) {\n // We aren't animating so set a default so that the final callback forces the vertical location\n initialVerticalScroll = { shouldScroll: false, scrollOffset: -1, itemTop: -1 };\n }\n\n this.range.setRange(middle - interval / 2, middle + interval / 2, { animation: animation }, finalVerticalCallback, verticalAnimationFrame); \n }\n};\n\n/**\n * Set Timeline window such that it fits all items\n * @param {Object} [options] Available options:\n * `animation: boolean | {duration: number, easingFunction: string}`\n * If true (default), the range is animated\n * smoothly to the new window. An object can be\n * provided to specify duration and easing function.\n * Default duration is 500 ms, and default easing\n * function is 'easeInOutQuad'.\n * @param {function} [callback]\n */\nTimeline.prototype.fit = function (options, callback) {\n var animation = (options && options.animation !== undefined) ? options.animation : true;\n var range;\n\n var dataset = this.itemsData && this.itemsData.getDataSet();\n if (dataset.length === 1 && dataset.get()[0].end === undefined) {\n // a single item -> don't fit, just show a range around the item from -4 to +3 days\n range = this.getDataRange();\n this.moveTo(range.min.valueOf(), {animation}, callback);\n }\n else {\n // exactly fit the items (plus a small margin)\n range = this.getItemRange();\n this.range.setRange(range.min, range.max, { animation: animation }, callback);\n }\n};\n\n/**\n *\n * @param {vis.Item} item\n * @returns {number}\n */\nfunction getStart(item) {\n return util.convert(item.data.start, 'Date').valueOf()\n}\n\n/**\n *\n * @param {vis.Item} item\n * @returns {number}\n */\nfunction getEnd(item) {\n var end = item.data.end != undefined ? item.data.end : item.data.start;\n return util.convert(end, 'Date').valueOf();\n}\n\n/**\n * @param {vis.Timeline} timeline\n * @param {vis.Item} item\n * @return {{shouldScroll: bool, scrollOffset: number, itemTop: number}}\n */\nfunction getItemVerticalScroll(timeline, item) {\n if (!item.parent) {\n // The item no longer exists, so ignore this focus.\n return false;\n }\n\n var leftHeight = timeline.props.leftContainer.height;\n var contentHeight = timeline.props.left.height;\n \n var group = item.parent;\n var offset = group.top;\n var shouldScroll = true;\n var orientation = timeline.timeAxis.options.orientation.axis;\n \n var itemTop = function () {\n if (orientation == \"bottom\") {\n return group.height - item.top - item.height;\n }\n else {\n return item.top;\n }\n };\n\n var currentScrollHeight = timeline._getScrollTop() * -1;\n var targetOffset = offset + itemTop();\n var height = item.height;\n\n if (targetOffset < currentScrollHeight) {\n if (offset + leftHeight <= offset + itemTop() + height) {\n offset += itemTop() - timeline.itemSet.options.margin.item.vertical;\n }\n }\n else if (targetOffset + height > currentScrollHeight + leftHeight) {\n offset += itemTop() + height - leftHeight + timeline.itemSet.options.margin.item.vertical;\n }\n else {\n shouldScroll = false;\n }\n\n offset = Math.min(offset, contentHeight - leftHeight);\n\n return { shouldScroll: shouldScroll, scrollOffset: offset, itemTop: targetOffset };\n}\n\n/**\n * Determine the range of the items, taking into account their actual width\n * and a margin of 10 pixels on both sides.\n *\n * @returns {{min: Date, max: Date}}\n */\nTimeline.prototype.getItemRange = function () {\n // get a rough approximation for the range based on the items start and end dates\n var range = this.getDataRange();\n var min = range.min !== null ? range.min.valueOf() : null;\n var max = range.max !== null ? range.max.valueOf() : null;\n var minItem = null;\n var maxItem = null;\n\n if (min != null && max != null) {\n var interval = (max - min); // ms\n if (interval <= 0) {\n interval = 10;\n }\n var factor = interval / this.props.center.width;\n\n var redrawQueue = {};\n var redrawQueueLength = 0;\n\n // collect redraw functions\n util.forEach(this.itemSet.items, function (item, key) {\n if (item.groupShowing) {\n var returnQueue = true;\n redrawQueue[key] = item.redraw(returnQueue);\n redrawQueueLength = redrawQueue[key].length;\n }\n })\n\n var needRedraw = redrawQueueLength > 0;\n if (needRedraw) {\n // redraw all regular items\n for (var i = 0; i < redrawQueueLength; i++) {\n util.forEach(redrawQueue, function (fns) {\n fns[i]();\n });\n }\n }\n\n // calculate the date of the left side and right side of the items given\n util.forEach(this.itemSet.items, function (item) {\n var start = getStart(item);\n var end = getEnd(item);\n var startSide;\n var endSide;\n\n if (this.options.rtl) {\n startSide = start - (item.getWidthRight() + 10) * factor;\n endSide = end + (item.getWidthLeft() + 10) * factor;\n } else {\n startSide = start - (item.getWidthLeft() + 10) * factor;\n endSide = end + (item.getWidthRight() + 10) * factor;\n }\n\n if (startSide < min) {\n min = startSide;\n minItem = item;\n }\n if (endSide > max) {\n max = endSide;\n maxItem = item;\n }\n }.bind(this));\n\n if (minItem && maxItem) {\n var lhs = minItem.getWidthLeft() + 10;\n var rhs = maxItem.getWidthRight() + 10;\n var delta = this.props.center.width - lhs - rhs; // px\n\n if (delta > 0) {\n if (this.options.rtl) {\n min = getStart(minItem) - rhs * interval / delta; // ms\n max = getEnd(maxItem) + lhs * interval / delta; // ms\n } else {\n min = getStart(minItem) - lhs * interval / delta; // ms\n max = getEnd(maxItem) + rhs * interval / delta; // ms\n }\n }\n }\n }\n\n return {\n min: min != null ? new Date(min) : null,\n max: max != null ? new Date(max) : null\n }\n};\n\n/**\n * Calculate the data range of the items start and end dates\n * @returns {{min: Date, max: Date}}\n */\nTimeline.prototype.getDataRange = function() {\n var min = null;\n var max = null;\n\n var dataset = this.itemsData && this.itemsData.getDataSet();\n if (dataset) {\n dataset.forEach(function (item) {\n var start = util.convert(item.start, 'Date').valueOf();\n var end = util.convert(item.end != undefined ? item.end : item.start, 'Date').valueOf();\n if (min === null || start < min) {\n min = start;\n }\n if (max === null || end > max) {\n max = end;\n }\n });\n }\n\n return {\n min: min != null ? new Date(min) : null,\n max: max != null ? new Date(max) : null\n }\n};\n\n/**\n * Generate Timeline related information from an event\n * @param {Event} event\n * @return {Object} An object with related information, like on which area\n * The event happened, whether clicked on an item, etc.\n */\nTimeline.prototype.getEventProperties = function (event) {\n var clientX = event.center ? event.center.x : event.clientX;\n var clientY = event.center ? event.center.y : event.clientY;\n var x;\n if (this.options.rtl) {\n x = util.getAbsoluteRight(this.dom.centerContainer) - clientX;\n } else {\n x = clientX - util.getAbsoluteLeft(this.dom.centerContainer);\n }\n var y = clientY - util.getAbsoluteTop(this.dom.centerContainer);\n\n var item = this.itemSet.itemFromTarget(event);\n var group = this.itemSet.groupFromTarget(event);\n var customTime = CustomTime.customTimeFromTarget(event);\n\n var snap = this.itemSet.options.snap || null;\n var scale = this.body.util.getScale();\n var step = this.body.util.getStep();\n var time = this._toTime(x);\n var snappedTime = snap ? snap(time, scale, step) : time;\n\n var element = util.getTarget(event);\n var what = null;\n if (item != null) {what = 'item';}\n else if (customTime != null) {what = 'custom-time';}\n else if (util.hasParent(element, this.timeAxis.dom.foreground)) {what = 'axis';}\n else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {what = 'axis';}\n else if (util.hasParent(element, this.itemSet.dom.labelSet)) {what = 'group-label';}\n else if (util.hasParent(element, this.currentTime.bar)) {what = 'current-time';}\n else if (util.hasParent(element, this.dom.center)) {what = 'background';}\n\n return {\n event: event,\n item: item ? item.id : null,\n group: group ? group.groupId : null,\n what: what,\n pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX,\n pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY,\n x: x,\n y: y,\n time: time,\n snappedTime: snappedTime\n }\n};\n\n/**\n * Toggle Timeline rolling mode\n */\n\nTimeline.prototype.toggleRollingMode = function () {\n if (this.range.rolling) {\n this.range.stopRolling();\n } else {\n if (this.options.rollingMode == undefined) {\n this.setOptions(this.options)\n }\n this.range.startRolling();\n }\n\n}\n\nmodule.exports = Timeline;\n","/**\n *\n * @param {number} start\n * @param {number} end\n * @param {boolean} autoScaleStart\n * @param {boolean} autoScaleEnd\n * @param {number} containerHeight\n * @param {number} majorCharHeight\n * @param {boolean} zeroAlign\n * @param {function} formattingFunction\n * @constructor DataScale\n */\nfunction DataScale(start, end, autoScaleStart, autoScaleEnd, containerHeight, majorCharHeight, zeroAlign = false, formattingFunction=false) {\n this.majorSteps = [1, 2, 5, 10];\n this.minorSteps = [0.25, 0.5, 1, 2];\n this.customLines = null;\n\n this.containerHeight = containerHeight;\n this.majorCharHeight = majorCharHeight;\n this._start = start;\n this._end = end;\n\n this.scale = 1;\n this.minorStepIdx = -1;\n this.magnitudefactor = 1;\n this.determineScale();\n\n this.zeroAlign = zeroAlign;\n this.autoScaleStart = autoScaleStart;\n this.autoScaleEnd = autoScaleEnd;\n\n this.formattingFunction = formattingFunction;\n\n if (autoScaleStart || autoScaleEnd) {\n var me = this;\n var roundToMinor = function (value) {\n var rounded = value - (value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]));\n if (value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]) > 0.5 * (me.magnitudefactor * me.minorSteps[me.minorStepIdx])) {\n return rounded + (me.magnitudefactor * me.minorSteps[me.minorStepIdx]);\n }\n else {\n return rounded;\n }\n };\n if (autoScaleStart) {\n this._start -= this.magnitudefactor * 2 * this.minorSteps[this.minorStepIdx];\n this._start = roundToMinor(this._start);\n }\n\n if (autoScaleEnd) {\n this._end += this.magnitudefactor * this.minorSteps[this.minorStepIdx];\n this._end = roundToMinor(this._end);\n }\n this.determineScale();\n }\n}\n\nDataScale.prototype.setCharHeight = function (majorCharHeight) {\n this.majorCharHeight = majorCharHeight;\n};\n\nDataScale.prototype.setHeight = function (containerHeight) {\n this.containerHeight = containerHeight;\n};\n\nDataScale.prototype.determineScale = function () {\n var range = this._end - this._start;\n this.scale = this.containerHeight / range;\n var minimumStepValue = this.majorCharHeight / this.scale;\n var orderOfMagnitude = (range > 0)\n ? Math.round(Math.log(range) / Math.LN10)\n : 0;\n\n this.minorStepIdx = -1;\n this.magnitudefactor = Math.pow(10, orderOfMagnitude);\n\n var start = 0;\n if (orderOfMagnitude < 0) {\n start = orderOfMagnitude;\n }\n\n var solutionFound = false;\n for (var l = start; Math.abs(l) <= Math.abs(orderOfMagnitude); l++) {\n this.magnitudefactor = Math.pow(10, l);\n for (var j = 0; j < this.minorSteps.length; j++) {\n var stepSize = this.magnitudefactor * this.minorSteps[j];\n if (stepSize >= minimumStepValue) {\n solutionFound = true;\n this.minorStepIdx = j;\n break;\n }\n }\n if (solutionFound === true) {\n break;\n }\n }\n};\n\nDataScale.prototype.is_major = function (value) {\n return (value % (this.magnitudefactor * this.majorSteps[this.minorStepIdx]) === 0);\n};\n\nDataScale.prototype.getStep = function(){\n return this.magnitudefactor * this.minorSteps[this.minorStepIdx];\n};\n\nDataScale.prototype.getFirstMajor = function(){\n var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx];\n return this.convertValue(this._start + ((majorStep - (this._start % majorStep)) % majorStep));\n};\n\nDataScale.prototype.formatValue = function(current) {\n var returnValue = current.toPrecision(5);\n if (typeof this.formattingFunction === 'function') {\n returnValue = this.formattingFunction(current);\n }\n\n if (typeof returnValue === 'number') {\n return '' + returnValue;\n }\n else if (typeof returnValue === 'string') {\n return returnValue;\n }\n else {\n return current.toPrecision(5);\n }\n\n};\n\nDataScale.prototype.getLines = function () {\n var lines = [];\n var step = this.getStep();\n var bottomOffset = (step - (this._start % step)) % step;\n for (var i = (this._start + bottomOffset); this._end-i > 0.00001; i += step) {\n if (i != this._start) { //Skip the bottom line\n lines.push({major: this.is_major(i), y: this.convertValue(i), val: this.formatValue(i)});\n }\n }\n return lines;\n};\n\nDataScale.prototype.followScale = function (other) {\n var oldStepIdx = this.minorStepIdx;\n var oldStart = this._start;\n var oldEnd = this._end;\n\n var me = this;\n var increaseMagnitude = function () {\n me.magnitudefactor *= 2;\n };\n var decreaseMagnitude = function () {\n me.magnitudefactor /= 2;\n };\n\n if ((other.minorStepIdx <= 1 && this.minorStepIdx <= 1) || (other.minorStepIdx > 1 && this.minorStepIdx > 1)) {\n //easy, no need to change stepIdx nor multiplication factor\n } else if (other.minorStepIdx < this.minorStepIdx) {\n //I'm 5, they are 4 per major.\n this.minorStepIdx = 1;\n if (oldStepIdx == 2) {\n increaseMagnitude();\n } else {\n increaseMagnitude();\n increaseMagnitude();\n }\n } else {\n //I'm 4, they are 5 per major\n this.minorStepIdx = 2;\n if (oldStepIdx == 1) {\n decreaseMagnitude();\n } else {\n decreaseMagnitude();\n decreaseMagnitude();\n }\n }\n\n //Get masters stats:\n var otherZero = other.convertValue(0);\n var otherStep = other.getStep() * other.scale;\n\n var done = false;\n var count = 0;\n //Loop until magnitude is correct for given constrains.\n while (!done && count++ <5) {\n\n //Get my stats:\n this.scale = otherStep / (this.minorSteps[this.minorStepIdx] * this.magnitudefactor);\n var newRange = this.containerHeight / this.scale;\n\n //For the case the magnitudefactor has changed:\n this._start = oldStart;\n this._end = this._start + newRange;\n\n var myOriginalZero = this._end * this.scale;\n var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx];\n var majorOffset = this.getFirstMajor() - other.getFirstMajor();\n\n if (this.zeroAlign) {\n var zeroOffset = otherZero - myOriginalZero;\n this._end += (zeroOffset / this.scale);\n this._start = this._end - newRange;\n } else {\n if (!this.autoScaleStart) {\n this._start += majorStep - (majorOffset / this.scale);\n this._end = this._start + newRange;\n } else {\n this._start -= majorOffset / this.scale;\n this._end = this._start + newRange;\n }\n }\n if (!this.autoScaleEnd && this._end > oldEnd+0.00001) {\n //Need to decrease magnitude to prevent scale overshoot! (end)\n decreaseMagnitude();\n done = false;\n continue;\n }\n if (!this.autoScaleStart && this._start < oldStart-0.00001) {\n if (this.zeroAlign && oldStart >= 0) {\n console.warn(\"Can't adhere to given 'min' range, due to zeroalign\");\n } else {\n //Need to decrease magnitude to prevent scale overshoot! (start)\n decreaseMagnitude();\n done = false;\n continue;\n }\n }\n if (this.autoScaleStart && this.autoScaleEnd && newRange < (oldEnd-oldStart)){\n increaseMagnitude();\n done = false;\n continue;\n }\n done = true;\n }\n};\n\nDataScale.prototype.convertValue = function (value) {\n return this.containerHeight - ((value - this._start) * this.scale);\n};\n\nDataScale.prototype.screenToValue = function (pixels) {\n return ((this.containerHeight - pixels) / this.scale) + this._start;\n};\n\nmodule.exports = DataScale;","import util from 'vis-util';\nimport DOMutil from '../../DOMutil';\nimport Component from './Component';\nimport DataScale from './DataScale';\n\nimport './css/dataaxis.css';\n\n/**\n * A horizontal time axis\n * @param {Object} body\n * @param {Object} [options] See DataAxis.setOptions for the available\n * options.\n * @param {SVGElement} svg\n * @param {vis.LineGraph.options} linegraphOptions\n * @constructor DataAxis\n * @extends Component\n */\nfunction DataAxis(body, options, svg, linegraphOptions) {\n this.id = util.randomUUID();\n this.body = body;\n\n this.defaultOptions = {\n orientation: 'left', // supported: 'left', 'right'\n showMinorLabels: true,\n showMajorLabels: true,\n icons: false,\n majorLinesOffset: 7,\n minorLinesOffset: 4,\n labelOffsetX: 10,\n labelOffsetY: 2,\n iconWidth: 20,\n width: '40px',\n visible: true,\n alignZeros: true,\n left: {\n range: {min: undefined, max: undefined},\n format: function (value) {\n return '' + parseFloat(value.toPrecision(3));\n },\n title: {text: undefined, style: undefined}\n },\n right: {\n range: {min: undefined, max: undefined},\n format: function (value) {\n return '' + parseFloat(value.toPrecision(3));\n },\n title: {text: undefined, style: undefined}\n }\n };\n\n this.linegraphOptions = linegraphOptions;\n this.linegraphSVG = svg;\n this.props = {};\n this.DOMelements = { // dynamic elements\n lines: {},\n labels: {},\n title: {}\n };\n\n this.dom = {};\n this.scale = undefined;\n this.range = {start: 0, end: 0};\n\n this.options = util.extend({}, this.defaultOptions);\n this.conversionFactor = 1;\n\n this.setOptions(options);\n this.width = Number(('' + this.options.width).replace(\"px\", \"\"));\n this.minWidth = this.width;\n this.height = this.linegraphSVG.getBoundingClientRect().height;\n this.hidden = false;\n\n this.stepPixels = 25;\n this.zeroCrossing = -1;\n this.amountOfSteps = -1;\n\n this.lineOffset = 0;\n this.master = true;\n this.masterAxis = null;\n this.svgElements = {};\n this.iconsRemoved = false;\n\n this.groups = {};\n this.amountOfGroups = 0;\n\n // create the HTML DOM\n this._create();\n if (this.scale == undefined) {\n this._redrawLabels();\n }\n this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups};\n\n var me = this;\n this.body.emitter.on(\"verticalDrag\", function () {\n me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px';\n });\n}\n\nDataAxis.prototype = new Component();\n\n\nDataAxis.prototype.addGroup = function (label, graphOptions) {\n if (!this.groups.hasOwnProperty(label)) {\n this.groups[label] = graphOptions;\n }\n this.amountOfGroups += 1;\n};\n\nDataAxis.prototype.updateGroup = function (label, graphOptions) {\n if (!this.groups.hasOwnProperty(label)) {\n this.amountOfGroups += 1;\n }\n this.groups[label] = graphOptions;\n};\n\nDataAxis.prototype.removeGroup = function (label) {\n if (this.groups.hasOwnProperty(label)) {\n delete this.groups[label];\n this.amountOfGroups -= 1;\n }\n};\n\n\nDataAxis.prototype.setOptions = function (options) {\n if (options) {\n var redraw = false;\n if (this.options.orientation != options.orientation && options.orientation !== undefined) {\n redraw = true;\n }\n var fields = [\n 'orientation',\n 'showMinorLabels',\n 'showMajorLabels',\n 'icons',\n 'majorLinesOffset',\n 'minorLinesOffset',\n 'labelOffsetX',\n 'labelOffsetY',\n 'iconWidth',\n 'width',\n 'visible',\n 'left',\n 'right',\n 'alignZeros'\n ];\n util.selectiveDeepExtend(fields, this.options, options);\n\n this.minWidth = Number(('' + this.options.width).replace(\"px\", \"\"));\n if (redraw === true && this.dom.frame) {\n this.hide();\n this.show();\n }\n }\n};\n\n\n/**\n * Create the HTML DOM for the DataAxis\n */\nDataAxis.prototype._create = function () {\n this.dom.frame = document.createElement('div');\n this.dom.frame.style.width = this.options.width;\n this.dom.frame.style.height = this.height;\n\n this.dom.lineContainer = document.createElement('div');\n this.dom.lineContainer.style.width = '100%';\n this.dom.lineContainer.style.height = this.height;\n this.dom.lineContainer.style.position = 'relative';\n this.dom.lineContainer.style.visibility = 'visible';\n this.dom.lineContainer.style.display = 'block';\n\n // create svg element for graph drawing.\n this.svg = document.createElementNS('http://www.w3.org/2000/svg', \"svg\");\n this.svg.style.position = \"absolute\";\n this.svg.style.top = '0px';\n this.svg.style.height = '100%';\n this.svg.style.width = '100%';\n this.svg.style.display = \"block\";\n this.dom.frame.appendChild(this.svg);\n};\n\nDataAxis.prototype._redrawGroupIcons = function () {\n DOMutil.prepareElements(this.svgElements);\n\n var x;\n var iconWidth = this.options.iconWidth;\n var iconHeight = 15;\n var iconOffset = 4;\n var y = iconOffset + 0.5 * iconHeight;\n\n if (this.options.orientation === 'left') {\n x = iconOffset;\n }\n else {\n x = this.width - iconWidth - iconOffset;\n }\n\n var groupArray = Object.keys(this.groups);\n groupArray.sort(function (a, b) {\n return (a < b ? -1 : 1);\n })\n\n for (var i = 0; i < groupArray.length; i++) {\n var groupId = groupArray[i];\n if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) {\n this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y);\n y += iconHeight + iconOffset;\n }\n }\n\n DOMutil.cleanupElements(this.svgElements);\n this.iconsRemoved = false;\n};\n\nDataAxis.prototype._cleanupIcons = function () {\n if (this.iconsRemoved === false) {\n DOMutil.prepareElements(this.svgElements);\n DOMutil.cleanupElements(this.svgElements);\n this.iconsRemoved = true;\n }\n};\n\n/**\n * Create the HTML DOM for the DataAxis\n */\nDataAxis.prototype.show = function () {\n this.hidden = false;\n if (!this.dom.frame.parentNode) {\n if (this.options.orientation === 'left') {\n this.body.dom.left.appendChild(this.dom.frame);\n }\n else {\n this.body.dom.right.appendChild(this.dom.frame);\n }\n }\n\n if (!this.dom.lineContainer.parentNode) {\n this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer);\n }\n this.dom.lineContainer.style.display = 'block';\n};\n\n/**\n * Create the HTML DOM for the DataAxis\n */\nDataAxis.prototype.hide = function () {\n this.hidden = true;\n if (this.dom.frame.parentNode) {\n this.dom.frame.parentNode.removeChild(this.dom.frame);\n }\n\n this.dom.lineContainer.style.display = 'none';\n};\n\n/**\n * Set a range (start and end)\n * @param {number} start\n * @param {number} end\n */\nDataAxis.prototype.setRange = function (start, end) {\n this.range.start = start;\n this.range.end = end;\n};\n\n/**\n * Repaint the component\n * @return {boolean} Returns true if the component is resized\n */\nDataAxis.prototype.redraw = function () {\n var resized = false;\n var activeGroups = 0;\n\n // Make sure the line container adheres to the vertical scrolling.\n this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px';\n\n for (var groupId in this.groups) {\n if (this.groups.hasOwnProperty(groupId)) {\n if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) {\n activeGroups++;\n }\n }\n }\n if (this.amountOfGroups === 0 || activeGroups === 0) {\n this.hide();\n }\n else {\n this.show();\n this.height = Number(this.linegraphSVG.style.height.replace(\"px\", \"\"));\n\n // svg offsetheight did not work in firefox and explorer...\n this.dom.lineContainer.style.height = this.height + 'px';\n this.width = this.options.visible === true ? Number(('' + this.options.width).replace(\"px\", \"\")) : 0;\n\n var props = this.props;\n var frame = this.dom.frame;\n\n // update classname\n frame.className = 'vis-data-axis';\n\n // calculate character width and height\n this._calculateCharSize();\n\n var orientation = this.options.orientation;\n var showMinorLabels = this.options.showMinorLabels;\n var showMajorLabels = this.options.showMajorLabels;\n\n // determine the width and height of the elements for the axis\n props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;\n props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;\n\n props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset;\n props.minorLineHeight = 1;\n props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset;\n props.majorLineHeight = 1;\n\n // take frame offline while updating (is almost twice as fast)\n if (orientation === 'left') {\n frame.style.top = '0';\n frame.style.left = '0';\n frame.style.bottom = '';\n frame.style.width = this.width + 'px';\n frame.style.height = this.height + \"px\";\n this.props.width = this.body.domProps.left.width;\n this.props.height = this.body.domProps.left.height;\n }\n else { // right\n frame.style.top = '';\n frame.style.bottom = '0';\n frame.style.left = '0';\n frame.style.width = this.width + 'px';\n frame.style.height = this.height + \"px\";\n this.props.width = this.body.domProps.right.width;\n this.props.height = this.body.domProps.right.height;\n }\n\n resized = this._redrawLabels();\n resized = this._isResized() || resized;\n\n if (this.options.icons === true) {\n this._redrawGroupIcons();\n }\n else {\n this._cleanupIcons();\n }\n\n this._redrawTitle(orientation);\n }\n return resized;\n};\n\n/**\n * Repaint major and minor text labels and vertical grid lines\n *\n * @returns {boolean}\n * @private\n */\nDataAxis.prototype._redrawLabels = function () {\n var resized = false;\n DOMutil.prepareElements(this.DOMelements.lines);\n DOMutil.prepareElements(this.DOMelements.labels);\n var orientation = this.options['orientation'];\n var customRange = this.options[orientation].range != undefined ? this.options[orientation].range : {};\n\n //Override range with manual options:\n var autoScaleEnd = true;\n if (customRange.max != undefined) {\n this.range.end = customRange.max;\n autoScaleEnd = false;\n }\n var autoScaleStart = true;\n if (customRange.min != undefined) {\n this.range.start = customRange.min;\n autoScaleStart = false;\n }\n\n this.scale = new DataScale(\n this.range.start,\n this.range.end,\n autoScaleStart,\n autoScaleEnd,\n this.dom.frame.offsetHeight,\n this.props.majorCharHeight,\n this.options.alignZeros,\n this.options[orientation].format\n );\n\n if (this.master === false && this.masterAxis != undefined) {\n this.scale.followScale(this.masterAxis.scale);\n this.dom.lineContainer.style.display = 'none';\n } else{\n this.dom.lineContainer.style.display = 'block';\n }\n\n //Is updated in side-effect of _redrawLabel():\n this.maxLabelSize = 0;\n\n var lines = this.scale.getLines();\n lines.forEach(\n line=> {\n var y = line.y;\n var isMajor = line.major;\n if (this.options['showMinorLabels'] && isMajor === false) {\n this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-minor', this.props.minorCharHeight);\n }\n if (isMajor) {\n if (y >= 0) {\n this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-major', this.props.majorCharHeight);\n }\n }\n if (this.master === true) {\n if (isMajor) {\n this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-major', this.options.majorLinesOffset, this.props.majorLineWidth);\n }\n else {\n this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-minor', this.options.minorLinesOffset, this.props.minorLineWidth);\n }\n }\n });\n\n // Note that title is rotated, so we're using the height, not width!\n var titleWidth = 0;\n if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) {\n titleWidth = this.props.titleCharHeight;\n }\n var offset = this.options.icons === true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15;\n\n // this will resize the yAxis to accommodate the labels.\n if (this.maxLabelSize > (this.width - offset) && this.options.visible === true) {\n this.width = this.maxLabelSize + offset;\n this.options.width = this.width + \"px\";\n DOMutil.cleanupElements(this.DOMelements.lines);\n DOMutil.cleanupElements(this.DOMelements.labels);\n this.redraw();\n resized = true;\n }\n // this will resize the yAxis if it is too big for the labels.\n else if (this.maxLabelSize < (this.width - offset) && this.options.visible === true && this.width > this.minWidth) {\n this.width = Math.max(this.minWidth, this.maxLabelSize + offset);\n this.options.width = this.width + \"px\";\n DOMutil.cleanupElements(this.DOMelements.lines);\n DOMutil.cleanupElements(this.DOMelements.labels);\n this.redraw();\n resized = true;\n }\n else {\n DOMutil.cleanupElements(this.DOMelements.lines);\n DOMutil.cleanupElements(this.DOMelements.labels);\n resized = false;\n }\n\n return resized;\n};\n\nDataAxis.prototype.convertValue = function (value) {\n return this.scale.convertValue(value);\n};\n\nDataAxis.prototype.screenToValue = function (x) {\n return this.scale.screenToValue(x);\n};\n\n/**\n * Create a label for the axis at position x\n *\n * @param {number} y\n * @param {string} text\n * @param {'top'|'right'|'bottom'|'left'} orientation\n * @param {string} className\n * @param {number} characterHeight\n * @private\n */\nDataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) {\n // reuse redundant label\n var label = DOMutil.getDOMElement('div', this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift();\n label.className = className;\n label.innerHTML = text;\n if (orientation === 'left') {\n label.style.left = '-' + this.options.labelOffsetX + 'px';\n label.style.textAlign = \"right\";\n }\n else {\n label.style.right = '-' + this.options.labelOffsetX + 'px';\n label.style.textAlign = \"left\";\n }\n\n label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px';\n\n text += '';\n\n var largestWidth = Math.max(this.props.majorCharWidth, this.props.minorCharWidth);\n if (this.maxLabelSize < text.length * largestWidth) {\n this.maxLabelSize = text.length * largestWidth;\n }\n};\n\n/**\n * Create a minor line for the axis at position y\n * @param {number} y\n * @param {'top'|'right'|'bottom'|'left'} orientation\n * @param {string} className\n * @param {number} offset\n * @param {number} width\n */\nDataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) {\n if (this.master === true) {\n var line = DOMutil.getDOMElement('div', this.DOMelements.lines, this.dom.lineContainer); //this.dom.redundant.lines.shift();\n line.className = className;\n line.innerHTML = '';\n\n if (orientation === 'left') {\n line.style.left = (this.width - offset) + 'px';\n }\n else {\n line.style.right = (this.width - offset) + 'px';\n }\n\n line.style.width = width + 'px';\n line.style.top = y + 'px';\n }\n};\n\n/**\n * Create a title for the axis\n * @private\n * @param {'top'|'right'|'bottom'|'left'} orientation\n */\nDataAxis.prototype._redrawTitle = function (orientation) {\n DOMutil.prepareElements(this.DOMelements.title);\n\n // Check if the title is defined for this axes\n if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) {\n var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame);\n title.className = 'vis-y-axis vis-title vis-' + orientation;\n title.innerHTML = this.options[orientation].title.text;\n\n // Add style - if provided\n if (this.options[orientation].title.style !== undefined) {\n util.addCssText(title, this.options[orientation].title.style);\n }\n\n if (orientation === 'left') {\n title.style.left = this.props.titleCharHeight + 'px';\n }\n else {\n title.style.right = this.props.titleCharHeight + 'px';\n }\n\n title.style.width = this.height + 'px';\n }\n\n // we need to clean up in case we did not use all elements.\n DOMutil.cleanupElements(this.DOMelements.title);\n};\n\n\n/**\n * Determine the size of text on the axis (both major and minor axis).\n * The size is calculated only once and then cached in this.props.\n * @private\n */\nDataAxis.prototype._calculateCharSize = function () {\n // determine the char width and height on the minor axis\n if (!('minorCharHeight' in this.props)) {\n var textMinor = document.createTextNode('0');\n var measureCharMinor = document.createElement('div');\n measureCharMinor.className = 'vis-y-axis vis-minor vis-measure';\n measureCharMinor.appendChild(textMinor);\n this.dom.frame.appendChild(measureCharMinor);\n\n this.props.minorCharHeight = measureCharMinor.clientHeight;\n this.props.minorCharWidth = measureCharMinor.clientWidth;\n\n this.dom.frame.removeChild(measureCharMinor);\n }\n\n if (!('majorCharHeight' in this.props)) {\n var textMajor = document.createTextNode('0');\n var measureCharMajor = document.createElement('div');\n measureCharMajor.className = 'vis-y-axis vis-major vis-measure';\n measureCharMajor.appendChild(textMajor);\n this.dom.frame.appendChild(measureCharMajor);\n\n this.props.majorCharHeight = measureCharMajor.clientHeight;\n this.props.majorCharWidth = measureCharMajor.clientWidth;\n\n this.dom.frame.removeChild(measureCharMajor);\n }\n\n if (!('titleCharHeight' in this.props)) {\n var textTitle = document.createTextNode('0');\n var measureCharTitle = document.createElement('div');\n measureCharTitle.className = 'vis-y-axis vis-title vis-measure';\n measureCharTitle.appendChild(textTitle);\n this.dom.frame.appendChild(measureCharTitle);\n\n this.props.titleCharHeight = measureCharTitle.clientHeight;\n this.props.titleCharWidth = measureCharTitle.clientWidth;\n\n this.dom.frame.removeChild(measureCharTitle);\n }\n};\n\nexport default DataAxis;\n","var DOMutil = require('../../../DOMutil');\n\n/**\n *\n * @param {number | string} groupId\n * @param {Object} options // TODO: Describe options\n *\n * @constructor Points\n */\nfunction Points(groupId, options) { // eslint-disable-line no-unused-vars\n}\n\n/**\n * draw the data points\n *\n * @param {Array} dataset\n * @param {GraphGroup} group\n * @param {Object} framework | SVG DOM element\n * @param {number} [offset]\n */\nPoints.draw = function (dataset, group, framework, offset) {\n offset = offset || 0;\n var callback = getCallback(framework, group);\n\n for (var i = 0; i < dataset.length; i++) {\n if (!callback) {\n // draw the point the simple way.\n DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group), framework.svgElements, framework.svg, dataset[i].label);\n }\n else {\n var callbackResult = callback(dataset[i], group); // result might be true, false or an object\n if (callbackResult === true || typeof callbackResult === 'object') {\n DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group, callbackResult), framework.svgElements, framework.svg, dataset[i].label);\n }\n }\n }\n};\n\nPoints.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {\n var fillHeight = iconHeight * 0.5;\n\n var outline = DOMutil.getSVGElement(\"rect\", framework.svgElements, framework.svg);\n outline.setAttributeNS(null, \"x\", x);\n outline.setAttributeNS(null, \"y\", y - fillHeight);\n outline.setAttributeNS(null, \"width\", iconWidth);\n outline.setAttributeNS(null, \"height\", 2 * fillHeight);\n outline.setAttributeNS(null, \"class\", \"vis-outline\");\n\n //Don't call callback on icon\n DOMutil.drawPoint(x + 0.5 * iconWidth, y, getGroupTemplate(group), framework.svgElements, framework.svg);\n};\n\n/**\n *\n * @param {vis.Group} group\n * @param {any} callbackResult\n * @returns {{style: *, styles: (*|string), size: *, className: *}}\n */\nfunction getGroupTemplate(group, callbackResult) {\n callbackResult = (typeof callbackResult === 'undefined') ? {} : callbackResult;\n return {\n style: callbackResult.style || group.options.drawPoints.style,\n styles: callbackResult.styles || group.options.drawPoints.styles,\n size: callbackResult.size || group.options.drawPoints.size,\n className: callbackResult.className || group.className\n };\n}\n\n/**\n *\n * @param {Object} framework | SVG DOM element\n * @param {vis.Group} group\n * @returns {function}\n */\nfunction getCallback(framework, group) {\n var callback = undefined;\n // check for the graph2d onRender\n if (framework.options && framework.options.drawPoints && framework.options.drawPoints.onRender && typeof framework.options.drawPoints.onRender == 'function') {\n callback = framework.options.drawPoints.onRender;\n }\n\n // override it with the group onRender if defined\n if (group.group.options && group.group.options.drawPoints && group.group.options.drawPoints.onRender && typeof group.group.options.drawPoints.onRender == 'function') {\n callback = group.group.options.drawPoints.onRender;\n }\n return callback;\n}\n\nmodule.exports = Points;\n","var DOMutil = require('../../../DOMutil');\nvar Points = require('./points');\n\n/**\n *\n * @param {vis.GraphGroup.id} groupId\n * @param {Object} options // TODO: Describe options\n * @constructor Bargraph\n */\nfunction Bargraph(groupId, options) { // eslint-disable-line no-unused-vars\n}\n\nBargraph.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {\n var fillHeight = iconHeight * 0.5;\n var outline = DOMutil.getSVGElement(\"rect\", framework.svgElements, framework.svg);\n outline.setAttributeNS(null, \"x\", x);\n outline.setAttributeNS(null, \"y\", y - fillHeight);\n outline.setAttributeNS(null, \"width\", iconWidth);\n outline.setAttributeNS(null, \"height\", 2 * fillHeight);\n outline.setAttributeNS(null, \"class\", \"vis-outline\");\n\n var barWidth = Math.round(0.3 * iconWidth);\n var originalWidth = group.options.barChart.width;\n var scale = originalWidth / barWidth;\n var bar1Height = Math.round(0.4 * iconHeight);\n var bar2Height = Math.round(0.75 * iconHeight);\n\n var offset = Math.round((iconWidth - (2 * barWidth)) / 3);\n\n DOMutil.drawBar(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, barWidth, bar1Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);\n DOMutil.drawBar(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);\n\n if (group.options.drawPoints.enabled == true) {\n var groupTemplate = {\n style: group.options.drawPoints.style,\n styles: group.options.drawPoints.styles,\n size: (group.options.drawPoints.size / scale),\n className: group.className\n };\n DOMutil.drawPoint(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, groupTemplate, framework.svgElements, framework.svg);\n DOMutil.drawPoint(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, groupTemplate, framework.svgElements, framework.svg);\n }\n};\n\n/**\n * draw a bar graph\n *\n * @param {Array.} groupIds\n * @param {Object} processedGroupData\n * @param {{svg: Object, svgElements: Array.