slider-shim.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /**
  2. * slider-shim.js
  3. * Minimal jQuery plugin shim for the bootstrap-slider API used by prettygcode.js.
  4. *
  5. * Supports the subset used by PrettyGCode:
  6. * $(el).slider(opts) — init
  7. * $(el).slider("setValue", v) — set value
  8. * $(el).slider("setMax", v) — update max
  9. * $(el).on("slide", fn) — fires with event.value
  10. * $(el).on("slideStart", fn)
  11. * $(el).on("slideStop", fn)
  12. */
  13. (function ($) {
  14. 'use strict';
  15. $.fn.slider = function (optsOrCmd, cmdArg1, cmdArg2, cmdArg3) {
  16. return this.each(function () {
  17. var $el = $(this);
  18. var data = $el.data('_pgslider');
  19. // ---------- init ----------
  20. if (!data || typeof optsOrCmd === 'object') {
  21. var opts = $.extend({
  22. id: null,
  23. orientation: 'horizontal',
  24. reversed: false,
  25. min: 0,
  26. max: 100,
  27. value: 0,
  28. }, typeof optsOrCmd === 'object' ? optsOrCmd : {});
  29. // Build the DOM. opts.id is HTML-attribute-escaped before
  30. // interpolation so a future caller passing a tainted id can't
  31. // break out of the attribute (CodeQL: js/html-constructed-from-input).
  32. function escapeAttr(s) {
  33. return String(s).replace(/&/g, '&')
  34. .replace(/"/g, '"')
  35. .replace(/'/g, ''')
  36. .replace(/</g, '&lt;')
  37. .replace(/>/g, '&gt;');
  38. }
  39. var isVertical = opts.orientation === 'vertical';
  40. var trackHtml =
  41. '<div class="slider' + (isVertical ? ' slider-vertical' : '') + '"' +
  42. (opts.id ? ' id="' + escapeAttr(opts.id) + '"' : '') + '>' +
  43. '<div class="slider-track"><div class="slider-selection"></div></div>' +
  44. '<div class="slider-handle round">0</div>' +
  45. '</div>';
  46. $el.html(trackHtml);
  47. var $slider = $el.find('.slider');
  48. var $handle = $el.find('.slider-handle');
  49. var $selection = $el.find('.slider-selection');
  50. var isDragging = false;
  51. data = {
  52. opts: opts,
  53. $slider: $slider,
  54. $handle: $handle,
  55. $selection: $selection,
  56. };
  57. $el.data('_pgslider', data);
  58. function pct(v) {
  59. var range = data.opts.max - data.opts.min;
  60. if (range === 0) return 0;
  61. var p = (v - data.opts.min) / range * 100;
  62. return opts.reversed ? 100 - p : p;
  63. }
  64. function updateUI(val) {
  65. var p = pct(val);
  66. $handle.text(val);
  67. if (isVertical) {
  68. $handle.css({ top: p + '%', bottom: '' });
  69. $selection.css({ height: (100 - p) + '%', top: p + '%' });
  70. } else {
  71. $handle.css({ left: p + '%' });
  72. $selection.css({ width: p + '%' });
  73. }
  74. }
  75. data.updateUI = updateUI;
  76. updateUI(opts.value);
  77. data.opts.value = opts.value;
  78. // Mouse interaction
  79. function getValueFromEvent(e) {
  80. var offset = $slider.offset();
  81. var range = data.opts.max - data.opts.min;
  82. var p;
  83. if (isVertical) {
  84. var h = $slider.height();
  85. p = (e.pageY - offset.top) / h;
  86. } else {
  87. var w = $slider.width();
  88. p = (e.pageX - offset.left) / w;
  89. }
  90. p = Math.max(0, Math.min(1, p));
  91. if (opts.reversed) p = 1 - p;
  92. return Math.round(data.opts.min + p * range);
  93. }
  94. $slider.on('mousedown', function (e) {
  95. isDragging = true;
  96. var val = getValueFromEvent(e);
  97. data.opts.value = val;
  98. updateUI(val);
  99. var ev = $.Event('slideStart'); ev.value = val;
  100. $el.trigger(ev);
  101. e.preventDefault();
  102. });
  103. $(document).on('mousemove.pgslider_' + $el.attr('id'), function (e) {
  104. if (!isDragging) return;
  105. var val = getValueFromEvent(e);
  106. data.opts.value = val;
  107. updateUI(val);
  108. var ev = $.Event('slide'); ev.value = val;
  109. $el.trigger(ev);
  110. });
  111. $(document).on('mouseup.pgslider_' + $el.attr('id'), function (e) {
  112. if (!isDragging) return;
  113. isDragging = false;
  114. var val = getValueFromEvent(e);
  115. data.opts.value = val;
  116. updateUI(val);
  117. var ev = $.Event('slideStop'); ev.value = val;
  118. $el.trigger(ev);
  119. });
  120. return;
  121. }
  122. // ---------- commands ----------
  123. if (optsOrCmd === 'setValue') {
  124. data.opts.value = cmdArg1;
  125. data.updateUI(cmdArg1);
  126. // prettygcode.js calls slider('setValue', N, false, true) after loading
  127. // — the third arg means "trigger the slide event so listeners update state"
  128. if (cmdArg3) {
  129. var ev = $.Event('slide'); ev.value = cmdArg1; $el.trigger(ev);
  130. }
  131. } else if (optsOrCmd === 'setMax') {
  132. data.opts.max = cmdArg1;
  133. data.updateUI(data.opts.value);
  134. }
  135. });
  136. };
  137. }(jQuery));