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.

346 lines
8.6 KiB

  1. var util = require('../util');
  2. /**
  3. * @constructor Slider
  4. *
  5. * An html slider control with start/stop/prev/next buttons
  6. * @param {Element} container The element where the slider will be created
  7. * @param {Object} options Available options:
  8. * {boolean} visible If true (default) the
  9. * slider is visible.
  10. */
  11. function Slider(container, options) {
  12. if (container === undefined) {
  13. throw new Error('No container element defined');
  14. }
  15. this.container = container;
  16. this.visible = (options && options.visible != undefined) ? options.visible : true;
  17. if (this.visible) {
  18. this.frame = document.createElement('DIV');
  19. //this.frame.style.backgroundColor = '#E5E5E5';
  20. this.frame.style.width = '100%';
  21. this.frame.style.position = 'relative';
  22. this.container.appendChild(this.frame);
  23. this.frame.prev = document.createElement('INPUT');
  24. this.frame.prev.type = 'BUTTON';
  25. this.frame.prev.value = 'Prev';
  26. this.frame.appendChild(this.frame.prev);
  27. this.frame.play = document.createElement('INPUT');
  28. this.frame.play.type = 'BUTTON';
  29. this.frame.play.value = 'Play';
  30. this.frame.appendChild(this.frame.play);
  31. this.frame.next = document.createElement('INPUT');
  32. this.frame.next.type = 'BUTTON';
  33. this.frame.next.value = 'Next';
  34. this.frame.appendChild(this.frame.next);
  35. this.frame.bar = document.createElement('INPUT');
  36. this.frame.bar.type = 'BUTTON';
  37. this.frame.bar.style.position = 'absolute';
  38. this.frame.bar.style.border = '1px solid red';
  39. this.frame.bar.style.width = '100px';
  40. this.frame.bar.style.height = '6px';
  41. this.frame.bar.style.borderRadius = '2px';
  42. this.frame.bar.style.MozBorderRadius = '2px';
  43. this.frame.bar.style.border = '1px solid #7F7F7F';
  44. this.frame.bar.style.backgroundColor = '#E5E5E5';
  45. this.frame.appendChild(this.frame.bar);
  46. this.frame.slide = document.createElement('INPUT');
  47. this.frame.slide.type = 'BUTTON';
  48. this.frame.slide.style.margin = '0px';
  49. this.frame.slide.value = ' ';
  50. this.frame.slide.style.position = 'relative';
  51. this.frame.slide.style.left = '-100px';
  52. this.frame.appendChild(this.frame.slide);
  53. // create events
  54. var me = this;
  55. this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);};
  56. this.frame.prev.onclick = function (event) {me.prev(event);};
  57. this.frame.play.onclick = function (event) {me.togglePlay(event);};
  58. this.frame.next.onclick = function (event) {me.next(event);};
  59. }
  60. this.onChangeCallback = undefined;
  61. this.values = [];
  62. this.index = undefined;
  63. this.playTimeout = undefined;
  64. this.playInterval = 1000; // milliseconds
  65. this.playLoop = true;
  66. }
  67. /**
  68. * Select the previous index
  69. */
  70. Slider.prototype.prev = function() {
  71. var index = this.getIndex();
  72. if (index > 0) {
  73. index--;
  74. this.setIndex(index);
  75. }
  76. };
  77. /**
  78. * Select the next index
  79. */
  80. Slider.prototype.next = function() {
  81. var index = this.getIndex();
  82. if (index < this.values.length - 1) {
  83. index++;
  84. this.setIndex(index);
  85. }
  86. };
  87. /**
  88. * Select the next index
  89. */
  90. Slider.prototype.playNext = function() {
  91. var start = new Date();
  92. var index = this.getIndex();
  93. if (index < this.values.length - 1) {
  94. index++;
  95. this.setIndex(index);
  96. }
  97. else if (this.playLoop) {
  98. // jump to the start
  99. index = 0;
  100. this.setIndex(index);
  101. }
  102. var end = new Date();
  103. var diff = (end - start);
  104. // calculate how much time it to to set the index and to execute the callback
  105. // function.
  106. var interval = Math.max(this.playInterval - diff, 0);
  107. // document.title = diff // TODO: cleanup
  108. var me = this;
  109. this.playTimeout = setTimeout(function() {me.playNext();}, interval);
  110. };
  111. /**
  112. * Toggle start or stop playing
  113. */
  114. Slider.prototype.togglePlay = function() {
  115. if (this.playTimeout === undefined) {
  116. this.play();
  117. } else {
  118. this.stop();
  119. }
  120. };
  121. /**
  122. * Start playing
  123. */
  124. Slider.prototype.play = function() {
  125. // Test whether already playing
  126. if (this.playTimeout) return;
  127. this.playNext();
  128. if (this.frame) {
  129. this.frame.play.value = 'Stop';
  130. }
  131. };
  132. /**
  133. * Stop playing
  134. */
  135. Slider.prototype.stop = function() {
  136. clearInterval(this.playTimeout);
  137. this.playTimeout = undefined;
  138. if (this.frame) {
  139. this.frame.play.value = 'Play';
  140. }
  141. };
  142. /**
  143. * Set a callback function which will be triggered when the value of the
  144. * slider bar has changed.
  145. */
  146. Slider.prototype.setOnChangeCallback = function(callback) {
  147. this.onChangeCallback = callback;
  148. };
  149. /**
  150. * Set the interval for playing the list
  151. * @param {Number} interval The interval in milliseconds
  152. */
  153. Slider.prototype.setPlayInterval = function(interval) {
  154. this.playInterval = interval;
  155. };
  156. /**
  157. * Retrieve the current play interval
  158. * @return {Number} interval The interval in milliseconds
  159. */
  160. Slider.prototype.getPlayInterval = function() {
  161. return this.playInterval;
  162. };
  163. /**
  164. * Set looping on or off
  165. * @pararm {boolean} doLoop If true, the slider will jump to the start when
  166. * the end is passed, and will jump to the end
  167. * when the start is passed.
  168. */
  169. Slider.prototype.setPlayLoop = function(doLoop) {
  170. this.playLoop = doLoop;
  171. };
  172. /**
  173. * Execute the onchange callback function
  174. */
  175. Slider.prototype.onChange = function() {
  176. if (this.onChangeCallback !== undefined) {
  177. this.onChangeCallback();
  178. }
  179. };
  180. /**
  181. * redraw the slider on the correct place
  182. */
  183. Slider.prototype.redraw = function() {
  184. if (this.frame) {
  185. // resize the bar
  186. this.frame.bar.style.top = (this.frame.clientHeight/2 -
  187. this.frame.bar.offsetHeight/2) + 'px';
  188. this.frame.bar.style.width = (this.frame.clientWidth -
  189. this.frame.prev.clientWidth -
  190. this.frame.play.clientWidth -
  191. this.frame.next.clientWidth - 30) + 'px';
  192. // position the slider button
  193. var left = this.indexToLeft(this.index);
  194. this.frame.slide.style.left = (left) + 'px';
  195. }
  196. };
  197. /**
  198. * Set the list with values for the slider
  199. * @param {Array} values A javascript array with values (any type)
  200. */
  201. Slider.prototype.setValues = function(values) {
  202. this.values = values;
  203. if (this.values.length > 0)
  204. this.setIndex(0);
  205. else
  206. this.index = undefined;
  207. };
  208. /**
  209. * Select a value by its index
  210. * @param {Number} index
  211. */
  212. Slider.prototype.setIndex = function(index) {
  213. if (index < this.values.length) {
  214. this.index = index;
  215. this.redraw();
  216. this.onChange();
  217. }
  218. else {
  219. throw new Error('Index out of range');
  220. }
  221. };
  222. /**
  223. * retrieve the index of the currently selected vaue
  224. * @return {Number} index
  225. */
  226. Slider.prototype.getIndex = function() {
  227. return this.index;
  228. };
  229. /**
  230. * retrieve the currently selected value
  231. * @return {*} value
  232. */
  233. Slider.prototype.get = function() {
  234. return this.values[this.index];
  235. };
  236. Slider.prototype._onMouseDown = function(event) {
  237. // only react on left mouse button down
  238. var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1);
  239. if (!leftButtonDown) return;
  240. this.startClientX = event.clientX;
  241. this.startSlideX = parseFloat(this.frame.slide.style.left);
  242. this.frame.style.cursor = 'move';
  243. // add event listeners to handle moving the contents
  244. // we store the function onmousemove and onmouseup in the graph, so we can
  245. // remove the eventlisteners lateron in the function mouseUp()
  246. var me = this;
  247. this.onmousemove = function (event) {me._onMouseMove(event);};
  248. this.onmouseup = function (event) {me._onMouseUp(event);};
  249. util.addEventListener(document, 'mousemove', this.onmousemove);
  250. util.addEventListener(document, 'mouseup', this.onmouseup);
  251. util.preventDefault(event);
  252. };
  253. Slider.prototype.leftToIndex = function (left) {
  254. var width = parseFloat(this.frame.bar.style.width) -
  255. this.frame.slide.clientWidth - 10;
  256. var x = left - 3;
  257. var index = Math.round(x / width * (this.values.length-1));
  258. if (index < 0) index = 0;
  259. if (index > this.values.length-1) index = this.values.length-1;
  260. return index;
  261. };
  262. Slider.prototype.indexToLeft = function (index) {
  263. var width = parseFloat(this.frame.bar.style.width) -
  264. this.frame.slide.clientWidth - 10;
  265. var x = index / (this.values.length-1) * width;
  266. var left = x + 3;
  267. return left;
  268. };
  269. Slider.prototype._onMouseMove = function (event) {
  270. var diff = event.clientX - this.startClientX;
  271. var x = this.startSlideX + diff;
  272. var index = this.leftToIndex(x);
  273. this.setIndex(index);
  274. util.preventDefault();
  275. };
  276. Slider.prototype._onMouseUp = function (event) { // eslint-disable-line no-unused-vars
  277. this.frame.style.cursor = 'auto';
  278. // remove event listeners
  279. util.removeEventListener(document, 'mousemove', this.onmousemove);
  280. util.removeEventListener(document, 'mouseup', this.onmouseup);
  281. util.preventDefault();
  282. };
  283. module.exports = Slider;