/*----------------------------------------------------------------------------\
|                                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) 1999 - 2002 Erik Arvidsson                   |
|-----------------------------------------------------------------------------|
| This software is provided "as is", without warranty of any kind, express or |
| implied, including  but not limited  to the warranties of  merchantability, |
| fitness for a particular purpose and noninfringement. In no event shall the |
| authors or  copyright  holders be  liable for any claim,  damages or  other |
| liability, whether  in an  action of  contract, tort  or otherwise, arising |
| from,  out of  or in  connection with  the software or  the  use  or  other |
| dealings in the software.                                                   |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| This  software is  available under the  three different licenses  mentioned |
| below.  To use this software you must chose, and qualify, for one of those. |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| The WebFX Non-Commercial License          http://webfx.eae.net/license.html |
| Permits  anyone the right to use the  software in a  non-commercial context |
| free of charge.                                                             |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| The WebFX Commercial license           http://webfx.eae.net/commercial.html |
| Permits the  license holder the right to use  the software in a  commercial |
| context. Such license must be specifically obtained, however it's valid for |
| any number of  implementations of the licensed software.                    |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| GPL - The GNU General Public License    http://www.gnu.org/licenses/gpl.txt |
| Permits anyone the right to use and modify the software without limitations |
| as long as proper  credits are given  and the original  and modified source |
| code are included. Requires  that the final product, software derivate from |
| the original  source or any  software  utilizing a GPL  component, such  as |
| this, is also licensed under the GPL license.                               |
|-----------------------------------------------------------------------------|
| 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                               |
|-----------------------------------------------------------------------------|
| 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      |
|-----------------------------------------------------------------------------|
| Created 2002-10-14 | All changes are in the log above. | Updated 2003-1-27 |
\----------------------------------------------------------------------------*/


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 = 1;
        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();
};
