|  |  | @ -4,8 +4,8 @@ | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * A dynamic, browser-based visualization library. | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * @version 2.0.1-SNAPSHOT | 
			
		
	
		
			
				
					|  |  |  | * @date    2014-06-24 | 
			
		
	
		
			
				
					|  |  |  | * @version @@version | 
			
		
	
		
			
				
					|  |  |  | * @date    @@date | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * @license | 
			
		
	
		
			
				
					|  |  |  | * Copyright (C) 2011-2014 Almende B.V, http://almende.com | 
			
		
	
	
		
			
				
					|  |  | @ -1366,6 +1366,151 @@ util._mergeOptions = function (mergeTarget, options, option) { | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd | 
			
		
	
		
			
				
					|  |  |  | * arrays. This is done by giving a boolean value true if you want to use the byEnd. | 
			
		
	
		
			
				
					|  |  |  | * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check | 
			
		
	
		
			
				
					|  |  |  | * if the time we selected (start or end) is within the current range). | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is | 
			
		
	
		
			
				
					|  |  |  | * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest, | 
			
		
	
		
			
				
					|  |  |  | * either the start OR end time has to be in the range. | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * @param {{byStart: Item[], byEnd: Item[]}} orderedItems | 
			
		
	
		
			
				
					|  |  |  | * @param {{start: number, end: number}} range | 
			
		
	
		
			
				
					|  |  |  | * @param {Boolean} byEnd | 
			
		
	
		
			
				
					|  |  |  | * @returns {number} | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | util.binarySearch = function(orderedItems, range, field, field2) { | 
			
		
	
		
			
				
					|  |  |  | var array = orderedItems; | 
			
		
	
		
			
				
					|  |  |  | var interval = range.end - range.start; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var found = false; | 
			
		
	
		
			
				
					|  |  |  | var low = 0; | 
			
		
	
		
			
				
					|  |  |  | var high = array.length; | 
			
		
	
		
			
				
					|  |  |  | var guess = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | var newGuess; | 
			
		
	
		
			
				
					|  |  |  | var value; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (high == 0) {guess = -1;} | 
			
		
	
		
			
				
					|  |  |  | else if (high == 1) { | 
			
		
	
		
			
				
					|  |  |  | value = field2 === undefined ? array[guess][field] : array[guess][field][field2]; | 
			
		
	
		
			
				
					|  |  |  | if ((value > range.start - interval) && (value < range.end)) { | 
			
		
	
		
			
				
					|  |  |  | guess =  0; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | guess = -1; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | high -= 1; | 
			
		
	
		
			
				
					|  |  |  | while (found == false) { | 
			
		
	
		
			
				
					|  |  |  | value = field2 === undefined ? array[guess][field] : array[guess][field][field2]; | 
			
		
	
		
			
				
					|  |  |  | if ((value > range.start - interval) && (value < range.end)) { | 
			
		
	
		
			
				
					|  |  |  | found = true; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | if (value < range.start - interval) { // it is too small --> increase low | 
			
		
	
		
			
				
					|  |  |  | low = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else {  // it is too big --> decrease high | 
			
		
	
		
			
				
					|  |  |  | high = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | newGuess = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | // not in list; | 
			
		
	
		
			
				
					|  |  |  | if (guess == newGuess) { | 
			
		
	
		
			
				
					|  |  |  | guess = -1; | 
			
		
	
		
			
				
					|  |  |  | found = true; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | guess = newGuess; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | return guess; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd | 
			
		
	
		
			
				
					|  |  |  | * arrays. This is done by giving a boolean value true if you want to use the byEnd. | 
			
		
	
		
			
				
					|  |  |  | * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check | 
			
		
	
		
			
				
					|  |  |  | * if the time we selected (start or end) is within the current range). | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is | 
			
		
	
		
			
				
					|  |  |  | * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest, | 
			
		
	
		
			
				
					|  |  |  | * either the start OR end time has to be in the range. | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * @param {Array} orderedItems | 
			
		
	
		
			
				
					|  |  |  | * @param {{start: number, end: number}} target | 
			
		
	
		
			
				
					|  |  |  | * @param {Boolean} byEnd | 
			
		
	
		
			
				
					|  |  |  | * @returns {number} | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | util.binarySearchGeneric = function(orderedItems, target, field, sidePreference) { | 
			
		
	
		
			
				
					|  |  |  | var array = orderedItems; | 
			
		
	
		
			
				
					|  |  |  | var found = false; | 
			
		
	
		
			
				
					|  |  |  | var low = 0; | 
			
		
	
		
			
				
					|  |  |  | var high = array.length; | 
			
		
	
		
			
				
					|  |  |  | var guess = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | var newGuess; | 
			
		
	
		
			
				
					|  |  |  | var prevValue, value, nextValue; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (high == 0) {guess = -1;} | 
			
		
	
		
			
				
					|  |  |  | else if (high == 1) { | 
			
		
	
		
			
				
					|  |  |  | value = array[guess][field]; | 
			
		
	
		
			
				
					|  |  |  | if (value == target) { | 
			
		
	
		
			
				
					|  |  |  | guess =  0; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | guess = -1; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | high -= 1; | 
			
		
	
		
			
				
					|  |  |  | while (found == false) { | 
			
		
	
		
			
				
					|  |  |  | prevValue = array[Math.max(0,guess - 1)][field]; | 
			
		
	
		
			
				
					|  |  |  | value = array[guess][field]; | 
			
		
	
		
			
				
					|  |  |  | nextValue = array[Math.min(array.length-1,guess + 1)][field]; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (value == target || prevValue < target && value > target || value < target && nextValue > target) { | 
			
		
	
		
			
				
					|  |  |  | found = true; | 
			
		
	
		
			
				
					|  |  |  | if (value != target) { | 
			
		
	
		
			
				
					|  |  |  | if (sidePreference == 'before') { | 
			
		
	
		
			
				
					|  |  |  | if (prevValue < target && value > target) { | 
			
		
	
		
			
				
					|  |  |  | guess = Math.max(0,guess - 1); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | if (value < target && nextValue > target) { | 
			
		
	
		
			
				
					|  |  |  | guess = Math.min(array.length-1,guess + 1); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | if (value < target) { // it is too small --> increase low | 
			
		
	
		
			
				
					|  |  |  | low = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else {  // it is too big --> decrease high | 
			
		
	
		
			
				
					|  |  |  | high = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | newGuess = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | // not in list; | 
			
		
	
		
			
				
					|  |  |  | if (guess == newGuess) { | 
			
		
	
		
			
				
					|  |  |  | guess = -2; | 
			
		
	
		
			
				
					|  |  |  | found = true; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | guess = newGuess; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | return guess; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * Created by Alex on 6/20/14. | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
	
		
			
				
					|  |  | @ -1424,7 +1569,7 @@ DOMutil.getSVGElement = function (elementType, JSONcontainer, svgContainer) { | 
			
		
	
		
			
				
					|  |  |  | // check if there is an redundant element | 
			
		
	
		
			
				
					|  |  |  | if (JSONcontainer[elementType].redundant.length > 0) { | 
			
		
	
		
			
				
					|  |  |  | element = JSONcontainer[elementType].redundant[0]; | 
			
		
	
		
			
				
					|  |  |  | JSONcontainer[elementType].redundant.shift() | 
			
		
	
		
			
				
					|  |  |  | JSONcontainer[elementType].redundant.shift(); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | // create a new element and add it to the SVG | 
			
		
	
	
		
			
				
					|  |  | @ -1449,7 +1594,7 @@ DOMutil.getSVGElement = function (elementType, JSONcontainer, svgContainer) { | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * @param elementType | 
			
		
	
		
			
				
					|  |  |  | * @param JSONcontainer | 
			
		
	
		
			
				
					|  |  |  | * @param svgContainer | 
			
		
	
		
			
				
					|  |  |  | * @param DOMContainer | 
			
		
	
		
			
				
					|  |  |  | * @returns {*} | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
	
		
			
				
					|  |  | @ -1460,7 +1605,7 @@ DOMutil.getDOMElement = function (elementType, JSONcontainer, DOMContainer) { | 
			
		
	
		
			
				
					|  |  |  | // check if there is an redundant element | 
			
		
	
		
			
				
					|  |  |  | if (JSONcontainer[elementType].redundant.length > 0) { | 
			
		
	
		
			
				
					|  |  |  | element = JSONcontainer[elementType].redundant[0]; | 
			
		
	
		
			
				
					|  |  |  | JSONcontainer[elementType].redundant.shift() | 
			
		
	
		
			
				
					|  |  |  | JSONcontainer[elementType].redundant.shift(); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | // create a new element and add it to the SVG | 
			
		
	
	
		
			
				
					|  |  | @ -2747,7 +2892,7 @@ DataView.prototype.unsubscribe = DataView.prototype.off; | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { | 
			
		
	
		
			
				
					|  |  |  | this.id = groupId; | 
			
		
	
		
			
				
					|  |  |  | var fields = ['style','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] | 
			
		
	
		
			
				
					|  |  |  | var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] | 
			
		
	
		
			
				
					|  |  |  | this.options = util.selectiveDeepExtend(fields,{},options); | 
			
		
	
		
			
				
					|  |  |  | this.usingDefaultStyle = group.className === undefined; | 
			
		
	
		
			
				
					|  |  |  | this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; | 
			
		
	
	
		
			
				
					|  |  | @ -2755,11 +2900,24 @@ function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { | 
			
		
	
		
			
				
					|  |  |  | if (this.usingDefaultStyle == true) { | 
			
		
	
		
			
				
					|  |  |  | this.groupsUsingDefaultStyles[0] += 1; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | this.itemsData = []; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | GraphGroup.prototype.setItems = function(items) { | 
			
		
	
		
			
				
					|  |  |  | if (items != null) { | 
			
		
	
		
			
				
					|  |  |  | this.itemsData = items; | 
			
		
	
		
			
				
					|  |  |  | if (this.options.sort == true) { | 
			
		
	
		
			
				
					|  |  |  | this.itemsData.sort(function (a,b) {return a.x - b.x;}) | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | this.itemsData = []; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | GraphGroup.prototype.setOptions = function(options) { | 
			
		
	
		
			
				
					|  |  |  | if (options !== undefined) { | 
			
		
	
		
			
				
					|  |  |  | var fields = ['yAxisOrientation','style','barChart']; | 
			
		
	
		
			
				
					|  |  |  | var fields = ['yAxisOrientation','style','barChart','sort']; | 
			
		
	
		
			
				
					|  |  |  | util.selectiveDeepExtend(fields, this.options, options); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | util._mergeOptions(this.options, options,'catmullRom'); | 
			
		
	
	
		
			
				
					|  |  | @ -3218,12 +3376,14 @@ DataAxis.prototype.setRange = function (start, end) { | 
			
		
	
		
			
				
					|  |  |  | * @return {boolean} Returns true if the component is resized | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | DataAxis.prototype.redraw = function () { | 
			
		
	
		
			
				
					|  |  |  | var changeCalled = false; | 
			
		
	
		
			
				
					|  |  |  | if (this.amountOfGroups == 0) { | 
			
		
	
		
			
				
					|  |  |  | this.hide(); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | this.height = this.linegraphSVG.offsetHeight; | 
			
		
	
		
			
				
					|  |  |  | this.show(); | 
			
		
	
		
			
				
					|  |  |  | this.height = Number(this.linegraphSVG.style.height.replace("px","")); | 
			
		
	
		
			
				
					|  |  |  | // svg offsetheight did not work in firefox and explorer... | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | this.dom.lineContainer.style.height = this.height + 'px'; | 
			
		
	
		
			
				
					|  |  |  | this.width = this.options.visible ? Number(this.options.width.replace("px","")) : 0; | 
			
		
	
	
		
			
				
					|  |  | @ -3265,12 +3425,12 @@ DataAxis.prototype.redraw = function () { | 
			
		
	
		
			
				
					|  |  |  | frame.style.width = this.width + 'px'; | 
			
		
	
		
			
				
					|  |  |  | frame.style.height = this.height + "px"; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | this._redrawLabels(); | 
			
		
	
		
			
				
					|  |  |  | changeCalled = this._redrawLabels(); | 
			
		
	
		
			
				
					|  |  |  | if (this.options.icons == true) { | 
			
		
	
		
			
				
					|  |  |  | this._redrawGroupIcons(); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | return changeCalled; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
	
		
			
				
					|  |  | @ -3283,10 +3443,8 @@ DataAxis.prototype._redrawLabels = function () { | 
			
		
	
		
			
				
					|  |  |  | var orientation = this.options['orientation']; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // calculate range and step (step such that we have space for 7 characters per label) | 
			
		
	
		
			
				
					|  |  |  | var start = this.yRange.start; | 
			
		
	
		
			
				
					|  |  |  | var end = this.yRange.end; | 
			
		
	
		
			
				
					|  |  |  | var minimumStep = (this.props.majorCharHeight || 10); //in pixels | 
			
		
	
		
			
				
					|  |  |  | var step = new DataStep(start, end, minimumStep, this.dom.frame.offsetHeight); | 
			
		
	
		
			
				
					|  |  |  | var step = new DataStep(this.yRange.start, this.yRange.end, minimumStep, this.dom.frame.offsetHeight); | 
			
		
	
		
			
				
					|  |  |  | this.step = step; | 
			
		
	
		
			
				
					|  |  |  | step.first(); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
	
		
			
				
					|  |  | @ -3306,6 +3464,7 @@ DataAxis.prototype._redrawLabels = function () { | 
			
		
	
		
			
				
					|  |  |  | amountOfSteps = this.height / stepPixels; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | this.valueAtZero = step.marginEnd; | 
			
		
	
		
			
				
					|  |  |  | var marginStartPos = 0; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
	
		
			
				
					|  |  | @ -3316,6 +3475,7 @@ DataAxis.prototype._redrawLabels = function () { | 
			
		
	
		
			
				
					|  |  |  | this.maxLabelSize = 0; | 
			
		
	
		
			
				
					|  |  |  | var y = 0; | 
			
		
	
		
			
				
					|  |  |  | while (max < Math.round(amountOfSteps)) { | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | y = Math.round(max * stepPixels); | 
			
		
	
		
			
				
					|  |  |  | marginStartPos = max * stepPixels; | 
			
		
	
		
			
				
					|  |  |  | var isMajor = step.isMajor(); | 
			
		
	
	
		
			
				
					|  |  | @ -3340,19 +3500,29 @@ DataAxis.prototype._redrawLabels = function () { | 
			
		
	
		
			
				
					|  |  |  | max++; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | this.conversionFactor = marginStartPos/((amountOfSteps-1) * step.step); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var offset = this.options.icons == true ? this.options.iconWidth + this.options.labelOffsetX + 15 : this.options.labelOffsetX + 15; | 
			
		
	
		
			
				
					|  |  |  | // this will resize the yAxis to accomodate the labels. | 
			
		
	
		
			
				
					|  |  |  | if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { | 
			
		
	
		
			
				
					|  |  |  | this.width = this.maxLabelSize + offset; | 
			
		
	
		
			
				
					|  |  |  | this.options.width = this.width + "px"; | 
			
		
	
		
			
				
					|  |  |  | this.body.emitter.emit("changed"); | 
			
		
	
		
			
				
					|  |  |  | DOMutil.cleanupElements(this.DOMelements); | 
			
		
	
		
			
				
					|  |  |  | this.redraw(); | 
			
		
	
		
			
				
					|  |  |  | return; | 
			
		
	
		
			
				
					|  |  |  | return true; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | // this will resize the yAxis if it is too big for the labels. | 
			
		
	
		
			
				
					|  |  |  | else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true) { | 
			
		
	
		
			
				
					|  |  |  | this.width = this.maxLabelSize + offset; | 
			
		
	
		
			
				
					|  |  |  | this.options.width = this.width + "px"; | 
			
		
	
		
			
				
					|  |  |  | DOMutil.cleanupElements(this.DOMelements); | 
			
		
	
		
			
				
					|  |  |  | this.redraw(); | 
			
		
	
		
			
				
					|  |  |  | return true; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | DOMutil.cleanupElements(this.DOMelements); | 
			
		
	
		
			
				
					|  |  |  | return false; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | this.conversionFactor = marginStartPos/((amountOfSteps-1) * step.step); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | DOMutil.cleanupElements(this.DOMelements); | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
	
		
			
				
					|  |  | @ -3477,6 +3647,8 @@ function Linegraph(body, options) { | 
			
		
	
		
			
				
					|  |  |  | this.defaultOptions = { | 
			
		
	
		
			
				
					|  |  |  | yAxisOrientation: 'left', | 
			
		
	
		
			
				
					|  |  |  | defaultGroup: 'default', | 
			
		
	
		
			
				
					|  |  |  | sort: true, | 
			
		
	
		
			
				
					|  |  |  | sampling: true, | 
			
		
	
		
			
				
					|  |  |  | graphHeight: '400px', | 
			
		
	
		
			
				
					|  |  |  | shaded: { | 
			
		
	
		
			
				
					|  |  |  | enabled: false, | 
			
		
	
	
		
			
				
					|  |  | @ -3623,7 +3795,7 @@ Linegraph.prototype._create = function(){ | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype.setOptions = function(options) { | 
			
		
	
		
			
				
					|  |  |  | if (options) { | 
			
		
	
		
			
				
					|  |  |  | var fields = ['defaultGroup','graphHeight','yAxisOrientation','style','barChart','dataAxis']; | 
			
		
	
		
			
				
					|  |  |  | var fields = ['sampling','defaultGroup','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort']; | 
			
		
	
		
			
				
					|  |  |  | util.selectiveDeepExtend(fields, this.options, options); | 
			
		
	
		
			
				
					|  |  |  | util._mergeOptions(this.options, options,'catmullRom'); | 
			
		
	
		
			
				
					|  |  |  | util._mergeOptions(this.options, options,'drawPoints'); | 
			
		
	
	
		
			
				
					|  |  | @ -3790,6 +3962,7 @@ Linegraph.prototype.setGroups = function(groups) { | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._onUpdate = function(ids) { | 
			
		
	
		
			
				
					|  |  |  | this._updateUngrouped(); | 
			
		
	
		
			
				
					|  |  |  | this._updateAllGroupData(); | 
			
		
	
		
			
				
					|  |  |  | this._updateGraph(); | 
			
		
	
		
			
				
					|  |  |  | this.redraw(); | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
	
		
			
				
					|  |  | @ -3800,7 +3973,9 @@ Linegraph.prototype._onUpdateGroups  = function (groupIds) { | 
			
		
	
		
			
				
					|  |  |  | var group = this.groupsData.get(groupIds[i]); | 
			
		
	
		
			
				
					|  |  |  | this._updateGroup(group, groupIds[i]); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | this._updateUngrouped(); | 
			
		
	
		
			
				
					|  |  |  | this._updateAllGroupData(); | 
			
		
	
		
			
				
					|  |  |  | this._updateGraph(); | 
			
		
	
		
			
				
					|  |  |  | this.redraw(); | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
	
		
			
				
					|  |  | @ -3861,6 +4036,20 @@ Linegraph.prototype._updateGroup = function (group, groupId) { | 
			
		
	
		
			
				
					|  |  |  | this.legendRight.redraw(); | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._updateAllGroupData = function () { | 
			
		
	
		
			
				
					|  |  |  | if (this.itemsData != null) { | 
			
		
	
		
			
				
					|  |  |  | for (var groupId in this.groups) { | 
			
		
	
		
			
				
					|  |  |  | if (this.groups.hasOwnProperty(groupId)) { | 
			
		
	
		
			
				
					|  |  |  | this.groups[groupId].setItems(this.itemsData.get({filter: | 
			
		
	
		
			
				
					|  |  |  | function (item) { | 
			
		
	
		
			
				
					|  |  |  | return (item.group == groupId); | 
			
		
	
		
			
				
					|  |  |  | }, | 
			
		
	
		
			
				
					|  |  |  | type: {x:"Date"}} | 
			
		
	
		
			
				
					|  |  |  | )); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * Create or delete the group holding all ungrouped items. This group is used when | 
			
		
	
	
		
			
				
					|  |  | @ -3945,16 +4134,92 @@ Linegraph.prototype._updateGraph = function () { | 
			
		
	
		
			
				
					|  |  |  | // reset the svg elements | 
			
		
	
		
			
				
					|  |  |  | DOMutil.prepareElements(this.svgElements); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // todo: discuss with Jos why this filter is so HORRIBLY slow (factor 5!) | 
			
		
	
		
			
				
					|  |  |  | //        groupData = group.itemsData.get({filter: | 
			
		
	
		
			
				
					|  |  |  | //          function (item) { | 
			
		
	
		
			
				
					|  |  |  | //            return (item.x > minDate && item.x < maxDate); | 
			
		
	
		
			
				
					|  |  |  | //          }} | 
			
		
	
		
			
				
					|  |  |  | //        ); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (this.width != 0 && this.itemsData != null) { | 
			
		
	
		
			
				
					|  |  |  | // look at different lines | 
			
		
	
		
			
				
					|  |  |  | var groupIds = this.itemsData.distinct('group'); | 
			
		
	
		
			
				
					|  |  |  | var group; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var group, groupData, preprocessedGroup, i; | 
			
		
	
		
			
				
					|  |  |  | var preprocessedGroupData = []; | 
			
		
	
		
			
				
					|  |  |  | var processedGroupData = []; | 
			
		
	
		
			
				
					|  |  |  | var groupRanges = []; | 
			
		
	
		
			
				
					|  |  |  | var changeCalled = false; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // this is the range of the SVG canvas | 
			
		
	
		
			
				
					|  |  |  | var minDate = this.body.util.toTime(- this.body.domProps.root.width); | 
			
		
	
		
			
				
					|  |  |  | var maxDate = this.body.util.toTime(2 * this.body.domProps.root.width); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // first select and preprocess the data from the datasets. | 
			
		
	
		
			
				
					|  |  |  | // the groups have their preselection of data, we now loop over this data to see | 
			
		
	
		
			
				
					|  |  |  | // what data we need to draw. Sorted data is much faster. | 
			
		
	
		
			
				
					|  |  |  | // more optimization is possible by doing the sampling before and using the binary search | 
			
		
	
		
			
				
					|  |  |  | // to find the end date to determine the increment. | 
			
		
	
		
			
				
					|  |  |  | if (groupIds.length > 0) { | 
			
		
	
		
			
				
					|  |  |  | this._updateYAxis(groupIds); | 
			
		
	
		
			
				
					|  |  |  | for (var i = 0; i < groupIds.length; i++) { | 
			
		
	
		
			
				
					|  |  |  | for (i = 0; i < groupIds.length; i++) { | 
			
		
	
		
			
				
					|  |  |  | group = this.groups[groupIds[i]]; | 
			
		
	
		
			
				
					|  |  |  | this._drawGraph(group); | 
			
		
	
		
			
				
					|  |  |  | groupData = []; | 
			
		
	
		
			
				
					|  |  |  | // optimization for sorted data | 
			
		
	
		
			
				
					|  |  |  | if (group.options.sort == true) { | 
			
		
	
		
			
				
					|  |  |  | var guess = Math.max(0,util.binarySearchGeneric(group.itemsData, minDate, 'x', 'before')); | 
			
		
	
		
			
				
					|  |  |  | for (var j = guess; j < group.itemsData.length; j++) { | 
			
		
	
		
			
				
					|  |  |  | var item = group.itemsData[j]; | 
			
		
	
		
			
				
					|  |  |  | if (item !== undefined) { | 
			
		
	
		
			
				
					|  |  |  | if (item.x > maxDate) { | 
			
		
	
		
			
				
					|  |  |  | groupData.push(item); | 
			
		
	
		
			
				
					|  |  |  | break; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | groupData.push(item); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | for (var j = 0; j < group.itemsData.length; j++) { | 
			
		
	
		
			
				
					|  |  |  | var item = group.itemsData[j]; | 
			
		
	
		
			
				
					|  |  |  | if (item !== undefined) { | 
			
		
	
		
			
				
					|  |  |  | if (item.x > minDate && item.x < maxDate) { | 
			
		
	
		
			
				
					|  |  |  | groupData.push(item); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | // preprocess, split into ranges and data | 
			
		
	
		
			
				
					|  |  |  | preprocessedGroup = this._preprocessData(groupData, group); | 
			
		
	
		
			
				
					|  |  |  | groupRanges.push({min: preprocessedGroup.min, max: preprocessedGroup.max}); | 
			
		
	
		
			
				
					|  |  |  | preprocessedGroupData.push(preprocessedGroup.data); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // update the Y axis first, we use this data to draw at the correct Y points | 
			
		
	
		
			
				
					|  |  |  | // changeCalled is required to clean the SVG on a change emit. | 
			
		
	
		
			
				
					|  |  |  | changeCalled = this._updateYAxis(groupIds, groupRanges); | 
			
		
	
		
			
				
					|  |  |  | if (changeCalled == true) { | 
			
		
	
		
			
				
					|  |  |  | DOMutil.cleanupElements(this.svgElements); | 
			
		
	
		
			
				
					|  |  |  | this.body.emitter.emit("change"); | 
			
		
	
		
			
				
					|  |  |  | return; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // with the yAxis scaled correctly, use this to get the Y values of the points. | 
			
		
	
		
			
				
					|  |  |  | for (i = 0; i < groupIds.length; i++) { | 
			
		
	
		
			
				
					|  |  |  | group = this.groups[groupIds[i]]; | 
			
		
	
		
			
				
					|  |  |  | processedGroupData.push(this._convertYvalues(preprocessedGroupData[i],group.options)) | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // draw the groups | 
			
		
	
		
			
				
					|  |  |  | for (i = 0; i < groupIds.length; i++) { | 
			
		
	
		
			
				
					|  |  |  | group = this.groups[groupIds[i]]; | 
			
		
	
		
			
				
					|  |  |  | if (group.options.style == 'line') { | 
			
		
	
		
			
				
					|  |  |  | this._drawLineGraph(processedGroupData[i], group); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | this._drawBarGraph (processedGroupData[i], group); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
	
		
			
				
					|  |  | @ -3968,13 +4233,13 @@ Linegraph.prototype._updateGraph = function () { | 
			
		
	
		
			
				
					|  |  |  | * @param {array} groupIds | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._updateYAxis = function (groupIds) { | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._updateYAxis = function (groupIds, groupRanges) { | 
			
		
	
		
			
				
					|  |  |  | var changeCalled = false; | 
			
		
	
		
			
				
					|  |  |  | var yAxisLeftUsed = false; | 
			
		
	
		
			
				
					|  |  |  | var yAxisRightUsed = false; | 
			
		
	
		
			
				
					|  |  |  | var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; | 
			
		
	
		
			
				
					|  |  |  | var orientation = 'left'; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // if groups are present | 
			
		
	
		
			
				
					|  |  |  | if (groupIds.length > 0) { | 
			
		
	
		
			
				
					|  |  |  | for (var i = 0; i < groupIds.length; i++) { | 
			
		
	
	
		
			
				
					|  |  | @ -3984,32 +4249,19 @@ Linegraph.prototype._updateYAxis = function (groupIds) { | 
			
		
	
		
			
				
					|  |  |  | orientation = 'right'; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var view = new vis.DataSet(this.itemsData.get({filter: function (item) { | 
			
		
	
		
			
				
					|  |  |  | return item.group == groupIds[i]; | 
			
		
	
		
			
				
					|  |  |  | }})); | 
			
		
	
		
			
				
					|  |  |  | minVal = view.min("y").y; | 
			
		
	
		
			
				
					|  |  |  | maxVal = view.max("y").y; | 
			
		
	
		
			
				
					|  |  |  | minVal = groupRanges[i].min; | 
			
		
	
		
			
				
					|  |  |  | maxVal = groupRanges[i].max; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (orientation == 'left') { | 
			
		
	
		
			
				
					|  |  |  | yAxisLeftUsed = true; | 
			
		
	
		
			
				
					|  |  |  | if (minLeft > minVal) { | 
			
		
	
		
			
				
					|  |  |  | minLeft = minVal; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | if (maxLeft < maxVal) { | 
			
		
	
		
			
				
					|  |  |  | maxLeft = maxVal; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | minLeft = minLeft > minVal ? minVal : minLeft; | 
			
		
	
		
			
				
					|  |  |  | maxLeft = maxLeft < maxVal ? maxVal : maxLeft; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | yAxisRightUsed = true; | 
			
		
	
		
			
				
					|  |  |  | if (minRight > minVal) { | 
			
		
	
		
			
				
					|  |  |  | minRight = minVal; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | if (maxRight < maxVal) { | 
			
		
	
		
			
				
					|  |  |  | maxRight = maxVal; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | minRight = minRight > minVal ? minVal : minRight; | 
			
		
	
		
			
				
					|  |  |  | maxRight = maxRight < maxVal ? maxVal : maxRight; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | delete view; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | if (yAxisLeftUsed == true) { | 
			
		
	
		
			
				
					|  |  |  | this.yAxisLeft.setRange(minLeft, maxLeft); | 
			
		
	
	
		
			
				
					|  |  | @ -4040,13 +4292,14 @@ Linegraph.prototype._updateYAxis = function (groupIds) { | 
			
		
	
		
			
				
					|  |  |  | if (yAxisRightUsed == true) { | 
			
		
	
		
			
				
					|  |  |  | this.yAxisLeft.lineOffset = this.yAxisRight.width; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | this.yAxisLeft.redraw(); | 
			
		
	
		
			
				
					|  |  |  | changeCalled = this.yAxisLeft.redraw() || changeCalled; | 
			
		
	
		
			
				
					|  |  |  | this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; | 
			
		
	
		
			
				
					|  |  |  | this.yAxisRight.redraw(); | 
			
		
	
		
			
				
					|  |  |  | changeCalled = this.yAxisRight.redraw() || changeCalled; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | this.yAxisRight.redraw(); | 
			
		
	
		
			
				
					|  |  |  | changeCalled = this.yAxisRight.redraw() || changeCalled; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | return changeCalled; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
	
		
			
				
					|  |  | @ -4075,31 +4328,14 @@ Linegraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * determine if the graph is a bar or line, get the group options and the datapoints. Then draw the graph. | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * @param groupId | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._drawGraph = function (group) { | 
			
		
	
		
			
				
					|  |  |  | var datapoints = this.itemsData.get({filter: function (item) {return item.group == group.id;}, type: {x:"Date"}}); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (group.options.style == 'line') { | 
			
		
	
		
			
				
					|  |  |  | this._drawLineGraph(datapoints, group); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | this._drawBarGraph(datapoints, group); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * draw a bar graph | 
			
		
	
		
			
				
					|  |  |  | * @param datapoints | 
			
		
	
		
			
				
					|  |  |  | * @param group | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._drawBarGraph = function (datapoints, group) { | 
			
		
	
		
			
				
					|  |  |  | if (datapoints != null) { | 
			
		
	
		
			
				
					|  |  |  | if (datapoints.length > 0) { | 
			
		
	
		
			
				
					|  |  |  | var dataset = this._prepareData(datapoints, group.options); | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._drawBarGraph = function (dataset, group) { | 
			
		
	
		
			
				
					|  |  |  | if (dataset != null) { | 
			
		
	
		
			
				
					|  |  |  | if (dataset.length > 0) { | 
			
		
	
		
			
				
					|  |  |  | var width, coreDistance; | 
			
		
	
		
			
				
					|  |  |  | var minWidth = 0.1 * group.options.barChart.width; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
	
		
			
				
					|  |  | @ -4128,12 +4364,11 @@ Linegraph.prototype._drawBarGraph = function (datapoints, group) { | 
			
		
	
		
			
				
					|  |  |  | * @param datapoints | 
			
		
	
		
			
				
					|  |  |  | * @param group | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._drawLineGraph = function (datapoints, group) { | 
			
		
	
		
			
				
					|  |  |  | if (datapoints != null) { | 
			
		
	
		
			
				
					|  |  |  | if (datapoints.length > 0) { | 
			
		
	
		
			
				
					|  |  |  | var dataset = this._prepareData(datapoints, group.options); | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._drawLineGraph = function (dataset, group) { | 
			
		
	
		
			
				
					|  |  |  | if (dataset != null) { | 
			
		
	
		
			
				
					|  |  |  | if (dataset.length > 0) { | 
			
		
	
		
			
				
					|  |  |  | var path, d; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var svgHeight = Number(this.svg.style.height.replace("px","")); | 
			
		
	
		
			
				
					|  |  |  | path = DOMutil.getSVGElement('path', this.svgElements, this.svg); | 
			
		
	
		
			
				
					|  |  |  | path.setAttributeNS(null, "class", group.className); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
	
		
			
				
					|  |  | @ -4153,7 +4388,7 @@ Linegraph.prototype._drawLineGraph = function (datapoints, group) { | 
			
		
	
		
			
				
					|  |  |  | dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | dFill = "M" + dataset[0].x + "," + this.svg.offsetHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + this.svg.offsetHeight; | 
			
		
	
		
			
				
					|  |  |  | dFill = "M" + dataset[0].x + "," + svgHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + svgHeight; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | fillPath.setAttributeNS(null, "class", group.className + " fill"); | 
			
		
	
		
			
				
					|  |  |  | fillPath.setAttributeNS(null, "d", dFill); | 
			
		
	
	
		
			
				
					|  |  | @ -4185,6 +4420,48 @@ Linegraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg) | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the | 
			
		
	
		
			
				
					|  |  |  | * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for | 
			
		
	
		
			
				
					|  |  |  | * the yAxis. | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * @param datapoints | 
			
		
	
		
			
				
					|  |  |  | * @returns {Array} | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._preprocessData = function (datapoints, group) { | 
			
		
	
		
			
				
					|  |  |  | var extractedData = []; | 
			
		
	
		
			
				
					|  |  |  | var xValue, yValue, increment; | 
			
		
	
		
			
				
					|  |  |  | var toScreen = this.body.util.toScreen; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var yMin = datapoints[0].y; | 
			
		
	
		
			
				
					|  |  |  | var yMax = datapoints[0].y; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop | 
			
		
	
		
			
				
					|  |  |  | // of width changing of the yAxis. | 
			
		
	
		
			
				
					|  |  |  | if (group.options.sampling == true) { | 
			
		
	
		
			
				
					|  |  |  | var xDistance = this.body.util.toGlobalScreen(datapoints[datapoints.length-1].x) - this.body.util.toGlobalScreen(datapoints[0].x); | 
			
		
	
		
			
				
					|  |  |  | var amountOfPoints = datapoints.length; | 
			
		
	
		
			
				
					|  |  |  | var pointsPerPixel = amountOfPoints/xDistance; | 
			
		
	
		
			
				
					|  |  |  | increment = Math.max(1,Math.round(pointsPerPixel)); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | increment = 1; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | for (var i = 0; i < amountOfPoints; i += increment) { | 
			
		
	
		
			
				
					|  |  |  | xValue = toScreen(datapoints[i].x) + this.width - 1; | 
			
		
	
		
			
				
					|  |  |  | yValue = datapoints[i].y; | 
			
		
	
		
			
				
					|  |  |  | extractedData.push({x: xValue, y: yValue}); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | yMin = yMin > yValue ? yValue : yMin; | 
			
		
	
		
			
				
					|  |  |  | yMax = yMax < yValue ? yValue : yMax; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // extractedData.sort(function (a,b) {return a.x - b.x;}); | 
			
		
	
		
			
				
					|  |  |  | return {min: yMin, max: yMax, data: extractedData}; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * This uses the DataAxis object to generate the correct Y coordinate on the SVG window. It uses the | 
			
		
	
		
			
				
					|  |  |  | * util function toScreen to get the x coordinate from the timestamp. | 
			
		
	
	
		
			
				
					|  |  | @ -4194,11 +4471,11 @@ Linegraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg) | 
			
		
	
		
			
				
					|  |  |  | * @returns {Array} | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._prepareData = function (datapoints, options) { | 
			
		
	
		
			
				
					|  |  |  | Linegraph.prototype._convertYvalues = function (datapoints, options) { | 
			
		
	
		
			
				
					|  |  |  | var extractedData = []; | 
			
		
	
		
			
				
					|  |  |  | var xValue, yValue; | 
			
		
	
		
			
				
					|  |  |  | var axis = this.yAxisLeft; | 
			
		
	
		
			
				
					|  |  |  | var toScreen = this.body.util.toScreen; | 
			
		
	
		
			
				
					|  |  |  | var svgHeight = Number(this.svg.style.height.replace("px","")); | 
			
		
	
		
			
				
					|  |  |  | this.zeroPosition = 0; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (options.yAxisOrientation == 'right') { | 
			
		
	
	
		
			
				
					|  |  | @ -4206,12 +4483,12 @@ Linegraph.prototype._prepareData = function (datapoints, options) { | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | for (var i = 0; i < datapoints.length; i++) { | 
			
		
	
		
			
				
					|  |  |  | xValue = Math.round(toScreen(datapoints[i].x) + this.width - 1); | 
			
		
	
		
			
				
					|  |  |  | xValue = datapoints[i].x; | 
			
		
	
		
			
				
					|  |  |  | yValue = Math.round(axis.convertValue(datapoints[i].y)); | 
			
		
	
		
			
				
					|  |  |  | extractedData.push({x: xValue, y: yValue}); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | this.zeroPosition = Math.min(this.svg.offsetHeight, axis.convertValue(0)); | 
			
		
	
		
			
				
					|  |  |  | this.zeroPosition = Math.min(svgHeight, axis.convertValue(0)); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // extractedData.sort(function (a,b) {return a.x - b.x;}); | 
			
		
	
		
			
				
					|  |  |  | return extractedData; | 
			
		
	
	
		
			
				
					|  |  | @ -4251,12 +4528,12 @@ Linegraph.prototype._catmullRomUniform = function(data) { | 
			
		
	
		
			
				
					|  |  |  | //    bp0 = { x: p2.x,                               y: p2.y }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | d += "C" + | 
			
		
	
		
			
				
					|  |  |  | Math.round(bp1.x) + "," + | 
			
		
	
		
			
				
					|  |  |  | Math.round(bp1.y) + " " + | 
			
		
	
		
			
				
					|  |  |  | Math.round(bp2.x) + "," + | 
			
		
	
		
			
				
					|  |  |  | Math.round(bp2.y) + " " + | 
			
		
	
		
			
				
					|  |  |  | Math.round(p2.x) + "," + | 
			
		
	
		
			
				
					|  |  |  | Math.round(p2.y) + " "; | 
			
		
	
		
			
				
					|  |  |  | bp1.x + "," + | 
			
		
	
		
			
				
					|  |  |  | bp1.y + " " + | 
			
		
	
		
			
				
					|  |  |  | bp2.x + "," + | 
			
		
	
		
			
				
					|  |  |  | bp2.y + " " + | 
			
		
	
		
			
				
					|  |  |  | p2.x + "," + | 
			
		
	
		
			
				
					|  |  |  | p2.y + " "; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | return d; | 
			
		
	
	
		
			
				
					|  |  | @ -4331,12 +4608,12 @@ Linegraph.prototype._catmullRom = function(data, group) { | 
			
		
	
		
			
				
					|  |  |  | if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} | 
			
		
	
		
			
				
					|  |  |  | if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} | 
			
		
	
		
			
				
					|  |  |  | d += "C" + | 
			
		
	
		
			
				
					|  |  |  | Math.round(bp1.x) + "," + | 
			
		
	
		
			
				
					|  |  |  | Math.round(bp1.y) + " " + | 
			
		
	
		
			
				
					|  |  |  | Math.round(bp2.x) + "," + | 
			
		
	
		
			
				
					|  |  |  | Math.round(bp2.y) + " " + | 
			
		
	
		
			
				
					|  |  |  | Math.round(p2.x) + "," + | 
			
		
	
		
			
				
					|  |  |  | Math.round(p2.y) + " "; | 
			
		
	
		
			
				
					|  |  |  | bp1.x + "," + | 
			
		
	
		
			
				
					|  |  |  | bp1.y + " " + | 
			
		
	
		
			
				
					|  |  |  | bp2.x + "," + | 
			
		
	
		
			
				
					|  |  |  | bp2.y + " " + | 
			
		
	
		
			
				
					|  |  |  | p2.x + "," + | 
			
		
	
		
			
				
					|  |  |  | p2.y + " "; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | return d; | 
			
		
	
	
		
			
				
					|  |  | @ -4354,10 +4631,10 @@ Linegraph.prototype._linear = function(data) { | 
			
		
	
		
			
				
					|  |  |  | var d = ""; | 
			
		
	
		
			
				
					|  |  |  | for (var i = 0; i < data.length; i++) { | 
			
		
	
		
			
				
					|  |  |  | if (i == 0) { | 
			
		
	
		
			
				
					|  |  |  | d += Math.round(data[i].x) + "," + Math.round(data[i].y); | 
			
		
	
		
			
				
					|  |  |  | d += data[i].x + "," + data[i].y; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | d += " " + Math.round(data[i].x) + "," + Math.round(data[i].y); | 
			
		
	
		
			
				
					|  |  |  | d += " " + data[i].x + "," + data[i].y; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | return d; | 
			
		
	
	
		
			
				
					|  |  | @ -4427,10 +4704,11 @@ function DataStep(start, end, minimumStep, containerHeight, forcedStepSize) { | 
			
		
	
		
			
				
					|  |  |  | DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, forcedStepSize) { | 
			
		
	
		
			
				
					|  |  |  | this._start = start; | 
			
		
	
		
			
				
					|  |  |  | this._end = end; | 
			
		
	
		
			
				
					|  |  |  | this.setFirst(); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (this.autoScale) { | 
			
		
	
		
			
				
					|  |  |  | this.setMinimumStep(minimumStep, containerHeight, forcedStepSize); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | this.setFirst(); | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
	
		
			
				
					|  |  | @ -4447,8 +4725,13 @@ DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { | 
			
		
	
		
			
				
					|  |  |  | var minorStepIdx = -1; | 
			
		
	
		
			
				
					|  |  |  | var magnitudefactor = Math.pow(10,orderOfMagnitude); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var start = 0; | 
			
		
	
		
			
				
					|  |  |  | if (orderOfMagnitude < 0) { | 
			
		
	
		
			
				
					|  |  |  | start = orderOfMagnitude; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var solutionFound = false; | 
			
		
	
		
			
				
					|  |  |  | for (var i = 0; i <= orderOfMagnitude; i++) { | 
			
		
	
		
			
				
					|  |  |  | 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]; | 
			
		
	
	
		
			
				
					|  |  | @ -8928,14 +9211,14 @@ Group.prototype._updateVisibleItems = function(orderedItems, visibleItems, range | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // If there were no visible items previously, use binarySearch to find a visible ItemPoint or ItemRange (based on startTime) | 
			
		
	
		
			
				
					|  |  |  | if (newVisibleItems.length == 0) { | 
			
		
	
		
			
				
					|  |  |  | initialPosByStart = this._binarySearch(orderedItems, range, false); | 
			
		
	
		
			
				
					|  |  |  | initialPosByStart = util.binarySearch(orderedItems.byStart, range, 'data','start'); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | initialPosByStart = orderedItems.byStart.indexOf(newVisibleItems[0]); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // use visible search to find a visible ItemRange (only based on endTime) | 
			
		
	
		
			
				
					|  |  |  | var initialPosByEnd = this._binarySearch(orderedItems, range, true); | 
			
		
	
		
			
				
					|  |  |  | var initialPosByEnd = util.binarySearch(orderedItems.byEnd, range, 'data','end'); | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // if we found a initial ID to use, trace it up and down until we meet an invisible item. | 
			
		
	
		
			
				
					|  |  |  | if (initialPosByStart != -1) { | 
			
		
	
	
		
			
				
					|  |  | @ -8960,72 +9243,7 @@ Group.prototype._updateVisibleItems = function(orderedItems, visibleItems, range | 
			
		
	
		
			
				
					|  |  |  | return newVisibleItems; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd | 
			
		
	
		
			
				
					|  |  |  | * arrays. This is done by giving a boolean value true if you want to use the byEnd. | 
			
		
	
		
			
				
					|  |  |  | * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check | 
			
		
	
		
			
				
					|  |  |  | * if the time we selected (start or end) is within the current range). | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is | 
			
		
	
		
			
				
					|  |  |  | * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest, | 
			
		
	
		
			
				
					|  |  |  | * either the start OR end time has to be in the range. | 
			
		
	
		
			
				
					|  |  |  | * | 
			
		
	
		
			
				
					|  |  |  | * @param {{byStart: Item[], byEnd: Item[]}} orderedItems | 
			
		
	
		
			
				
					|  |  |  | * @param {{start: number, end: number}} range | 
			
		
	
		
			
				
					|  |  |  | * @param {Boolean} byEnd | 
			
		
	
		
			
				
					|  |  |  | * @returns {number} | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | Group.prototype._binarySearch = function(orderedItems, range, byEnd) { | 
			
		
	
		
			
				
					|  |  |  | var array = []; | 
			
		
	
		
			
				
					|  |  |  | var byTime = byEnd ? 'end' : 'start'; | 
			
		
	
		
			
				
					|  |  |  | if (byEnd == true) {array = orderedItems.byEnd;  } | 
			
		
	
		
			
				
					|  |  |  | else               {array = orderedItems.byStart;} | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var interval = range.end - range.start; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | var found = false; | 
			
		
	
		
			
				
					|  |  |  | var low = 0; | 
			
		
	
		
			
				
					|  |  |  | var high = array.length; | 
			
		
	
		
			
				
					|  |  |  | var guess = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | var newGuess; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | if (high == 0) {guess = -1;} | 
			
		
	
		
			
				
					|  |  |  | else if (high == 1) { | 
			
		
	
		
			
				
					|  |  |  | if ((array[guess].data[byTime] > range.start - interval) && (array[guess].data[byTime] < range.end)) { | 
			
		
	
		
			
				
					|  |  |  | guess =  0; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | guess = -1; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | high -= 1; | 
			
		
	
		
			
				
					|  |  |  | while (found == false) { | 
			
		
	
		
			
				
					|  |  |  | if ((array[guess].data[byTime] > range.start - interval) && (array[guess].data[byTime] < range.end)) { | 
			
		
	
		
			
				
					|  |  |  | found = true; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | if (array[guess].data[byTime] < range.start - interval) { // it is too small --> increase low | 
			
		
	
		
			
				
					|  |  |  | low = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else {  // it is too big --> decrease high | 
			
		
	
		
			
				
					|  |  |  | high = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | newGuess = Math.floor(0.5*(high+low)); | 
			
		
	
		
			
				
					|  |  |  | // not in list; | 
			
		
	
		
			
				
					|  |  |  | if (guess == newGuess) { | 
			
		
	
		
			
				
					|  |  |  | guess = -1; | 
			
		
	
		
			
				
					|  |  |  | found = true; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | else { | 
			
		
	
		
			
				
					|  |  |  | guess = newGuess; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | return guess; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * this function checks if an item is invisible. If it is NOT we make it visible | 
			
		
	
	
		
			
				
					|  |  | @ -9775,6 +9993,22 @@ Timeline.prototype._toScreen = function(time) { | 
			
		
	
		
			
				
					|  |  |  | return (time.valueOf() - conversion.offset) * conversion.scale; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * Convert a datetime (Date object) into a position on the root | 
			
		
	
		
			
				
					|  |  |  | * This is used to get the pixel density estimate for the screen, not the center panel | 
			
		
	
		
			
				
					|  |  |  | * @param {Date}   time A date | 
			
		
	
		
			
				
					|  |  |  | * @return {int}   x    The position on root in pixels which corresponds | 
			
		
	
		
			
				
					|  |  |  | *                      with the given date. | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | // TODO: move this function to Range | 
			
		
	
		
			
				
					|  |  |  | Graph2d.prototype._toGlobalScreen = function(time) { | 
			
		
	
		
			
				
					|  |  |  | var conversion = this.range.conversion(this.props.root.width); | 
			
		
	
		
			
				
					|  |  |  | return (time.valueOf() - conversion.offset) * conversion.scale; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * Initialize watching when option autoResize is true | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
	
		
			
				
					|  |  | @ -9970,6 +10204,7 @@ function Graph2d (container, items, options, groups) { | 
			
		
	
		
			
				
					|  |  |  | util: { | 
			
		
	
		
			
				
					|  |  |  | snap: null, // will be specified after TimeAxis is created | 
			
		
	
		
			
				
					|  |  |  | toScreen: me._toScreen.bind(me), | 
			
		
	
		
			
				
					|  |  |  | toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width | 
			
		
	
		
			
				
					|  |  |  | toTime: me._toTime.bind(me) | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
	
		
			
				
					|  |  | @ -10005,6 +10240,7 @@ function Graph2d (container, items, options, groups) { | 
			
		
	
		
			
				
					|  |  |  | this.setOptions(options); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! | 
			
		
	
		
			
				
					|  |  |  | if (groups) { | 
			
		
	
		
			
				
					|  |  |  | this.setGroups(groups); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
	
		
			
				
					|  |  | @ -10602,7 +10838,7 @@ Graph2d.prototype.redraw = function() { | 
			
		
	
		
			
				
					|  |  |  | resized = component.redraw() || resized; | 
			
		
	
		
			
				
					|  |  |  | }); | 
			
		
	
		
			
				
					|  |  |  | if (resized) { | 
			
		
	
		
			
				
					|  |  |  | // keep repainting until all sizes are settled | 
			
		
	
		
			
				
					|  |  |  | // keep redrawing until all sizes are settled | 
			
		
	
		
			
				
					|  |  |  | this.redraw(); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
	
		
			
				
					|  |  | @ -10637,6 +10873,21 @@ Graph2d.prototype._toScreen = function(time) { | 
			
		
	
		
			
				
					|  |  |  | return (time.valueOf() - conversion.offset) * conversion.scale; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * Convert a datetime (Date object) into a position on the root | 
			
		
	
		
			
				
					|  |  |  | * This is used to get the pixel density estimate for the screen, not the center panel | 
			
		
	
		
			
				
					|  |  |  | * @param {Date}   time A date | 
			
		
	
		
			
				
					|  |  |  | * @return {int}   x    The position on root in pixels which corresponds | 
			
		
	
		
			
				
					|  |  |  | *                      with the given date. | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
		
			
				
					|  |  |  | */ | 
			
		
	
		
			
				
					|  |  |  | // TODO: move this function to Range | 
			
		
	
		
			
				
					|  |  |  | Graph2d.prototype._toGlobalScreen = function(time) { | 
			
		
	
		
			
				
					|  |  |  | var conversion = this.range.conversion(this.props.root.width); | 
			
		
	
		
			
				
					|  |  |  | return (time.valueOf() - conversion.offset) * conversion.scale; | 
			
		
	
		
			
				
					|  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  | * Initialize watching when option autoResize is true | 
			
		
	
		
			
				
					|  |  |  | * @private | 
			
		
	
	
		
			
				
					|  |  | @ -18989,6 +19240,7 @@ function Graph (container, data, options) { | 
			
		
	
		
			
				
					|  |  |  | // these functions are triggered when the dataset is edited | 
			
		
	
		
			
				
					|  |  |  | this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
			
		
	
		
			
				
					|  |  |  | // set constant values | 
			
		
	
		
			
				
					|  |  |  | this.constants = { | 
			
		
	
		
			
				
					|  |  |  | nodes: { | 
			
		
	
	
		
			
				
					|  |  |  |