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