1 /*----------------------------------------------------------------------------\
\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
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
35 Slider.isSupported = typeof document.createElement != "undefined" &&
\r
36 typeof document.documentElement != "undefined" &&
\r
37 typeof document.documentElement.offsetWidth == "number";
\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
50 if (Slider.isSupported && oElement) {
\r
52 this.document = oElement.ownerDocument || oElement.document;
\r
54 this.element = oElement;
\r
55 this.element.slider = this;
\r
56 this.element.unselectable = "on";
\r
58 // add class name tag to class name
\r
59 this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;
\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
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
78 this.input = oInput;
\r
82 this._range.onchange = function () {
\r
83 oThis.recalculate();
\r
84 if (typeof oThis.onchange == "function")
\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
100 this._timer.ontimer = function () {
\r
104 // extra recalculate for ie
\r
105 window.setTimeout(function() {
\r
106 oThis.recalculate();
\r
110 this.input.onchange = function (e) {
\r
111 oThis.setValue(oThis.input.value);
\r
116 Slider.eventHandlers = {
\r
118 // helpers to make events a bit easier
\r
119 getEvent: function (e, el) {
\r
122 e = el.document.parentWindow.event;
\r
126 if (!e.srcElement) {
\r
128 while (el != null && el.nodeType != 1)
\r
129 el = el.parentNode;
\r
132 if (typeof e.offsetX == "undefined") {
\r
133 e.offsetX = e.layerX;
\r
134 e.offsetY = e.layerY;
\r
140 getDocument: function (e) {
\r
142 return e.target.ownerDocument;
\r
143 return e.srcElement.document;
\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
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
164 getHandle: function (e) {
\r
165 var el = e.target || e.srcElement;
\r
167 while (el != null && !re.test(el.className)) {
\r
168 el = el.parentNode;
\r
174 onfocus: function (e) {
\r
175 var s = this.slider;
\r
177 s.handle.className = "handle hover";
\r
180 onblur: function (e) {
\r
181 var s = this.slider
\r
182 s._focused = false;
\r
183 s.handle.className = "handle";
\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
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
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
206 Slider._currentInstance = s;
\r
207 var doc = s.document;
\r
209 if (doc.addEventListener) {
\r
210 doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
\r
211 doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
\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
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
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
239 onmousemove: function (e) {
\r
240 e = Slider.eventHandlers.getEvent(e, this);
\r
242 if (Slider._sliderDragData) { // drag
\r
243 var s = Slider._sliderDragData.slider;
\r
245 var boundSize = s.getMaximum() - s.getMinimum();
\r
246 var size, pos, reset;
\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
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
259 s.setValue(reset ? Slider._sliderDragData.startValue :
\r
260 s.getMinimum() + boundSize * pos / size);
\r
264 var s = Slider._currentInstance;
\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
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
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
289 if (Slider._sliderDragData) { // end drag
\r
290 Slider._sliderDragData = null;
\r
294 s._increasing = null;
\r
296 Slider._currentInstance = null;
\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
305 case 33: // page up
\r
306 s.setValue(s.getValue() + s.getBlockIncrement());
\r
308 case 34: // page down
\r
309 s.setValue(s.getValue() - s.getBlockIncrement());
\r
312 s.setValue(s.getOrientation() == "horizontal" ?
\r
317 s.setValue(s.getOrientation() == "horizontal" ?
\r
323 s.setValue(s.getValue() + s.getUnitIncrement());
\r
328 s.setValue(s.getValue() - s.getUnitIncrement());
\r
332 if (kc >= 33 && kc <= 40) {
\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
345 onmousewheel: function (e) {
\r
346 e = Slider.eventHandlers.getEvent(e, this);
\r
347 var s = this.slider;
\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
359 Slider.prototype.classNameTag = "dynamic-slider-control",
\r
361 Slider.prototype.setValue = function (v) {
\r
362 this._range.setValue(v);
\r
363 this.input.value = this.getValue();
\r
366 Slider.prototype.getValue = function () {
\r
367 return this._range.getValue();
\r
370 Slider.prototype.setMinimum = function (v) {
\r
371 this._range.setMinimum(v);
\r
372 this.input.value = this.getValue();
\r
375 Slider.prototype.getMinimum = function () {
\r
376 return this._range.getMinimum();
\r
379 Slider.prototype.setMaximum = function (v) {
\r
380 this._range.setMaximum(v);
\r
381 this.input.value = this.getValue();
\r
384 Slider.prototype.getMaximum = function () {
\r
385 return this._range.getMaximum();
\r
388 Slider.prototype.setUnitIncrement = function (v) {
\r
389 this._unitIncrement = v;
\r
392 Slider.prototype.getUnitIncrement = function () {
\r
393 return this._unitIncrement;
\r
396 Slider.prototype.setBlockIncrement = function (v) {
\r
397 this._blockIncrement = v;
\r
400 Slider.prototype.getBlockIncrement = function () {
\r
401 return this._blockIncrement;
\r
404 Slider.prototype.getOrientation = function () {
\r
405 return this._orientation;
\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
415 this._orientation = sOrientation;
\r
416 this.recalculate();
\r
421 Slider.prototype.recalculate = function() {
\r
422 if (!Slider.isSupported || !this.element) return;
\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
431 // this assumes a border-box layout
\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
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
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
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
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
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
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
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
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
488 this._timer.start();
\r