diff --git a/HISTORY.md b/HISTORY.md index 8ca7c259..fcf886d0 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,10 +11,12 @@ http://visjs.org ### Graph2d - Fixed round-off errors of zero on the y-axis. +- added show major/minor lines options to dataAxis. ### Timeline - Support for custom date formatting of the labels on the time axis. +- added show major/minor lines options to timeline. ## 2014-12-09, version 3.7.2 diff --git a/dist/vis.js b/dist/vis.js index efbe3065..4bbeb840 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -106,7 +106,7 @@ return /******/ (function(modules) { // webpackBootstrap exports.Graph2d = __webpack_require__(42); exports.timeline = { DateUtil: __webpack_require__(24), - DataStep: __webpack_require__(44), + DataStep: __webpack_require__(50), Range: __webpack_require__(21), stack: __webpack_require__(28), TimeStep: __webpack_require__(38), @@ -123,12 +123,12 @@ return /******/ (function(modules) { // webpackBootstrap Component: __webpack_require__(23), CurrentTime: __webpack_require__(39), CustomTime: __webpack_require__(41), - DataAxis: __webpack_require__(45), - GraphGroup: __webpack_require__(46), + DataAxis: __webpack_require__(44), + GraphGroup: __webpack_require__(45), Group: __webpack_require__(27), BackgroundGroup: __webpack_require__(31), ItemSet: __webpack_require__(26), - Legend: __webpack_require__(50), + Legend: __webpack_require__(49), LineGraph: __webpack_require__(43), TimeAxis: __webpack_require__(37) } @@ -18069,6 +18069,8 @@ return /******/ (function(modules) { // webpackBootstrap // TODO: implement timeaxis orientations 'left' and 'right' showMinorLabels: true, showMajorLabels: true, + showMajorLines: true, + showMinorLines: true, format: null }; this.options = util.extend({}, this.defaultOptions); @@ -18094,7 +18096,7 @@ return /******/ (function(modules) { // webpackBootstrap TimeAxis.prototype.setOptions = function(options) { if (options) { // copy all options that we know - util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels','hiddenDates', 'format'], this.options, options); + util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels', 'showMinorLines', 'showMajorLines','hiddenDates', 'format'], this.options, options); // apply locale to moment.js // TODO: not so nice, this is applied globally to moment.js @@ -18253,9 +18255,11 @@ return /******/ (function(modules) { // webpackBootstrap } this._repaintMajorText(x, step.getLabelMajor(), orientation); } - this._repaintMajorLine(x, orientation); + if (this.options.showMajorLines == true) { + this._repaintMajorLine(x, orientation); + } } - else { + else if (this.options.showMinorLines == true) { this._repaintMinorLine(x, orientation); } @@ -19631,10 +19635,10 @@ return /******/ (function(modules) { // webpackBootstrap var DataSet = __webpack_require__(7); var DataView = __webpack_require__(9); var Component = __webpack_require__(23); - var DataAxis = __webpack_require__(45); - var GraphGroup = __webpack_require__(46); - var Legend = __webpack_require__(50); - var BarGraphFunctions = __webpack_require__(49); + var DataAxis = __webpack_require__(44); + var GraphGroup = __webpack_require__(45); + var Legend = __webpack_require__(49); + var BarGraphFunctions = __webpack_require__(48); var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items @@ -19678,6 +19682,8 @@ return /******/ (function(modules) { // webpackBootstrap dataAxis: { showMinorLabels: true, showMajorLabels: true, + showMinorLines: true, + showMajorLines: true, icons: false, width: '40px', visible: true, @@ -20618,582 +20624,305 @@ return /******/ (function(modules) { // webpackBootstrap /* 44 */ /***/ function(module, exports, __webpack_require__) { + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(6); + var Component = __webpack_require__(23); + var DataStep = __webpack_require__(50); + /** - * @constructor DataStep - * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an - * end data point. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 - * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + * A horizontal time axis + * @param {Object} [options] See DataAxis.setOptions for the available + * options. + * @constructor DataAxis + * @extends Component + * @param body */ - function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { - // variables - this.current = 0; + function DataAxis (body, options, svg, linegraphOptions) { + this.id = util.randomUUID(); + this.body = body; - this.autoScale = true; - this.stepIndex = 0; - this.step = 1; - this.scale = 1; + this.defaultOptions = { + orientation: 'left', // supported: 'left', 'right' + showMinorLabels: true, + showMajorLabels: true, + showMinorLines: true, + showMajorLines: true, + icons: true, + majorLinesOffset: 7, + minorLinesOffset: 4, + labelOffsetX: 10, + labelOffsetY: 2, + iconWidth: 20, + width: '40px', + visible: true, + alignZeros: true, + customRange: { + left: {min:undefined, max:undefined}, + right: {min:undefined, max:undefined} + }, + title: { + left: {text:undefined}, + right: {text:undefined} + }, + format: { + left: {decimals: undefined}, + right: {decimals: undefined} + } + }; - this.marginStart; - this.marginEnd; - this.deadSpace = 0; + this.linegraphOptions = linegraphOptions; + this.linegraphSVG = svg; + this.props = {}; + this.DOMelements = { // dynamic elements + lines: {}, + labels: {}, + title: {} + }; - this.majorSteps = [1, 2, 5, 10]; - this.minorSteps = [0.25, 0.5, 1, 2]; + this.dom = {}; - this.alignZeros = alignZeros; + this.range = {start:0, end:0}; - this.setRange(start, end, minimumStep, containerHeight, customRange); - } + this.options = util.extend({}, this.defaultOptions); + this.conversionFactor = 1; + this.setOptions(options); + this.width = Number(('' + this.options.width).replace("px","")); + this.minWidth = this.width; + this.height = this.linegraphSVG.offsetHeight; + this.hidden = false; + this.stepPixels = 25; + this.stepPixelsForced = 25; + this.zeroCrossing = -1; - /** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Number} [start] The start date and time. - * @param {Number} [end] The end date and time. - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ - DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { - this._start = customRange.min === undefined ? start : customRange.min; - this._end = customRange.max === undefined ? end : customRange.max; + this.lineOffset = 0; + this.master = true; + this.svgElements = {}; + this.iconsRemoved = false; - if (this._start == this._end) { - this._start -= 0.75; - this._end += 1; - } - if (this.autoScale == true) { - this.setMinimumStep(minimumStep, containerHeight); - } + this.groups = {}; + this.amountOfGroups = 0; - this.setFirst(customRange); - }; + // create the HTML DOM + this._create(); - /** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds - */ - DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { - // round to floor - var size = this._end - this._start; - var safeSize = size * 1.2; - var minimumStepValue = minimumStep * (safeSize / containerHeight); - var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); + var me = this; + this.body.emitter.on("verticalDrag", function() { + me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; + }); + } - var minorStepIdx = -1; - var magnitudefactor = Math.pow(10,orderOfMagnitude); + DataAxis.prototype = new Component(); - var start = 0; - if (orderOfMagnitude < 0) { - start = orderOfMagnitude; - } - var solutionFound = false; - for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { - magnitudefactor = Math.pow(10,i); - for (var j = 0; j < this.minorSteps.length; j++) { - var stepSize = magnitudefactor * this.minorSteps[j]; - if (stepSize >= minimumStepValue) { - solutionFound = true; - minorStepIdx = j; - break; - } - } - if (solutionFound == true) { - break; - } + DataAxis.prototype.addGroup = function(label, graphOptions) { + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; } - this.stepIndex = minorStepIdx; - this.scale = magnitudefactor; - this.step = magnitudefactor * this.minorSteps[minorStepIdx]; + this.amountOfGroups += 1; }; + DataAxis.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; + }; - - /** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date - */ - DataStep.prototype.setFirst = function(customRange) { - if (customRange === undefined) { - customRange = {}; - } - - var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; - var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; - - this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; - this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; - - // if we need to align the zero's we need to make sure that there is a zero to use. - if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { - this.marginEnd += this.marginEnd % this.step; + DataAxis.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; } + }; - this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; - this.marginRange = this.marginEnd - this.marginStart; + DataAxis.prototype.setOptions = function (options) { + if (options) { + var redraw = false; + if (this.options.orientation != options.orientation && options.orientation !== undefined) { + redraw = true; + } + var fields = [ + 'orientation', + 'showMinorLabels', + 'showMajorLabels', + 'showMajorLines', + 'showMinorLines', + 'icons', + 'majorLinesOffset', + 'minorLinesOffset', + 'labelOffsetX', + 'labelOffsetY', + 'iconWidth', + 'width', + 'visible', + 'customRange', + 'title', + 'format', + 'alignZeros' + ]; + util.selectiveExtend(fields, this.options, options); - this.current = this.marginEnd; - }; + this.minWidth = Number(('' + this.options.width).replace("px","")); - DataStep.prototype.roundToMinor = function(value) { - var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); - if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { - return rounded + (this.scale * this.minorSteps[this.stepIndex]); - } - else { - return rounded; + if (redraw == true && this.dom.frame) { + this.hide(); + this.show(); + } } - } + }; /** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date + * Create the HTML DOM for the DataAxis */ - DataStep.prototype.hasNext = function () { - return (this.current >= this.marginStart); - }; + DataAxis.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.style.width = this.options.width; + this.dom.frame.style.height = this.height; - /** - * Do the next step - */ - DataStep.prototype.next = function() { - var prev = this.current; - this.current -= this.step; - - // safety mechanism: if current time is still unchanged, move to the end - if (this.current == prev) { - this.current = this._end; - } - }; + this.dom.lineContainer = document.createElement('div'); + this.dom.lineContainer.style.width = '100%'; + this.dom.lineContainer.style.height = this.height; + this.dom.lineContainer.style.position = 'relative'; - /** - * Do the next step - */ - DataStep.prototype.previous = function() { - this.current += this.step; - this.marginEnd += this.step; - this.marginRange = this.marginEnd - this.marginStart; + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = "absolute"; + this.svg.style.top = '0px'; + this.svg.style.height = '100%'; + this.svg.style.width = '100%'; + this.svg.style.display = "block"; + this.dom.frame.appendChild(this.svg); }; + DataAxis.prototype._redrawGroupIcons = function () { + DOMutil.prepareElements(this.svgElements); + var x; + var iconWidth = this.options.iconWidth; + var iconHeight = 15; + var iconOffset = 4; + var y = iconOffset + 0.5 * iconHeight; - /** - * Get the current datetime - * @return {String} current The current date - */ - DataStep.prototype.getCurrent = function(decimals) { - // prevent round-off errors when close to zero - var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; - var toPrecision = '' + Number(current).toPrecision(5); - - // If decimals is specified, then limit or extend the string as required - if(decimals !== undefined && !isNaN(Number(decimals))) { - // If string includes exponent, then we need to add it to the end - var exp = ""; - var index = toPrecision.indexOf("e"); - if(index != -1) { - // Get the exponent - exp = toPrecision.slice(index); - // Remove the exponent in case we need to zero-extend - toPrecision = toPrecision.slice(0, index); - } - index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); - if(index === -1) { - // No decimal found - if we want decimals, then we need to add it - if(decimals !== 0) { - toPrecision += '.'; - } - // Calculate how long the string should be - index = toPrecision.length + decimals; - } - else if(decimals !== 0) { - // Calculate how long the string should be - accounting for the decimal place - index += decimals + 1; - } - if(index > toPrecision.length) { - // We need to add zeros! - for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { - toPrecision += '0'; - } - } - else { - // we need to remove characters - toPrecision = toPrecision.slice(0, index); - } - // Add the exponent if there is one - toPrecision += exp; + if (this.options.orientation == 'left') { + x = iconOffset; } else { - if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { - // If no decimal is specified, and there are decimal places, remove trailing zeros - for (var i = toPrecision.length - 1; i > 0; i--) { - if (toPrecision[i] == "0") { - toPrecision = toPrecision.slice(0, i); - } - else if (toPrecision[i] == "." || toPrecision[i] == ",") { - toPrecision = toPrecision.slice(0, i); - break; - } - else { - break; - } + x = this.width - iconWidth - iconOffset; + } + + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + iconOffset; } } } - return toPrecision; + DOMutil.cleanupElements(this.svgElements); + this.iconsRemoved = false; }; - + DataAxis.prototype._cleanupIcons = function() { + if (this.iconsRemoved == false) { + DOMutil.prepareElements(this.svgElements); + DOMutil.cleanupElements(this.svgElements); + this.iconsRemoved = true; + } + } /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate + * Create the HTML DOM for the DataAxis */ - DataStep.prototype.snap = function(date) { + DataAxis.prototype.show = function() { + this.hidden = false; + if (!this.dom.frame.parentNode) { + if (this.options.orientation == 'left') { + this.body.dom.left.appendChild(this.dom.frame); + } + else { + this.body.dom.right.appendChild(this.dom.frame); + } + } + if (!this.dom.lineContainer.parentNode) { + this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); + } }; /** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. + * Create the HTML DOM for the DataAxis */ - DataStep.prototype.isMajor = function() { - return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); - }; - - module.exports = DataStep; - + DataAxis.prototype.hide = function() { + this.hidden = true; + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } -/***/ }, -/* 45 */ -/***/ function(module, exports, __webpack_require__) { + if (this.dom.lineContainer.parentNode) { + this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); + } + }; - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var Component = __webpack_require__(23); - var DataStep = __webpack_require__(44); + /** + * Set a range (start and end) + * @param end + * @param start + * @param end + */ + DataAxis.prototype.setRange = function (start, end) { + if (this.master == false && this.options.alignZeros == true && this.zeroCrossing != -1) { + if (start > 0) { + start = 0; + } + } + this.range.start = start; + this.range.end = end; + }; /** - * A horizontal time axis - * @param {Object} [options] See DataAxis.setOptions for the available - * options. - * @constructor DataAxis - * @extends Component - * @param body + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - function DataAxis (body, options, svg, linegraphOptions) { - this.id = util.randomUUID(); - this.body = body; + DataAxis.prototype.redraw = function () { + var changeCalled = false; + var activeGroups = 0; + + // Make sure the line container adheres to the vertical scrolling. + this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; - this.defaultOptions = { - orientation: 'left', // supported: 'left', 'right' - showMinorLabels: true, - showMajorLabels: true, - icons: true, - majorLinesOffset: 7, - minorLinesOffset: 4, - labelOffsetX: 10, - labelOffsetY: 2, - iconWidth: 20, - width: '40px', - visible: true, - alignZeros: true, - customRange: { - left: {min:undefined, max:undefined}, - right: {min:undefined, max:undefined} - }, - title: { - left: {text:undefined}, - right: {text:undefined} - }, - format: { - left: {decimals: undefined}, - right: {decimals: undefined} + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + activeGroups++; + } } - }; + } + if (this.amountOfGroups == 0 || activeGroups == 0) { + this.hide(); + } + else { + this.show(); + this.height = Number(this.linegraphSVG.style.height.replace("px","")); - this.linegraphOptions = linegraphOptions; - this.linegraphSVG = svg; - this.props = {}; - this.DOMelements = { // dynamic elements - lines: {}, - labels: {}, - title: {} - }; + // svg offsetheight did not work in firefox and explorer... + this.dom.lineContainer.style.height = this.height + 'px'; + this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; - this.dom = {}; + var props = this.props; + var frame = this.dom.frame; - this.range = {start:0, end:0}; + // update classname + frame.className = 'dataaxis'; - this.options = util.extend({}, this.defaultOptions); - this.conversionFactor = 1; + // calculate character width and height + this._calculateCharSize(); - this.setOptions(options); - this.width = Number(('' + this.options.width).replace("px","")); - this.minWidth = this.width; - this.height = this.linegraphSVG.offsetHeight; - this.hidden = false; - - this.stepPixels = 25; - this.stepPixelsForced = 25; - this.zeroCrossing = -1; - - this.lineOffset = 0; - this.master = true; - this.svgElements = {}; - this.iconsRemoved = false; - - - this.groups = {}; - this.amountOfGroups = 0; - - // create the HTML DOM - this._create(); - - var me = this; - this.body.emitter.on("verticalDrag", function() { - me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; - }); - } - - DataAxis.prototype = new Component(); - - - DataAxis.prototype.addGroup = function(label, graphOptions) { - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; - } - this.amountOfGroups += 1; - }; - - DataAxis.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; - }; - - DataAxis.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; - } - }; - - - DataAxis.prototype.setOptions = function (options) { - if (options) { - var redraw = false; - if (this.options.orientation != options.orientation && options.orientation !== undefined) { - redraw = true; - } - var fields = [ - 'orientation', - 'showMinorLabels', - 'showMajorLabels', - 'icons', - 'majorLinesOffset', - 'minorLinesOffset', - 'labelOffsetX', - 'labelOffsetY', - 'iconWidth', - 'width', - 'visible', - 'customRange', - 'title', - 'format', - 'alignZeros' - ]; - util.selectiveExtend(fields, this.options, options); - - this.minWidth = Number(('' + this.options.width).replace("px","")); - - if (redraw == true && this.dom.frame) { - this.hide(); - this.show(); - } - } - }; - - - /** - * Create the HTML DOM for the DataAxis - */ - DataAxis.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.style.width = this.options.width; - this.dom.frame.style.height = this.height; - - this.dom.lineContainer = document.createElement('div'); - this.dom.lineContainer.style.width = '100%'; - this.dom.lineContainer.style.height = this.height; - this.dom.lineContainer.style.position = 'relative'; - - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = "absolute"; - this.svg.style.top = '0px'; - this.svg.style.height = '100%'; - this.svg.style.width = '100%'; - this.svg.style.display = "block"; - this.dom.frame.appendChild(this.svg); - }; - - DataAxis.prototype._redrawGroupIcons = function () { - DOMutil.prepareElements(this.svgElements); - - var x; - var iconWidth = this.options.iconWidth; - var iconHeight = 15; - var iconOffset = 4; - var y = iconOffset + 0.5 * iconHeight; - - if (this.options.orientation == 'left') { - x = iconOffset; - } - else { - x = this.width - iconWidth - iconOffset; - } - - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + iconOffset; - } - } - } - - DOMutil.cleanupElements(this.svgElements); - this.iconsRemoved = false; - }; - - DataAxis.prototype._cleanupIcons = function() { - if (this.iconsRemoved == false) { - DOMutil.prepareElements(this.svgElements); - DOMutil.cleanupElements(this.svgElements); - this.iconsRemoved = true; - } - } - - /** - * Create the HTML DOM for the DataAxis - */ - DataAxis.prototype.show = function() { - this.hidden = false; - if (!this.dom.frame.parentNode) { - if (this.options.orientation == 'left') { - this.body.dom.left.appendChild(this.dom.frame); - } - else { - this.body.dom.right.appendChild(this.dom.frame); - } - } - - if (!this.dom.lineContainer.parentNode) { - this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); - } - }; - - /** - * Create the HTML DOM for the DataAxis - */ - DataAxis.prototype.hide = function() { - this.hidden = true; - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } - - if (this.dom.lineContainer.parentNode) { - this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); - } - }; - - /** - * Set a range (start and end) - * @param end - * @param start - * @param end - */ - DataAxis.prototype.setRange = function (start, end) { - if (this.master == false && this.options.alignZeros == true && this.zeroCrossing != -1) { - if (start > 0) { - start = 0; - } - } - this.range.start = start; - this.range.end = end; - }; - - /** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ - DataAxis.prototype.redraw = function () { - var changeCalled = false; - var activeGroups = 0; - - // Make sure the line container adheres to the vertical scrolling. - this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; - - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - activeGroups++; - } - } - } - if (this.amountOfGroups == 0 || activeGroups == 0) { - this.hide(); - } - else { - 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 == true ? Number(('' + this.options.width).replace("px","")) : 0; - - var props = this.props; - var frame = this.dom.frame; - - // update classname - frame.className = 'dataaxis'; - - // calculate character width and height - this._calculateCharSize(); - - var orientation = this.options.orientation; - var showMinorLabels = this.options.showMinorLabels; - var showMajorLabels = this.options.showMajorLabels; + var orientation = this.options.orientation; + var showMinorLabels = this.options.showMinorLabels; + var showMajorLabels = this.options.showMajorLabels; // determine the width and height of the elements for the axis props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; @@ -21318,9 +21047,11 @@ return /******/ (function(modules) { // webpackBootstrap if (y >= 0) { this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); } - this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + if (this.options.showMajorLines == true) { + this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + } } - else { + else if (this.options.showMinorLines == true) { this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); } @@ -21532,14 +21263,14 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 46 */ +/* 45 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); var DOMutil = __webpack_require__(6); - var Line = __webpack_require__(47); - var Bar = __webpack_require__(49); - var Points = __webpack_require__(48); + var Line = __webpack_require__(46); + var Bar = __webpack_require__(48); + var Points = __webpack_require__(47); /** * /** @@ -21737,14 +21468,14 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 47 */ +/* 46 */ /***/ function(module, exports, __webpack_require__) { /** * Created by Alex on 11/11/2014. */ var DOMutil = __webpack_require__(6); - var Points = __webpack_require__(48); + var Points = __webpack_require__(47); function Line(groupId, options) { this.groupId = groupId; @@ -21961,7 +21692,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 48 */ +/* 47 */ /***/ function(module, exports, __webpack_require__) { /** @@ -22009,14 +21740,14 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Points; /***/ }, -/* 49 */ +/* 48 */ /***/ function(module, exports, __webpack_require__) { /** * Created by Alex on 11/11/2014. */ var DOMutil = __webpack_require__(6); - var Points = __webpack_require__(48); + var Points = __webpack_require__(47); function Bargraph(groupId, options) { this.groupId = groupId; @@ -22243,7 +21974,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Bargraph; /***/ }, -/* 50 */ +/* 49 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); @@ -22273,183 +22004,464 @@ return /******/ (function(modules) { // webpackBootstrap this.options = util.extend({},this.defaultOptions); this.linegraphOptions = linegraphOptions; - this.svgElements = {}; - this.dom = {}; - this.groups = {}; - this.amountOfGroups = 0; - this._create(); + this.svgElements = {}; + this.dom = {}; + this.groups = {}; + this.amountOfGroups = 0; + this._create(); + + this.setOptions(options); + } + + Legend.prototype = new Component(); + + Legend.prototype.clear = function() { + this.groups = {}; + this.amountOfGroups = 0; + } + + Legend.prototype.addGroup = function(label, graphOptions) { + + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; + } + this.amountOfGroups += 1; + }; + + Legend.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; + }; + + Legend.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; + } + }; + + Legend.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.className = 'legend'; + this.dom.frame.style.position = "absolute"; + this.dom.frame.style.top = "10px"; + this.dom.frame.style.display = "block"; + + this.dom.textArea = document.createElement('div'); + this.dom.textArea.className = 'legendText'; + this.dom.textArea.style.position = "relative"; + this.dom.textArea.style.top = "0px"; + + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = 'absolute'; + this.svg.style.top = 0 +'px'; + this.svg.style.width = this.options.iconSize + 5 + 'px'; + this.svg.style.height = '100%'; + + this.dom.frame.appendChild(this.svg); + this.dom.frame.appendChild(this.dom.textArea); + }; + + /** + * Hide the component from the DOM + */ + Legend.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } + }; + + /** + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed + */ + Legend.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } + }; + + Legend.prototype.setOptions = function(options) { + var fields = ['enabled','orientation','icons','left','right']; + util.selectiveDeepExtend(fields, this.options, options); + }; + + Legend.prototype.redraw = function() { + var activeGroups = 0; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + activeGroups++; + } + } + } + + if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { + this.hide(); + } + else { + this.show(); + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { + this.dom.frame.style.left = '4px'; + this.dom.frame.style.textAlign = "left"; + this.dom.textArea.style.textAlign = "left"; + this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.right = ''; + this.svg.style.left = 0 +'px'; + this.svg.style.right = ''; + } + else { + this.dom.frame.style.right = '4px'; + this.dom.frame.style.textAlign = "right"; + this.dom.textArea.style.textAlign = "right"; + this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.left = ''; + this.svg.style.right = 0 +'px'; + this.svg.style.left = ''; + } + + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { + this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.bottom = ''; + } + else { + var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; + this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.top = ''; + } + + if (this.options.icons == false) { + this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; + this.dom.textArea.style.right = ''; + this.dom.textArea.style.left = ''; + this.svg.style.width = '0px'; + } + else { + this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' + this.drawLegendIcons(); + } + + var content = ''; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + content += this.groups[groupId].content + '
'; + } + } + } + this.dom.textArea.innerHTML = content; + this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; + } + }; + + Legend.prototype.drawLegendIcons = function() { + if (this.dom.frame.parentNode) { + DOMutil.prepareElements(this.svgElements); + var padding = window.getComputedStyle(this.dom.frame).paddingTop; + var iconOffset = Number(padding.replace('px','')); + var x = iconOffset; + var iconWidth = this.options.iconSize; + var iconHeight = 0.75 * this.options.iconSize; + var y = iconOffset + 0.5 * iconHeight + 3; + + this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; + + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + this.options.iconSpacing; + } + } + } + + DOMutil.cleanupElements(this.svgElements); + } + }; + + module.exports = Legend; + + +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @constructor DataStep + * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an + * end data point. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. + * + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + */ + function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { + // variables + this.current = 0; + + this.autoScale = true; + this.stepIndex = 0; + this.step = 1; + this.scale = 1; + + this.marginStart; + this.marginEnd; + this.deadSpace = 0; + + this.majorSteps = [1, 2, 5, 10]; + this.minorSteps = [0.25, 0.5, 1, 2]; + + this.alignZeros = alignZeros; + + this.setRange(start, end, minimumStep, containerHeight, customRange); + } + + + + /** + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Number} [start] The start date and time. + * @param {Number} [end] The end date and time. + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + */ + DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { + this._start = customRange.min === undefined ? start : customRange.min; + this._end = customRange.max === undefined ? end : customRange.max; + + if (this._start == this._end) { + this._start -= 0.75; + this._end += 1; + } + + if (this.autoScale == true) { + this.setMinimumStep(minimumStep, containerHeight); + } + + this.setFirst(customRange); + }; + + /** + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds + */ + DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { + // round to floor + var size = this._end - this._start; + var safeSize = size * 1.2; + var minimumStepValue = minimumStep * (safeSize / containerHeight); + var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); + + var minorStepIdx = -1; + var magnitudefactor = Math.pow(10,orderOfMagnitude); + + var start = 0; + if (orderOfMagnitude < 0) { + start = orderOfMagnitude; + } + + var solutionFound = false; + for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { + magnitudefactor = Math.pow(10,i); + for (var j = 0; j < this.minorSteps.length; j++) { + var stepSize = magnitudefactor * this.minorSteps[j]; + if (stepSize >= minimumStepValue) { + solutionFound = true; + minorStepIdx = j; + break; + } + } + if (solutionFound == true) { + break; + } + } + this.stepIndex = minorStepIdx; + this.scale = magnitudefactor; + this.step = magnitudefactor * this.minorSteps[minorStepIdx]; + }; + - this.setOptions(options); - } - Legend.prototype = new Component(); + /** + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date + */ + DataStep.prototype.setFirst = function(customRange) { + if (customRange === undefined) { + customRange = {}; + } - Legend.prototype.clear = function() { - this.groups = {}; - this.amountOfGroups = 0; - } + var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; + var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; - Legend.prototype.addGroup = function(label, graphOptions) { + this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; + this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; + // if we need to align the zero's we need to make sure that there is a zero to use. + if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { + this.marginEnd += this.marginEnd % this.step; } - this.amountOfGroups += 1; - }; - Legend.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; - }; + this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; + this.marginRange = this.marginEnd - this.marginStart; - Legend.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; - } - }; - Legend.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.className = 'legend'; - this.dom.frame.style.position = "absolute"; - this.dom.frame.style.top = "10px"; - this.dom.frame.style.display = "block"; + this.current = this.marginEnd; + }; - this.dom.textArea = document.createElement('div'); - this.dom.textArea.className = 'legendText'; - this.dom.textArea.style.position = "relative"; - this.dom.textArea.style.top = "0px"; + DataStep.prototype.roundToMinor = function(value) { + var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); + if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { + return rounded + (this.scale * this.minorSteps[this.stepIndex]); + } + else { + return rounded; + } + } - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = 'absolute'; - this.svg.style.top = 0 +'px'; - this.svg.style.width = this.options.iconSize + 5 + 'px'; - this.svg.style.height = '100%'; - this.dom.frame.appendChild(this.svg); - this.dom.frame.appendChild(this.dom.textArea); + /** + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date + */ + DataStep.prototype.hasNext = function () { + return (this.current >= this.marginStart); }; /** - * Hide the component from the DOM + * Do the next step */ - Legend.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); + DataStep.prototype.next = function() { + var prev = this.current; + this.current -= this.step; + + // safety mechanism: if current time is still unchanged, move to the end + if (this.current == prev) { + this.current = this._end; } }; /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed + * Do the next step */ - Legend.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); - } + DataStep.prototype.previous = function() { + this.current += this.step; + this.marginEnd += this.step; + this.marginRange = this.marginEnd - this.marginStart; }; - Legend.prototype.setOptions = function(options) { - var fields = ['enabled','orientation','icons','left','right']; - util.selectiveDeepExtend(fields, this.options, options); - }; - Legend.prototype.redraw = function() { - var activeGroups = 0; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - activeGroups++; - } - } - } - if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { - this.hide(); - } - else { - this.show(); - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { - this.dom.frame.style.left = '4px'; - this.dom.frame.style.textAlign = "left"; - this.dom.textArea.style.textAlign = "left"; - this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.right = ''; - this.svg.style.left = 0 +'px'; - this.svg.style.right = ''; - } - else { - this.dom.frame.style.right = '4px'; - this.dom.frame.style.textAlign = "right"; - this.dom.textArea.style.textAlign = "right"; - this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.left = ''; - this.svg.style.right = 0 +'px'; - this.svg.style.left = ''; - } + /** + * Get the current datetime + * @return {String} current The current date + */ + DataStep.prototype.getCurrent = function(decimals) { + // prevent round-off errors when close to zero + var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; + var toPrecision = '' + Number(current).toPrecision(5); - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { - this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.bottom = ''; + // If decimals is specified, then limit or extend the string as required + if(decimals !== undefined && !isNaN(Number(decimals))) { + // If string includes exponent, then we need to add it to the end + var exp = ""; + var index = toPrecision.indexOf("e"); + if(index != -1) { + // Get the exponent + exp = toPrecision.slice(index); + // Remove the exponent in case we need to zero-extend + toPrecision = toPrecision.slice(0, index); } - else { - var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; - this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.top = ''; + index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); + if(index === -1) { + // No decimal found - if we want decimals, then we need to add it + if(decimals !== 0) { + toPrecision += '.'; + } + // Calculate how long the string should be + index = toPrecision.length + decimals; } - - if (this.options.icons == false) { - this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; - this.dom.textArea.style.right = ''; - this.dom.textArea.style.left = ''; - this.svg.style.width = '0px'; + else if(decimals !== 0) { + // Calculate how long the string should be - accounting for the decimal place + index += decimals + 1; + } + if(index > toPrecision.length) { + // We need to add zeros! + for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { + toPrecision += '0'; + } } else { - this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' - this.drawLegendIcons(); + // we need to remove characters + toPrecision = toPrecision.slice(0, index); } - - var content = ''; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - content += this.groups[groupId].content + '
'; + // Add the exponent if there is one + toPrecision += exp; + } + else { + if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { + // If no decimal is specified, and there are decimal places, remove trailing zeros + for (var i = toPrecision.length - 1; i > 0; i--) { + if (toPrecision[i] == "0") { + toPrecision = toPrecision.slice(0, i); + } + else if (toPrecision[i] == "." || toPrecision[i] == ",") { + toPrecision = toPrecision.slice(0, i); + break; + } + else { + break; } } } - this.dom.textArea.innerHTML = content; - this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; } + + return toPrecision; }; - Legend.prototype.drawLegendIcons = function() { - if (this.dom.frame.parentNode) { - DOMutil.prepareElements(this.svgElements); - var padding = window.getComputedStyle(this.dom.frame).paddingTop; - var iconOffset = Number(padding.replace('px','')); - var x = iconOffset; - var iconWidth = this.options.iconSize; - var iconHeight = 0.75 * this.options.iconSize; - var y = iconOffset + 0.5 * iconHeight + 3; - this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + this.options.iconSpacing; - } - } - } + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + DataStep.prototype.snap = function(date) { - DOMutil.cleanupElements(this.svgElements); - } }; - module.exports = Legend; + /** + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. + */ + DataStep.prototype.isMajor = function() { + return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); + }; + + module.exports = DataStep; /***/ }, diff --git a/docs/graph2d.html b/docs/graph2d.html index 05ab0579..bcab9602 100644 --- a/docs/graph2d.html +++ b/docs/graph2d.html @@ -452,6 +452,18 @@ The options colored in green can also be used as options for the groups. All opt true Toggle the drawing of the major labels on the Y axis. + + dataAxis.showMajorLines + Boolean + true + Toggle the drawing of the major lines on the Y axis. + + + dataAxis.showMinorLines + Boolean + true + Toggle the drawing of the major lines on the Y axis. + dataAxis.icons Boolean @@ -713,6 +725,22 @@ The options colored in green can also be used as options for the groups. All opt visible. + + showMajorLines + boolean + true + By default, the timeline shows both minor and major date lines on the + time axis. You can use this option to hide the lines from the major dates. + + + + showMinorLines + boolean + true + By default, the timeline shows both minor and major date lines on the + time axis. You can use this option to hide the lines from the minor dates. + + start Date | Number | String diff --git a/docs/timeline.html b/docs/timeline.html index 72b2a3b8..d537db95 100644 --- a/docs/timeline.html +++ b/docs/timeline.html @@ -743,6 +743,23 @@ var options = { visible. + + + showMajorLines + boolean + true + By default, the timeline shows both minor and major date lines on the + time axis. You can use this option to hide the lines from the major dates. + + + + showMinorLines + boolean + true + By default, the timeline shows both minor and major date lines on the + time axis. You can use this option to hide the lines from the minor dates. + + stack Boolean diff --git a/examples/graph2d/01_basic.html b/examples/graph2d/01_basic.html index 8b0510b8..84565a9e 100644 --- a/examples/graph2d/01_basic.html +++ b/examples/graph2d/01_basic.html @@ -44,7 +44,7 @@ var dataset = new vis.DataSet(items); var options = { start: '2014-06-10', - end: '2014-06-18' + end: '2014-06-18', }; var graph2d = new vis.Graph2d(container, dataset, options); diff --git a/lib/timeline/component/DataAxis.js b/lib/timeline/component/DataAxis.js index 0c23f416..5ff846bd 100644 --- a/lib/timeline/component/DataAxis.js +++ b/lib/timeline/component/DataAxis.js @@ -19,6 +19,8 @@ function DataAxis (body, options, svg, linegraphOptions) { orientation: 'left', // supported: 'left', 'right' showMinorLabels: true, showMajorLabels: true, + showMinorLines: true, + showMajorLines: true, icons: true, majorLinesOffset: 7, minorLinesOffset: 4, @@ -118,6 +120,8 @@ DataAxis.prototype.setOptions = function (options) { 'orientation', 'showMinorLabels', 'showMajorLabels', + 'showMajorLines', + 'showMinorLines', 'icons', 'majorLinesOffset', 'minorLinesOffset', @@ -417,9 +421,11 @@ DataAxis.prototype._redrawLabels = function () { if (y >= 0) { this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); } - this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + if (this.options.showMajorLines == true) { + this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + } } - else { + else if (this.options.showMinorLines == true) { this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); } diff --git a/lib/timeline/component/LineGraph.js b/lib/timeline/component/LineGraph.js index b2fa0807..19d6f421 100644 --- a/lib/timeline/component/LineGraph.js +++ b/lib/timeline/component/LineGraph.js @@ -50,6 +50,8 @@ function LineGraph(body, options) { dataAxis: { showMinorLabels: true, showMajorLabels: true, + showMinorLines: true, + showMajorLines: true, icons: false, width: '40px', visible: true, diff --git a/lib/timeline/component/TimeAxis.js b/lib/timeline/component/TimeAxis.js index de6fd7b5..f0622f6f 100644 --- a/lib/timeline/component/TimeAxis.js +++ b/lib/timeline/component/TimeAxis.js @@ -40,6 +40,8 @@ function TimeAxis (body, options) { // TODO: implement timeaxis orientations 'left' and 'right' showMinorLabels: true, showMajorLabels: true, + showMajorLines: true, + showMinorLines: true, format: null }; this.options = util.extend({}, this.defaultOptions); @@ -65,7 +67,7 @@ TimeAxis.prototype = new Component(); TimeAxis.prototype.setOptions = function(options) { if (options) { // copy all options that we know - util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels','hiddenDates', 'format'], this.options, options); + util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels', 'showMinorLines', 'showMajorLines','hiddenDates', 'format'], this.options, options); // apply locale to moment.js // TODO: not so nice, this is applied globally to moment.js @@ -224,9 +226,11 @@ TimeAxis.prototype._repaintLabels = function () { } this._repaintMajorText(x, step.getLabelMajor(), orientation); } - this._repaintMajorLine(x, orientation); + if (this.options.showMajorLines == true) { + this._repaintMajorLine(x, orientation); + } } - else { + else if (this.options.showMinorLines == true) { this._repaintMinorLine(x, orientation); }