moved qdb here because matt is lazy
[public/www-new.git] / pub / qdb / res / themes / default / js / slider / slider.js
1 /*----------------------------------------------------------------------------\\r
2 |                                Slider 1.02                                  |\r
3 |-----------------------------------------------------------------------------|\r
4 |                         Created by Erik Arvidsson                           |\r
5 |                  (http://webfx.eae.net/contact.html#erik)                   |\r
6 |                      For WebFX (http://webfx.eae.net/)                      |\r
7 |-----------------------------------------------------------------------------|\r
8 | A  slider  control that  degrades  to an  input control  for non  supported |\r
9 | browsers.                                                                   |\r
10 |-----------------------------------------------------------------------------|\r
11 |                Copyright (c) 2002, 2003, 2006 Erik Arvidsson                |\r
12 |-----------------------------------------------------------------------------|\r
13 | Licensed under the Apache License, Version 2.0 (the "License"); you may not |\r
14 | use this file except in compliance with the License.  You may obtain a copy |\r
15 | of the License at http://www.apache.org/licenses/LICENSE-2.0                |\r
16 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |\r
17 | Unless  required  by  applicable law or  agreed  to  in  writing,  software |\r
18 | distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT |\r
19 | WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the |\r
20 | License  for the  specific language  governing permissions  and limitations |\r
21 | under the License.                                                          |\r
22 |-----------------------------------------------------------------------------|\r
23 | Dependencies: timer.js - an OO abstraction of timers                        |\r
24 |               range.js - provides the data model for the slider             |\r
25 |               winclassic.css or any other css file describing the look      |\r
26 |-----------------------------------------------------------------------------|\r
27 | 2002-10-14 | Original version released                                      |\r
28 | 2003-03-27 | Added a test in the constructor for missing oElement arg       |\r
29 | 2003-11-27 | Only use mousewheel when focused                               |\r
30 | 2006-05-28 | Changed license to Apache Software License 2.0.                |\r
31 |-----------------------------------------------------------------------------|\r
32 | Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |\r
33 \----------------------------------------------------------------------------*/\r
34 \r
35 Slider.isSupported = typeof document.createElement != "undefined" &&\r
36         typeof document.documentElement != "undefined" &&\r
37         typeof document.documentElement.offsetWidth == "number";\r
38 \r
39 \r
40 function Slider(oElement, oInput, sOrientation) {\r
41         if (!oElement) return;\r
42         this._orientation = sOrientation || "horizontal";\r
43         this._range = new Range();\r
44         this._range.setExtent(0);\r
45         this._blockIncrement = 10;\r
46         this._unitIncrement = 1;\r
47         this._timer = new Timer(100);\r
48 \r
49 \r
50         if (Slider.isSupported && oElement) {\r
51 \r
52                 this.document = oElement.ownerDocument || oElement.document;\r
53 \r
54                 this.element = oElement;\r
55                 this.element.slider = this;\r
56                 this.element.unselectable = "on";\r
57 \r
58                 // add class name tag to class name\r
59                 this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;\r
60 \r
61                 // create line\r
62                 this.line = this.document.createElement("DIV");\r
63                 this.line.className = "line";\r
64                 this.line.unselectable = "on";\r
65                 this.line.appendChild(this.document.createElement("DIV"));\r
66                 this.element.appendChild(this.line);\r
67 \r
68                 // create handle\r
69                 this.handle = this.document.createElement("DIV");\r
70                 this.handle.className = "handle";\r
71                 this.handle.unselectable = "on";\r
72                 this.handle.appendChild(this.document.createElement("DIV"));\r
73                 this.handle.firstChild.appendChild(\r
74                         this.document.createTextNode(String.fromCharCode(160)));\r
75                 this.element.appendChild(this.handle);\r
76         }\r
77 \r
78         this.input = oInput;\r
79 \r
80         // events\r
81         var oThis = this;\r
82         this._range.onchange = function () {\r
83                 oThis.recalculate();\r
84                 if (typeof oThis.onchange == "function")\r
85                         oThis.onchange();\r
86         };\r
87 \r
88         if (Slider.isSupported && oElement) {\r
89                 this.element.onfocus            = Slider.eventHandlers.onfocus;\r
90                 this.element.onblur                     = Slider.eventHandlers.onblur;\r
91                 this.element.onmousedown        = Slider.eventHandlers.onmousedown;\r
92                 this.element.onmouseover        = Slider.eventHandlers.onmouseover;\r
93                 this.element.onmouseout         = Slider.eventHandlers.onmouseout;\r
94                 this.element.onkeydown          = Slider.eventHandlers.onkeydown;\r
95                 this.element.onkeypress         = Slider.eventHandlers.onkeypress;\r
96                 this.element.onmousewheel       = Slider.eventHandlers.onmousewheel;\r
97                 this.handle.onselectstart       =\r
98                 this.element.onselectstart      = function () { return false; };\r
99 \r
100                 this._timer.ontimer = function () {\r
101                         oThis.ontimer();\r
102                 };\r
103 \r
104                 // extra recalculate for ie\r
105                 window.setTimeout(function() {\r
106                         oThis.recalculate();\r
107                 }, 1);\r
108         }\r
109         else {\r
110                 this.input.onchange = function (e) {\r
111                         oThis.setValue(oThis.input.value);\r
112                 };\r
113         }\r
114 }\r
115 \r
116 Slider.eventHandlers = {\r
117 \r
118         // helpers to make events a bit easier\r
119         getEvent:       function (e, el) {\r
120                 if (!e) {\r
121                         if (el)\r
122                                 e = el.document.parentWindow.event;\r
123                         else\r
124                                 e = window.event;\r
125                 }\r
126                 if (!e.srcElement) {\r
127                         var el = e.target;\r
128                         while (el != null && el.nodeType != 1)\r
129                                 el = el.parentNode;\r
130                         e.srcElement = el;\r
131                 }\r
132                 if (typeof e.offsetX == "undefined") {\r
133                         e.offsetX = e.layerX;\r
134                         e.offsetY = e.layerY;\r
135                 }\r
136 \r
137                 return e;\r
138         },\r
139 \r
140         getDocument:    function (e) {\r
141                 if (e.target)\r
142                         return e.target.ownerDocument;\r
143                 return e.srcElement.document;\r
144         },\r
145 \r
146         getSlider:      function (e) {\r
147                 var el = e.target || e.srcElement;\r
148                 while (el != null && el.slider == null) {\r
149                         el = el.parentNode;\r
150                 }\r
151                 if (el)\r
152                         return el.slider;\r
153                 return null;\r
154         },\r
155 \r
156         getLine:        function (e) {\r
157                 var el = e.target || e.srcElement;\r
158                 while (el != null && el.className != "line")    {\r
159                         el = el.parentNode;\r
160                 }\r
161                 return el;\r
162         },\r
163 \r
164         getHandle:      function (e) {\r
165                 var el = e.target || e.srcElement;\r
166                 var re = /handle/;\r
167                 while (el != null && !re.test(el.className))    {\r
168                         el = el.parentNode;\r
169                 }\r
170                 return el;\r
171         },\r
172         // end helpers\r
173 \r
174         onfocus:        function (e) {\r
175                 var s = this.slider;\r
176                 s._focused = true;\r
177                 s.handle.className = "handle hover";\r
178         },\r
179 \r
180         onblur: function (e) {\r
181                 var s = this.slider\r
182                 s._focused = false;\r
183                 s.handle.className = "handle";\r
184         },\r
185 \r
186         onmouseover:    function (e) {\r
187                 e = Slider.eventHandlers.getEvent(e, this);\r
188                 var s = this.slider;\r
189                 if (e.srcElement == s.handle)\r
190                         s.handle.className = "handle hover";\r
191         },\r
192 \r
193         onmouseout:     function (e) {\r
194                 e = Slider.eventHandlers.getEvent(e, this);\r
195                 var s = this.slider;\r
196                 if (e.srcElement == s.handle && !s._focused)\r
197                         s.handle.className = "handle";\r
198         },\r
199 \r
200         onmousedown:    function (e) {\r
201                 e = Slider.eventHandlers.getEvent(e, this);\r
202                 var s = this.slider;\r
203                 if (s.element.focus)\r
204                         s.element.focus();\r
205 \r
206                 Slider._currentInstance = s;\r
207                 var doc = s.document;\r
208 \r
209                 if (doc.addEventListener) {\r
210                         doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);\r
211                         doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);\r
212                 }\r
213                 else if (doc.attachEvent) {\r
214                         doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);\r
215                         doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);\r
216                         doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);\r
217                         s.element.setCapture();\r
218                 }\r
219 \r
220                 if (Slider.eventHandlers.getHandle(e)) {        // start drag\r
221                         Slider._sliderDragData = {\r
222                                 screenX:        e.screenX,\r
223                                 screenY:        e.screenY,\r
224                                 dx:                     e.screenX - s.handle.offsetLeft,\r
225                                 dy:                     e.screenY - s.handle.offsetTop,\r
226                                 startValue:     s.getValue(),\r
227                                 slider:         s\r
228                         };\r
229                 }\r
230                 else {\r
231                         var lineEl = Slider.eventHandlers.getLine(e);\r
232                         s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);\r
233                         s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);\r
234                         s._increasing = null;\r
235                         s.ontimer();\r
236                 }\r
237         },\r
238 \r
239         onmousemove:    function (e) {\r
240                 e = Slider.eventHandlers.getEvent(e, this);\r
241 \r
242                 if (Slider._sliderDragData) {   // drag\r
243                         var s = Slider._sliderDragData.slider;\r
244 \r
245                         var boundSize = s.getMaximum() - s.getMinimum();\r
246                         var size, pos, reset;\r
247 \r
248                         if (s._orientation == "horizontal") {\r
249                                 size = s.element.offsetWidth - s.handle.offsetWidth;\r
250                                 pos = e.screenX - Slider._sliderDragData.dx;\r
251                                 reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;\r
252                         }\r
253                         else {\r
254                                 size = s.element.offsetHeight - s.handle.offsetHeight;\r
255                                 pos = s.element.offsetHeight - s.handle.offsetHeight -\r
256                                         (e.screenY - Slider._sliderDragData.dy);\r
257                                 reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;\r
258                         }\r
259                         s.setValue(reset ? Slider._sliderDragData.startValue :\r
260                                                 s.getMinimum() + boundSize * pos / size);\r
261                         return false;\r
262                 }\r
263                 else {\r
264                         var s = Slider._currentInstance;\r
265                         if (s != null) {\r
266                                 var lineEl = Slider.eventHandlers.getLine(e);\r
267                                 s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);\r
268                                 s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);\r
269                         }\r
270                 }\r
271 \r
272         },\r
273 \r
274         onmouseup:      function (e) {\r
275                 e = Slider.eventHandlers.getEvent(e, this);\r
276                 var s = Slider._currentInstance;\r
277                 var doc = s.document;\r
278                 if (doc.removeEventListener) {\r
279                         doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);\r
280                         doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);\r
281                 }\r
282                 else if (doc.detachEvent) {\r
283                         doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);\r
284                         doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);\r
285                         doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);\r
286                         s.element.releaseCapture();\r
287                 }\r
288 \r
289                 if (Slider._sliderDragData) {   // end drag\r
290                         Slider._sliderDragData = null;\r
291                 }\r
292                 else {\r
293                         s._timer.stop();\r
294                         s._increasing = null;\r
295                 }\r
296                 Slider._currentInstance = null;\r
297         },\r
298 \r
299         onkeydown:      function (e) {\r
300                 e = Slider.eventHandlers.getEvent(e, this);\r
301                 //var s = Slider.eventHandlers.getSlider(e);\r
302                 var s = this.slider;\r
303                 var kc = e.keyCode;\r
304                 switch (kc) {\r
305                         case 33:        // page up\r
306                                 s.setValue(s.getValue() + s.getBlockIncrement());\r
307                                 break;\r
308                         case 34:        // page down\r
309                                 s.setValue(s.getValue() - s.getBlockIncrement());\r
310                                 break;\r
311                         case 35:        // end\r
312                                 s.setValue(s.getOrientation() == "horizontal" ?\r
313                                         s.getMaximum() :\r
314                                         s.getMinimum());\r
315                                 break;\r
316                         case 36:        // home\r
317                                 s.setValue(s.getOrientation() == "horizontal" ?\r
318                                         s.getMinimum() :\r
319                                         s.getMaximum());\r
320                                 break;\r
321                         case 38:        // up\r
322                         case 39:        // right\r
323                                 s.setValue(s.getValue() + s.getUnitIncrement());\r
324                                 break;\r
325 \r
326                         case 37:        // left\r
327                         case 40:        // down\r
328                                 s.setValue(s.getValue() - s.getUnitIncrement());\r
329                                 break;\r
330                 }\r
331 \r
332                 if (kc >= 33 && kc <= 40) {\r
333                         return false;\r
334                 }\r
335         },\r
336 \r
337         onkeypress:     function (e) {\r
338                 e = Slider.eventHandlers.getEvent(e, this);\r
339                 var kc = e.keyCode;\r
340                 if (kc >= 33 && kc <= 40) {\r
341                         return false;\r
342                 }\r
343         },\r
344 \r
345         onmousewheel:   function (e) {\r
346                 e = Slider.eventHandlers.getEvent(e, this);\r
347                 var s = this.slider;\r
348                 if (s._focused) {\r
349                         s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());\r
350                         // windows inverts this on horizontal sliders. That does not\r
351                         // make sense to me\r
352                         return false;\r
353                 }\r
354         }\r
355 };\r
356 \r
357 \r
358 \r
359 Slider.prototype.classNameTag = "dynamic-slider-control",\r
360 \r
361 Slider.prototype.setValue = function (v) {\r
362         this._range.setValue(v);\r
363         this.input.value = this.getValue();\r
364 };\r
365 \r
366 Slider.prototype.getValue = function () {\r
367         return this._range.getValue();\r
368 };\r
369 \r
370 Slider.prototype.setMinimum = function (v) {\r
371         this._range.setMinimum(v);\r
372         this.input.value = this.getValue();\r
373 };\r
374 \r
375 Slider.prototype.getMinimum = function () {\r
376         return this._range.getMinimum();\r
377 };\r
378 \r
379 Slider.prototype.setMaximum = function (v) {\r
380         this._range.setMaximum(v);\r
381         this.input.value = this.getValue();\r
382 };\r
383 \r
384 Slider.prototype.getMaximum = function () {\r
385         return this._range.getMaximum();\r
386 };\r
387 \r
388 Slider.prototype.setUnitIncrement = function (v) {\r
389         this._unitIncrement = v;\r
390 };\r
391 \r
392 Slider.prototype.getUnitIncrement = function () {\r
393         return this._unitIncrement;\r
394 };\r
395 \r
396 Slider.prototype.setBlockIncrement = function (v) {\r
397         this._blockIncrement = v;\r
398 };\r
399 \r
400 Slider.prototype.getBlockIncrement = function () {\r
401         return this._blockIncrement;\r
402 };\r
403 \r
404 Slider.prototype.getOrientation = function () {\r
405         return this._orientation;\r
406 };\r
407 \r
408 Slider.prototype.setOrientation = function (sOrientation) {\r
409         if (sOrientation != this._orientation) {\r
410                 if (Slider.isSupported && this.element) {\r
411                         // add class name tag to class name\r
412                         this.element.className = this.element.className.replace(this._orientation,\r
413                                                                         sOrientation);\r
414                 }\r
415                 this._orientation = sOrientation;\r
416                 this.recalculate();\r
417 \r
418         }\r
419 };\r
420 \r
421 Slider.prototype.recalculate = function() {\r
422         if (!Slider.isSupported || !this.element) return;\r
423 \r
424         var w = this.element.offsetWidth;\r
425         var h = this.element.offsetHeight;\r
426         var hw = this.handle.offsetWidth;\r
427         var hh = this.handle.offsetHeight;\r
428         var lw = this.line.offsetWidth;\r
429         var lh = this.line.offsetHeight;\r
430 \r
431         // this assumes a border-box layout\r
432 \r
433         if (this._orientation == "horizontal") {\r
434                 this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /\r
435                         (this.getMaximum() - this.getMinimum()) + "px";\r
436                 this.handle.style.top = (h - hh) / 2 + "px";\r
437 \r
438                 this.line.style.top = (h - lh) / 2 + "px";\r
439                 this.line.style.left = hw / 2 + "px";\r
440                 //this.line.style.right = hw / 2 + "px";\r
441                 this.line.style.width = Math.max(0, w - hw - 2)+ "px";\r
442                 this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";\r
443         }\r
444         else {\r
445                 this.handle.style.left = (w - hw) / 2 + "px";\r
446                 this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) /\r
447                         (this.getMaximum() - this.getMinimum()) + "px";\r
448 \r
449                 this.line.style.left = (w - lw) / 2 + "px";\r
450                 this.line.style.top = hh / 2 + "px";\r
451                 this.line.style.height = Math.max(0, h - hh - 2) + "px";        //hard coded border width\r
452                 //this.line.style.bottom = hh / 2 + "px";\r
453                 this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px";     //hard coded border width\r
454         }\r
455 };\r
456 \r
457 Slider.prototype.ontimer = function () {\r
458         var hw = this.handle.offsetWidth;\r
459         var hh = this.handle.offsetHeight;\r
460         var hl = this.handle.offsetLeft;\r
461         var ht = this.handle.offsetTop;\r
462 \r
463         if (this._orientation == "horizontal") {\r
464                 if (this._mouseX > hl + hw &&\r
465                         (this._increasing == null || this._increasing)) {\r
466                         this.setValue(this.getValue() + this.getBlockIncrement());\r
467                         this._increasing = true;\r
468                 }\r
469                 else if (this._mouseX < hl &&\r
470                         (this._increasing == null || !this._increasing)) {\r
471                         this.setValue(this.getValue() - this.getBlockIncrement());\r
472                         this._increasing = false;\r
473                 }\r
474         }\r
475         else {\r
476                 if (this._mouseY > ht + hh &&\r
477                         (this._increasing == null || !this._increasing)) {\r
478                         this.setValue(this.getValue() - this.getBlockIncrement());\r
479                         this._increasing = false;\r
480                 }\r
481                 else if (this._mouseY < ht &&\r
482                         (this._increasing == null || this._increasing)) {\r
483                         this.setValue(this.getValue() + this.getBlockIncrement());\r
484                         this._increasing = true;\r
485                 }\r
486         }\r
487 \r
488         this._timer.start();\r
489 };