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.

216 lines
6.3 KiB

10 years ago
10 years ago
10 years ago
10 years ago
  1. /**
  2. * @constructor DataStep
  3. * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an
  4. * end data point. The class itself determines the best scale (step size) based on the
  5. * provided start Date, end Date, and minimumStep.
  6. *
  7. * If minimumStep is provided, the step size is chosen as close as possible
  8. * to the minimumStep but larger than minimumStep. If minimumStep is not
  9. * provided, the scale is set to 1 DAY.
  10. * The minimumStep should correspond with the onscreen size of about 6 characters
  11. *
  12. * Alternatively, you can set a scale by hand.
  13. * After creation, you can initialize the class by executing first(). Then you
  14. * can iterate from the start date to the end date via next(). You can check if
  15. * the end date is reached with the function hasNext(). After each step, you can
  16. * retrieve the current date via getCurrent().
  17. * The DataStep has scales ranging from milliseconds, seconds, minutes, hours,
  18. * days, to years.
  19. *
  20. * Version: 1.2
  21. *
  22. * @param {Date} [start] The start date, for example new Date(2010, 9, 21)
  23. * or new Date(2010, 9, 21, 23, 45, 00)
  24. * @param {Date} [end] The end date
  25. * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
  26. */
  27. function DataStep(start, end, minimumStep, containerHeight, forcedStepSize) {
  28. // variables
  29. this.current = 0;
  30. this.autoScale = true;
  31. this.stepIndex = 0;
  32. this.step = 1;
  33. this.scale = 1;
  34. this.marginStart;
  35. this.marginEnd;
  36. this.majorSteps = [1, 2, 5, 10];
  37. this.minorSteps = [0.25, 0.5, 1, 2];
  38. this.setRange(start, end, minimumStep, containerHeight, forcedStepSize);
  39. }
  40. /**
  41. * Set a new range
  42. * If minimumStep is provided, the step size is chosen as close as possible
  43. * to the minimumStep but larger than minimumStep. If minimumStep is not
  44. * provided, the scale is set to 1 DAY.
  45. * The minimumStep should correspond with the onscreen size of about 6 characters
  46. * @param {Number} [start] The start date and time.
  47. * @param {Number} [end] The end date and time.
  48. * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
  49. */
  50. DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, forcedStepSize) {
  51. this._start = start;
  52. this._end = end;
  53. if (this.autoScale) {
  54. this.setMinimumStep(minimumStep, containerHeight, forcedStepSize);
  55. }
  56. this.setFirst();
  57. };
  58. /**
  59. * Automatically determine the scale that bests fits the provided minimum step
  60. * @param {Number} [minimumStep] The minimum step size in milliseconds
  61. */
  62. DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
  63. // round to floor
  64. var size = this._end - this._start;
  65. var safeSize = size * 1.1;
  66. var minimumStepValue = minimumStep * (safeSize / containerHeight);
  67. var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10);
  68. var minorStepIdx = -1;
  69. var magnitudefactor = Math.pow(10,orderOfMagnitude);
  70. var start = 0;
  71. if (orderOfMagnitude < 0) {
  72. start = orderOfMagnitude;
  73. }
  74. var solutionFound = false;
  75. for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) {
  76. magnitudefactor = Math.pow(10,i);
  77. for (var j = 0; j < this.minorSteps.length; j++) {
  78. var stepSize = magnitudefactor * this.minorSteps[j];
  79. if (stepSize >= minimumStepValue) {
  80. solutionFound = true;
  81. minorStepIdx = j;
  82. break;
  83. }
  84. }
  85. if (solutionFound == true) {
  86. break;
  87. }
  88. }
  89. this.stepIndex = minorStepIdx;
  90. this.scale = magnitudefactor;
  91. this.step = magnitudefactor * this.minorSteps[minorStepIdx];
  92. };
  93. /**
  94. * Set the range iterator to the start date.
  95. */
  96. DataStep.prototype.first = function() {
  97. this.setFirst();
  98. };
  99. /**
  100. * Round the current date to the first minor date value
  101. * This must be executed once when the current date is set to start Date
  102. */
  103. DataStep.prototype.setFirst = function() {
  104. var niceStart = this._start - (this.scale * this.minorSteps[this.stepIndex]);
  105. var niceEnd = this._end + (this.scale * this.minorSteps[this.stepIndex]);
  106. this.marginEnd = this.roundToMinor(niceEnd);
  107. this.marginStart = this.roundToMinor(niceStart);
  108. this.marginRange = this.marginEnd - this.marginStart;
  109. this.current = this.marginEnd;
  110. };
  111. DataStep.prototype.roundToMinor = function(value) {
  112. var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex]));
  113. if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) {
  114. return rounded + (this.scale * this.minorSteps[this.stepIndex]);
  115. }
  116. else {
  117. return rounded;
  118. }
  119. }
  120. /**
  121. * Check if the there is a next step
  122. * @return {boolean} true if the current date has not passed the end date
  123. */
  124. DataStep.prototype.hasNext = function () {
  125. return (this.current >= this.marginStart);
  126. };
  127. /**
  128. * Do the next step
  129. */
  130. DataStep.prototype.next = function() {
  131. var prev = this.current;
  132. this.current -= this.step;
  133. // safety mechanism: if current time is still unchanged, move to the end
  134. if (this.current == prev) {
  135. this.current = this._end;
  136. }
  137. };
  138. /**
  139. * Do the next step
  140. */
  141. DataStep.prototype.previous = function() {
  142. this.current += this.step;
  143. this.marginEnd += this.step;
  144. this.marginRange = this.marginEnd - this.marginStart;
  145. };
  146. /**
  147. * Get the current datetime
  148. * @return {String} current The current date
  149. */
  150. DataStep.prototype.getCurrent = function() {
  151. var toPrecision = '' + Number(this.current).toPrecision(5);
  152. for (var i = toPrecision.length-1; i > 0; i--) {
  153. if (toPrecision[i] == "0") {
  154. toPrecision = toPrecision.slice(0,i);
  155. }
  156. else if (toPrecision[i] == "." || toPrecision[i] == ",") {
  157. toPrecision = toPrecision.slice(0,i);
  158. break;
  159. }
  160. else{
  161. break;
  162. }
  163. }
  164. return toPrecision;
  165. };
  166. /**
  167. * Snap a date to a rounded value.
  168. * The snap intervals are dependent on the current scale and step.
  169. * @param {Date} date the date to be snapped.
  170. * @return {Date} snappedDate
  171. */
  172. DataStep.prototype.snap = function(date) {
  173. };
  174. /**
  175. * Check if the current value is a major value (for example when the step
  176. * is DAY, a major value is each first day of the MONTH)
  177. * @return {boolean} true if current date is major, else false.
  178. */
  179. DataStep.prototype.isMajor = function() {
  180. return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0);
  181. };
  182. module.exports = DataStep;