source: Dev/trunk/src/client/dojox/form/RangeSlider.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 14.3 KB
Line 
1define([
2        "dojo/_base/declare",
3        "dojo/_base/lang",
4        "dojo/_base/array",
5        "dojo/_base/fx",
6        "dojo/_base/event",
7        "dojo/_base/sniff",
8        "dojo/dom-style",
9        "dojo/dom-geometry",
10        "dojo/keys",
11        "dijit",
12        "dojo/dnd/Mover",
13        "dojo/dnd/Moveable",
14        "dojo/text!./resources/HorizontalRangeSlider.html",
15        "dojo/text!./resources/VerticalRangeSlider.html",
16        "dijit/form/HorizontalSlider",
17        "dijit/form/VerticalSlider",
18        "dijit/form/_FormValueWidget",
19        "dijit/focus",
20        "dojo/fx",
21        "dojox/fx" // unused?
22], function(declare, lang, array, fx, event, has, domStyle, domGeometry, keys, dijit, Mover, Moveable, hTemplate, vTemplate, HorizontalSlider, VerticalSlider, FormValueWidget, FocusManager, fxUtils){
23
24        // make these functions once:
25        var sortReversed = function(a, b){ return b - a; },
26                sortForward = function(a, b){ return a - b; }
27        ;
28
29
30        var RangeSliderMixin = declare("dojox.form._RangeSliderMixin", null, {
31
32                value: [0,100],
33                postMixInProperties: function(){
34                        this.inherited(arguments);
35                        this.value = array.map(this.value, function(i){ return parseInt(i, 10); });
36                },
37
38                postCreate: function(){
39                        this.inherited(arguments);
40                        // we sort the values!
41                        // TODO: re-think, how to set the value
42                        this.value.sort(this._isReversed() ? sortReversed : sortForward);
43
44                        // define a custom constructor for a SliderMoverMax that points back to me
45                        var _self = this;
46                        var mover = declare(SliderMoverMax, {
47                                constructor: function(){
48                                        this.widget = _self;
49                                }
50                        });
51
52                        this._movableMax = new Moveable(this.sliderHandleMax,{ mover: mover });
53
54                        //The valuenow of the sliderHandle (min) usually determines the valuemin of sliderHandleMax
55                        //and valuenow of sliderHandleMax usually determines the valueMax of sliderHandle
56                        //However, in our RangeSlider dragging one handle past the other one causes both to
57                        //'snap' together and move so both sliders will have the same min, max values                   
58
59                        this.sliderHandle.setAttribute("aria-valuemin", this.minimum);
60                        this.sliderHandle.setAttribute("aria-valuemax", this.maximum);
61                        this.sliderHandleMax.setAttribute("aria-valuemin", this.minimum);
62                        this.sliderHandleMax.setAttribute("aria-valuemax", this.maximum);
63                       
64                        // a dnd for the bar!
65                        var barMover = declare(SliderBarMover, {
66                                constructor: function(){
67                                        this.widget = _self;
68                                }
69                        });
70                        this._movableBar = new Moveable(this.progressBar,{ mover: barMover });
71                               
72                        // Remove these from the progress bar since it isn't a slider
73                        this.focusNode.removeAttribute("aria-valuemin");
74                        this.focusNode.removeAttribute("aria-valuemax");
75                        this.focusNode.removeAttribute("aria-valuenow");
76
77                },             
78
79                destroy: function(){
80                        this.inherited(arguments);
81                        this._movableMax.destroy();
82                        this._movableBar.destroy();
83                },
84
85                _onKeyPress: function(/*Event*/ e){
86                        if(this.disabled || this.readOnly || e.altKey || e.ctrlKey){ return; }
87
88                        var useMaxValue = e.target === this.sliderHandleMax;
89                        var barFocus = e.target === this.progressBar;
90                        var k = lang.delegate(keys, this.isLeftToRight() ? {PREV_ARROW: keys.LEFT_ARROW, NEXT_ARROW: keys.RIGHT_ARROW}
91                                                                         : {PREV_ARROW: keys.RIGHT_ARROW, NEXT_ARROW: keys.LEFT_ARROW});                       
92                        var delta = 0;
93                        var down = false;
94
95                        switch(e.keyCode){
96                                case k.HOME       :     this._setValueAttr(this.minimum, true, useMaxValue);event.stop(e);return;
97                                case k.END        :     this._setValueAttr(this.maximum, true, useMaxValue);event.stop(e);return;
98                                case k.PREV_ARROW :
99                                case k.DOWN_ARROW :     down = true;
100                                case k.NEXT_ARROW :
101                                case k.UP_ARROW   :     delta = 1; break;
102                                case k.PAGE_DOWN  :     down = true;
103                                case k.PAGE_UP    :     delta = this.pageIncrement; break;
104                                default           : this.inherited(arguments);return;
105                        }
106                       
107                        if(down){delta = -delta;}
108
109                        if(delta){
110                                if(barFocus){
111                                        this._bumpValue([
112                                                { change: delta, useMaxValue: false },
113                                                { change: delta, useMaxValue: true }
114                                        ]);
115                                }else{
116                                        this._bumpValue(delta, useMaxValue);
117                                }
118                                event.stop(e);
119                        }
120                },
121
122                _onHandleClickMax: function(e){
123                        if(this.disabled || this.readOnly){ return; }
124                        if(!has("ie")){
125                                // make sure you get focus when dragging the handle
126                                // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
127                                FocusManager.focus(this.sliderHandleMax);
128                        }
129                        event.stop(e);
130                },
131
132                _onClkIncBumper: function(){
133                        this._setValueAttr(this._descending === false ? this.minimum : this.maximum, true, true);
134                },
135
136                _bumpValue: function(signedChange, useMaxValue){
137
138                        // we pass an array to _setValueAttr when signedChange is an array
139                        var value = lang.isArray(signedChange) ? [
140                                        this._getBumpValue(signedChange[0].change, signedChange[0].useMaxValue),
141                                        this._getBumpValue(signedChange[1].change, signedChange[1].useMaxValue)
142                                ]
143                                : this._getBumpValue(signedChange, useMaxValue)
144
145                        this._setValueAttr(value, true, useMaxValue);
146                },
147
148                _getBumpValue: function(signedChange, useMaxValue){
149
150                        var idx = useMaxValue ? 1 : 0;
151                        if(this._isReversed()){
152                                idx = 1 - idx;
153                        }
154
155                        var s = domStyle.getComputedStyle(this.sliderBarContainer),
156                                c = domGeometry.getContentBox(this.sliderBarContainer, s),
157                                count = this.discreteValues,
158                                myValue = this.value[idx]
159                        ;
160
161                        if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
162                        count--;
163
164                        var value = (myValue - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
165                        if(value < 0){ value = 0; }
166                        if(value > count){ value = count; }
167
168                        return value * (this.maximum - this.minimum) / count + this.minimum;
169                },
170
171                _onBarClick: function(e){
172                        if(this.disabled || this.readOnly){ return; }
173                        if(!has("ie")){
174                                // make sure you get focus when dragging the handle
175                                // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
176                                FocusManager.focus(this.progressBar);
177                        }
178                        event.stop(e);
179                },
180
181                _onRemainingBarClick: function(e){
182                        if(this.disabled || this.readOnly){ return; }
183                        if(!has("ie")){
184                                // make sure you get focus when dragging the handle
185                                // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
186                                FocusManager.focus(this.progressBar);
187                        }
188
189                        // now we set the min/max-value of the slider!
190                        var abspos = domGeometry.position(this.sliderBarContainer, true),
191                                bar = domGeometry.position(this.progressBar, true),
192                                relMousePos = e[this._mousePixelCoord] - abspos[this._startingPixelCoord],
193                                leftPos = bar[this._startingPixelCoord],
194                                rightPos = leftPos + bar[this._pixelCount],
195                                isMaxVal = this._isReversed() ? relMousePos <= leftPos : relMousePos >= rightPos,
196                                p = this._isReversed() ? abspos[this._pixelCount] - relMousePos : relMousePos
197                        ;
198
199                        this._setPixelValue(p, abspos[this._pixelCount], true, isMaxVal);
200                        event.stop(e);
201                },
202
203                _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean*/ priorityChange, /*Boolean*/ isMaxVal){
204                        if(this.disabled || this.readOnly){ return; }
205                        var myValue = this._getValueByPixelValue(pixelValue, maxPixels);
206                        this._setValueAttr(myValue, priorityChange, isMaxVal);
207                },
208
209                _getValueByPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels){
210                        pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
211                        var count = this.discreteValues;
212                        if(count <= 1 || count == Infinity){ count = maxPixels; }
213                        count--;
214                        var pixelsPerValue = maxPixels / count;
215                        var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
216                        return (this.maximum-this.minimum)*wholeIncrements/count + this.minimum;
217                },
218
219                _setValueAttr: function(/*Array or Number*/ value, /*Boolean, optional*/ priorityChange, /*Boolean, optional*/ isMaxVal){
220                        // we pass an array, when we move the slider with the bar
221                        var actValue = this.value;
222                        if(!lang.isArray(value)){
223                                if(isMaxVal){
224                                        if(this._isReversed()){
225                                                actValue[0] = value;
226                                        }else{
227                                                actValue[1] = value;
228                                        }
229                                }else{
230                                        if(this._isReversed()){
231                                                actValue[1] = value;
232                                        }else{
233                                                actValue[0] = value;
234                                        }
235                                }
236                        }else{
237                                actValue = value;
238                        }
239                        // we have to reset this values. don't know the reason for that
240                        this._lastValueReported = "";
241                        this.valueNode.value = this.value = value = actValue;
242
243                        this.value.sort(this._isReversed() ? sortReversed : sortForward);
244
245                        this.sliderHandle.setAttribute("aria-valuenow", actValue[0]);
246                        this.sliderHandleMax.setAttribute("aria-valuenow", actValue[1]);
247                       
248                        // not calling the _setValueAttr-function of Slider, but the super-super-class (needed for the onchange-event!)
249                        FormValueWidget.prototype._setValueAttr.apply(this, arguments);
250                        this._printSliderBar(priorityChange, isMaxVal);
251                },
252
253                _printSliderBar: function(priorityChange, isMaxVal){
254                        var percentMin = (this.value[0] - this.minimum) / (this.maximum - this.minimum);
255                        var percentMax = (this.value[1] - this.minimum) / (this.maximum - this.minimum);
256                        var percentMinSave = percentMin;
257                        if(percentMin > percentMax){
258                                percentMin = percentMax;
259                                percentMax = percentMinSave;
260                        }
261                        var sliderHandleVal = this._isReversed() ? ((1-percentMin)*100) : (percentMin * 100);
262                        var sliderHandleMaxVal = this._isReversed() ? ((1-percentMax)*100) : (percentMax * 100);
263                        var progressBarVal = this._isReversed() ? ((1-percentMax)*100) : (percentMin * 100);
264                        if(priorityChange && this.slideDuration > 0 && this.progressBar.style[this._progressPixelSize]){
265                                // animate the slider
266                                var percent = isMaxVal ? percentMax : percentMin;
267                                var _this = this;
268                                var props = {};
269                                var start = parseFloat(this.progressBar.style[this._handleOffsetCoord]);
270                                var duration = this.slideDuration / 10; // * (percent-start/100);
271                                if(duration === 0){ return; }
272                                if(duration < 0){ duration = 0 - duration; }
273                                var propsHandle = {};
274                                var propsHandleMax = {};
275                                var propsBar = {};
276                                // hui, a lot of animations :-)
277                                propsHandle[this._handleOffsetCoord] = { start: this.sliderHandle.style[this._handleOffsetCoord], end: sliderHandleVal, units:"%"};
278                                propsHandleMax[this._handleOffsetCoord] = { start: this.sliderHandleMax.style[this._handleOffsetCoord], end: sliderHandleMaxVal, units:"%"};
279                                propsBar[this._handleOffsetCoord] = { start: this.progressBar.style[this._handleOffsetCoord], end: progressBarVal, units:"%"};
280                                propsBar[this._progressPixelSize] = { start: this.progressBar.style[this._progressPixelSize], end: (percentMax - percentMin) * 100, units:"%"};
281                                var animHandle = fx.animateProperty({node: this.sliderHandle,duration: duration, properties: propsHandle});
282                                var animHandleMax = fx.animateProperty({node: this.sliderHandleMax,duration: duration, properties: propsHandleMax});
283                                var animBar = fx.animateProperty({node: this.progressBar,duration: duration, properties: propsBar});
284                                var animCombine = fxUtils.combine([animHandle, animHandleMax, animBar]);
285                                animCombine.play();
286                        }else{
287                                this.sliderHandle.style[this._handleOffsetCoord] = sliderHandleVal + "%";
288                                this.sliderHandleMax.style[this._handleOffsetCoord] = sliderHandleMaxVal + "%";
289                                this.progressBar.style[this._handleOffsetCoord] = progressBarVal + "%";
290                                this.progressBar.style[this._progressPixelSize] = ((percentMax - percentMin) * 100) + "%";
291                        }
292                }
293        });
294
295        var SliderMoverMax = declare("dijit.form._SliderMoverMax", dijit.form._SliderMover, {
296
297                onMouseMove: function(e){
298                        var widget = this.widget;
299                        var abspos = widget._abspos;
300                        if(!abspos){
301                                abspos = widget._abspos = domGeometry.position(widget.sliderBarContainer, true);
302                                widget._setPixelValue_ = lang.hitch(widget, "_setPixelValue");
303                                widget._isReversed_ = widget._isReversed();
304                        }
305
306                        var pixelValue = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
307                        widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false, true);
308                },
309
310                destroy: function(e){
311                        Mover.prototype.destroy.apply(this, arguments);
312                        var widget = this.widget;
313                        widget._abspos = null;
314                        widget._setValueAttr(widget.value, true);
315                }
316        });
317
318        var SliderBarMover = declare("dijit.form._SliderBarMover", Mover, {
319
320                onMouseMove: function(e){
321                        var widget = this.widget;
322                        if(widget.disabled || widget.readOnly){ return; }
323                        var abspos = widget._abspos;
324                        var bar = widget._bar;
325                        var mouseOffset = widget._mouseOffset;
326                        if(!abspos){
327                                abspos = widget._abspos = domGeometry.position(widget.sliderBarContainer, true);
328                                widget._setPixelValue_ = lang.hitch(widget, "_setPixelValue");
329                                widget._getValueByPixelValue_ = lang.hitch(widget, "_getValueByPixelValue");
330                                widget._isReversed_ = widget._isReversed();
331                        }
332
333                        if(!bar){
334                                bar = widget._bar = domGeometry.position(widget.progressBar, true);
335                        }
336
337                        if(!mouseOffset){
338                                mouseOffset = widget._mouseOffset = e[widget._mousePixelCoord] - bar[widget._startingPixelCoord];
339                        }
340
341                        var pixelValueMin = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord] - mouseOffset,
342                                pixelValueMax = pixelValueMin + bar[widget._pixelCount];
343                                // we don't narrow the slider when it reaches the bumper!
344                                // maybe there is a simpler way
345                                pixelValues = [pixelValueMin, pixelValueMax]
346                        ;
347
348                        pixelValues.sort(sortForward);
349
350                        if(pixelValues[0] <= 0){
351                                pixelValues[0] = 0;
352                                pixelValues[1] = bar[widget._pixelCount];
353                        }
354                        if(pixelValues[1] >= abspos[widget._pixelCount]){
355                                pixelValues[1] = abspos[widget._pixelCount];
356                                pixelValues[0] = abspos[widget._pixelCount] - bar[widget._pixelCount];
357                        }
358                        // getting the real values by pixel
359                        var myValues = [
360                                widget._getValueByPixelValue(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValues[0]) : pixelValues[0], abspos[widget._pixelCount]),
361                                widget._getValueByPixelValue(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValues[1]) : pixelValues[1], abspos[widget._pixelCount])
362                        ];
363                        // and setting the value of the widget
364                        widget._setValueAttr(myValues, false, false);
365                },
366
367                destroy: function(){
368                        Mover.prototype.destroy.apply(this, arguments);
369                        var widget = this.widget;
370                        widget._abspos = null;
371                        widget._bar = null;
372                        widget._mouseOffset = null;
373                        widget._setValueAttr(widget.value, true);
374                }
375        });
376
377        declare("dojox.form.HorizontalRangeSlider", [HorizontalSlider, RangeSliderMixin], {
378                // summary:
379                //              A form widget that allows one to select a range with two horizontally draggable images
380                templateString: hTemplate
381        });
382
383        declare("dojox.form.VerticalRangeSlider", [VerticalSlider, RangeSliderMixin], {
384                // summary:
385                //              A form widget that allows one to select a range with two vertically draggable images
386                templateString: vTemplate
387        });
388
389        return RangeSliderMixin;
390
391});
Note: See TracBrowser for help on using the repository browser.