Browse Source

almost working hiddenDates, generic and specific. Only start times now

v3_develop
Alex de Mulder 10 years ago
parent
commit
95f7586248
7 changed files with 591 additions and 143 deletions
  1. +291
    -67
      dist/vis.js
  2. +9
    -9
      examples/timeline/hiding_times.html
  3. +9
    -7
      lib/timeline/Core.js
  4. +257
    -51
      lib/timeline/DateUtil.js
  5. +4
    -3
      lib/timeline/Range.js
  6. +15
    -1
      lib/timeline/TimeStep.js
  7. +6
    -5
      lib/timeline/component/TimeAxis.js

+ 291
- 67
dist/vis.js View File

@ -5,7 +5,7 @@
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 3.5.0 * @version 3.5.0
* @date 2014-10-03
* @date 2014-10-06
* *
* @license * @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com * Copyright (C) 2011-2014 Almende B.V, http://almende.com
@ -11886,7 +11886,7 @@ return /******/ (function(modules) { // webpackBootstrap
Range.prototype.setOptions = function (options) { Range.prototype.setOptions = function (options) {
if (options) { if (options) {
// copy the options that we know // copy the options that we know
var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hide'];
var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates'];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
if ('start' in options || 'end' in options) { if ('start' in options || 'end' in options) {
@ -11921,7 +11921,6 @@ return /******/ (function(modules) { // webpackBootstrap
Range.prototype.setRange = function(start, end, animate) { Range.prototype.setRange = function(start, end, animate) {
var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null;
var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null;
this._cancelAnimation(); this._cancelAnimation();
if (animate) { if (animate) {
@ -11941,6 +11940,7 @@ return /******/ (function(modules) { // webpackBootstrap
var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration);
changed = me._applyRange(s, e); changed = me._applyRange(s, e);
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
anyChanged = anyChanged || changed; anyChanged = anyChanged || changed;
if (changed) { if (changed) {
me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end)}); me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end)});
@ -11963,6 +11963,7 @@ return /******/ (function(modules) { // webpackBootstrap
} }
else { else {
var changed = this._applyRange(_start, _end); var changed = this._applyRange(_start, _end);
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
if (changed) { if (changed) {
var params = {start: new Date(this.start), end: new Date(this.end)}; var params = {start: new Date(this.start), end: new Date(this.end)};
this.body.emitter.emit('rangechange', params); this.body.emitter.emit('rangechange', params);
@ -12205,7 +12206,6 @@ return /******/ (function(modules) { // webpackBootstrap
} }
this.previousDelta = delta; this.previousDelta = delta;
this._applyRange(newStart, newEnd); this._applyRange(newStart, newEnd);
// fire a rangechange event // fire a rangechange event
@ -12417,6 +12417,7 @@ return /******/ (function(modules) { // webpackBootstrap
var safeDates = DateUtil.snapAwayFromHidden(this.body.hiddenDates, this, newStart, newEnd, delta, true); var safeDates = DateUtil.snapAwayFromHidden(this.body.hiddenDates, this, newStart, newEnd, delta, true);
//console.log(new Date(this.start), new Date(this.end), new Date(newStart), new Date(newEnd),new Date(safeDates.newStart), new Date(safeDates.newEnd)); //console.log(new Date(this.start), new Date(this.end), new Date(newStart), new Date(newEnd),new Date(safeDates.newStart), new Date(safeDates.newEnd));
if (safeDates !== false) { if (safeDates !== false) {
newStart = safeDates.newStart; newStart = safeDates.newStart;
newEnd = safeDates.newEnd; newEnd = safeDates.newEnd;
} }
@ -12569,26 +12570,165 @@ return /******/ (function(modules) { // webpackBootstrap
var moment = __webpack_require__(2); var moment = __webpack_require__(2);
exports.convertHiddenOptions = function(timeline) {
var hiddenTimes = timeline.options.hide;
if (Array.isArray(hiddenTimes) == true) {
for (var i = 0; i < hiddenTimes.length; i++) {
var dateItem = {};
dateItem.start = moment(hiddenTimes[i].start).toDate().valueOf();
dateItem.end = moment(hiddenTimes[i].end).toDate().valueOf();
timeline.body.hiddenDates.push(dateItem);
/**
* used in Core to convert the options into a volatile variable
*
* @param Core
*/
exports.convertHiddenOptions = function(body, hiddenDates) {
var hiddenTimes = hiddenDates.specific;
if (hiddenTimes) {
if (Array.isArray(hiddenTimes) == true) {
for (var i = 0; i < hiddenTimes.length; i++) {
var dateItem = {};
dateItem.start = moment(hiddenTimes[i].start).toDate().valueOf();
dateItem.end = moment(hiddenTimes[i].end).toDate().valueOf();
body.hiddenDates.push(dateItem);
}
body.hiddenDates.sort(function (a, b) {
return a.start - b.start;
}); // sort by start time
}
else {
body.hiddenDates = [{
start: moment(hiddenTimes.start).toDate().valueOf(),
end: moment(hiddenTimes.end).toDate().valueOf()
}
];
} }
timeline.body.hiddenDates.sort(function(a,b) {return a.start - b.start;}); // sort by start time
} }
else {
timeline.body.hiddenDates = [{
start:moment(hiddenTimes.start).toDate().valueOf(),
end:moment(hiddenTimes.end).toDate().valueOf()
};
exports.updateHiddenDates = function (body, hiddenDates) {
if (hiddenDates && hiddenDates.periodic) {
body.hiddenDates = [];
exports.convertHiddenOptions(body, hiddenDates);
var start = moment(body.range.start);
var end = moment(body.range.end);
if (hiddenDates.periodic.days) {
var nextStartDay = moment(body.range.start);
var nextEndDay = moment(body.range.start);
for (var i = 0; i < hiddenDates.periodic.days.length; i++) {
var startDay = hiddenDates.periodic.days[i].start;
var endDay = hiddenDates.periodic.days[i].end;
nextStartDay.isoWeekday(startDay);
nextEndDay.isoWeekday(endDay);
if (start < nextStartDay) {
nextStartDay.isoWeekday(startDay - 7);
}
if (start < nextEndDay) {
nextEndDay.isoWeekday(endDay - 7);
}
nextStartDay.milliseconds(0);
nextStartDay.seconds(0);
nextStartDay.minutes(0);
nextStartDay.hours(0);
nextEndDay.milliseconds(0);
nextEndDay.seconds(0);
nextEndDay.minutes(0);
nextEndDay.hours(0);
while (nextStartDay < end) {
body.hiddenDates.push({start: nextStartDay.valueOf(), end: nextEndDay.valueOf()});
nextStartDay.isoWeekday(startDay + 7);
nextEndDay.isoWeekday(endDay + 7);
}
body.hiddenDates.push({start: nextStartDay.valueOf(), end: nextEndDay.valueOf()});
} }
];
}
if (hiddenDates.periodic.times) {
var nextStartDay = moment(body.range.start);
var nextEndDay = moment(body.range.start);
end = end.valueOf();
for (var i = 0; i < hiddenDates.periodic.times.length; i++) {
var startTime = hiddenDates.periodic.times[i].start.split(":");
var endTime = hiddenDates.periodic.times[i].end.split(":");
nextStartDay.milliseconds(0);
nextStartDay.seconds(startTime[2]);
nextStartDay.minutes(startTime[1]);
nextStartDay.hours(startTime[0]);
nextEndDay.milliseconds(0);
nextEndDay.seconds(endTime[2]);
nextEndDay.minutes(endTime[1]);
nextEndDay.hours(endTime[0]);
nextStartDay = nextStartDay.valueOf();
nextEndDay = nextEndDay.valueOf();
if (endTime[0] < startTime[0]) {
nextEndDay += 3600000*24;
}
nextStartDay -= 7*3600000*24;
nextEndDay -= 7*3600000*24;
while (nextStartDay < (end + 7*3600000*24)) {
body.hiddenDates.push({start: nextStartDay.valueOf(), end: nextEndDay.valueOf()});
nextStartDay += 3600000*24;
nextEndDay += 3600000*24;
}
}
}
exports.removeDuplicates(body);
//var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
//var endHidden = exports.isHidden(body.range.end,body.hiddenDates);
} }
} }
exports.removeDuplicates = function(body) {
var hiddenDates = body.hiddenDates;
var safeDates = [];
for (var i = 0; i < hiddenDates.length; i++) {
for (var j = 0; j < hiddenDates.length; j++) {
if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) {
// j inside i
if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
hiddenDates[j].remove = true;
}
// j start inside i
else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) {
hiddenDates[i].end = hiddenDates[j].end;
hiddenDates[j].remove = true;
}
// j end inside i
else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
hiddenDates[i].start = hiddenDates[j].start;
hiddenDates[j].remove = true;
}
}
}
}
for (var i = 0; i < hiddenDates.length; i++) {
if (hiddenDates[i].remove !== true) {
safeDates.push(hiddenDates[i]);
}
}
body.hiddenDates = safeDates;
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 previousTime
*/
exports.stepOverHiddenDates = function(timeStep, previousTime) { exports.stepOverHiddenDates = function(timeStep, previousTime) {
var stepInHidden = false; var stepInHidden = false;
var currentValue = timeStep.current.valueOf(); var currentValue = timeStep.current.valueOf();
@ -12602,39 +12742,84 @@ return /******/ (function(modules) { // webpackBootstrap
} }
if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) {
timeStep.current = moment(endDate).toDate();
var prevValue = moment(previousTime);
var newValue = moment(endDate);
if (prevValue.dayOfYear() != newValue.dayOfYear()) {
timeStep.switchedDay = true;
}
timeStep.current = newValue.toDate();
} }
}
};
exports.toScreen = function(timeline, time, width) {
var hidden = exports.isHidden(time, timeline.body.hiddenDates)
/**
* Used in TimeStep to avoid the hidden times.
* @param timeStep
* @param previousTime
*/
exports.checkFirstStep = function(timeStep) {
var stepInHidden = false;
var currentValue = timeStep.current.valueOf();
for (var i = 0; i < timeStep.hiddenDates.length; i++) {
var startDate = timeStep.hiddenDates[i].start;
var endDate = timeStep.hiddenDates[i].end;
if (currentValue >= startDate && currentValue < endDate) {
stepInHidden = true;
break;
}
}
if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) {
var newValue = moment(endDate);
timeStep.current = newValue.toDate();
}
};
/**
* replaces the Core toScreen methods
* @param Core
* @param time
* @param width
* @returns {number}
*/
exports.toScreen = function(Core, time, width) {
var hidden = exports.isHidden(time, Core.body.hiddenDates)
if (hidden.hidden == true) { if (hidden.hidden == true) {
time = hidden.startDate; time = hidden.startDate;
} }
var res = exports.correctTimeForDuration(timeline.body.hiddenDates, timeline.range, time);
var res = exports.correctTimeForDuration(Core.body.hiddenDates, Core.range, time);
var duration = res.duration; var duration = res.duration;
time = res.time; time = res.time;
var conversion = timeline.range.conversion(width, duration);
var conversion = Core.range.conversion(width, duration);
return (time.valueOf() - conversion.offset) * conversion.scale; return (time.valueOf() - conversion.offset) * conversion.scale;
}
};
/**
* Replaces the core toTime methods
* @param body
* @param range
* @param x
* @param width
* @returns {Date}
*/
exports.toTime = function(body, range, x, width) { exports.toTime = function(body, range, x, width) {
var duration = exports.getHiddenDuration(body.hiddenDates, range); var duration = exports.getHiddenDuration(body.hiddenDates, range);
var conversion = range.conversion(width, duration); var conversion = range.conversion(width, duration);
var time = new Date(x / conversion.scale + conversion.offset);
//var hidden = exports.isHidden(time, timeline.body.hiddenDates)
//if (hidden.hidden == true) {
// time = hidden.startDate;
//}
//time = exports.correctTimeForDuration(body.hiddenDates, range, time).time;
return time;
}
return new Date(x / conversion.scale + conversion.offset);
};
/**
* Support function
*
* @param hiddenTimes
* @param range
* @returns {number}
*/
exports.getHiddenDuration = function(hiddenTimes, range) { exports.getHiddenDuration = function(hiddenTimes, range) {
var duration = 0; var duration = 0;
for (var i = 0; i < hiddenTimes.length; i++) { for (var i = 0; i < hiddenTimes.length; i++) {
@ -12646,13 +12831,20 @@ return /******/ (function(modules) { // webpackBootstrap
} }
} }
return duration; return duration;
}
};
/**
* Support function
* @param hiddenTimes
* @param range
* @param time
* @returns {{duration: number, time: *, offset: number}}
*/
exports.correctTimeForDuration = function(hiddenTimes, range, time) { exports.correctTimeForDuration = function(hiddenTimes, range, time) {
var duration = 0; var duration = 0;
var timeOffset = 0; var timeOffset = 0;
time = moment(time).toDate().valueOf()
time = moment(time).toDate().valueOf();
for (var i = 0; i < hiddenTimes.length; i++) { for (var i = 0; i < hiddenTimes.length; i++) {
var startDate = hiddenTimes[i].start; var startDate = hiddenTimes[i].start;
@ -12667,48 +12859,63 @@ return /******/ (function(modules) { // webpackBootstrap
} }
time -= timeOffset; time -= timeOffset;
return {duration: duration, time:time, offset: timeOffset}; return {duration: duration, time:time, offset: timeOffset};
}
};
/**
* Used with zooming and dragging
*
* @param hiddenTimes
* @param range
* @param start
* @param end
* @param delta
* @param zoom
* @returns {*}
*/
exports.snapAwayFromHidden = function(hiddenTimes, range, start, end, delta, zoom) { exports.snapAwayFromHidden = function(hiddenTimes, range, start, end, delta, zoom) {
zoom = zoom || false; zoom = zoom || false;
var newStart = start; var newStart = start;
var newEnd = end; var newEnd = end;
var newDates = false;
for (var i = 0; i < hiddenTimes.length; i++) { for (var i = 0; i < hiddenTimes.length; i++) {
var startDate = hiddenTimes[i].start; var startDate = hiddenTimes[i].start;
var endDate = hiddenTimes[i].end; var endDate = hiddenTimes[i].end;
if (start >= startDate && start < endDate) { // if the start is entering a hidden zone if (start >= startDate && start < endDate) { // if the start is entering a hidden zone
range.deltaDifference += delta;
newDates = true;
// start from left, snap to right
if (range.previousDelta - delta > 0 && zoom == false || zoom == true && range.previousDelta - delta < 0) { // from the left if (range.previousDelta - delta > 0 && zoom == false || zoom == true && range.previousDelta - delta < 0) { // from the left
console.log("start from left, snap to right")
newStart = endDate + 1; newStart = endDate + 1;
} }
else { // from the right
console.log("start from right, snap to left")
else { // start from right, snap to left
newStart = startDate - 1; newStart = startDate - 1;
} }
return {newStart: newStart, newEnd: newEnd};
} }
else if (end >= startDate && end < endDate) { // if the start is entering a hidden zone
range.deltaDifference += delta;
if (range.previousDelta - delta < 0) { // from the right
console.log("end from right, snap to left")
if (end >= startDate && end < endDate) { // if the end is entering a hidden zone
newDates = true;
if (range.previousDelta - delta < 0) { // end from right, snap to left
newEnd = startDate - 1; newEnd = startDate - 1;
} }
else { // from the left
console.log("end from left, snap to right")
else { // end from left, snap to right
newEnd = endDate + 1; newEnd = endDate + 1;
} }
return {newStart: newStart, newEnd: newEnd};
} }
} }
if (newDates == true) {
range.deltaDifference += delta;
return {newStart: newStart, newEnd: newEnd};
}
return false; return false;
}
};
/**
* Check if a time is hidden
*
* @param time
* @param hiddenTimes
* @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
*/
exports.isHidden = function(time, hiddenTimes) { exports.isHidden = function(time, hiddenTimes) {
var isHidden = false; var isHidden = false;
for (var i = 0; i < hiddenTimes.length; i++) { for (var i = 0; i < hiddenTimes.length; i++) {
@ -12903,11 +13110,11 @@ return /******/ (function(modules) { // webpackBootstrap
Core.prototype.setOptions = function (options) { Core.prototype.setOptions = function (options) {
if (options) { if (options) {
// copy the known options // copy the known options
var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hide'];
var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates'];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
if ('hide' in this.options) {
DateUtil.convertHiddenOptions(this);
if ('hiddenDates' in this.options) {
DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates);
} }
if ('clickToUse' in options) { if ('clickToUse' in options) {
@ -13160,13 +13367,15 @@ return /******/ (function(modules) { // webpackBootstrap
* option autoResize=false * option autoResize=false
*/ */
Core.prototype.redraw = function() { Core.prototype.redraw = function() {
var resized = false,
options = this.options,
props = this.props,
dom = this.dom;
var resized = false;
var options = this.options;
var props = this.props;
var dom = this.dom;
if (!dom) return; // when destroyed if (!dom) return; // when destroyed
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
// update class names // update class names
if (options.orientation == 'top') { if (options.orientation == 'top') {
util.addClassName(dom.root, 'top'); util.addClassName(dom.root, 'top');
@ -13580,6 +13789,7 @@ return /******/ (function(modules) { // webpackBootstrap
var util = __webpack_require__(1); var util = __webpack_require__(1);
var Component = __webpack_require__(22); var Component = __webpack_require__(22);
var TimeStep = __webpack_require__(26); var TimeStep = __webpack_require__(26);
var DateUtil = __webpack_require__(23);
var moment = __webpack_require__(2); var moment = __webpack_require__(2);
/** /**
@ -13642,7 +13852,7 @@ return /******/ (function(modules) { // webpackBootstrap
TimeAxis.prototype.setOptions = function(options) { TimeAxis.prototype.setOptions = function(options) {
if (options) { if (options) {
// copy all options that we know // copy all options that we know
util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels','hide'], this.options, options);
util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels','hiddenDates'], this.options, options);
// apply locale to moment.js // apply locale to moment.js
// TODO: not so nice, this is applied globally to moment.js // TODO: not so nice, this is applied globally to moment.js
@ -13689,10 +13899,10 @@ return /******/ (function(modules) { // webpackBootstrap
* @return {boolean} Returns true if the component is resized * @return {boolean} Returns true if the component is resized
*/ */
TimeAxis.prototype.redraw = function () { TimeAxis.prototype.redraw = function () {
var options = this.options,
props = this.props,
foreground = this.dom.foreground,
background = this.dom.background;
var options = this.options;
var props = this.props;
var foreground = this.dom.foreground;
var background = this.dom.background;
// determine the correct parent DOM element (depending on option orientation) // determine the correct parent DOM element (depending on option orientation)
var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom;
@ -14033,6 +14243,8 @@ return /******/ (function(modules) { // webpackBootstrap
// initialize the range // initialize the range
this.setRange(start, end, minimumStep); this.setRange(start, end, minimumStep);
// hidden Dates options
this.switchedDay = false;
this.hiddenDates = hiddenDates; this.hiddenDates = hiddenDates;
if (hiddenDates === undefined) { if (hiddenDates === undefined) {
this.hiddenDates = []; this.hiddenDates = [];
@ -14395,6 +14607,19 @@ return /******/ (function(modules) { // webpackBootstrap
* @return {boolean} true if current date is major, else false. * @return {boolean} true if current date is major, else false.
*/ */
TimeStep.prototype.isMajor = function() { TimeStep.prototype.isMajor = function() {
if (this.switchedDay == true) {
this.switchedDay = false;
switch (this.scale) {
case TimeStep.SCALE.MILLISECOND:
case TimeStep.SCALE.SECOND:
case TimeStep.SCALE.MINUTE:
case TimeStep.SCALE.HOUR:
return true;
default:
return false;
}
}
switch (this.scale) { switch (this.scale) {
case TimeStep.SCALE.MILLISECOND: case TimeStep.SCALE.MILLISECOND:
return (this.current.getMilliseconds() == 0); return (this.current.getMilliseconds() == 0);
@ -14402,7 +14627,6 @@ return /******/ (function(modules) { // webpackBootstrap
return (this.current.getSeconds() == 0); return (this.current.getSeconds() == 0);
case TimeStep.SCALE.MINUTE: case TimeStep.SCALE.MINUTE:
return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); return (this.current.getHours() == 0) && (this.current.getMinutes() == 0);
// Note: this is no bug. Major label is equal for both minute and hour scale
case TimeStep.SCALE.HOUR: case TimeStep.SCALE.HOUR:
return (this.current.getHours() == 0); return (this.current.getHours() == 0);
case TimeStep.SCALE.WEEKDAY: // intentional fall through case TimeStep.SCALE.WEEKDAY: // intentional fall through

+ 9
- 9
examples/timeline/hiding_times.html View File

@ -31,16 +31,16 @@
// Configuration for the Timeline // Configuration for the Timeline
var options = { var options = {
hide: [
{
start: '2014-04-20 20:00:00',
end: '2014-04-21 9:00:00'
},
{
start: '2014-04-05 00:00:00',
end: '2014-04-10 00:00:00'
hiddenDates: {
// specific:[
// {start: '2014-04-21 00:00:00', end: '2014-04-22 00:00:00'},
// {start: '2014-04-05 00:00:00', end: '2014-04-10 00:00:00'}
// ],
periodic: {
times: [{start:'20:00:00', end:'09:00:00'}],
days: [{start: 6, end:1}] // 1 - 7 : Monday - Sunday
} }
],
},
start: '2014-04-17', start: '2014-04-17',
end: '2014-05-01', end: '2014-05-01',
height: '200px' height: '200px'

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

@ -174,11 +174,11 @@ Core.prototype._create = function (container) {
Core.prototype.setOptions = function (options) { Core.prototype.setOptions = function (options) {
if (options) { if (options) {
// copy the known options // copy the known options
var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hide'];
var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates'];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
if ('hide' in this.options) {
DateUtil.convertHiddenOptions(this);
if ('hiddenDates' in this.options) {
DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates);
} }
if ('clickToUse' in options) { if ('clickToUse' in options) {
@ -431,13 +431,15 @@ Core.prototype.getWindow = function() {
* option autoResize=false * option autoResize=false
*/ */
Core.prototype.redraw = function() { Core.prototype.redraw = function() {
var resized = false,
options = this.options,
props = this.props,
dom = this.dom;
var resized = false;
var options = this.options;
var props = this.props;
var dom = this.dom;
if (!dom) return; // when destroyed if (!dom) return; // when destroyed
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
// update class names // update class names
if (options.orientation == 'top') { if (options.orientation == 'top') {
util.addClassName(dom.root, 'top'); util.addClassName(dom.root, 'top');

+ 257
- 51
lib/timeline/DateUtil.js View File

@ -4,26 +4,165 @@
var moment = require('../module/moment'); var moment = require('../module/moment');
exports.convertHiddenOptions = function(timeline) {
var hiddenTimes = timeline.options.hide;
if (Array.isArray(hiddenTimes) == true) {
for (var i = 0; i < hiddenTimes.length; i++) {
var dateItem = {};
dateItem.start = moment(hiddenTimes[i].start).toDate().valueOf();
dateItem.end = moment(hiddenTimes[i].end).toDate().valueOf();
timeline.body.hiddenDates.push(dateItem);
/**
* used in Core to convert the options into a volatile variable
*
* @param Core
*/
exports.convertHiddenOptions = function(body, hiddenDates) {
var hiddenTimes = hiddenDates.specific;
if (hiddenTimes) {
if (Array.isArray(hiddenTimes) == true) {
for (var i = 0; i < hiddenTimes.length; i++) {
var dateItem = {};
dateItem.start = moment(hiddenTimes[i].start).toDate().valueOf();
dateItem.end = moment(hiddenTimes[i].end).toDate().valueOf();
body.hiddenDates.push(dateItem);
}
body.hiddenDates.sort(function (a, b) {
return a.start - b.start;
}); // sort by start time
}
else {
body.hiddenDates = [{
start: moment(hiddenTimes.start).toDate().valueOf(),
end: moment(hiddenTimes.end).toDate().valueOf()
}
];
}
}
};
exports.updateHiddenDates = function (body, hiddenDates) {
if (hiddenDates && hiddenDates.periodic) {
body.hiddenDates = [];
exports.convertHiddenOptions(body, hiddenDates);
var start = moment(body.range.start);
var end = moment(body.range.end);
if (hiddenDates.periodic.days) {
var nextStartDay = moment(body.range.start);
var nextEndDay = moment(body.range.start);
for (var i = 0; i < hiddenDates.periodic.days.length; i++) {
var startDay = hiddenDates.periodic.days[i].start;
var endDay = hiddenDates.periodic.days[i].end;
nextStartDay.isoWeekday(startDay);
nextEndDay.isoWeekday(endDay);
if (start < nextStartDay) {
nextStartDay.isoWeekday(startDay - 7);
}
if (start < nextEndDay) {
nextEndDay.isoWeekday(endDay - 7);
}
nextStartDay.milliseconds(0);
nextStartDay.seconds(0);
nextStartDay.minutes(0);
nextStartDay.hours(0);
nextEndDay.milliseconds(0);
nextEndDay.seconds(0);
nextEndDay.minutes(0);
nextEndDay.hours(0);
while (nextStartDay < end) {
body.hiddenDates.push({start: nextStartDay.valueOf(), end: nextEndDay.valueOf()});
nextStartDay.isoWeekday(startDay + 7);
nextEndDay.isoWeekday(endDay + 7);
}
body.hiddenDates.push({start: nextStartDay.valueOf(), end: nextEndDay.valueOf()});
}
}
if (hiddenDates.periodic.times) {
var nextStartDay = moment(body.range.start);
var nextEndDay = moment(body.range.start);
end = end.valueOf();
for (var i = 0; i < hiddenDates.periodic.times.length; i++) {
var startTime = hiddenDates.periodic.times[i].start.split(":");
var endTime = hiddenDates.periodic.times[i].end.split(":");
nextStartDay.milliseconds(0);
nextStartDay.seconds(startTime[2]);
nextStartDay.minutes(startTime[1]);
nextStartDay.hours(startTime[0]);
nextEndDay.milliseconds(0);
nextEndDay.seconds(endTime[2]);
nextEndDay.minutes(endTime[1]);
nextEndDay.hours(endTime[0]);
nextStartDay = nextStartDay.valueOf();
nextEndDay = nextEndDay.valueOf();
if (endTime[0] < startTime[0]) {
nextEndDay += 3600000*24;
}
nextStartDay -= 7*3600000*24;
nextEndDay -= 7*3600000*24;
while (nextStartDay < (end + 7*3600000*24)) {
body.hiddenDates.push({start: nextStartDay.valueOf(), end: nextEndDay.valueOf()});
nextStartDay += 3600000*24;
nextEndDay += 3600000*24;
}
}
} }
timeline.body.hiddenDates.sort(function(a,b) {return a.start - b.start;}); // sort by start time
exports.removeDuplicates(body);
//var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
//var endHidden = exports.isHidden(body.range.end,body.hiddenDates);
} }
else {
timeline.body.hiddenDates = [{
start:moment(hiddenTimes.start).toDate().valueOf(),
end:moment(hiddenTimes.end).toDate().valueOf()
}
exports.removeDuplicates = function(body) {
var hiddenDates = body.hiddenDates;
var safeDates = [];
for (var i = 0; i < hiddenDates.length; i++) {
for (var j = 0; j < hiddenDates.length; j++) {
if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) {
// j inside i
if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
hiddenDates[j].remove = true;
}
// j start inside i
else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) {
hiddenDates[i].end = hiddenDates[j].end;
hiddenDates[j].remove = true;
}
// j end inside i
else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
hiddenDates[i].start = hiddenDates[j].start;
hiddenDates[j].remove = true;
}
} }
];
}
}
for (var i = 0; i < hiddenDates.length; i++) {
if (hiddenDates[i].remove !== true) {
safeDates.push(hiddenDates[i]);
}
}
body.hiddenDates = safeDates;
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 previousTime
*/
exports.stepOverHiddenDates = function(timeStep, previousTime) { exports.stepOverHiddenDates = function(timeStep, previousTime) {
var stepInHidden = false; var stepInHidden = false;
var currentValue = timeStep.current.valueOf(); var currentValue = timeStep.current.valueOf();
@ -37,39 +176,84 @@ exports.stepOverHiddenDates = function(timeStep, previousTime) {
} }
if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) {
timeStep.current = moment(endDate).toDate();
var prevValue = moment(previousTime);
var newValue = moment(endDate);
if (prevValue.dayOfYear() != newValue.dayOfYear()) {
timeStep.switchedDay = true;
}
timeStep.current = newValue.toDate();
} }
}
};
exports.toScreen = function(timeline, time, width) {
var hidden = exports.isHidden(time, timeline.body.hiddenDates)
/**
* Used in TimeStep to avoid the hidden times.
* @param timeStep
* @param previousTime
*/
exports.checkFirstStep = function(timeStep) {
var stepInHidden = false;
var currentValue = timeStep.current.valueOf();
for (var i = 0; i < timeStep.hiddenDates.length; i++) {
var startDate = timeStep.hiddenDates[i].start;
var endDate = timeStep.hiddenDates[i].end;
if (currentValue >= startDate && currentValue < endDate) {
stepInHidden = true;
break;
}
}
if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) {
var newValue = moment(endDate);
timeStep.current = newValue.toDate();
}
};
/**
* replaces the Core toScreen methods
* @param Core
* @param time
* @param width
* @returns {number}
*/
exports.toScreen = function(Core, time, width) {
var hidden = exports.isHidden(time, Core.body.hiddenDates)
if (hidden.hidden == true) { if (hidden.hidden == true) {
time = hidden.startDate; time = hidden.startDate;
} }
var res = exports.correctTimeForDuration(timeline.body.hiddenDates, timeline.range, time);
var res = exports.correctTimeForDuration(Core.body.hiddenDates, Core.range, time);
var duration = res.duration; var duration = res.duration;
time = res.time; time = res.time;
var conversion = timeline.range.conversion(width, duration);
var conversion = Core.range.conversion(width, duration);
return (time.valueOf() - conversion.offset) * conversion.scale; return (time.valueOf() - conversion.offset) * conversion.scale;
}
};
/**
* Replaces the core toTime methods
* @param body
* @param range
* @param x
* @param width
* @returns {Date}
*/
exports.toTime = function(body, range, x, width) { exports.toTime = function(body, range, x, width) {
var duration = exports.getHiddenDuration(body.hiddenDates, range); var duration = exports.getHiddenDuration(body.hiddenDates, range);
var conversion = range.conversion(width, duration); var conversion = range.conversion(width, duration);
var time = new Date(x / conversion.scale + conversion.offset);
//var hidden = exports.isHidden(time, timeline.body.hiddenDates)
//if (hidden.hidden == true) {
// time = hidden.startDate;
//}
//time = exports.correctTimeForDuration(body.hiddenDates, range, time).time;
return time;
}
return new Date(x / conversion.scale + conversion.offset);
};
/**
* Support function
*
* @param hiddenTimes
* @param range
* @returns {number}
*/
exports.getHiddenDuration = function(hiddenTimes, range) { exports.getHiddenDuration = function(hiddenTimes, range) {
var duration = 0; var duration = 0;
for (var i = 0; i < hiddenTimes.length; i++) { for (var i = 0; i < hiddenTimes.length; i++) {
@ -81,13 +265,20 @@ exports.getHiddenDuration = function(hiddenTimes, range) {
} }
} }
return duration; return duration;
}
};
/**
* Support function
* @param hiddenTimes
* @param range
* @param time
* @returns {{duration: number, time: *, offset: number}}
*/
exports.correctTimeForDuration = function(hiddenTimes, range, time) { exports.correctTimeForDuration = function(hiddenTimes, range, time) {
var duration = 0; var duration = 0;
var timeOffset = 0; var timeOffset = 0;
time = moment(time).toDate().valueOf()
time = moment(time).toDate().valueOf();
for (var i = 0; i < hiddenTimes.length; i++) { for (var i = 0; i < hiddenTimes.length; i++) {
var startDate = hiddenTimes[i].start; var startDate = hiddenTimes[i].start;
@ -102,48 +293,63 @@ exports.correctTimeForDuration = function(hiddenTimes, range, time) {
} }
time -= timeOffset; time -= timeOffset;
return {duration: duration, time:time, offset: timeOffset}; return {duration: duration, time:time, offset: timeOffset};
}
};
/**
* Used with zooming and dragging
*
* @param hiddenTimes
* @param range
* @param start
* @param end
* @param delta
* @param zoom
* @returns {*}
*/
exports.snapAwayFromHidden = function(hiddenTimes, range, start, end, delta, zoom) { exports.snapAwayFromHidden = function(hiddenTimes, range, start, end, delta, zoom) {
zoom = zoom || false; zoom = zoom || false;
var newStart = start; var newStart = start;
var newEnd = end; var newEnd = end;
var newDates = false;
for (var i = 0; i < hiddenTimes.length; i++) { for (var i = 0; i < hiddenTimes.length; i++) {
var startDate = hiddenTimes[i].start; var startDate = hiddenTimes[i].start;
var endDate = hiddenTimes[i].end; var endDate = hiddenTimes[i].end;
if (start >= startDate && start < endDate) { // if the start is entering a hidden zone if (start >= startDate && start < endDate) { // if the start is entering a hidden zone
range.deltaDifference += delta;
newDates = true;
// start from left, snap to right
if (range.previousDelta - delta > 0 && zoom == false || zoom == true && range.previousDelta - delta < 0) { // from the left if (range.previousDelta - delta > 0 && zoom == false || zoom == true && range.previousDelta - delta < 0) { // from the left
console.log("start from left, snap to right")
newStart = endDate + 1; newStart = endDate + 1;
} }
else { // from the right
console.log("start from right, snap to left")
else { // start from right, snap to left
newStart = startDate - 1; newStart = startDate - 1;
} }
return {newStart: newStart, newEnd: newEnd};
} }
else if (end >= startDate && end < endDate) { // if the start is entering a hidden zone
range.deltaDifference += delta;
if (range.previousDelta - delta < 0) { // from the right
console.log("end from right, snap to left")
if (end >= startDate && end < endDate) { // if the end is entering a hidden zone
newDates = true;
if (range.previousDelta - delta < 0) { // end from right, snap to left
newEnd = startDate - 1; newEnd = startDate - 1;
} }
else { // from the left
console.log("end from left, snap to right")
else { // end from left, snap to right
newEnd = endDate + 1; newEnd = endDate + 1;
} }
return {newStart: newStart, newEnd: newEnd};
} }
} }
if (newDates == true) {
range.deltaDifference += delta;
return {newStart: newStart, newEnd: newEnd};
}
return false; return false;
}
};
/**
* Check if a time is hidden
*
* @param time
* @param hiddenTimes
* @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
*/
exports.isHidden = function(time, hiddenTimes) { exports.isHidden = function(time, hiddenTimes) {
var isHidden = false; var isHidden = false;
for (var i = 0; i < hiddenTimes.length; i++) { for (var i = 0; i < hiddenTimes.length; i++) {

+ 4
- 3
lib/timeline/Range.js View File

@ -79,7 +79,7 @@ Range.prototype = new Component();
Range.prototype.setOptions = function (options) { Range.prototype.setOptions = function (options) {
if (options) { if (options) {
// copy the options that we know // copy the options that we know
var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hide'];
var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates'];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
if ('start' in options || 'end' in options) { if ('start' in options || 'end' in options) {
@ -114,7 +114,6 @@ function validateDirection (direction) {
Range.prototype.setRange = function(start, end, animate) { Range.prototype.setRange = function(start, end, animate) {
var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null;
var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null;
this._cancelAnimation(); this._cancelAnimation();
if (animate) { if (animate) {
@ -134,6 +133,7 @@ Range.prototype.setRange = function(start, end, animate) {
var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration);
changed = me._applyRange(s, e); changed = me._applyRange(s, e);
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
anyChanged = anyChanged || changed; anyChanged = anyChanged || changed;
if (changed) { if (changed) {
me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end)}); me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end)});
@ -156,6 +156,7 @@ Range.prototype.setRange = function(start, end, animate) {
} }
else { else {
var changed = this._applyRange(_start, _end); var changed = this._applyRange(_start, _end);
DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
if (changed) { if (changed) {
var params = {start: new Date(this.start), end: new Date(this.end)}; var params = {start: new Date(this.start), end: new Date(this.end)};
this.body.emitter.emit('rangechange', params); this.body.emitter.emit('rangechange', params);
@ -398,7 +399,6 @@ Range.prototype._onDrag = function (event) {
} }
this.previousDelta = delta; this.previousDelta = delta;
this._applyRange(newStart, newEnd); this._applyRange(newStart, newEnd);
// fire a rangechange event // fire a rangechange event
@ -610,6 +610,7 @@ Range.prototype.zoom = function(scale, center, delta) {
var safeDates = DateUtil.snapAwayFromHidden(this.body.hiddenDates, this, newStart, newEnd, delta, true); var safeDates = DateUtil.snapAwayFromHidden(this.body.hiddenDates, this, newStart, newEnd, delta, true);
//console.log(new Date(this.start), new Date(this.end), new Date(newStart), new Date(newEnd),new Date(safeDates.newStart), new Date(safeDates.newEnd)); //console.log(new Date(this.start), new Date(this.end), new Date(newStart), new Date(newEnd),new Date(safeDates.newStart), new Date(safeDates.newEnd));
if (safeDates !== false) { if (safeDates !== false) {
newStart = safeDates.newStart; newStart = safeDates.newStart;
newEnd = safeDates.newEnd; newEnd = safeDates.newEnd;
} }

+ 15
- 1
lib/timeline/TimeStep.js View File

@ -40,6 +40,8 @@ function TimeStep(start, end, minimumStep, hiddenDates) {
// initialize the range // initialize the range
this.setRange(start, end, minimumStep); this.setRange(start, end, minimumStep);
// hidden Dates options
this.switchedDay = false;
this.hiddenDates = hiddenDates; this.hiddenDates = hiddenDates;
if (hiddenDates === undefined) { if (hiddenDates === undefined) {
this.hiddenDates = []; this.hiddenDates = [];
@ -402,6 +404,19 @@ TimeStep.prototype.snap = function(date) {
* @return {boolean} true if current date is major, else false. * @return {boolean} true if current date is major, else false.
*/ */
TimeStep.prototype.isMajor = function() { TimeStep.prototype.isMajor = function() {
if (this.switchedDay == true) {
this.switchedDay = false;
switch (this.scale) {
case TimeStep.SCALE.MILLISECOND:
case TimeStep.SCALE.SECOND:
case TimeStep.SCALE.MINUTE:
case TimeStep.SCALE.HOUR:
return true;
default:
return false;
}
}
switch (this.scale) { switch (this.scale) {
case TimeStep.SCALE.MILLISECOND: case TimeStep.SCALE.MILLISECOND:
return (this.current.getMilliseconds() == 0); return (this.current.getMilliseconds() == 0);
@ -409,7 +424,6 @@ TimeStep.prototype.isMajor = function() {
return (this.current.getSeconds() == 0); return (this.current.getSeconds() == 0);
case TimeStep.SCALE.MINUTE: case TimeStep.SCALE.MINUTE:
return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); return (this.current.getHours() == 0) && (this.current.getMinutes() == 0);
// Note: this is no bug. Major label is equal for both minute and hour scale
case TimeStep.SCALE.HOUR: case TimeStep.SCALE.HOUR:
return (this.current.getHours() == 0); return (this.current.getHours() == 0);
case TimeStep.SCALE.WEEKDAY: // intentional fall through case TimeStep.SCALE.WEEKDAY: // intentional fall through

+ 6
- 5
lib/timeline/component/TimeAxis.js View File

@ -1,6 +1,7 @@
var util = require('../../util'); var util = require('../../util');
var Component = require('./Component'); var Component = require('./Component');
var TimeStep = require('../TimeStep'); var TimeStep = require('../TimeStep');
var DateUtil = require('../DateUtil');
var moment = require('../../module/moment'); var moment = require('../../module/moment');
/** /**
@ -63,7 +64,7 @@ TimeAxis.prototype = new Component();
TimeAxis.prototype.setOptions = function(options) { TimeAxis.prototype.setOptions = function(options) {
if (options) { if (options) {
// copy all options that we know // copy all options that we know
util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels','hide'], this.options, options);
util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels','hiddenDates'], this.options, options);
// apply locale to moment.js // apply locale to moment.js
// TODO: not so nice, this is applied globally to moment.js // TODO: not so nice, this is applied globally to moment.js
@ -110,10 +111,10 @@ TimeAxis.prototype.destroy = function() {
* @return {boolean} Returns true if the component is resized * @return {boolean} Returns true if the component is resized
*/ */
TimeAxis.prototype.redraw = function () { TimeAxis.prototype.redraw = function () {
var options = this.options,
props = this.props,
foreground = this.dom.foreground,
background = this.dom.background;
var options = this.options;
var props = this.props;
var foreground = this.dom.foreground;
var background = this.dom.background;
// determine the correct parent DOM element (depending on option orientation) // determine the correct parent DOM element (depending on option orientation)
var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom;

Loading…
Cancel
Save