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.

428 lines
13 KiB

  1. /**
  2. * Created by Alex on 10/3/2014.
  3. */
  4. var moment = require('../module/moment');
  5. /**
  6. * used in Core to convert the options into a volatile variable
  7. *
  8. * @param Core
  9. */
  10. exports.convertHiddenOptions = function(body, hiddenDates) {
  11. body.hiddenDates = [];
  12. if (hiddenDates) {
  13. if (Array.isArray(hiddenDates) == true) {
  14. for (var i = 0; i < hiddenDates.length; i++) {
  15. if (hiddenDates[i].repeat === undefined) {
  16. var dateItem = {};
  17. dateItem.start = moment(hiddenDates[i].start).toDate().valueOf();
  18. dateItem.end = moment(hiddenDates[i].end).toDate().valueOf();
  19. body.hiddenDates.push(dateItem);
  20. }
  21. }
  22. body.hiddenDates.sort(function (a, b) {
  23. return a.start - b.start;
  24. }); // sort by start time
  25. }
  26. }
  27. };
  28. /**
  29. * create new entrees for the repeating hidden dates
  30. * @param body
  31. * @param hiddenDates
  32. */
  33. exports.updateHiddenDates = function (body, hiddenDates) {
  34. if (hiddenDates && body.domProps.centerContainer.width !== undefined) {
  35. exports.convertHiddenOptions(body, hiddenDates);
  36. var start = moment(body.range.start);
  37. var end = moment(body.range.end);
  38. var totalRange = (body.range.end - body.range.start);
  39. var pixelTime = totalRange / body.domProps.centerContainer.width;
  40. for (var i = 0; i < hiddenDates.length; i++) {
  41. if (hiddenDates[i].repeat !== undefined) {
  42. var startDate = moment(hiddenDates[i].start);
  43. var endDate = moment(hiddenDates[i].end);
  44. var duration = endDate - startDate;
  45. if (duration >= 4 * pixelTime) {
  46. var offset = 0;
  47. switch (hiddenDates[i].repeat) {
  48. case "daily": // case of time
  49. if (startDate.day() != endDate.day()) {
  50. offset = 1;
  51. }
  52. startDate.dayOfYear(start.dayOfYear());
  53. startDate.year(start.year());
  54. startDate.subtract(7,'days');
  55. endDate.dayOfYear(start.dayOfYear());
  56. endDate.year(start.year());
  57. endDate.subtract(7,'days');
  58. endDate.add(offset,'days');
  59. break;
  60. case "weekly":
  61. if (startDate.week() != endDate.week()) {
  62. offset = 1;
  63. }
  64. startDate.week(start.week() - 1);
  65. startDate.year(start.year());
  66. endDate.week(start.week() - 1);
  67. endDate.year(start.year());
  68. endDate.add(offset,'weeks');
  69. break
  70. case "monthly":
  71. if (startDate.month() != endDate.month()) {
  72. offset = 1;
  73. }
  74. startDate.month(start.month() - 1)
  75. startDate.year(start.year());
  76. endDate.month(start.month() - 1);
  77. endDate.year(start.year());
  78. endDate.add(offset,'months');
  79. break;
  80. case "yearly":
  81. if (startDate.year() != endDate.year()) {
  82. offset = 1;
  83. }
  84. startDate.year(start.year() - 1);
  85. endDate.year(start.year() - 1);
  86. endDate.add(offset,'years');
  87. break;
  88. default:
  89. console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
  90. return;
  91. }
  92. while (startDate < end) {
  93. body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()});
  94. switch (hiddenDates[i].repeat) {
  95. case "daily":
  96. startDate.add(1, 'days');
  97. endDate.add(1, 'days');
  98. break;
  99. case "weekly":
  100. startDate.add(7, 'days');
  101. endDate.add(7, 'days');
  102. break
  103. case "monthly":
  104. startDate.add(1, 'months');
  105. endDate.add(1, 'months');
  106. break;
  107. case "yearly":
  108. startDate.add(1, 'y');
  109. endDate.add(1, 'y');
  110. break;
  111. default:
  112. console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
  113. return;
  114. }
  115. }
  116. body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()});
  117. }
  118. }
  119. }
  120. // remove duplicates, merge where possible
  121. exports.removeDuplicates(body);
  122. // ensure the new positions are not on hidden dates
  123. var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
  124. var endHidden = exports.isHidden(body.range.end,body.hiddenDates);
  125. var rangeStart = body.range.start;
  126. var rangeEnd = body.range.end;
  127. if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;}
  128. if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;}
  129. if (startHidden.hidden == true || endHidden.hidden == true) {
  130. body.range._applyRange(rangeStart, rangeEnd);
  131. }
  132. }
  133. }
  134. /**
  135. * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up.
  136. * Scales with N^2
  137. * @param body
  138. */
  139. exports.removeDuplicates = function(body) {
  140. var hiddenDates = body.hiddenDates;
  141. var safeDates = [];
  142. for (var i = 0; i < hiddenDates.length; i++) {
  143. for (var j = 0; j < hiddenDates.length; j++) {
  144. if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) {
  145. // j inside i
  146. if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
  147. hiddenDates[j].remove = true;
  148. }
  149. // j start inside i
  150. else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) {
  151. hiddenDates[i].end = hiddenDates[j].end;
  152. hiddenDates[j].remove = true;
  153. }
  154. // j end inside i
  155. else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
  156. hiddenDates[i].start = hiddenDates[j].start;
  157. hiddenDates[j].remove = true;
  158. }
  159. }
  160. }
  161. }
  162. for (var i = 0; i < hiddenDates.length; i++) {
  163. if (hiddenDates[i].remove !== true) {
  164. safeDates.push(hiddenDates[i]);
  165. }
  166. }
  167. body.hiddenDates = safeDates;
  168. body.hiddenDates.sort(function (a, b) {
  169. return a.start - b.start;
  170. }); // sort by start time
  171. }
  172. exports.printDates = function(dates) {
  173. for (var i =0; i < dates.length; i++) {
  174. console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove);
  175. }
  176. }
  177. /**
  178. * Used in TimeStep to avoid the hidden times.
  179. * @param timeStep
  180. * @param previousTime
  181. */
  182. exports.stepOverHiddenDates = function(timeStep, previousTime) {
  183. var stepInHidden = false;
  184. var currentValue = timeStep.current.valueOf();
  185. for (var i = 0; i < timeStep.hiddenDates.length; i++) {
  186. var startDate = timeStep.hiddenDates[i].start;
  187. var endDate = timeStep.hiddenDates[i].end;
  188. if (currentValue >= startDate && currentValue < endDate) {
  189. stepInHidden = true;
  190. break;
  191. }
  192. }
  193. if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) {
  194. var prevValue = moment(previousTime);
  195. var newValue = moment(endDate);
  196. //check if the next step should be major
  197. if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;}
  198. else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;}
  199. else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;}
  200. timeStep.current = newValue.toDate();
  201. }
  202. };
  203. /**
  204. * Used in TimeStep to avoid the hidden times.
  205. * @param timeStep
  206. * @param previousTime
  207. */
  208. exports.checkFirstStep = function(timeStep) {
  209. var stepInHidden = false;
  210. var currentValue = timeStep.current.valueOf();
  211. for (var i = 0; i < timeStep.hiddenDates.length; i++) {
  212. var startDate = timeStep.hiddenDates[i].start;
  213. var endDate = timeStep.hiddenDates[i].end;
  214. if (currentValue >= startDate && currentValue < endDate) {
  215. stepInHidden = true;
  216. break;
  217. }
  218. }
  219. if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) {
  220. var newValue = moment(endDate);
  221. timeStep.current = newValue.toDate();
  222. }
  223. };
  224. /**
  225. * replaces the Core toScreen methods
  226. * @param Core
  227. * @param time
  228. * @param width
  229. * @returns {number}
  230. */
  231. exports.toScreen = function(Core, time, width) {
  232. var hidden = exports.isHidden(time, Core.body.hiddenDates)
  233. if (hidden.hidden == true) {
  234. time = hidden.startDate;
  235. }
  236. var duration = exports.getHiddenDuration(Core.body.hiddenDates, Core.range);
  237. time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time);
  238. var conversion = Core.range.conversion(width, duration);
  239. return (time.valueOf() - conversion.offset) * conversion.scale;
  240. };
  241. /**
  242. * Replaces the core toTime methods
  243. * @param body
  244. * @param range
  245. * @param x
  246. * @param width
  247. * @returns {Date}
  248. */
  249. exports.toTime = function(body, range, x, width) {
  250. var hiddenDuration = exports.getHiddenDuration(body.hiddenDates, range);
  251. var totalDuration = range.end - range.start - hiddenDuration;
  252. var partialDuration = totalDuration * x / width;
  253. var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(body.hiddenDates,range, partialDuration);
  254. var newTime = new Date(accumulatedHiddenDuration + partialDuration + range.start);
  255. return newTime;
  256. };
  257. /**
  258. * Support function
  259. *
  260. * @param hiddenTimes
  261. * @param range
  262. * @returns {number}
  263. */
  264. exports.getHiddenDuration = function(hiddenTimes, range) {
  265. var duration = 0;
  266. for (var i = 0; i < hiddenTimes.length; i++) {
  267. var startDate = hiddenTimes[i].start;
  268. var endDate = hiddenTimes[i].end;
  269. // if time after the cutout, and the
  270. if (startDate >= range.start && endDate < range.end) {
  271. duration += endDate - startDate;
  272. }
  273. }
  274. return duration;
  275. };
  276. /**
  277. * Support function
  278. * @param hiddenDates
  279. * @param range
  280. * @param time
  281. * @returns {{duration: number, time: *, offset: number}}
  282. */
  283. exports.correctTimeForHidden = function(hiddenDates, range, time) {
  284. time = moment(time).toDate().valueOf();
  285. time -= exports.getHiddenDurationBefore(hiddenDates,range,time);
  286. return time;
  287. };
  288. exports.getHiddenDurationBefore = function(hiddenDates, range, time) {
  289. var timeOffset = 0;
  290. time = moment(time).toDate().valueOf();
  291. for (var i = 0; i < hiddenDates.length; i++) {
  292. var startDate = hiddenDates[i].start;
  293. var endDate = hiddenDates[i].end;
  294. // if time after the cutout, and the
  295. if (startDate >= range.start && endDate < range.end) {
  296. if (time >= endDate) {
  297. timeOffset += (endDate - startDate);
  298. }
  299. }
  300. }
  301. return timeOffset;
  302. }
  303. /**
  304. * sum the duration from start to finish, including the hidden duration,
  305. * until the required amount has been reached, return the accumulated hidden duration
  306. * @param hiddenDates
  307. * @param range
  308. * @param time
  309. * @returns {{duration: number, time: *, offset: number}}
  310. */
  311. exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) {
  312. var hiddenDuration = 0;
  313. var duration = 0;
  314. var previousPoint = range.start;
  315. //exports.printDates(hiddenDates)
  316. for (var i = 0; i < hiddenDates.length; i++) {
  317. var startDate = hiddenDates[i].start;
  318. var endDate = hiddenDates[i].end;
  319. // if time after the cutout, and the
  320. if (startDate >= range.start && endDate < range.end) {
  321. duration += startDate - previousPoint;
  322. previousPoint = endDate;
  323. if (duration >= requiredDuration) {
  324. break;
  325. }
  326. else {
  327. hiddenDuration += endDate - startDate;
  328. }
  329. }
  330. }
  331. return hiddenDuration;
  332. };
  333. /**
  334. * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true
  335. * @param hiddenTimes
  336. * @param time
  337. * @param direction
  338. * @param correctionEnabled
  339. * @returns {*}
  340. */
  341. exports.snapAwayFromHidden = function(hiddenTimes, time, direction, correctionEnabled) {
  342. var isHidden = exports.isHidden(time, hiddenTimes);
  343. if (isHidden.hidden == true) {
  344. if (direction < 0) {
  345. if (correctionEnabled == true) {
  346. return isHidden.startDate - (isHidden.endDate - time) - 1;
  347. }
  348. else {
  349. return isHidden.startDate - 1;
  350. }
  351. }
  352. else {
  353. if (correctionEnabled == true) {
  354. return isHidden.endDate + (time - isHidden.startDate) + 1;
  355. }
  356. else {
  357. return isHidden.endDate + 1;
  358. }
  359. }
  360. }
  361. else {
  362. return time;
  363. }
  364. }
  365. /**
  366. * Check if a time is hidden
  367. *
  368. * @param time
  369. * @param hiddenTimes
  370. * @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
  371. */
  372. exports.isHidden = function(time, hiddenTimes) {
  373. var isHidden = false;
  374. for (var i = 0; i < hiddenTimes.length; i++) {
  375. var startDate = hiddenTimes[i].start;
  376. var endDate = hiddenTimes[i].end;
  377. if (time >= startDate && time < endDate) { // if the start is entering a hidden zone
  378. isHidden = true;
  379. break;
  380. }
  381. }
  382. return {hidden: isHidden, startDate: startDate, endDate: endDate};
  383. }