Slider.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. define("dojox/mobile/Slider", [
  2. "dojo/_base/array",
  3. "dojo/_base/connect",
  4. "dojo/_base/declare",
  5. "dojo/_base/lang",
  6. "dojo/_base/window",
  7. "dojo/dom-class",
  8. "dojo/dom-construct",
  9. "dojo/dom-geometry",
  10. "dojo/dom-style",
  11. "dojo/keys",
  12. "dijit/_WidgetBase",
  13. "dijit/form/_FormValueMixin"
  14. ],
  15. function(array, connect, declare, lang, win, domClass, domConstruct, domGeometry, domStyle, keys, WidgetBase, FormValueMixin){
  16. /*=====
  17. WidgetBase = dijit._WidgetBase;
  18. FormValueMixin = dijit.form._FormValueMixin;
  19. =====*/
  20. return declare("dojox.mobile.Slider", [WidgetBase, FormValueMixin], {
  21. // summary:
  22. // A non-templated Slider widget similar to the HTML5 INPUT type=range.
  23. //
  24. // value: [const] Number
  25. // The current slider value.
  26. value: 0,
  27. // min: [const] Number
  28. // The first value the slider can be set to.
  29. min: 0,
  30. // max: [const] Number
  31. // The last value the slider can be set to.
  32. max: 100,
  33. // step: [const] Number
  34. // The delta from 1 value to another.
  35. // This causes the slider handle to snap/jump to the closest possible value.
  36. // A value of 0 means continuous (as much as allowed by pixel resolution).
  37. step: 1,
  38. baseClass: "mblSlider",
  39. // flip: [const] Boolean
  40. // Specifies if the slider should change its default: ascending <--> descending.
  41. flip: false,
  42. // orientation: [const] String
  43. // The slider direction.
  44. // "H": horizontal
  45. // "V": vertical
  46. // "auto": use width/height comparison at instantiation time (default is "H" if width/height are 0)
  47. orientation: "auto",
  48. // halo: Number
  49. // Size of the boundary that extends beyond the edges of the slider
  50. // to make it easier to touch.
  51. halo: "8pt",
  52. buildRendering: function(){
  53. this.focusNode = this.domNode = domConstruct.create("div", {});
  54. this.valueNode = domConstruct.create("input", (this.srcNodeRef && this.srcNodeRef.name) ? { type: "hidden", name: this.srcNodeRef.name } : { type: "hidden" }, this.domNode, "last");
  55. var relativeParent = domConstruct.create("div", { style: { position:"relative", height:"100%", width:"100%" } }, this.domNode, "last");
  56. this.progressBar = domConstruct.create("div", { style:{ position:"absolute" }, "class":"mblSliderProgressBar" }, relativeParent, "last");
  57. this.touchBox = domConstruct.create("div", { style:{ position:"absolute" }, "class":"mblSliderTouchBox" }, relativeParent, "last");
  58. this.handle = domConstruct.create("div", { style:{ position:"absolute" }, "class":"mblSliderHandle" }, relativeParent, "last");
  59. this.inherited(arguments);
  60. },
  61. _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
  62. // summary:
  63. // Hook so set('value', value) works.
  64. value = Math.max(Math.min(value, this.max), this.min);
  65. var fromPercent = (this.value - this.min) * 100 / (this.max - this.min);
  66. this.valueNode.value = value;
  67. this.inherited(arguments);
  68. if(!this._started){ return; } // don't move images until all the properties are set
  69. this.focusNode.setAttribute("aria-valuenow", value);
  70. var toPercent = (value - this.min) * 100 / (this.max - this.min);
  71. // now perform visual slide
  72. var horizontal = this.orientation != "V";
  73. if(priorityChange === true){
  74. domClass.add(this.handle, "mblSliderTransition");
  75. domClass.add(this.progressBar, "mblSliderTransition");
  76. }else{
  77. domClass.remove(this.handle, "mblSliderTransition");
  78. domClass.remove(this.progressBar, "mblSliderTransition");
  79. }
  80. domStyle.set(this.handle, this._attrs.handleLeft, (this._reversed ? (100-toPercent) : toPercent) + "%");
  81. domStyle.set(this.progressBar, this._attrs.width, toPercent + "%");
  82. },
  83. postCreate: function(){
  84. this.inherited(arguments);
  85. function beginDrag(e){
  86. function getEventData(e){
  87. point = isMouse ? e[this._attrs.pageX] : (e.touches ? e.touches[0][this._attrs.pageX] : e[this._attrs.clientX]);
  88. pixelValue = point - startPixel;
  89. pixelValue = Math.min(Math.max(pixelValue, 0), maxPixels);
  90. var discreteValues = this.step ? ((this.max - this.min) / this.step) : maxPixels;
  91. if(discreteValues <= 1 || discreteValues == Infinity ){ discreteValues = maxPixels; }
  92. var wholeIncrements = Math.round(pixelValue * discreteValues / maxPixels);
  93. value = (this.max - this.min) * wholeIncrements / discreteValues;
  94. value = this._reversed ? (this.max - value) : (this.min + value);
  95. }
  96. function continueDrag(e){
  97. e.preventDefault();
  98. lang.hitch(this, getEventData)(e);
  99. this.set('value', value, false);
  100. }
  101. function endDrag(e){
  102. e.preventDefault();
  103. array.forEach(actionHandles, lang.hitch(this, "disconnect"));
  104. actionHandles = [];
  105. this.set('value', this.value, true);
  106. }
  107. e.preventDefault();
  108. var isMouse = e.type == "mousedown";
  109. var box = domGeometry.position(node, false); // can't use true since the added docScroll and the returned x are body-zoom incompatibile
  110. var bodyZoom = domStyle.get(win.body(), "zoom") || 1;
  111. if(isNaN(bodyZoom)){ bodyZoom = 1; }
  112. var nodeZoom = domStyle.get(node, "zoom") || 1;
  113. if(isNaN(nodeZoom)){ nodeZoom = 1; }
  114. var startPixel = box[this._attrs.x] * nodeZoom * bodyZoom + domGeometry.docScroll()[this._attrs.x];
  115. var maxPixels = box[this._attrs.w] * nodeZoom * bodyZoom;
  116. lang.hitch(this, getEventData)(e);
  117. if(e.target == this.touchBox){
  118. this.set('value', value, true);
  119. }
  120. array.forEach(actionHandles, connect.disconnect);
  121. var root = win.doc.documentElement;
  122. var actionHandles = [
  123. this.connect(root, isMouse ? "onmousemove" : "ontouchmove", continueDrag),
  124. this.connect(root, isMouse ? "onmouseup" : "ontouchend", endDrag)
  125. ];
  126. }
  127. function keyPress(/*Event*/ e){
  128. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
  129. var step = this.step,
  130. multiplier = 1,
  131. newValue;
  132. switch(e.keyCode){
  133. case keys.HOME:
  134. newValue = this.min;
  135. break;
  136. case keys.END:
  137. newValue = this.max;
  138. break;
  139. case keys.RIGHT_ARROW:
  140. multiplier = -1;
  141. case keys.LEFT_ARROW:
  142. newValue = this.value + multiplier * ((flip && horizontal) ? step : -step);
  143. break;
  144. case keys.DOWN_ARROW:
  145. multiplier = -1;
  146. case keys.UP_ARROW:
  147. newValue = this.value + multiplier * ((!flip || horizontal) ? step : -step);
  148. break;
  149. default:
  150. return;
  151. }
  152. e.preventDefault();
  153. this._setValueAttr(newValue, false);
  154. }
  155. function keyUp(/*Event*/ e){
  156. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
  157. this._setValueAttr(this.value, true);
  158. }
  159. var point, pixelValue, value,
  160. node = this.domNode;
  161. if(this.orientation == "auto"){
  162. this.orientation = node.offsetHeight <= node.offsetWidth ? "H" : "V";
  163. }
  164. // add V or H suffix to baseClass for styling purposes
  165. domClass.add(this.domNode, array.map(this.baseClass.split(" "), lang.hitch(this, function(c){ return c+this.orientation; })));
  166. var horizontal = this.orientation != "V",
  167. ltr = horizontal ? this.isLeftToRight() : false,
  168. flip = !!this.flip;
  169. // _reversed is complicated since you can have flipped right-to-left and vertical is upside down by default
  170. this._reversed = !((horizontal && ((ltr && !flip) || (!ltr && flip))) || (!horizontal && flip));
  171. this._attrs = horizontal ? { x:'x', w:'w', l:'l', r:'r', pageX:'pageX', clientX:'clientX', handleLeft:"left", left:this._reversed ? "right" : "left", width:"width" } : { x:'y', w:'h', l:'t', r:'b', pageX:'pageY', clientX:'clientY', handleLeft:"top", left:this._reversed ? "bottom" : "top", width:"height" };
  172. this.progressBar.style[this._attrs.left] = "0px";
  173. this.connect(this.touchBox, "ontouchstart", beginDrag);
  174. this.connect(this.touchBox, "onmousedown", beginDrag); // in case this works
  175. this.connect(this.handle, "ontouchstart", beginDrag);
  176. this.connect(this.handle, "onmousedown", beginDrag); // in case this works
  177. this.connect(this.domNode, "onkeypress", keyPress); // for desktop a11y
  178. this.connect(this.domNode, "onkeyup", keyUp); // fire onChange on desktop
  179. this.startup();
  180. this.set('value', this.value);
  181. }
  182. });
  183. });