vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

449 lines
13 KiB

/**
* Created by Alex on 10/3/2014.
*/
var moment = require('../module/moment');
/**
* used in Core to convert the options into a volatile variable
*
* @param Core
*/
exports.convertHiddenOptions = function(body, hiddenDates) {
var specificHiddenDates = hiddenDates.specific;
if (specificHiddenDates) {
if (Array.isArray(specificHiddenDates) == true) {
for (var i = 0; i < specificHiddenDates.length; i++) {
var dateItem = {};
dateItem.start = moment(specificHiddenDates[i].start).toDate().valueOf();
dateItem.end = moment(specificHiddenDates[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(specificHiddenDates.start).toDate().valueOf(),
end: moment(specificHiddenDates.end).toDate().valueOf()
}
];
}
}
// allowing multiple input formats
var periodicHiddenDates = hiddenDates.periodic;
if (periodicHiddenDates) {
if (periodicHiddenDates.times) {
if (Array.isArray(periodicHiddenDates.times) != true) {
periodicHiddenDates.times = [periodicHiddenDates.times];
}
}
if (periodicHiddenDates.days) {
if (Array.isArray(periodicHiddenDates.days) != true) {
periodicHiddenDates.days = [periodicHiddenDates.days];
}
}
}
};
/**
* create new entrees for the periodic hidden dates
* @param body
* @param hiddenDates
*/
exports.updateHiddenDates = function (body, hiddenDates) {
if (hiddenDates && hiddenDates.periodic && body.domProps.centerContainer.width !== undefined) {
body.hiddenDates = [];
exports.convertHiddenOptions(body, hiddenDates);
var start = moment(body.range.start);
var end = moment(body.range.end);
var totalRange = (body.range.end - body.range.start);
var pixelTime = totalRange/body.domProps.centerContainer.width;
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);
if (nextEndDay < nextStartDay) {
nextEndDay.isoWeekday(endDay + 7);
}
var duration = nextEndDay - nextStartDay;
if (duration < 4 * pixelTime) {
break;
}
else {
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;
}
var duration = nextEndDay - nextStartDay;
if (duration < 4 * pixelTime) {
break;
}
else {
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;
}
}
}
}
// remove duplicates, merge where possible
exports.removeDuplicates(body);
// ensure the new positions are not on hidden dates
var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
var endHidden = exports.isHidden(body.range.end,body.hiddenDates);
var rangeStart = body.range.start;
var rangeEnd = body.range.end;
if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;}
if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;}
if (startHidden.hidden == true || endHidden.hidden == true) {
body.range._applyRange(rangeStart, rangeEnd);
}
}
}
/**
* remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up.
* Scales with N^2
* @param body
*/
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) {
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() && currentValue != previousTime) {
var prevValue = moment(previousTime);
var newValue = moment(endDate);
//check if the next step should be major
if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;}
else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;}
else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;}
timeStep.current = newValue.toDate();
}
};
/**
* 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) {
time = hidden.startDate;
}
var duration = exports.getHiddenDuration(Core.body.hiddenDates, Core.range);
time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time);
var conversion = Core.range.conversion(width, duration);
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) {
var hiddenDuration = exports.getHiddenDuration(body.hiddenDates, range);
var totalDuration = range.end - range.start - hiddenDuration;
var partialDuration = totalDuration * x / width;
var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(body.hiddenDates,range, partialDuration);
var newTime = new Date(accumulatedHiddenDuration + partialDuration + range.start);
return newTime;
};
/**
* Support function
*
* @param hiddenTimes
* @param range
* @returns {number}
*/
exports.getHiddenDuration = function(hiddenTimes, range) {
var duration = 0;
for (var i = 0; i < hiddenTimes.length; i++) {
var startDate = hiddenTimes[i].start;
var endDate = hiddenTimes[i].end;
// if time after the cutout, and the
if (startDate >= range.start && endDate < range.end) {
duration += endDate - startDate;
}
}
return duration;
};
/**
* Support function
* @param hiddenDates
* @param range
* @param time
* @returns {{duration: number, time: *, offset: number}}
*/
exports.correctTimeForHidden = function(hiddenDates, range, time) {
time = moment(time).toDate().valueOf();
time -= exports.getHiddenDurationBefore(hiddenDates,range,time);
return time;
};
exports.getHiddenDurationBefore = function(hiddenDates, range, time) {
var timeOffset = 0;
time = moment(time).toDate().valueOf();
for (var i = 0; i < hiddenDates.length; i++) {
var startDate = hiddenDates[i].start;
var endDate = hiddenDates[i].end;
// if time after the cutout, and the
if (startDate >= range.start && endDate < range.end) {
if (time >= endDate) {
timeOffset += (endDate - startDate);
}
}
}
return timeOffset;
}
/**
* sum the duration from start to finish, including the hidden duration,
* until the required amount has been reached, return the accumulated hidden duration
* @param hiddenDates
* @param range
* @param time
* @returns {{duration: number, time: *, offset: number}}
*/
exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) {
var hiddenDuration = 0;
var duration = 0;
var previousPoint = range.start;
//exports.printDates(hiddenDates)
for (var i = 0; i < hiddenDates.length; i++) {
var startDate = hiddenDates[i].start;
var endDate = hiddenDates[i].end;
// if time after the cutout, and the
if (startDate >= range.start && endDate < range.end) {
duration += startDate - previousPoint;
previousPoint = endDate;
if (duration >= requiredDuration) {
break;
}
else {
hiddenDuration += endDate - startDate;
}
}
}
return hiddenDuration;
};
/**
* used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true
* @param hiddenTimes
* @param time
* @param direction
* @param correctionEnabled
* @returns {*}
*/
exports.snapAwayFromHidden = function(hiddenTimes, time, direction, correctionEnabled) {
var isHidden = exports.isHidden(time, hiddenTimes);
if (isHidden.hidden == true) {
if (direction < 0) {
if (correctionEnabled == true) {
return isHidden.startDate - (isHidden.endDate - time) - 1;
}
else {
return isHidden.startDate - 1;
}
}
else {
if (correctionEnabled == true) {
return isHidden.endDate + (time - isHidden.startDate) + 1;
}
else {
return isHidden.endDate + 1;
}
}
}
else {
return time;
}
}
/**
* Check if a time is hidden
*
* @param time
* @param hiddenTimes
* @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
*/
exports.isHidden = function(time, hiddenTimes) {
var isHidden = false;
for (var i = 0; i < hiddenTimes.length; i++) {
var startDate = hiddenTimes[i].start;
var endDate = hiddenTimes[i].end;
if (time >= startDate && time < endDate) { // if the start is entering a hidden zone
isHidden = true;
break;
}
}
return {hidden: isHidden, startDate: startDate, endDate: endDate};
}