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.

349 lines
8.6 KiB

  1. var util = require('../util');
  2. /**
  3. * An html slider control with start/stop/prev/next buttons
  4. *
  5. * @constructor Slider
  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. * @param {function} callback
  147. */
  148. Slider.prototype.setOnChangeCallback = function(callback) {
  149. this.onChangeCallback = callback;
  150. };
  151. /**
  152. * Set the interval for playing the list
  153. * @param {number} interval The interval in milliseconds
  154. */
  155. Slider.prototype.setPlayInterval = function(interval) {
  156. this.playInterval = interval;
  157. };
  158. /**
  159. * Retrieve the current play interval
  160. * @return {number} interval The interval in milliseconds
  161. */
  162. Slider.prototype.getPlayInterval = function() {
  163. return this.playInterval;
  164. };
  165. /**
  166. * Set looping on or off
  167. * @param {boolean} doLoop If true, the slider will jump to the start when
  168. * the end is passed, and will jump to the end
  169. * when the start is passed.
  170. *
  171. */
  172. Slider.prototype.setPlayLoop = function(doLoop) {
  173. this.playLoop = doLoop;
  174. };
  175. /**
  176. * Execute the onchange callback function
  177. */
  178. Slider.prototype.onChange = function() {
  179. if (this.onChangeCallback !== undefined) {
  180. this.onChangeCallback();
  181. }
  182. };
  183. /**
  184. * redraw the slider on the correct place
  185. */
  186. Slider.prototype.redraw = function() {
  187. if (this.frame) {
  188. // resize the bar
  189. this.frame.bar.style.top = (this.frame.clientHeight/2 -
  190. this.frame.bar.offsetHeight/2) + 'px';
  191. this.frame.bar.style.width = (this.frame.clientWidth -
  192. this.frame.prev.clientWidth -
  193. this.frame.play.clientWidth -
  194. this.frame.next.clientWidth - 30) + 'px';
  195. // position the slider button
  196. var left = this.indexToLeft(this.index);
  197. this.frame.slide.style.left = (left) + 'px';
  198. }
  199. };
  200. /**
  201. * Set the list with values for the slider
  202. * @param {Array} values A javascript array with values (any type)
  203. */
  204. Slider.prototype.setValues = function(values) {
  205. this.values = values;
  206. if (this.values.length > 0)
  207. this.setIndex(0);
  208. else
  209. this.index = undefined;
  210. };
  211. /**
  212. * Select a value by its index
  213. * @param {number} index
  214. */
  215. Slider.prototype.setIndex = function(index) {
  216. if (index < this.values.length) {
  217. this.index = index;
  218. this.redraw();
  219. this.onChange();
  220. }
  221. else {
  222. throw new Error('Index out of range');
  223. }
  224. };
  225. /**
  226. * retrieve the index of the currently selected vaue
  227. * @return {number} index
  228. */
  229. Slider.prototype.getIndex = function() {
  230. return this.index;
  231. };
  232. /**
  233. * retrieve the currently selected value
  234. * @return {*} value
  235. */
  236. Slider.prototype.get = function() {
  237. return this.values[this.index];
  238. };
  239. Slider.prototype._onMouseDown = function(event) {
  240. // only react on left mouse button down
  241. var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1);
  242. if (!leftButtonDown) return;
  243. this.startClientX = event.clientX;
  244. this.startSlideX = parseFloat(this.frame.slide.style.left);
  245. this.frame.style.cursor = 'move';
  246. // add event listeners to handle moving the contents
  247. // we store the function onmousemove and onmouseup in the graph, so we can
  248. // remove the eventlisteners lateron in the function mouseUp()
  249. var me = this;
  250. this.onmousemove = function (event) {me._onMouseMove(event);};
  251. this.onmouseup = function (event) {me._onMouseUp(event);};
  252. util.addEventListener(document, 'mousemove', this.onmousemove);
  253. util.addEventListener(document, 'mouseup', this.onmouseup);
  254. util.preventDefault(event);
  255. };
  256. Slider.prototype.leftToIndex = function (left) {
  257. var width = parseFloat(this.frame.bar.style.width) -
  258. this.frame.slide.clientWidth - 10;
  259. var x = left - 3;
  260. var index = Math.round(x / width * (this.values.length-1));
  261. if (index < 0) index = 0;
  262. if (index > this.values.length-1) index = this.values.length-1;
  263. return index;
  264. };
  265. Slider.prototype.indexToLeft = function (index) {
  266. var width = parseFloat(this.frame.bar.style.width) -
  267. this.frame.slide.clientWidth - 10;
  268. var x = index / (this.values.length-1) * width;
  269. var left = x + 3;
  270. return left;
  271. };
  272. Slider.prototype._onMouseMove = function (event) {
  273. var diff = event.clientX - this.startClientX;
  274. var x = this.startSlideX + diff;
  275. var index = this.leftToIndex(x);
  276. this.setIndex(index);
  277. util.preventDefault();
  278. };
  279. Slider.prototype._onMouseUp = function (event) { // eslint-disable-line no-unused-vars
  280. this.frame.style.cursor = 'auto';
  281. // remove event listeners
  282. util.removeEventListener(document, 'mousemove', this.onmousemove);
  283. util.removeEventListener(document, 'mouseup', this.onmouseup);
  284. util.preventDefault();
  285. };
  286. module.exports = Slider;