HorizontalSlider.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. require({cache:{
  2. 'url:dijit/form/templates/HorizontalSlider.html':"<table class=\"dijit dijitReset dijitSlider dijitSliderH\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\" data-dojo-attach-event=\"onkeypress:_onKeyPress,onkeyup:_onKeyUp\"\n\trole=\"presentation\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td data-dojo-attach-point=\"topDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationT dijitSliderDecorationH\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderDecrementIconH\" style=\"display:none\" data-dojo-attach-point=\"decrementButton\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperH dijitSliderLeftBumper\" data-dojo-attach-event=\"press:_onClkDecBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><input data-dojo-attach-point=\"valueNode\" type=\"hidden\" ${!nameAttrSetting}\n\t\t\t/><div class=\"dijitReset dijitSliderBarContainerH\" role=\"presentation\" data-dojo-attach-point=\"sliderBarContainer\"\n\t\t\t\t><div role=\"presentation\" data-dojo-attach-point=\"progressBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderProgressBar dijitSliderProgressBarH\" data-dojo-attach-event=\"press:_onBarClick\"\n\t\t\t\t\t><div class=\"dijitSliderMoveable dijitSliderMoveableH\"\n\t\t\t\t\t\t><div data-dojo-attach-point=\"sliderHandle,focusNode\" class=\"dijitSliderImageHandle dijitSliderImageHandleH\" data-dojo-attach-event=\"press:_onHandleClick\" role=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t\t><div role=\"presentation\" data-dojo-attach-point=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderRemainingBar dijitSliderRemainingBarH\" data-dojo-attach-event=\"press:_onBarClick\"></div\n\t\t\t></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperH dijitSliderRightBumper\" data-dojo-attach-event=\"press:_onClkIncBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderIncrementIconH\" style=\"display:none\" data-dojo-attach-point=\"incrementButton\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td data-dojo-attach-point=\"containerNode,bottomDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationB dijitSliderDecorationH\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n></table>\n"}});
  3. define("dijit/form/HorizontalSlider", [
  4. "dojo/_base/array", // array.forEach
  5. "dojo/_base/declare", // declare
  6. "dojo/dnd/move",
  7. "dojo/_base/event", // event.stop
  8. "dojo/_base/fx", // fx.animateProperty
  9. "dojo/dom-geometry", // domGeometry.position
  10. "dojo/dom-style", // domStyle.getComputedStyle
  11. "dojo/keys", // keys.DOWN_ARROW keys.END keys.HOME keys.LEFT_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.RIGHT_ARROW keys.UP_ARROW
  12. "dojo/_base/lang", // lang.hitch
  13. "dojo/_base/sniff", // has("ie") has("mozilla")
  14. "dojo/dnd/Moveable", // Moveable
  15. "dojo/dnd/Mover", // Mover Mover.prototype.destroy.apply
  16. "dojo/query", // query
  17. "../registry", // registry.findWidgets
  18. "../focus", // focus.focus()
  19. "../typematic",
  20. "./Button",
  21. "./_FormValueWidget",
  22. "../_Container",
  23. "dojo/text!./templates/HorizontalSlider.html"
  24. ], function(array, declare, move, event, fx, domGeometry, domStyle, keys, lang, has, Moveable, Mover, query,
  25. registry, focus, typematic, Button, _FormValueWidget, _Container, template){
  26. /*=====
  27. var Button = dijit.form.Button;
  28. var _FormValueWidget = dijit.form._FormValueWidget;
  29. var _Container = dijit._Container;
  30. =====*/
  31. // module:
  32. // dijit/form/HorizontalSlider
  33. // summary:
  34. // A form widget that allows one to select a value with a horizontally draggable handle
  35. var _SliderMover = declare("dijit.form._SliderMover", Mover, {
  36. onMouseMove: function(e){
  37. var widget = this.widget;
  38. var abspos = widget._abspos;
  39. if(!abspos){
  40. abspos = widget._abspos = domGeometry.position(widget.sliderBarContainer, true);
  41. widget._setPixelValue_ = lang.hitch(widget, "_setPixelValue");
  42. widget._isReversed_ = widget._isReversed();
  43. }
  44. var pixelValue = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
  45. widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false);
  46. },
  47. destroy: function(e){
  48. Mover.prototype.destroy.apply(this, arguments);
  49. var widget = this.widget;
  50. widget._abspos = null;
  51. widget._setValueAttr(widget.value, true);
  52. }
  53. });
  54. var HorizontalSlider = declare("dijit.form.HorizontalSlider", [_FormValueWidget, _Container], {
  55. // summary:
  56. // A form widget that allows one to select a value with a horizontally draggable handle
  57. templateString: template,
  58. // Overrides FormValueWidget.value to indicate numeric value
  59. value: 0,
  60. // showButtons: [const] Boolean
  61. // Show increment/decrement buttons at the ends of the slider?
  62. showButtons: true,
  63. // minimum:: [const] Integer
  64. // The minimum value the slider can be set to.
  65. minimum: 0,
  66. // maximum: [const] Integer
  67. // The maximum value the slider can be set to.
  68. maximum: 100,
  69. // discreteValues: Integer
  70. // If specified, indicates that the slider handle has only 'discreteValues' possible positions,
  71. // and that after dragging the handle, it will snap to the nearest possible position.
  72. // Thus, the slider has only 'discreteValues' possible values.
  73. //
  74. // For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has
  75. // three possible positions, representing values 10, 20, or 30.
  76. //
  77. // If discreteValues is not specified or if it's value is higher than the number of pixels
  78. // in the slider bar, then the slider handle can be moved freely, and the slider's value will be
  79. // computed/reported based on pixel position (in this case it will likely be fractional,
  80. // such as 123.456789).
  81. discreteValues: Infinity,
  82. // pageIncrement: Integer
  83. // If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions)
  84. // that the slider handle is moved via pageup/pagedown keys.
  85. // If discreteValues is not specified, it indicates the number of pixels.
  86. pageIncrement: 2,
  87. // clickSelect: Boolean
  88. // If clicking the slider bar changes the value or not
  89. clickSelect: true,
  90. // slideDuration: Number
  91. // The time in ms to take to animate the slider handle from 0% to 100%,
  92. // when clicking the slider bar to make the handle move.
  93. slideDuration: registry.defaultDuration,
  94. // Map widget attributes to DOMNode attributes.
  95. _setIdAttr: "", // Override _FormWidget which sends id to focusNode
  96. baseClass: "dijitSlider",
  97. // Apply CSS classes to up/down arrows and handle per mouse state
  98. cssStateNodes: {
  99. incrementButton: "dijitSliderIncrementButton",
  100. decrementButton: "dijitSliderDecrementButton",
  101. focusNode: "dijitSliderThumb"
  102. },
  103. _mousePixelCoord: "pageX",
  104. _pixelCount: "w",
  105. _startingPixelCoord: "x",
  106. _handleOffsetCoord: "left",
  107. _progressPixelSize: "width",
  108. _onKeyUp: function(/*Event*/ e){
  109. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
  110. this._setValueAttr(this.value, true);
  111. },
  112. _onKeyPress: function(/*Event*/ e){
  113. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
  114. switch(e.charOrCode){
  115. case keys.HOME:
  116. this._setValueAttr(this.minimum, false);
  117. break;
  118. case keys.END:
  119. this._setValueAttr(this.maximum, false);
  120. break;
  121. // this._descending === false: if ascending vertical (min on top)
  122. // (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical
  123. case ((this._descending || this.isLeftToRight()) ? keys.RIGHT_ARROW : keys.LEFT_ARROW):
  124. case (this._descending === false ? keys.DOWN_ARROW : keys.UP_ARROW):
  125. case (this._descending === false ? keys.PAGE_DOWN : keys.PAGE_UP):
  126. this.increment(e);
  127. break;
  128. case ((this._descending || this.isLeftToRight()) ? keys.LEFT_ARROW : keys.RIGHT_ARROW):
  129. case (this._descending === false ? keys.UP_ARROW : keys.DOWN_ARROW):
  130. case (this._descending === false ? keys.PAGE_UP : keys.PAGE_DOWN):
  131. this.decrement(e);
  132. break;
  133. default:
  134. return;
  135. }
  136. event.stop(e);
  137. },
  138. _onHandleClick: function(e){
  139. if(this.disabled || this.readOnly){ return; }
  140. if(!has("ie")){
  141. // make sure you get focus when dragging the handle
  142. // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
  143. focus.focus(this.sliderHandle);
  144. }
  145. event.stop(e);
  146. },
  147. _isReversed: function(){
  148. // summary:
  149. // Returns true if direction is from right to left
  150. // tags:
  151. // protected extension
  152. return !this.isLeftToRight();
  153. },
  154. _onBarClick: function(e){
  155. if(this.disabled || this.readOnly || !this.clickSelect){ return; }
  156. focus.focus(this.sliderHandle);
  157. event.stop(e);
  158. var abspos = domGeometry.position(this.sliderBarContainer, true);
  159. var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
  160. this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true);
  161. this._movable.onMouseDown(e);
  162. },
  163. _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean?*/ priorityChange){
  164. if(this.disabled || this.readOnly){ return; }
  165. var count = this.discreteValues;
  166. if(count <= 1 || count == Infinity){ count = maxPixels; }
  167. count--;
  168. var pixelsPerValue = maxPixels / count;
  169. var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
  170. this._setValueAttr(Math.max(Math.min((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, this.maximum), this.minimum), priorityChange);
  171. },
  172. _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
  173. // summary:
  174. // Hook so set('value', value) works.
  175. this._set("value", value);
  176. this.valueNode.value = value;
  177. this.focusNode.setAttribute("aria-valuenow", value);
  178. this.inherited(arguments);
  179. var percent = (value - this.minimum) / (this.maximum - this.minimum);
  180. var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar;
  181. var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar;
  182. if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
  183. this._inProgressAnim.stop(true);
  184. }
  185. if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){
  186. // animate the slider
  187. var _this = this;
  188. var props = {};
  189. var start = parseFloat(progressBar.style[this._progressPixelSize]);
  190. var duration = this.slideDuration * (percent-start/100);
  191. if(duration == 0){ return; }
  192. if(duration < 0){ duration = 0 - duration; }
  193. props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" };
  194. this._inProgressAnim = fx.animateProperty({ node: progressBar, duration: duration,
  195. onAnimate: function(v){
  196. remainingBar.style[_this._progressPixelSize] = (100 - parseFloat(v[_this._progressPixelSize])) + "%";
  197. },
  198. onEnd: function(){
  199. delete _this._inProgressAnim;
  200. },
  201. properties: props
  202. });
  203. this._inProgressAnim.play();
  204. }else{
  205. progressBar.style[this._progressPixelSize] = (percent*100) + "%";
  206. remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%";
  207. }
  208. },
  209. _bumpValue: function(signedChange, /*Boolean?*/ priorityChange){
  210. if(this.disabled || this.readOnly){ return; }
  211. var s = domStyle.getComputedStyle(this.sliderBarContainer);
  212. var c = domGeometry.getContentBox(this.sliderBarContainer, s);
  213. var count = this.discreteValues;
  214. if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
  215. count--;
  216. var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
  217. if(value < 0){ value = 0; }
  218. if(value > count){ value = count; }
  219. value = value * (this.maximum - this.minimum) / count + this.minimum;
  220. this._setValueAttr(value, priorityChange);
  221. },
  222. _onClkBumper: function(val){
  223. if(this.disabled || this.readOnly || !this.clickSelect){ return; }
  224. this._setValueAttr(val, true);
  225. },
  226. _onClkIncBumper: function(){
  227. this._onClkBumper(this._descending === false ? this.minimum : this.maximum);
  228. },
  229. _onClkDecBumper: function(){
  230. this._onClkBumper(this._descending === false ? this.maximum : this.minimum);
  231. },
  232. decrement: function(/*Event*/ e){
  233. // summary:
  234. // Decrement slider
  235. // tags:
  236. // private
  237. this._bumpValue(e.charOrCode == keys.PAGE_DOWN ? -this.pageIncrement : -1);
  238. },
  239. increment: function(/*Event*/ e){
  240. // summary:
  241. // Increment slider
  242. // tags:
  243. // private
  244. this._bumpValue(e.charOrCode == keys.PAGE_UP ? this.pageIncrement : 1);
  245. },
  246. _mouseWheeled: function(/*Event*/ evt){
  247. // summary:
  248. // Event handler for mousewheel where supported
  249. event.stop(evt);
  250. var janky = !has("mozilla");
  251. var scroll = evt[(janky ? "wheelDelta" : "detail")] * (janky ? 1 : -1);
  252. this._bumpValue(scroll < 0 ? -1 : 1, true); // negative scroll acts like a decrement
  253. },
  254. startup: function(){
  255. if(this._started){ return; }
  256. array.forEach(this.getChildren(), function(child){
  257. if(this[child.container] != this.containerNode){
  258. this[child.container].appendChild(child.domNode);
  259. }
  260. }, this);
  261. this.inherited(arguments);
  262. },
  263. _typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){
  264. if(count == -1){
  265. this._setValueAttr(this.value, true);
  266. }else{
  267. this[(button == (this._descending? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e);
  268. }
  269. },
  270. buildRendering: function(){
  271. this.inherited(arguments);
  272. if(this.showButtons){
  273. this.incrementButton.style.display="";
  274. this.decrementButton.style.display="";
  275. }
  276. // find any associated label element and add to slider focusnode.
  277. var label = query('label[for="'+this.id+'"]');
  278. if(label.length){
  279. label[0].id = (this.id+"_label");
  280. this.focusNode.setAttribute("aria-labelledby", label[0].id);
  281. }
  282. this.focusNode.setAttribute("aria-valuemin", this.minimum);
  283. this.focusNode.setAttribute("aria-valuemax", this.maximum);
  284. },
  285. postCreate: function(){
  286. this.inherited(arguments);
  287. if(this.showButtons){
  288. this._connects.push(typematic.addMouseListener(
  289. this.decrementButton, this, "_typematicCallback", 25, 500));
  290. this._connects.push(typematic.addMouseListener(
  291. this.incrementButton, this, "_typematicCallback", 25, 500));
  292. }
  293. this.connect(this.domNode, !has("mozilla") ? "onmousewheel" : "DOMMouseScroll", "_mouseWheeled");
  294. // define a custom constructor for a SliderMover that points back to me
  295. var mover = declare(_SliderMover, {
  296. widget: this
  297. });
  298. this._movable = new Moveable(this.sliderHandle, {mover: mover});
  299. this._layoutHackIE7();
  300. },
  301. destroy: function(){
  302. this._movable.destroy();
  303. if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
  304. this._inProgressAnim.stop(true);
  305. }
  306. this._supportingWidgets = registry.findWidgets(this.domNode); // tells destroy about pseudo-child widgets (ruler/labels)
  307. this.inherited(arguments);
  308. }
  309. });
  310. HorizontalSlider._Mover = _SliderMover; // for monkey patching
  311. return HorizontalSlider;
  312. });