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.

221 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 (start == end) {
  54. this._start = start - 0.75;
  55. this._end = end + 1;
  56. }
  57. if (this.autoScale) {
  58. this.setMinimumStep(minimumStep, containerHeight, forcedStepSize);
  59. }
  60. this.setFirst();
  61. };
  62. /**
  63. * Automatically determine the scale that bests fits the provided minimum step
  64. * @param {Number} [minimumStep] The minimum step size in milliseconds
  65. */
  66. DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
  67. // round to floor
  68. var size = this._end - this._start;
  69. var safeSize = size * 1.1;
  70. var minimumStepValue = minimumStep * (safeSize / containerHeight);
  71. var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10);
  72. var minorStepIdx = -1;
  73. var magnitudefactor = Math.pow(10,orderOfMagnitude);
  74. var start = 0;
  75. if (orderOfMagnitude < 0) {
  76. start = orderOfMagnitude;
  77. }
  78. var solutionFound = false;
  79. for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) {
  80. magnitudefactor = Math.pow(10,i);
  81. for (var j = 0; j < this.minorSteps.length; j++) {
  82. var stepSize = magnitudefactor * this.minorSteps[j];
  83. if (stepSize >= minimumStepValue) {
  84. solutionFound = true;
  85. minorStepIdx = j;
  86. break;
  87. }
  88. }
  89. if (solutionFound == true) {
  90. break;
  91. }
  92. }
  93. this.stepIndex = minorStepIdx;
  94. this.scale = magnitudefactor;
  95. this.step = magnitudefactor * this.minorSteps[minorStepIdx];
  96. };
  97. /**
  98. * Set the range iterator to the start date.
  99. */
  100. DataStep.prototype.first = function() {
  101. this.setFirst();
  102. };
  103. /**
  104. * Round the current date to the first minor date value
  105. * This must be executed once when the current date is set to start Date
  106. */
  107. DataStep.prototype.setFirst = function() {
  108. var niceStart = this._start - (this.scale * this.minorSteps[this.stepIndex]);
  109. var niceEnd = this._end + (this.scale * this.minorSteps[this.stepIndex]);
  110. this.marginEnd = this.roundToMinor(niceEnd);
  111. this.marginStart = this.roundToMinor(niceStart);
  112. this.marginRange = this.marginEnd - this.marginStart;
  113. this.current = this.marginEnd;
  114. };
  115. DataStep.prototype.roundToMinor = function(value) {
  116. var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex]));
  117. if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) {
  118. return rounded + (this.scale * this.minorSteps[this.stepIndex]);
  119. }
  120. else {
  121. return rounded;
  122. }
  123. }
  124. /**
  125. * Check if the there is a next step
  126. * @return {boolean} true if the current date has not passed the end date
  127. */
  128. DataStep.prototype.hasNext = function () {
  129. return (this.current >= this.marginStart);
  130. };
  131. /**
  132. * Do the next step
  133. */
  134. DataStep.prototype.next = function() {
  135. var prev = this.current;
  136. this.current -= this.step;
  137. // safety mechanism: if current time is still unchanged, move to the end
  138. if (this.current == prev) {
  139. this.current = this._end;
  140. }
  141. };
  142. /**
  143. * Do the next step
  144. */
  145. DataStep.prototype.previous = function() {
  146. this.current += this.step;
  147. this.marginEnd += this.step;
  148. this.marginRange = this.marginEnd - this.marginStart;
  149. };
  150. /**
  151. * Get the current datetime
  152. * @return {String} current The current date
  153. */
  154. DataStep.prototype.getCurrent = function() {
  155. var toPrecision = '' + Number(this.current).toPrecision(5);
  156. for (var i = toPrecision.length-1; i > 0; i--) {
  157. if (toPrecision[i] == "0") {
  158. toPrecision = toPrecision.slice(0,i);
  159. }
  160. else if (toPrecision[i] == "." || toPrecision[i] == ",") {
  161. toPrecision = toPrecision.slice(0,i);
  162. break;
  163. }
  164. else{
  165. break;
  166. }
  167. }
  168. return toPrecision;
  169. };
  170. /**
  171. * Snap a date to a rounded value.
  172. * The snap intervals are dependent on the current scale and step.
  173. * @param {Date} date the date to be snapped.
  174. * @return {Date} snappedDate
  175. */
  176. DataStep.prototype.snap = function(date) {
  177. };
  178. /**
  179. * Check if the current value is a major value (for example when the step
  180. * is DAY, a major value is each first day of the MONTH)
  181. * @return {boolean} true if current date is major, else false.
  182. */
  183. DataStep.prototype.isMajor = function() {
  184. return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0);
  185. };
  186. module.exports = DataStep;