/*----------------------------------------------------------------------------\ | Slider 1.02 | |-----------------------------------------------------------------------------| | Created by Erik Arvidsson | | (http://webfx.eae.net/contact.html#erik) | | For WebFX (http://webfx.eae.net/) | |-----------------------------------------------------------------------------| | A slider control that degrades to an input control for non supported | | browsers. | |-----------------------------------------------------------------------------| | Copyright (c) 2002, 2003, 2006 Erik Arvidsson | |-----------------------------------------------------------------------------| | Licensed under the Apache License, Version 2.0 (the "License"); you may not | | use this file except in compliance with the License. You may obtain a copy | | of the License at http://www.apache.org/licenses/LICENSE-2.0 | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Unless required by applicable law or agreed to in writing, software | | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | | License for the specific language governing permissions and limitations | | under the License. | |-----------------------------------------------------------------------------| | Dependencies: timer.js - an OO abstraction of timers | | range.js - provides the data model for the slider | | winclassic.css or any other css file describing the look | |-----------------------------------------------------------------------------| | 2002-10-14 | Original version released | | 2003-03-27 | Added a test in the constructor for missing oElement arg | | 2003-11-27 | Only use mousewheel when focused | | 2006-05-28 | Changed license to Apache Software License 2.0. | |-----------------------------------------------------------------------------| | Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 | \----------------------------------------------------------------------------*/ Slider.isSupported = typeof document.createElement != "undefined" && typeof document.documentElement != "undefined" && typeof document.documentElement.offsetWidth == "number"; function Slider(oElement, oInput, sOrientation) { if (!oElement) return; this._orientation = sOrientation || "horizontal"; this._range = new Range(); this._range.setExtent(0); this._blockIncrement = 10; this._unitIncrement = 1; this._timer = new Timer(100); if (Slider.isSupported && oElement) { this.document = oElement.ownerDocument || oElement.document; this.element = oElement; this.element.slider = this; this.element.unselectable = "on"; // add class name tag to class name this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className; // create line this.line = this.document.createElement("DIV"); this.line.className = "line"; this.line.unselectable = "on"; this.line.appendChild(this.document.createElement("DIV")); this.element.appendChild(this.line); // create handle this.handle = this.document.createElement("DIV"); this.handle.className = "handle"; this.handle.unselectable = "on"; this.handle.appendChild(this.document.createElement("DIV")); this.handle.firstChild.appendChild( this.document.createTextNode(String.fromCharCode(160))); this.element.appendChild(this.handle); } this.input = oInput; // events var oThis = this; this._range.onchange = function () { oThis.recalculate(); if (typeof oThis.onchange == "function") oThis.onchange(); }; if (Slider.isSupported && oElement) { this.element.onfocus = Slider.eventHandlers.onfocus; this.element.onblur = Slider.eventHandlers.onblur; this.element.onmousedown = Slider.eventHandlers.onmousedown; this.element.onmouseover = Slider.eventHandlers.onmouseover; this.element.onmouseout = Slider.eventHandlers.onmouseout; this.element.onkeydown = Slider.eventHandlers.onkeydown; this.element.onkeypress = Slider.eventHandlers.onkeypress; this.element.onmousewheel = Slider.eventHandlers.onmousewheel; this.handle.onselectstart = this.element.onselectstart = function () { return false; }; this._timer.ontimer = function () { oThis.ontimer(); }; // extra recalculate for ie window.setTimeout(function() { oThis.recalculate(); }, 1); } else { this.input.onchange = function (e) { oThis.setValue(oThis.input.value); }; } } Slider.eventHandlers = { // helpers to make events a bit easier getEvent: function (e, el) { if (!e) { if (el) e = el.document.parentWindow.event; else e = window.event; } if (!e.srcElement) { var el = e.target; while (el != null && el.nodeType != 1) el = el.parentNode; e.srcElement = el; } if (typeof e.offsetX == "undefined") { e.offsetX = e.layerX; e.offsetY = e.layerY; } return e; }, getDocument: function (e) { if (e.target) return e.target.ownerDocument; return e.srcElement.document; }, getSlider: function (e) { var el = e.target || e.srcElement; while (el != null && el.slider == null) { el = el.parentNode; } if (el) return el.slider; return null; }, getLine: function (e) { var el = e.target || e.srcElement; while (el != null && el.className != "line") { el = el.parentNode; } return el; }, getHandle: function (e) { var el = e.target || e.srcElement; var re = /handle/; while (el != null && !re.test(el.className)) { el = el.parentNode; } return el; }, // end helpers onfocus: function (e) { var s = this.slider; s._focused = true; s.handle.className = "handle hover"; }, onblur: function (e) { var s = this.slider s._focused = false; s.handle.className = "handle"; }, onmouseover: function (e) { e = Slider.eventHandlers.getEvent(e, this); var s = this.slider; if (e.srcElement == s.handle) s.handle.className = "handle hover"; }, onmouseout: function (e) { e = Slider.eventHandlers.getEvent(e, this); var s = this.slider; if (e.srcElement == s.handle && !s._focused) s.handle.className = "handle"; }, onmousedown: function (e) { e = Slider.eventHandlers.getEvent(e, this); var s = this.slider; if (s.element.focus) s.element.focus(); Slider._currentInstance = s; var doc = s.document; if (doc.addEventListener) { doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true); doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true); } else if (doc.attachEvent) { doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove); doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup); doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup); s.element.setCapture(); } if (Slider.eventHandlers.getHandle(e)) { // start drag Slider._sliderDragData = { screenX: e.screenX, screenY: e.screenY, dx: e.screenX - s.handle.offsetLeft, dy: e.screenY - s.handle.offsetTop, startValue: s.getValue(), slider: s }; } else { var lineEl = Slider.eventHandlers.getLine(e); s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0); s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0); s._increasing = null; s.ontimer(); } }, onmousemove: function (e) { e = Slider.eventHandlers.getEvent(e, this); if (Slider._sliderDragData) { // drag var s = Slider._sliderDragData.slider; var boundSize = s.getMaximum() - s.getMinimum(); var size, pos, reset; if (s._orientation == "horizontal") { size = s.element.offsetWidth - s.handle.offsetWidth; pos = e.screenX - Slider._sliderDragData.dx; reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100; } else { size = s.element.offsetHeight - s.handle.offsetHeight; pos = s.element.offsetHeight - s.handle.offsetHeight - (e.screenY - Slider._sliderDragData.dy); reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100; } s.setValue(reset ? Slider._sliderDragData.startValue : s.getMinimum() + boundSize * pos / size); return false; } else { var s = Slider._currentInstance; if (s != null) { var lineEl = Slider.eventHandlers.getLine(e); s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0); s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0); } } }, onmouseup: function (e) { e = Slider.eventHandlers.getEvent(e, this); var s = Slider._currentInstance; var doc = s.document; if (doc.removeEventListener) { doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true); doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true); } else if (doc.detachEvent) { doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove); doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup); doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup); s.element.releaseCapture(); } if (Slider._sliderDragData) { // end drag Slider._sliderDragData = null; } else { s._timer.stop(); s._increasing = null; } Slider._currentInstance = null; }, onkeydown: function (e) { e = Slider.eventHandlers.getEvent(e, this); //var s = Slider.eventHandlers.getSlider(e); var s = this.slider; var kc = e.keyCode; switch (kc) { case 33: // page up s.setValue(s.getValue() + s.getBlockIncrement()); break; case 34: // page down s.setValue(s.getValue() - s.getBlockIncrement()); break; case 35: // end s.setValue(s.getOrientation() == "horizontal" ? s.getMaximum() : s.getMinimum()); break; case 36: // home s.setValue(s.getOrientation() == "horizontal" ? s.getMinimum() : s.getMaximum()); break; case 38: // up case 39: // right s.setValue(s.getValue() + s.getUnitIncrement()); break; case 37: // left case 40: // down s.setValue(s.getValue() - s.getUnitIncrement()); break; } if (kc >= 33 && kc <= 40) { return false; } }, onkeypress: function (e) { e = Slider.eventHandlers.getEvent(e, this); var kc = e.keyCode; if (kc >= 33 && kc <= 40) { return false; } }, onmousewheel: function (e) { e = Slider.eventHandlers.getEvent(e, this); var s = this.slider; if (s._focused) { s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement()); // windows inverts this on horizontal sliders. That does not // make sense to me return false; } } }; Slider.prototype.classNameTag = "dynamic-slider-control", Slider.prototype.setValue = function (v) { this._range.setValue(v); this.input.value = this.getValue(); }; Slider.prototype.getValue = function () { return this._range.getValue(); }; Slider.prototype.setMinimum = function (v) { this._range.setMinimum(v); this.input.value = this.getValue(); }; Slider.prototype.getMinimum = function () { return this._range.getMinimum(); }; Slider.prototype.setMaximum = function (v) { this._range.setMaximum(v); this.input.value = this.getValue(); }; Slider.prototype.getMaximum = function () { return this._range.getMaximum(); }; Slider.prototype.setUnitIncrement = function (v) { this._unitIncrement = v; }; Slider.prototype.getUnitIncrement = function () { return this._unitIncrement; }; Slider.prototype.setBlockIncrement = function (v) { this._blockIncrement = v; }; Slider.prototype.getBlockIncrement = function () { return this._blockIncrement; }; Slider.prototype.getOrientation = function () { return this._orientation; }; Slider.prototype.setOrientation = function (sOrientation) { if (sOrientation != this._orientation) { if (Slider.isSupported && this.element) { // add class name tag to class name this.element.className = this.element.className.replace(this._orientation, sOrientation); } this._orientation = sOrientation; this.recalculate(); } }; Slider.prototype.recalculate = function() { if (!Slider.isSupported || !this.element) return; var w = this.element.offsetWidth; var h = this.element.offsetHeight; var hw = this.handle.offsetWidth; var hh = this.handle.offsetHeight; var lw = this.line.offsetWidth; var lh = this.line.offsetHeight; // this assumes a border-box layout if (this._orientation == "horizontal") { this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) / (this.getMaximum() - this.getMinimum()) + "px"; this.handle.style.top = (h - hh) / 2 + "px"; this.line.style.top = (h - lh) / 2 + "px"; this.line.style.left = hw / 2 + "px"; //this.line.style.right = hw / 2 + "px"; this.line.style.width = Math.max(0, w - hw - 2)+ "px"; this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px"; } else { this.handle.style.left = (w - hw) / 2 + "px"; this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) / (this.getMaximum() - this.getMinimum()) + "px"; this.line.style.left = (w - lw) / 2 + "px"; this.line.style.top = hh / 2 + "px"; this.line.style.height = Math.max(0, h - hh - 2) + "px"; //hard coded border width //this.line.style.bottom = hh / 2 + "px"; this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px"; //hard coded border width } }; Slider.prototype.ontimer = function () { var hw = this.handle.offsetWidth; var hh = this.handle.offsetHeight; var hl = this.handle.offsetLeft; var ht = this.handle.offsetTop; if (this._orientation == "horizontal") { if (this._mouseX > hl + hw && (this._increasing == null || this._increasing)) { this.setValue(this.getValue() + this.getBlockIncrement()); this._increasing = true; } else if (this._mouseX < hl && (this._increasing == null || !this._increasing)) { this.setValue(this.getValue() - this.getBlockIncrement()); this._increasing = false; } } else { if (this._mouseY > ht + hh && (this._increasing == null || !this._increasing)) { this.setValue(this.getValue() - this.getBlockIncrement()); this._increasing = false; } else if (this._mouseY < ht && (this._increasing == null || this._increasing)) { this.setValue(this.getValue() + this.getBlockIncrement()); this._increasing = true; } } this._timer.start(); };