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