/*----------------------------------------------------------------------------\
|                                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) {
    if (!oElement) return;
    this._orientation = "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 minus
        this.minus = this.document.createElement("DIV");
        this.minus.className = "minus"
        this.minus.appendChild( this.document.createElement("IMG") );
        this.minus.firstChild.src = "/avans/slider/left.minus.png";
        //this.minus.className = "line";
        //this.element.appendChild(this.minus);

        // create line
        this.line = this.document.createElement("DIV");
        this.line.className = "line";
        this.line.unselectable = "on";
        this.line.style.width = this.element.offsetWidth - this.minus.offsetWidth*2 + "px";
        this.line.appendChild(this.document.createElement("DIV"));
        this.line.firstChild.style.width = this.element.offsetWidth - this.minus.offsetWidth*2 + "px";
        this.element.appendChild(this.line);
		
		this.element.appendChild(this.minus);

        // 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.style.width = this.element.offsetWidth - this.minus.offsetWidth*2 + "px";
        this.handle.firstChild.appendChild(
            this.document.createTextNode(String.fromCharCode(160)));
        this.element.appendChild(this.handle);

        // create plus
        this.plus = this.document.createElement("DIV");
        this.plus.className = "plus"
        this.plus.appendChild( this.document.createElement("IMG") );
        this.plus.firstChild.src = "/avans/slider/right.plus.png";
        //this.minus.className = "line";
        this.element.appendChild(this.plus);

        this.plus.style.left = this.element.offsetWidth - this.plus.offsetWidth - 1 +"px";

    }

    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.plus.firstChild.onclick    = Slider.eventHandlers.increase;
        this.minus.firstChild.onclick   = Slider.eventHandlers.decrease;

        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;
    },

    getTarget:  function (e) {
        if (e.srcElement)  return e.srcElement;
        else return e.target;
    },

    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;
    },

    getInput: function (e) {
        var el = e.target || e.srcElement;
        while (el != null && el.input == null)   {
            el = el.parentNode;
        }
        if (el)
            return el.input;
        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 ss = Slider.eventHandlers.getSlider( e );
        if ( Slider.eventHandlers.getTarget(e) == ss.plus.firstChild ) return false;
        if ( Slider.eventHandlers.getTarget(e) == ss.minus.firstChild ) return false;
        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;

                size = s.element.offsetWidth - s.handle.offsetWidth;
                pos = e.screenX - Slider._sliderDragData.dx;
                reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 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;

        var oInput = s.input;
        if ( oInput && typeof oInput.onselect == "function") oInput.onselect();
    },

    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;
        }
    },

    increase: function (e) {
        e = Slider.eventHandlers.getEvent(e, this);
        var s = Slider.eventHandlers.getSlider(e);
        s.setValue(s.getValue() + s.getUnitIncrement());
        if ( s.input && typeof s.input.onselect == "function") s.input.onselect();
        return false;
    },

    decrease: function (e) {
        e = Slider.eventHandlers.getEvent(e, this);
        var s = Slider.eventHandlers.getSlider(e);
        s.setValue(s.getValue() - s.getUnitIncrement());
        if ( s.input && typeof s.input.onselect == "function") s.input.onselect();
        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;
    var cw = this.plus.offsetWidth+this.minus.offsetWidth;

    // this assumes a border-box layout
    if ( (this.getMaximum() - this.getMinimum()) == 0 ) return; 
    this.handle.style.left = this.minus.offsetWidth + (w - hw - cw) * (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 = (this.minus.offsetWidth + hw) / 2 + "px";
    //this.line.style.right = hw / 2 + "px";
    this.line.style.width = Math.max(0, w - cw + 2)+ "px";
    this.line.firstChild.style.width = Math.max(0, w - hw -cw - 4)+ "px";
};

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._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;
    }

    this._timer.start();
};

