1 /*----------------------------------------------------------------------------\
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 |
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 \----------------------------------------------------------------------------*/
35 Slider.isSupported = typeof document.createElement != "undefined" &&
36 typeof document.documentElement != "undefined" &&
37 typeof document.documentElement.offsetWidth == "number";
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);
50 if (Slider.isSupported && oElement) {
52 this.document = oElement.ownerDocument || oElement.document;
54 this.element = oElement;
55 this.element.slider = this;
56 this.element.unselectable = "on";
58 // add class name tag to class name
59 this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;
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);
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);
82 this._range.onchange = function () {
84 if (typeof oThis.onchange == "function")
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; };
100 this._timer.ontimer = function () {
104 // extra recalculate for ie
105 window.setTimeout(function() {
110 this.input.onchange = function (e) {
111 oThis.setValue(oThis.input.value);
116 Slider.eventHandlers = {
118 // helpers to make events a bit easier
119 getEvent: function (e, el) {
122 e = el.document.parentWindow.event;
128 while (el != null && el.nodeType != 1)
132 if (typeof e.offsetX == "undefined") {
133 e.offsetX = e.layerX;
134 e.offsetY = e.layerY;
140 getDocument: function (e) {
142 return e.target.ownerDocument;
143 return e.srcElement.document;
146 getSlider: function (e) {
147 var el = e.target || e.srcElement;
148 while (el != null && el.slider == null) {
156 getLine: function (e) {
157 var el = e.target || e.srcElement;
158 while (el != null && el.className != "line") {
164 getHandle: function (e) {
165 var el = e.target || e.srcElement;
167 while (el != null && !re.test(el.className)) {
174 onfocus: function (e) {
177 s.handle.className = "handle hover";
180 onblur: function (e) {
183 s.handle.className = "handle";
186 onmouseover: function (e) {
187 e = Slider.eventHandlers.getEvent(e, this);
189 if (e.srcElement == s.handle)
190 s.handle.className = "handle hover";
193 onmouseout: function (e) {
194 e = Slider.eventHandlers.getEvent(e, this);
196 if (e.srcElement == s.handle && !s._focused)
197 s.handle.className = "handle";
200 onmousedown: function (e) {
201 e = Slider.eventHandlers.getEvent(e, this);
206 Slider._currentInstance = s;
207 var doc = s.document;
209 if (doc.addEventListener) {
210 doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
211 doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
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();
220 if (Slider.eventHandlers.getHandle(e)) { // start drag
221 Slider._sliderDragData = {
224 dx: e.screenX - s.handle.offsetLeft,
225 dy: e.screenY - s.handle.offsetTop,
226 startValue: s.getValue(),
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;
239 onmousemove: function (e) {
240 e = Slider.eventHandlers.getEvent(e, this);
242 if (Slider._sliderDragData) { // drag
243 var s = Slider._sliderDragData.slider;
245 var boundSize = s.getMaximum() - s.getMinimum();
246 var size, pos, reset;
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;
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;
259 s.setValue(reset ? Slider._sliderDragData.startValue :
260 s.getMinimum() + boundSize * pos / size);
264 var s = Slider._currentInstance;
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);
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);
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();
289 if (Slider._sliderDragData) { // end drag
290 Slider._sliderDragData = null;
294 s._increasing = null;
296 Slider._currentInstance = null;
299 onkeydown: function (e) {
300 e = Slider.eventHandlers.getEvent(e, this);
301 //var s = Slider.eventHandlers.getSlider(e);
306 s.setValue(s.getValue() + s.getBlockIncrement());
308 case 34: // page down
309 s.setValue(s.getValue() - s.getBlockIncrement());
312 s.setValue(s.getOrientation() == "horizontal" ?
317 s.setValue(s.getOrientation() == "horizontal" ?
323 s.setValue(s.getValue() + s.getUnitIncrement());
328 s.setValue(s.getValue() - s.getUnitIncrement());
332 if (kc >= 33 && kc <= 40) {
337 onkeypress: function (e) {
338 e = Slider.eventHandlers.getEvent(e, this);
340 if (kc >= 33 && kc <= 40) {
345 onmousewheel: function (e) {
346 e = Slider.eventHandlers.getEvent(e, this);
349 s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
350 // windows inverts this on horizontal sliders. That does not
359 Slider.prototype.classNameTag = "dynamic-slider-control",
361 Slider.prototype.setValue = function (v) {
362 this._range.setValue(v);
363 this.input.value = this.getValue();
366 Slider.prototype.getValue = function () {
367 return this._range.getValue();
370 Slider.prototype.setMinimum = function (v) {
371 this._range.setMinimum(v);
372 this.input.value = this.getValue();
375 Slider.prototype.getMinimum = function () {
376 return this._range.getMinimum();
379 Slider.prototype.setMaximum = function (v) {
380 this._range.setMaximum(v);
381 this.input.value = this.getValue();
384 Slider.prototype.getMaximum = function () {
385 return this._range.getMaximum();
388 Slider.prototype.setUnitIncrement = function (v) {
389 this._unitIncrement = v;
392 Slider.prototype.getUnitIncrement = function () {
393 return this._unitIncrement;
396 Slider.prototype.setBlockIncrement = function (v) {
397 this._blockIncrement = v;
400 Slider.prototype.getBlockIncrement = function () {
401 return this._blockIncrement;
404 Slider.prototype.getOrientation = function () {
405 return this._orientation;
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,
415 this._orientation = sOrientation;
421 Slider.prototype.recalculate = function() {
422 if (!Slider.isSupported || !this.element) return;
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;
431 // this assumes a border-box layout
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";
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";
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";
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
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;
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;
469 else if (this._mouseX < hl &&
470 (this._increasing == null || !this._increasing)) {
471 this.setValue(this.getValue() - this.getBlockIncrement());
472 this._increasing = false;
476 if (this._mouseY > ht + hh &&
477 (this._increasing == null || !this._increasing)) {
478 this.setValue(this.getValue() - this.getBlockIncrement());
479 this._increasing = false;
481 else if (this._mouseY < ht &&
482 (this._increasing == null || this._increasing)) {
483 this.setValue(this.getValue() + this.getBlockIncrement());
484 this._increasing = true;