Browse Source

Fixed #24: Implemented support for time zones (see examples/timeline/other/timezone.html)

flowchartTest
jos 9 years ago
parent
commit
a9ca766af7
18 changed files with 888 additions and 673 deletions
  1. +7
    -0
      HISTORY.md
  2. +518
    -487
      dist/vis.js
  3. +1
    -1
      dist/vis.map
  4. +15
    -15
      dist/vis.min.js
  5. +35
    -1
      docs/graph2d/index.html
  6. +34
    -8
      docs/timeline/index.html
  7. +80
    -0
      examples/timeline/other/timezone.html
  8. +9
    -5
      lib/timeline/Core.js
  9. +21
    -18
      lib/timeline/DateUtil.js
  10. +3
    -0
      lib/timeline/Graph2d.js
  11. +10
    -6
      lib/timeline/Range.js
  12. +135
    -119
      lib/timeline/TimeStep.js
  13. +3
    -0
      lib/timeline/Timeline.js
  14. +4
    -3
      lib/timeline/component/CurrentTime.js
  15. +4
    -6
      lib/timeline/component/CustomTime.js
  16. +7
    -4
      lib/timeline/component/TimeAxis.js
  17. +1
    -0
      lib/timeline/optionsGraph2d.js
  18. +1
    -0
      lib/timeline/optionsTimeline.js

+ 7
- 0
HISTORY.md View File

@ -4,6 +4,13 @@ http://visjs.org
## not yet released, version 4.5.2-SNAPSHOT
### Timeline
- Implemented #24: support for custom timezones, see configuration option `moment`.
### Graph2d
- Implemented #24: support for custom timezones, see configuration option `moment`.

+ 518
- 487
dist/vis.js
File diff suppressed because it is too large
View File


+ 1
- 1
dist/vis.map
File diff suppressed because it is too large
View File


+ 15
- 15
dist/vis.min.js
File diff suppressed because it is too large
View File


+ 35
- 1
docs/graph2d/index.html View File

@ -160,8 +160,8 @@
<li><a href="#Methods">Methods</a></li>
<li><a href="#Events">Events</a></li>
<li><a href="#Localization">Localization</a></li>
<li><a href="#Time_zone">Time zone</a></li>
<li><a href="#Styles">Styles</a></li>
<li><a href="#Data_Policy">Data Policy</a></li>
</ul>
@ -717,6 +717,14 @@ onRender: function(item, group, graph2d) {
<td>'top-right'</td>
<td>Determine the position of the legend coupled to the right axis. Options are 'top-left', 'top-right', 'bottom-left' or 'bottom-right'.</td>
</tr>
<tr>
<td>moment</td>
<td>function</td>
<td>vis.moment</td>
<td>A constructor for creating a moment.js Date. Allows for applying a custom time zone. See section <a href="#Time_zone">Time zone</a> for more information.</td>
</tr>
<tr>
<td class="greenField">sampling</td>
<td>Boolean</td>
@ -1424,6 +1432,32 @@ Graph2d.off('rangechanged', onChange);
</tr>
</table>
<h2 id="Time_zone">Time zone</h2>
<p>
By default, the Timeline displays time in local time. To display a Timeline in an other time zone or in UTC, the date constructor can be overloaded via the configuration option <code>moment</code>, which by default is the constructor function of moment.js. More information about UTC with moment.js can be found in the docs: <a href="http://momentjs.com/docs/#/parsing/utc/">http://momentjs.com/docs/#/parsing/utc/</a>.
</p>
<p>
Examples:
</p>
<pre class="prettyprint lang-js">// display in UTC
var options = {
moment: function(date) {
return vis.moment(date).utc();
}
};
// display in UTC +08:00
var options = {
moment: function(date) {
return vis.moment(date).utcOffset('+08:00');
}
};
</pre>
<h2 id="Styles">Styles</h2>
<p>
All parts of the Graph2d have a class name and a default css style just like the Graph2d.

+ 34
- 8
docs/timeline/index.html View File

@ -108,8 +108,8 @@
<li><a href="#Editing_Items">Editing Items</a></li>
<li><a href="#Templates">Templates</a></li>
<li><a href="#Localization">Localization</a></li>
<li><a href="#Time_zone">Time zone</a></li>
<li><a href="#Styles">Styles</a></li>
<li><a href="#Data_Policy">Data Policy</a></li>
</ul>
<h2 id="Example">Example</h2>
@ -641,6 +641,13 @@ function (option, path) {
<td>A map with i18n locales. See section <a href="#Localization">Localization</a> for more information.</td>
</tr>
<tr>
<td>moment</td>
<td>function</td>
<td>vis.moment</td>
<td>A constructor for creating a moment.js Date. Allows for applying a custom time zone. See section <a href="#Time_zone">Time zone</a> for more information.</td>
</tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','margin', this);">
<td><span parent="margin" class="right-caret"></span> margin</td>
<td>number or Object</td>
@ -1543,6 +1550,32 @@ var options = {
</table>
<h2 id="Time_zone">Time zone</h2>
<p>
By default, the Timeline displays time in local time. To display a Timeline in an other time zone or in UTC, the date constructor can be overloaded via the configuration option <code>moment</code>, which by default is the constructor function of moment.js. More information about UTC with moment.js can be found in the docs: <a href="http://momentjs.com/docs/#/parsing/utc/">http://momentjs.com/docs/#/parsing/utc/</a>.
</p>
<p>
Examples:
</p>
<pre class="prettyprint lang-js">// display in UTC
var options = {
moment: function(date) {
return vis.moment(date).utc();
}
};
// display in UTC +08:00
var options = {
moment: function(date) {
return vis.moment(date).utcOffset('+08:00');
}
};
</pre>
<h2 id="Styles">Styles</h2>
<p>
All parts of the Timeline have a class name and a default css style.
@ -1620,13 +1653,6 @@ var options = {
&lt;/style&gt;
</pre>
<h2 id="Data_Policy">Data Policy</h2>
<p>
All code and data is processed and rendered in the browser.
No data is sent to any server.
</p>
</div>
<!-- Bootstrap core JavaScript

+ 80
- 0
examples/timeline/other/timezone.html View File

@ -0,0 +1,80 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline | Time zone</title>
<style type="text/css">
body, html {
font-family: sans-serif;
max-width: 800px;
}
</style>
<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
<script src="../../googleAnalytics.js"></script>
</head>
<body>
<h1>Time zone</h1>
<p>
The following demo shows how to display items in local time (default), in UTC, or for a specific time zone offset. By configuring your own <code>moment</code> constructor, you can display items in the time zone that you want. All timelines have the same start and end date.
</p>
<h2>Local time</h2>
<div id="local"></div>
<h2>UTC</h2>
<div id="utc"></div>
<h2>UTC +08:00</h2>
<div id="plus8"></div>
<script type="text/javascript">
// Create a DataSet (allows two way data-binding)
var today = vis.moment(vis.moment.utc().format('YYYY-MM-DDT00:00:00.000Z'));
var start = today.clone();
var end = today.clone().add(2, 'day');
var customTime = today.clone().add(28, 'hour');
var items = new vis.DataSet([
{id: 1, content: 'item 1', start: today.clone().add(8, 'hour')},
{id: 2, content: 'item 2', start: today.clone().add(16, 'hour')},
{id: 3, content: 'item 3', start: today.clone().add(32, 'hour')}
]);
// Create a timeline displaying in local time (default)
var timelineLocal = new vis.Timeline(document.getElementById('local'), items, {
editable: true,
start: start,
end: end
});
timelineLocal.addCustomTime(customTime);
// Create a timeline displaying in UTC
var timelineUTC = new vis.Timeline(document.getElementById('utc'), items, {
editable: true,
start: start,
end: end,
moment: function (date) {
return vis.moment(date).utc();
}
});
timelineUTC.addCustomTime(customTime);
// Create a timeline displaying in UTC +08:00
var timelinePlus8 = new vis.Timeline(document.getElementById('plus8'), items, {
editable: true,
start: start,
end: end,
moment: function (date) {
return vis.moment(date).utcOffset('+08:00');
}
});
timelinePlus8.addCustomTime(customTime);
</script>
</body>
</html>

+ 9
- 5
lib/timeline/Core.js View File

@ -213,7 +213,11 @@ Core.prototype._create = function (container) {
Core.prototype.setOptions = function (options) {
if (options) {
// copy the known options
var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates'];
var fields = [
'width', 'height', 'minHeight', 'maxHeight', 'autoResize',
'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates',
'locale', 'locales', 'moment'
];
util.selectiveExtend(fields, this.options, options);
if ('orientation' in options) {
@ -263,7 +267,7 @@ Core.prototype.setOptions = function (options) {
}
if ('hiddenDates' in this.options) {
DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates);
DateUtil.convertHiddenOptions(this.options.moment, this.body, this.options.hiddenDates);
}
if ('clickToUse' in options) {
@ -428,10 +432,10 @@ Core.prototype.addCustomTime = function (time, id) {
throw new Error('A custom time with id ' + JSON.stringify(id) + ' already exists');
}
var customTime = new CustomTime(this.body, {
var customTime = new CustomTime(this.body, util.extend({}, this.options, {
time : timestamp,
id : id
});
}));
this.customTimes.push(customTime);
this.components.push(customTime);
@ -595,7 +599,7 @@ Core.prototype._redraw = function() {
if (!dom) return; // when destroyed
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates);
// update class names
if (options.orientation == 'top') {

+ 21
- 18
lib/timeline/DateUtil.js View File

@ -1,12 +1,12 @@
var moment = require('../module/moment');
/**
* used in Core to convert the options into a volatile variable
*
* @param Core
* @param {function} moment
* @param {Object} body
* @param {Array} hiddenDates
*/
exports.convertHiddenOptions = function(body, hiddenDates) {
exports.convertHiddenOptions = function(moment, body, hiddenDates) {
body.hiddenDates = [];
if (hiddenDates) {
if (Array.isArray(hiddenDates) == true) {
@ -28,12 +28,13 @@ exports.convertHiddenOptions = function(body, hiddenDates) {
/**
* create new entrees for the repeating hidden dates
* @param body
* @param hiddenDates
* @param {function} moment
* @param {Object} body
* @param {Array} hiddenDates
*/
exports.updateHiddenDates = function (body, hiddenDates) {
exports.updateHiddenDates = function (moment, body, hiddenDates) {
if (hiddenDates && body.domProps.centerContainer.width !== undefined) {
exports.convertHiddenOptions(body, hiddenDates);
exports.convertHiddenOptions(moment, body, hiddenDates);
var start = moment(body.range.start);
var end = moment(body.range.end);
@ -208,20 +209,21 @@ exports.removeDuplicates = function(body) {
body.hiddenDates.sort(function (a, b) {
return a.start - b.start;
}); // sort by start time
}
};
exports.printDates = function(dates) {
for (var i =0; i < dates.length; i++) {
console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove);
}
}
};
/**
* Used in TimeStep to avoid the hidden times.
* @param timeStep
* @param {function} moment
* @param {TimeStep} timeStep
* @param previousTime
*/
exports.stepOverHiddenDates = function(timeStep, previousTime) {
exports.stepOverHiddenDates = function(moment, timeStep, previousTime) {
var stepInHidden = false;
var currentValue = timeStep.current.valueOf();
for (var i = 0; i < timeStep.hiddenDates.length; i++) {
@ -241,7 +243,7 @@ exports.stepOverHiddenDates = function(timeStep, previousTime) {
else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;}
else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;}
timeStep.current = newValue.toDate();
timeStep.current = newValue;
}
};
@ -282,13 +284,13 @@ exports.toScreen = function(Core, time, width) {
return (time.valueOf() - conversion.offset) * conversion.scale;
}
else {
var hidden = exports.isHidden(time, Core.body.hiddenDates)
var hidden = exports.isHidden(time, Core.body.hiddenDates);
if (hidden.hidden == true) {
time = hidden.startDate;
}
var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time);
time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, Core.range, time);
var conversion = Core.range.conversion(width, duration);
return (time.valueOf() - conversion.offset) * conversion.scale;
@ -344,18 +346,19 @@ exports.getHiddenDurationBetween = function(hiddenDates, start, end) {
/**
* Support function
* @param moment
* @param hiddenDates
* @param range
* @param time
* @returns {{duration: number, time: *, offset: number}}
*/
exports.correctTimeForHidden = function(hiddenDates, range, time) {
exports.correctTimeForHidden = function(moment, hiddenDates, range, time) {
time = moment(time).toDate().valueOf();
time -= exports.getHiddenDurationBefore(hiddenDates,range,time);
time -= exports.getHiddenDurationBefore(moment, hiddenDates,range,time);
return time;
};
exports.getHiddenDurationBefore = function(hiddenDates, range, time) {
exports.getHiddenDurationBefore = function(moment, hiddenDates, range, time) {
var timeOffset = 0;
time = moment(time).toDate().valueOf();

+ 3
- 0
lib/timeline/Graph2d.js View File

@ -1,5 +1,6 @@
var Emitter = require('emitter-component');
var Hammer = require('../module/hammer');
var moment = require('../module/moment');
var util = require('../util');
var DataSet = require('../DataSet');
var DataView = require('../DataView');
@ -44,6 +45,8 @@ function Graph2d (container, items, groups, options) {
item: 'bottom' // not relevant for Graph2d
},
moment: moment,
width: null,
height: null,
maxHeight: null,

+ 10
- 6
lib/timeline/Range.js View File

@ -27,6 +27,7 @@ function Range(body, options) {
this.defaultOptions = {
start: null,
end: null,
moment: moment,
direction: 'horizontal', // 'horizontal' or 'vertical'
moveable: true,
zoomable: true,
@ -78,7 +79,10 @@ Range.prototype = new Component();
Range.prototype.setOptions = function (options) {
if (options) {
// copy the options that we know
var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates', 'zoomKey'];
var fields = [
'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable',
'moment', 'activate', 'hiddenDates', 'zoomKey'
];
util.selectiveExtend(fields, this.options, options);
if ('start' in options || 'end' in options) {
@ -145,7 +149,7 @@ Range.prototype.setRange = function(start, end, animation, byUser) {
var e = (done || finalEnd === null) ? finalEnd : initEnd + (finalEnd - initEnd) * ease;
changed = me._applyRange(s, e);
DateUtil.updateHiddenDates(me.body, me.options.hiddenDates);
DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates);
anyChanged = anyChanged || changed;
if (changed) {
me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end), byUser:byUser});
@ -168,7 +172,7 @@ Range.prototype.setRange = function(start, end, animation, byUser) {
}
else {
var changed = this._applyRange(finalStart, finalEnd);
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates);
if (changed) {
var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser};
this.body.emitter.emit('rangechange', params);
@ -547,8 +551,8 @@ Range.prototype._onPinch = function (event) {
var scale = 1 / (event.scale + this.scaleOffset);
var centerDate = this._pointerToDate(this.props.touch.center);
var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate);
var hiddenDuration = DateUtil.getHiddenDurationBetween(this.options.moment, this.body.hiddenDates, this.start, this.end);
var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, centerDate);
var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
// calculate new start and end
@ -645,7 +649,7 @@ Range.prototype.zoom = function(scale, center, delta) {
}
var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, center);
var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, center);
var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
// calculate new start and end

+ 135
- 119
lib/timeline/TimeStep.js View File

@ -29,10 +29,12 @@ var util = require('../util');
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
*/
function TimeStep(start, end, minimumStep, hiddenDates) {
this.moment = moment;
// variables
this.current = new Date();
this._start = new Date();
this._end = new Date();
this.current = this.moment();
this._start = this.moment();
this._end = this.moment();
this.autoScale = true;
this.scale = 'day';
@ -77,6 +79,20 @@ TimeStep.FORMAT = {
}
};
/**
* Set custom constructor function for moment. Can be used to set dates
* to UTC or to set a utcOffset.
* @param {function} moment
*/
TimeStep.prototype.setMoment = function (moment) {
this.moment = moment;
// update the date properties, can have a new utcOffset
this.current = this.moment(this.current);
this._start = this.moment(this._start);
this._end = this.moment(this._end);
};
/**
* Set custom formatting for the minor an major labels of the TimeStep.
* Both `minorLabels` and `majorLabels` are an Object with properties:
@ -103,8 +119,8 @@ TimeStep.prototype.setRange = function(start, end, minimumStep) {
throw "No legal start or end date in method setRange";
}
this._start = (start != undefined) ? new Date(start.valueOf()) : new Date();
this._end = (end != undefined) ? new Date(end.valueOf()) : new Date();
this._start = (start != undefined) ? this.moment(start.valueOf()) : new Date();
this._end = (end != undefined) ? this.moment(end.valueOf()) : new Date();
if (this.autoScale) {
this.setMinimumStep(minimumStep);
@ -114,8 +130,8 @@ TimeStep.prototype.setRange = function(start, end, minimumStep) {
/**
* Set the range iterator to the start date.
*/
TimeStep.prototype.first = function() {
this.current = new Date(this._start.valueOf());
TimeStep.prototype.start = function() {
this.current = this._start.clone();
this.roundToMinor();
};
@ -129,28 +145,28 @@ TimeStep.prototype.roundToMinor = function() {
// noinspection FallThroughInSwitchStatementJS
switch (this.scale) {
case 'year':
this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step));
this.current.setMonth(0);
case 'month': this.current.setDate(1);
this.current.year(this.step * Math.floor(this.current.year() / this.step));
this.current.month(0);
case 'month': this.current.date(1);
case 'day': // intentional fall through
case 'weekday': this.current.setHours(0);
case 'hour': this.current.setMinutes(0);
case 'minute': this.current.setSeconds(0);
case 'second': this.current.setMilliseconds(0);
case 'weekday': this.current.hours(0);
case 'hour': this.current.minutes(0);
case 'minute': this.current.seconds(0);
case 'second': this.current.milliseconds(0);
//case 'millisecond': // nothing to do for milliseconds
}
if (this.step != 1) {
// round down to the first minor value that is a multiple of the current step size
switch (this.scale) {
case 'millisecond': this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break;
case 'second': this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break;
case 'minute': this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break;
case 'hour': this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break;
case 'millisecond': this.current.subtract(this.current.milliseconds() % this.step, 'milliseconds'); break;
case 'second': this.current.subtract(this.current.seconds() % this.step, 'seconds'); break;
case 'minute': this.current.subtract(this.current.minutes() % this.step, 'minutes'); break;
case 'hour': this.current.subtract(this.current.hours() % this.step, 'hours'); break;
case 'weekday': // intentional fall through
case 'day': this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break;
case 'month': this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break;
case 'year': this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break;
case 'day': this.current.subtract((this.current.date() - 1) % this.step); break;
case 'month': this.current.subtract(this.current.month() % this.step); break;
case 'year': this.current.subtract(this.current.year() % this.step); break;
default: break;
}
}
@ -172,67 +188,65 @@ TimeStep.prototype.next = function() {
// Two cases, needed to prevent issues with switching daylight savings
// (end of March and end of October)
if (this.current.getMonth() < 6) {
if (this.current.month() < 6) {
switch (this.scale) {
case 'millisecond':
this.current = new Date(this.current.valueOf() + this.step); break;
case 'second': this.current = new Date(this.current.valueOf() + this.step * 1000); break;
case 'minute': this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break;
case 'millisecond': this.current.add(this.step, 'millisecond'); break;
case 'second': this.current.add(this.step, 'second'); break;
case 'minute': this.current.add(this.step, 'minute'); break;
case 'hour':
this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60);
this.current.add(this.step, 'hour');
// in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...)
var h = this.current.getHours();
this.current.setHours(h - (h % this.step));
// TODO: is this still needed now we use the function of moment.js?
this.current.subtract(this.current.hours() % this.step);
break;
case 'weekday': // intentional fall through
case 'day': this.current.setDate(this.current.getDate() + this.step); break;
case 'month': this.current.setMonth(this.current.getMonth() + this.step); break;
case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break;
default: break;
case 'day': this.current.add(this.step, 'day'); break;
case 'month': this.current.add(this.step, 'month'); break;
case 'year': this.current.add(this.step, 'year'); break;
default: break;
}
}
else {
switch (this.scale) {
case 'millisecond': this.current = new Date(this.current.valueOf() + this.step); break;
case 'second': this.current.setSeconds(this.current.getSeconds() + this.step); break;
case 'minute': this.current.setMinutes(this.current.getMinutes() + this.step); break;
case 'hour': this.current.setHours(this.current.getHours() + this.step); break;
case 'millisecond': this.current.add(this.step, 'millisecond'); break;
case 'second': this.current.add(this.step, 'second'); break;
case 'minute': this.current.add(this.step, 'minute'); break;
case 'hour': this.current.add(this.step, 'hour'); break;
case 'weekday': // intentional fall through
case 'day': this.current.setDate(this.current.getDate() + this.step); break;
case 'month': this.current.setMonth(this.current.getMonth() + this.step); break;
case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break;
default: break;
case 'day': this.current.add(this.step, 'day'); break;
case 'month': this.current.add(this.step, 'month'); break;
case 'year': this.current.add(this.step, 'year'); break;
default: break;
}
}
if (this.step != 1) {
// round down to the correct major value
switch (this.scale) {
case 'millisecond': if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break;
case 'second': if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break;
case 'minute': if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break;
case 'hour': if(this.current.getHours() < this.step) this.current.setHours(0); break;
case 'millisecond': if(this.current.milliseconds() < this.step) this.current.milliseconds(0); break;
case 'second': if(this.current.seconds() < this.step) this.current.seconds(0); break;
case 'minute': if(this.current.minutes() < this.step) this.current.minutes(0); break;
case 'hour': if(this.current.hours() < this.step) this.current.hours(0); break;
case 'weekday': // intentional fall through
case 'day': if(this.current.getDate() < this.step+1) this.current.setDate(1); break;
case 'month': if(this.current.getMonth() < this.step) this.current.setMonth(0); break;
case 'day': if(this.current.date() < this.step+1) this.current.date(1); break;
case 'month': if(this.current.month() < this.step) this.current.month(0); break;
case 'year': break; // nothing to do for year
default: break;
default: break;
}
}
// safety mechanism: if current time is still unchanged, move to the end
if (this.current.valueOf() == prev) {
this.current = new Date(this._end.valueOf());
this.current = this._end.clone();
}
DateUtil.stepOverHiddenDates(this, prev);
DateUtil.stepOverHiddenDates(this.moment, this, prev);
};
/**
* Get the current datetime
* @return {Date} current The current date
* @return {Moment} current The current date
*/
TimeStep.prototype.getCurrent = function() {
return this.current;
@ -329,100 +343,100 @@ TimeStep.prototype.setMinimumStep = function(minimumStep) {
* @return {Date} snappedDate
*/
TimeStep.snap = function(date, scale, step) {
var clone = new Date(date.valueOf());
var clone = moment(date);
if (scale == 'year') {
var year = clone.getFullYear() + Math.round(clone.getMonth() / 12);
clone.setFullYear(Math.round(year / step) * step);
clone.setMonth(0);
clone.setDate(0);
clone.setHours(0);
clone.setMinutes(0);
clone.setSeconds(0);
clone.setMilliseconds(0);
var year = clone.year() + Math.round(clone.month() / 12);
clone.year(Math.round(year / step) * step);
clone.month(0);
clone.date(0);
clone.hours(0);
clone.minutes(0);
clone.seconds(0);
clone.mlliseconds(0);
}
else if (scale == 'month') {
if (clone.getDate() > 15) {
clone.setDate(1);
clone.setMonth(clone.getMonth() + 1);
if (clone.date() > 15) {
clone.date(1);
clone.add(1, 'month');
// important: first set Date to 1, after that change the month.
}
else {
clone.setDate(1);
clone.date(1);
}
clone.setHours(0);
clone.setMinutes(0);
clone.setSeconds(0);
clone.setMilliseconds(0);
clone.hours(0);
clone.minutes(0);
clone.seconds(0);
clone.milliseconds(0);
}
else if (scale == 'day') {
//noinspection FallthroughInSwitchStatementJS
switch (step) {
case 5:
case 2:
clone.setHours(Math.round(clone.getHours() / 24) * 24); break;
clone.hours(Math.round(clone.hours() / 24) * 24); break;
default:
clone.setHours(Math.round(clone.getHours() / 12) * 12); break;
clone.hours(Math.round(clone.hours() / 12) * 12); break;
}
clone.setMinutes(0);
clone.setSeconds(0);
clone.setMilliseconds(0);
clone.minutes(0);
clone.seconds(0);
clone.milliseconds(0);
}
else if (scale == 'weekday') {
//noinspection FallthroughInSwitchStatementJS
switch (step) {
case 5:
case 2:
clone.setHours(Math.round(clone.getHours() / 12) * 12); break;
clone.hours(Math.round(clone.hours() / 12) * 12); break;
default:
clone.setHours(Math.round(clone.getHours() / 6) * 6); break;
clone.hours(Math.round(clone.hours() / 6) * 6); break;
}
clone.setMinutes(0);
clone.setSeconds(0);
clone.setMilliseconds(0);
clone.minutes(0);
clone.seconds(0);
clone.milliseconds(0);
}
else if (scale == 'hour') {
switch (step) {
case 4:
clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break;
clone.minutes(Math.round(clone.minutes() / 60) * 60); break;
default:
clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break;
clone.minutes(Math.round(clone.minutes() / 30) * 30); break;
}
clone.setSeconds(0);
clone.setMilliseconds(0);
clone.seconds(0);
clone.milliseconds(0);
} else if (scale == 'minute') {
//noinspection FallthroughInSwitchStatementJS
switch (step) {
case 15:
case 10:
clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5);
clone.setSeconds(0);
clone.minutes(Math.round(clone.minutes() / 5) * 5);
clone.seconds(0);
break;
case 5:
clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break;
clone.seconds(Math.round(clone.seconds() / 60) * 60); break;
default:
clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break;
clone.seconds(Math.round(clone.seconds() / 30) * 30); break;
}
clone.setMilliseconds(0);
clone.milliseconds(0);
}
else if (scale == 'second') {
//noinspection FallthroughInSwitchStatementJS
switch (step) {
case 15:
case 10:
clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5);
clone.setMilliseconds(0);
clone.seconds(Math.round(clone.seconds() / 5) * 5);
clone.milliseconds(0);
break;
case 5:
clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break;
clone.milliseconds(Math.round(clone.milliseconds() / 1000) * 1000); break;
default:
clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break;
clone.milliseconds(Math.round(clone.milliseconds() / 500) * 500); break;
}
}
else if (scale == 'millisecond') {
var _step = step > 5 ? step / 2 : 1;
clone.setMilliseconds(Math.round(clone.getMilliseconds() / _step) * _step);
clone.milliseconds(Math.round(clone.milliseconds() / _step) * _step);
}
return clone;
@ -477,20 +491,21 @@ TimeStep.prototype.isMajor = function() {
}
}
var date = this.moment(this.current);
switch (this.scale) {
case 'millisecond':
return (this.current.getMilliseconds() == 0);
return (date.milliseconds() == 0);
case 'second':
return (this.current.getSeconds() == 0);
return (date.seconds() == 0);
case 'minute':
return (this.current.getHours() == 0) && (this.current.getMinutes() == 0);
return (date.hours() == 0) && (date.minutes() == 0);
case 'hour':
return (this.current.getHours() == 0);
return (date.hours() == 0);
case 'weekday': // intentional fall through
case 'day':
return (this.current.getDate() == 1);
return (date.date() == 1);
case 'month':
return (this.current.getMonth() == 0);
return (date.month() == 0);
case 'year':
return false;
default:
@ -511,7 +526,7 @@ TimeStep.prototype.getLabelMinor = function(date) {
}
var format = this.format.minorLabels[this.scale];
return (format && format.length > 0) ? moment(date).format(format) : '';
return (format && format.length > 0) ? this.moment(date).format(format) : '';
};
/**
@ -526,12 +541,13 @@ TimeStep.prototype.getLabelMajor = function(date) {
}
var format = this.format.majorLabels[this.scale];
return (format && format.length > 0) ? moment(date).format(format) : '';
return (format && format.length > 0) ? this.moment(date).format(format) : '';
};
TimeStep.prototype.getClassName = function() {
var m = moment(this.current);
var date = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function
var _moment = this.moment;
var m = this.moment(this.current);
var current = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function
var step = this.step;
function even(value) {
@ -542,10 +558,10 @@ TimeStep.prototype.getClassName = function() {
if (date.isSame(new Date(), 'day')) {
return ' vis-today';
}
if (date.isSame(moment().add(1, 'day'), 'day')) {
if (date.isSame(_moment().add(1, 'day'), 'day')) {
return ' vis-tomorrow';
}
if (date.isSame(moment().add(-1, 'day'), 'day')) {
if (date.isSame(_moment().add(-1, 'day'), 'day')) {
return ' vis-yesterday';
}
return '';
@ -565,37 +581,37 @@ TimeStep.prototype.getClassName = function() {
switch (this.scale) {
case 'millisecond':
return even(date.milliseconds()).trim();
return even(current.milliseconds()).trim();
case 'second':
return even(date.seconds()).trim();
return even(current.seconds()).trim();
case 'minute':
return even(date.minutes()).trim();
return even(current.minutes()).trim();
case 'hour':
var hours = date.hours();
var hours = current.hours();
if (this.step == 4) {
hours = hours + '-h' + (hours + 4);
}
return 'vis-h' + hours + today(date) + even(date.hours());
return 'vis-h' + hours + today(current) + even(current.hours());
case 'weekday':
return 'vis-' + date.format('dddd').toLowerCase() +
today(date) + currentWeek(date) + even(date.date());
return 'vis-' + current.format('dddd').toLowerCase() +
today(current) + currentWeek(current) + even(current.date());
case 'day':
var day = date.date();
var month = date.format('MMMM').toLowerCase();
return 'vis-day' + day + ' vis-' + month + currentMonth(date) + even(day - 1);
var day = current.date();
var month = current.format('MMMM').toLowerCase();
return 'vis-day' + day + ' vis-' + month + currentMonth(current) + even(day - 1);
case 'month':
return 'vis-' + date.format('MMMM').toLowerCase() +
currentMonth(date) + even(date.month());
return 'vis-' + current.format('MMMM').toLowerCase() +
currentMonth(current) + even(current.month());
case 'year':
var year = date.year();
return 'vis-year' + year + currentYear(date)+ even(year);
var year = current.year();
return 'vis-year' + year + currentYear(current)+ even(year);
default:
return '';

+ 3
- 0
lib/timeline/Timeline.js View File

@ -1,5 +1,6 @@
var Emitter = require('emitter-component');
var Hammer = require('../module/hammer');
var moment = require('../module/moment');
var util = require('../util');
var DataSet = require('../DataSet');
var DataView = require('../DataView');
@ -49,6 +50,8 @@ function Timeline (container, items, groups, options) {
item: 'bottom' // not relevant
},
moment: moment,
width: null,
height: null,
maxHeight: null,

+ 4
- 3
lib/timeline/component/CurrentTime.js View File

@ -18,6 +18,7 @@ function CurrentTime (body, options) {
this.defaultOptions = {
showCurrentTime: true,
moment: moment,
locales: locales,
locale: 'en'
};
@ -63,7 +64,7 @@ CurrentTime.prototype.destroy = function () {
CurrentTime.prototype.setOptions = function(options) {
if (options) {
// copy all options that we know
util.selectiveExtend(['showCurrentTime', 'locale', 'locales'], this.options, options);
util.selectiveExtend(['showCurrentTime', 'moment', 'locale', 'locales'], this.options, options);
}
};
@ -84,7 +85,7 @@ CurrentTime.prototype.redraw = function() {
this.start();
}
var now = new Date(new Date().valueOf() + this.offset);
var now = this.options.moment(new Date().valueOf() + this.offset);
var x = this.body.util.toScreen(now);
var locale = this.options.locales[this.options.locale];
@ -95,7 +96,7 @@ CurrentTime.prototype.redraw = function() {
}
locale = this.options.locales['en']; // fall back on english when not available
}
var title = locale.current + ' ' + locale.time + ': ' + moment(now).format('dddd, MMMM Do YYYY, H:mm:ss');
var title = locale.current + ' ' + locale.time + ': ' + now.format('dddd, MMMM Do YYYY, H:mm:ss');
title = title.charAt(0).toUpperCase() + title.substring(1);
this.bar.style.left = x + 'px';

+ 4
- 6
lib/timeline/component/CustomTime.js View File

@ -20,6 +20,7 @@ function CustomTime (body, options) {
// default options
this.defaultOptions = {
moment: moment,
locales: locales,
locale: 'en',
id: undefined
@ -52,7 +53,7 @@ CustomTime.prototype = new Component();
CustomTime.prototype.setOptions = function(options) {
if (options) {
// copy all options that we know
util.selectiveExtend(['locale', 'locales', 'id'], this.options, options);
util.selectiveExtend(['moment', 'locale', 'locales', 'id'], this.options, options);
}
};
@ -83,10 +84,6 @@ CustomTime.prototype._create = function() {
this.hammer.on('panmove', this._onDrag.bind(this));
this.hammer.on('panend', this._onDragEnd.bind(this));
this.hammer.get('pan').set({threshold:5, direction:30}); // 30 is ALL_DIRECTIONS in hammer.
// TODO: cleanup
//this.hammer.on('pan', function (event) {
// event.preventDefault();
//});
};
/**
@ -125,7 +122,8 @@ CustomTime.prototype.redraw = function () {
}
locale = this.options.locales['en']; // fall back on english when not available
}
var title = locale.time + ': ' + moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss');
var title = locale.time + ': ' + this.options.moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss');
title = title.charAt(0).toUpperCase() + title.substring(1);
this.bar.style.left = x + 'px';

+ 7
- 4
lib/timeline/component/TimeAxis.js View File

@ -40,6 +40,7 @@ function TimeAxis (body, options) {
showMinorLabels: true,
showMajorLabels: true,
format: TimeStep.FORMAT,
moment: moment,
timeAxis: null
};
this.options = util.extend({}, this.defaultOptions);
@ -69,7 +70,8 @@ TimeAxis.prototype.setOptions = function(options) {
'showMinorLabels',
'showMajorLabels',
'hiddenDates',
'timeAxis'
'timeAxis',
'moment'
], this.options, options);
// deep copy the format options
@ -194,10 +196,11 @@ TimeAxis.prototype._repaintLabels = function () {
var start = util.convert(this.body.range.start, 'Number');
var end = util.convert(this.body.range.end, 'Number');
var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf();
var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this.body.range, timeLabelsize);
var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this.body.range, timeLabelsize);
minimumStep -= this.body.util.toTime(0).valueOf();
var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates);
step.setMoment(this.options.moment);
if (this.options.format) {
step.setFormat(this.options.format);
}
@ -229,7 +232,7 @@ TimeAxis.prototype._repaintLabels = function () {
var max = 0;
var className;
step.first();
step.start();
next = step.getCurrent();
xNext = this.body.util.toScreen(next);
while (step.hasNext() && max < 1000) {
@ -247,7 +250,7 @@ TimeAxis.prototype._repaintLabels = function () {
xNext = this.body.util.toScreen(next);
width = xNext - x;
var labelFits = labelMinor.length * this.props.minorCharWidth < width;
var labelFits = (labelMinor.length + 1) * this.props.minorCharWidth < width;
if (this.options.showMinorLabels && labelFits) {
this._repaintMinorText(x, labelMinor, orientation, className);

+ 1
- 0
lib/timeline/optionsGraph2d.js View File

@ -125,6 +125,7 @@ let allOptions = {
},
__type__: {object}
},
moment: {'function': 'function'},
height: {string, number},
hiddenDates: {object, array},
locale:{string},

+ 1
- 0
lib/timeline/optionsTimeline.js View File

@ -62,6 +62,7 @@ let allOptions = {
},
__type__: {object}
},
moment: {'function': 'function'},
groupOrder: {string, 'function': 'function'},
height: {string, number},
hiddenDates: {object, array},

Loading…
Cancel
Save