source: Dev/branches/rest-dojo-ui/client/dojox/form/RangeSlider.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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