/*
  Unobtrusive Slider Control by frequency decoder v2.4 (http://www.frequency-decoder.com/)

  Released under a creative commons Attribution-Share Alike 3.0 Unported license (http://creativecommons.org/licenses/by-sa/3.0/)

  You are free:

  * to copy, distribute, display, and perform the work
  * to make derivative works
  * to make commercial use of the work

  Under the following conditions:

  by Attribution.
  --------------
  You must attribute the work in the manner specified by the author or licensor.

  sa
  --
  Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.

  * For any reuse or distribution, you must make clear to others the license terms of this work.
  * Any of these conditions can be waived if you get permission from the copyright holder.
  */

var fdSliderController = (function() {
        var sliders           = {},
        uniqueid          = 0,
        mouseWheelEnabled = true;

        var removeMouseWheelSupport = function() {
            mouseWheelEnabled = false;
        };
        var addEvent = function(obj, type, fn) {
            if( obj.attachEvent ) {
                obj["e"+type+fn] = fn;
                obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
                obj.attachEvent( "on"+type, obj[type+fn] );
            } else { obj.addEventListener( type, fn, true ); }
        };
        var removeEvent = function(obj, type, fn) {
            if( obj.detachEvent ) {
                try {
                    obj.detachEvent( "on"+type, obj[type+fn] );
                    obj[type+fn] = null;
                } catch(err) { };
            } else { obj.removeEventListener( type, fn, true ); }
        };
        var stopEvent = function(e) {
            if(e == null) e = document.parentWindow.event;
            if(e.stopPropagation) {
                e.stopPropagation();
                e.preventDefault();
            };

            /*@cc_on@*/
            /*@if(@_win32)
              e.cancelBubble = true;
              e.returnValue = false;
              /*@end@*/

            return false;
        };
        var joinNodeLists = function() {
            if(!arguments.length) { return []; };
            var nodeList = [];
            for (var i = 0; i < arguments.length; i++) {
                for (var j = 0, item; item = arguments[i][j]; j++) { nodeList[nodeList.length] = item; };
            };
            return nodeList;
        };

        // Function by Artem B. with a minor change by f.d.
        var parseCallbacks = function(cbs) {
            if(cbs == null) { return {}; };
            var func,
            type,
            cbObj = {},
            parts,
            obj;
            for(var i = 0, fn; fn = cbs[i]; i++) {
                type = fn.match(/(fd_slider_cb_(update|create|destroy|redraw|move|focus|blur)_)([^\s|$]+)/i)[1];
                fn   = fn.replace(new RegExp("^"+type), "").replace(/-/g, ".");
                type = type.replace(/^fd_slider_cb_/i, "").replace(/_$/, "");

                try {
                    if(fn.indexOf(".") != -1) {
                        parts = fn.split('.');
                        obj   = window;
                        for (var x = 0, part; part = obj[parts[x]]; x++) {
                            if(part instanceof Function) {
                                (function() {
                                    var method = part;
                                    func = function (data) { method.apply(obj, [data]) };
                                })();
                            } else {
                                obj = part;
                            };
                        };
                    } else {
                        func = window[fn];
                    };

                    if(!(func instanceof Function)) continue;
                    if(!(type in cbObj)) { cbObj[type] = []; };
                    cbObj[type][cbObj[type].length] = func;
                } catch (err) {};
            };
            return cbObj;
        };

        var parseClassNames = function(cbs) {
            if(cbs == null) { return ""; };
            var cns = [];
            for(var i = 0, cn; cn = cbs[i]; i++) {
                cns[cns.length] = cn.replace(/^fd_slider_cn_/, "");
            };
            return cns.join(" ");
        };
        var createSlider = function(options) {
            if(!options.inp || !options.inp.id) { return false; };
            destroySingleSlider(options.inp.id);
            sliders[options.inp.id] = new fdSlider(options);
            return true;
        };
        var init = function( elem ) {
            var ranges     = /fd_range_([-]{0,1}[0-9]+(d[0-9]+){0,1}){1}_([-]{0,1}[0-9]+(d[0-9]+){0,1}){1}/i,
            increment  = /fd_inc_([-]{0,1}[0-9]+(d[0-9]+){0,1}){1}/,
            kIncrement = /fd_maxinc_([-]{0,1}[0-9]+(d[0-9]+){0,1}){1}/,
            callbacks  = /((fd_slider_cb_(update|create|destroy|redraw|move|focus|blur)_)([^\s|$]+))/ig,
            classnames = /(fd_slider_cn_([a-zA-Z0-9_\-]+))/ig,
            inputs     = elem && elem.tagName && elem.tagName.search(/input|select/i) != -1 ? [elem] : joinNodeLists(document.getElementsByTagName('input'), document.getElementsByTagName('select')),
            range,
            tmp,
            options;

            for(var i = 0, inp; inp = inputs[i]; i++) {
                if((inp.tagName.toLowerCase() == "input" && inp.type == "text" && (inp.className.search(ranges) != -1 || inp.className.search(/fd_slider/) != -1)) || (inp.tagName.toLowerCase() == "select" && inp.className.search(/fd_slider/) != -1)) {
                    // If we haven't been passed a specific id and the slider exists then continue
                    if(!elem && inp.id && document.getElementById("fd-slider-"+inp.id)) { continue; };

                    // Create an id if necessary
                    if(!inp.id) { inp.id == "sldr" + uniqueid++; };

                    options = {
                        inp:            inp,
                        inc:            inp.className.search(increment)  != -1 ? inp.className.match(increment)[0].replace("fd_inc_", "").replace("d",".") : "1",
                        maxInc:         inp.className.search(kIncrement) != -1 ? inp.className.match(kIncrement)[0].replace("fd_maxinc_", "").replace("d",".") : false,
                        range:          [0,100],
                        callbacks:      parseCallbacks(inp.className.match(callbacks)),
                        classNames:     parseClassNames(inp.className.match(classnames)),
                        tween:          inp.className.search(/fd_tween/i) != -1,
                        vertical:       inp.className.search(/fd_vertical/i) != -1,
                        hideInput:      inp.className.search(/fd_hide_input/i) != -1,
                        clickJump:      inp.className.search(/fd_jump/i) != -1,
                        fullARIA:       inp.className.search(/fd_full_aria/i) != -1,
                        noMouseWheel:   inp.className.search(/fd_disable_mousewheel/i) != -1
                    };

                    if(inp.tagName.toLowerCase() == "select") {
                        options.range = [0, inp.options.length - 1];
                    } else if(inp.className.search(ranges) != -1) {
                        range = inp.className.match(ranges)[0].replace("fd_range_", "").replace(/d/g,".").split("_");
                        options.range = [range[0], range[1]];
                    };

                    createSlider(options);
                };
            };
            return true;
        };
        var destroySingleSlider = function(id) {
            if(id in sliders) {
                sliders[id].destroy();
                delete sliders[id];
                return true;
            };
            return false;
        };
        var destroyAllsliders = function(e) {
            for(slider in sliders) { sliders[slider].destroy(); };
        };
        var unload = function(e) {
            destroyAllsliders();
            sliders = null;
            removeEvent(window, "unload", unload);
            removeEvent(window, "resize", resize);
            removeOnloadEvent();
        };
        var resize = function(e) {
            for(slider in sliders) { sliders[slider].onResize(); };
        };
        var removeOnloadEvent = function() {
            removeEvent(window, "load", init);
            /*@cc_on@*/
            /*@if(@_win32)
              removeEvent(window, "load",   function() { setTimeout(onload, 200) });
              /*@end@*/
        };
        function fdSlider(options) {
            var inp         = options.inp,
                tagName     = inp.tagName.toLowerCase(),
                min         = +options.range[0],
                max         = +options.range[1],
                range       = Math.abs(max - min),
                inc         = tagName == "select" ? 1 : +options.inc||1,
                maxInc      = options.maxInc ? options.maxInc : inc * 2,
                precision   = options.inc.search(".") != -1 ? options.inc.substr(options.inc.indexOf(".")+1, options.inc.length - 1).length : 0,
                steps       = Math.ceil(range / inc),
                useTween    = !!options.tween,
                fullARIA    = !!options.fullARIA,
                hideInput   = !!options.hideInput,
                clickJump   = useTween ? false : !!options.clickJump,
                vertical    = !!options.vertical,
                callbacks   = options.callbacks,
                classNames  = options.classNames,
                noMWheel    = !!options.noMouseWheel,
                timer       = null,
                kbEnabled   = true,
                sliderH     = 0,
                sliderW     = 0,
                tweenX      = 0,
                tweenB      = 0,
                tweenC      = 0,
                tweenD      = 0,
                frame       = 0,
                x           = 0,
                y           = 0,
                maxPx       = 0,
                handlePos   = 0,
                destPos     = 0,
                mousePos    = 0,
                deltaPx     = 0,
                stepPx      = 0,
                self        = this,
                changeList  = {},
                initVal     = null,
                    outerWrapper,
                    wrapper,
                    handle,
                    bar;

                if(max < min) {
                    inc    = -inc;
                    maxInc = -maxInc;
                };

                function destroySlider() {
                    try {
                        removeEvent(outerWrapper, "mouseover", onMouseOver);
                        removeEvent(outerWrapper, "mouseout",  onMouseOut);
                        removeEvent(outerWrapper, "mousedown", onMouseDown);
                        removeEvent(handle, "focus",     onFocus);
                        removeEvent(handle, "blur",      onBlur);
                        if(!window.opera) {
                            removeEvent(handle, "keydown",   onKeyDown);
                            removeEvent(handle, "keypress",  onKeyPress);
                        } else {
                            removeEvent(handle, "keypress",  onKeyDown);
                        };
                        removeEvent(handle, "mousedown", onHandleMouseDown);
                        removeEvent(handle, "mouseup",   onHandleMouseUp);

                        if(mouseWheelEnabled && !noMWheel) {
                            if (window.addEventListener && !window.devicePixelRatio) window.removeEventListener('DOMMouseScroll', trackMouseWheel, false);
                            else {
                                removeEvent(document, "mousewheel", trackMouseWheel);
                                removeEvent(window,   "mousewheel", trackMouseWheel);
                            };
                        };
                    } catch(err) {};
                    wrapper = bar = handle = outerWrapper = timer = null;
                    callback("destroy");
                    callbacks = null;
                };

                function redraw() {
                    locate();
                    // Internet Explorer requires the try catch
                    try {
                        var sW = outerWrapper.offsetWidth,
                            sH = outerWrapper.offsetHeight,
                            hW = handle.offsetWidth,
                            hH = handle.offsetHeight,
                            bH = bar.offsetHeight,
                            bW = bar.offsetWidth;

                        maxPx     = vertical ? sH - hH : sW - hW;
                        stepPx    = maxPx / steps;
                        deltaPx   = maxPx / Math.ceil(range / maxInc);

                        sliderW = sW;
                        sliderH = sH;

                        valueToPixels();
                    } catch(err) { };
                    callback("redraw");
                };

                function callback(type) {

                    var cbObj = {"elem":inp, "value":tagName == "select" ? inp.options[inp.selectedIndex].value : inp.value};
                    if(type in callbacks) {
                        for(var i = 0, func; func = callbacks[type][i]; i++) {
                            func(cbObj);
                        };
                    };
                };

                function onFocus(e) {
                    outerWrapper.className = outerWrapper.className.replace('focused','') + ' focused';
                    if(mouseWheelEnabled && !noMWheel) {
                        addEvent(window, 'DOMMouseScroll', trackMouseWheel);
                        addEvent(document, 'mousewheel', trackMouseWheel);
                        if(!window.opera) addEvent(window,   'mousewheel', trackMouseWheel);
                    };
                    callback("focus");
                };

                function onBlur(e) {
                    outerWrapper.className = outerWrapper.className.replace(/focused|fd-fc-slider-hover|fd-slider-hover/g,'');
                    if(mouseWheelEnabled && !noMWheel) {
                        removeEvent(document, 'mousewheel', trackMouseWheel);
                        removeEvent(window, 'DOMMouseScroll', trackMouseWheel);
                        if(!window.opera) removeEvent(window,   'mousewheel', trackMouseWheel);
                    };
                    callback("blur");
                };

                function trackMouseWheel(e) {
                    if(!kbEnabled) return;
                    e = e || window.event;
                    var delta = 0;

                    if (e.wheelDelta) {
                        delta = e.wheelDelta/120;
                        if (window.opera && window.opera.version() < 9.2) delta = -delta;
                    } else if(e.detail) {
                        delta = -e.detail/3;
                    };

                    if(vertical) { delta = -delta; };

                    if(delta) {
                        var xtmp = vertical ? handle.offsetTop : handle.offsetLeft;
                        xtmp = (delta < 0) ? Math.ceil(xtmp + deltaPx) : Math.floor(xtmp - deltaPx);
                        pixelsToValue(Math.min(Math.max(xtmp, 0), maxPx));
                    }
                    return stopEvent(e);
                };

                function onKeyPress(e) {
                    e = e || document.parentWindow.event;
                    if ((e.keyCode >= 33 && e.keyCode <= 40) || !kbEnabled || e.keyCode == 45 || e.keyCode == 46) {
                        return stopEvent(e);
                    };
                    return true;
                };

                function onKeyDown(e) {
                    if(!kbEnabled) return true;

                    e = e || document.parentWindow.event;
                    var kc = e.keyCode != null ? e.keyCode : e.charCode;

                    if ( kc < 33 || (kc > 40 && (kc != 45 && kc != 46))) return true;

                    var value = tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex;
                    if(isNaN(value) || value < Math.min(min,max)) value = Math.min(min,max);

                    if( kc == 37 || kc == 40 || kc == 46 || kc == 34) {
                        // left, down, ins, page down
                        value -= (e.ctrlKey || kc == 34 ? maxInc : inc)
                            } else if( kc == 39 || kc == 38 || kc == 45 || kc == 33) {
                        // right, up, del, page up
                        value += (e.ctrlKey || kc == 33 ? maxInc : inc)
                            } else if( kc == 35 ) {
                        // max
                        value = max;
                    } else if( kc == 36 ) {
                        // min
                        value = min;
                    };

                    valueToPixels(value);
                    callback("update");

                    // Opera doesn't let us cancel key events so the up/down arrows and home/end buttons will scroll the screen - which sucks
                    return stopEvent(e);
                };

                function onMouseOver( e ) {
                    /*@cc_on@*/
                    /*@if(@_jscript_version <= 5.6)
                      if(this.className.search(/focused/) != -1) {
                      this.className = this.className.replace("fd-fc-slider-hover", "") +' fd-fc-slider-hover';
                      return;
                      }
                      /*@end@*/
                    this.className = this.className.replace(/fd\-slider\-hover/g,"") +' fd-slider-hover';
                };

                function onMouseOut( e ) {
                    /*@cc_on@*/
                    /*@if(@_jscript_version <= 5.6)
                      if(this.className.search(/focused/) != -1) {
                      this.className = this.className.replace("fd-fc-slider-hover", "");
                      return;
                      }
                      /*@end@*/
                    this.className = this.className.replace(/fd\-slider\-hover/g,"");
                };

                function onHandleMouseUp(e) {
                    e = e || window.event;
                    removeEvent(document, 'mousemove', trackMouse);
                    removeEvent(document, 'mouseup',   onHandleMouseUp);

                    kbEnabled = true;

                    // Opera fires the blur event when the mouseup event occurs on a button, so we attept to force a focus
                    if(window.opera) try { setTimeout(function() { onfocus(); }, 0); } catch(err) {};
                    document.body.className = document.body.className.replace(/slider-drag-vertical|slider-drag-horizontal/g, "");

                    return stopEvent(e);
                };

                function onHandleMouseDown(e) {
                    e = e || window.event;
                    mousePos  = vertical ? e.clientY : e.clientX;
                    handlePos = parseInt(vertical ? handle.offsetTop : handle.offsetLeft)||0;
                    kbEnabled = false;

                    clearTimeout(timer);
                    timer = null;

                    addEvent(document, 'mousemove', trackMouse);
                    addEvent(document, 'mouseup', onHandleMouseUp);

                    // Force a "focus" on the button on mouse events
                    if(window.devicePixelRatio || (document.all && !window.opera)) try { setTimeout(function() { handle.focus(); }, 0); } catch(err) {};

                    document.body.className += " slider-drag-" + (vertical ? "vertical" : "horizontal");
                };

                function onMouseUp( e ) {
                    e = e || window.event;
                    removeEvent(document, 'mouseup', onMouseUp);
                    if(!useTween) {
                        clearTimeout(timer);
                        timer = null;
                        kbEnabled = true;
                    };
                    return stopEvent(e);
                };

                function trackMouse( e ) {
                    e = e || window.event;
                    pixelsToValue(snapToNearestValue(handlePos + (vertical ? e.clientY - mousePos : e.clientX - mousePos)));
                };

                function onMouseDown( e ) {
                    e = e || window.event;
                    var targ;
                    if (e.target) targ = e.target;
                    else if (e.srcElement) targ = e.srcElement;
                    if (targ.nodeType == 3) targ = targ.parentNode;

                    if(targ.className.search("fd-slider-handle") != -1) { return true; };

                    try { setTimeout(function() { handle.focus(); }, 0); } catch(err) { };

                    clearTimeout(timer);
                    locate();

                    timer     = null;
                    kbEnabled = false;

                    var posx        = 0,
                        sLft        = 0,
                        sTop        = 0;

                    // Internet Explorer doctype woes
                    if (document.documentElement && document.documentElement.scrollTop) {
                        sTop = document.documentElement.scrollTop;
                        sLft = document.documentElement.scrollLeft;
                    } else if (document.body) {
                        sTop = document.body.scrollTop;
                        sLft = document.body.scrollLeft;
                    };

                    if (e.pageX)            posx = vertical ? e.pageY : e.pageX;
                    else if (e.clientX)     posx = vertical ? e.clientY + sTop : e.clientX + sLft;
                    posx -= vertical ? y + Math.round(handle.offsetHeight / 2) : x + Math.round(handle.offsetWidth / 2);
                    posx = snapToNearestValue(posx);

                    if(useTween) {
                        tweenTo(posx);
                    } else if(clickJump) {
                        pixelsToValue(posx);
                    } else {
                        addEvent(document, 'mouseup', onMouseUp);
                        destPos = posx;
                        onTimer();
                    };
                };

                function incrementHandle(numOfSteps) {
                    var value = tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex;
                    if(isNaN(value) || value < Math.min(min,max)) value = Math.min(min,max);
                    value += inc * numOfSteps;
                    valueToPixels(value);
                };

                function snapToNearestValue(px) {
                    var rem = px % stepPx;
                    if(rem && rem >= (stepPx / 2)) { px += (stepPx - rem); }
                    else { px -= rem;  };
                    return Math.min(Math.max(parseInt(px, 10), 0), maxPx);
                };

                function locate(){
                    var curleft = 0,
                        curtop  = 0,
                        obj     = outerWrapper;

                    // Try catch for IE's benefit
                    try {
                        while (obj.offsetParent) {
                            curleft += obj.offsetLeft;
                            curtop  += obj.offsetTop;
                            obj      = obj.offsetParent;
                        };
                    } catch(err) {};
                    x = curleft;
                    y = curtop;
                };

                function onTimer() {
                    var xtmp = vertical ? handle.offsetTop : handle.offsetLeft;
                    xtmp = Math.round((destPos < xtmp) ? Math.max(destPos, Math.floor(xtmp - deltaPx)) : Math.min(destPos, Math.ceil(xtmp + deltaPx)));
                    pixelsToValue(xtmp);
                    if(xtmp != destPos) timer = setTimeout(onTimer, steps > 20 ? 50 : 100);
                    else kbEnabled = true;
                };

                var tween = function(){
                    frame++;
                    var c = tweenC,
                    d = 20,
                    t = frame,
                    b = tweenB,
                    x = Math.ceil((t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b);

                    pixelsToValue(t == d ? tweenX : x);
                    callback("move");
                    if(t!=d) timer = setTimeout(tween, 20);
                    else {
                        clearTimeout(timer);
                        timer     = null;
                        kbEnabled = true;
                    };
                };

                function tweenTo(tx){
                    kbEnabled = false;
                    tweenX = parseInt(tx, 10);
                    tweenB = parseInt(vertical ? handle.style.top : handle.style.left, 10);
                    tweenC = tweenX - tweenB;
                    tweenD = 20;
                    frame  = 0;
                    if(!timer) timer = setTimeout(tween, 20);
                };

                function pixelsToValue(px) {
                    handle.style[vertical ? "top" : "left"] = px + "px";
                    var val = min + (Math.round(px / stepPx) * inc);
                    setInputValue((tagName == "select" || inc == 1) ? Math.round(val) : val);
                };

                function valueToPixels(val) {
                    var value = isNaN(val) ? tagName == "input" ? parseFloat(inp.value) : inp.selectedIndex : val;
                    if(isNaN(value) || value < Math.min(min,max)) value = Math.min(min,max);
                    else if(value > Math.max(min,max)) value = Math.max(min,max);
                    setInputValue(value);
                    handle.style[vertical ? "top" : "left"] = Math.round(((value - min) / inc) * stepPx) + "px";
                };

                function setInputValue(val) {
                    val = isNaN(val) ? min : val;
                    if(tagName == "select") {
                        try {
                            val = parseInt(val, 10);
                            if(inp.selectedIndex == val) return;
                            inp.options[val].selected = true;
                        } catch (err) {};
                    } else {
                        val = (min + (Math.round((val - min) / inc) * inc)).toFixed(precision);
                        if(inp.value == val) return;
                        inp.value = val;
                    };
                    updateAriaValues();
                    callback("update");
                };

                function findLabel() {
                    var label;
                    if(inp.parentNode && inp.parentNode.tagName.toLowerCase() == "label") label = inp.parentNode;
                    else {
                        var labelList = document.getElementsByTagName('label');
                        // loop through label array attempting to match each 'for' attribute to the id of the current element
                        for(var i = 0, lbl; lbl = labelList[i]; i++) {
                            // Internet Explorer requires the htmlFor test
                            if((lbl['htmlFor'] && lbl['htmlFor'] == inp.id) || (lbl.getAttribute('for') == inp.id)) {
                                label = lbl;
                                break;
                            };
                        };
                    };
                    if(label && !label.id) { label.id = inp.id + "_label"; };
                    return label;
                };

                function updateAriaValues() {
                    handle.setAttribute("aria-valuenow",  tagName == "select" ? inp.options[inp.selectedIndex].value : inp.value);
                    handle.setAttribute("aria-valuetext", tagName == "select" ? inp.options[inp.selectedIndex].text  : inp.value);
                };

                function onChange( e ) {
                    valueToPixels();
                    callback("update");
                    return true;
                };

                (function() {
                    if(hideInput) { inp.className += " fd_hide_slider_input"; }
                    else { addEvent(inp, 'change', onChange); };

                    outerWrapper              = document.createElement('div');
                    outerWrapper.className    = "fd-slider" + (vertical ? "-vertical " : " ") + classNames;
                    outerWrapper.id           = "fd-slider-" + inp.id;

                    wrapper                   = document.createElement('span');
                    wrapper.className         = "fd-slider-inner";

                    bar                       = document.createElement('span');
                    bar.className             = "fd-slider-bar";

                    if(fullARIA) {
                        handle            = document.createElement('span');
                        handle.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "0");
                    } else {
                        handle            = document.createElement('button');
                        handle.setAttribute("type", "button");
                    };

                    handle.className          = "fd-slider-handle";
                    handle.appendChild(document.createTextNode(String.fromCharCode(160)));

                    outerWrapper.appendChild(wrapper);
                    outerWrapper.appendChild(bar);
                    outerWrapper.appendChild(handle);

                    inp.parentNode.insertBefore(outerWrapper, inp);

                    /*@cc_on@*/
                    /*@if(@_win32)
                      handle.unselectable       = "on";
                      bar.unselectable          = "on";
                      wrapper.unselectable      = "on";
                      outerWrapper.unselectable = "on";
                      /*@end@*/

                    addEvent(outerWrapper, "mouseover", onMouseOver);
                    addEvent(outerWrapper, "mouseout",  onMouseOut);
                    addEvent(outerWrapper, "mousedown", onMouseDown);
                    if(!window.opera) {
                        addEvent(handle, "keydown",   onKeyDown);
                        addEvent(handle, "keypress",  onKeyPress);
                    } else {
                        addEvent(handle, "keypress",  onKeyDown);
                    };
                    addEvent(handle, "focus",     onFocus);
                    addEvent(handle, "blur",      onBlur);
                    addEvent(handle, "mousedown", onHandleMouseDown);
                    addEvent(handle, "mouseup",   onHandleMouseUp);

                    // Add ARIA accessibility info programmatically
                    handle.setAttribute("role",           "slider");
                    handle.setAttribute("aria-valuemin",  min);
                    handle.setAttribute("aria-valuemax",  max);

                    var lbl = findLabel();
                    if(lbl) {
                        handle.setAttribute("aria-labelledby", lbl.id);
                        handle.id = "fd-slider-handle-" + inp.id;
                        /*@cc_on
                          /*@if(@_win32)
                          lbl.setAttribute("htmlFor", handle.id);
                          @else @*/
                        lbl.setAttribute("for", handle.id);
                        /*@end
                          @*/
                    };

                    // Are there page instructions - the creation of the instructions has been left up to you fine reader...
                    if(document.getElementById("fd_slider_describedby")) {
                        handle.setAttribute("aria-describedby", "fd_slider_describedby");  // aaa:describedby
                    };

                    updateAriaValues();
                    callback("create");
                    redraw();
                })();

                return {
                    onResize:       function(e) { if(outerWrapper.offsetHeight != sliderH || outerWrapper.offsetWidth != sliderW) { redraw(); }; },
                        destroy:        function()  { destroySlider(); },
                        reset:          function()  { valueToPixels(); },
                        increment:      function(n) { incrementHandle(n); }
                };
        };

        addEvent(window, "load",   init);
        addEvent(window, "unload", unload);
        addEvent(window, "resize", resize);
        /*@cc_on@*/
        /*@if(@_win32)
          var onload = function(e) {
          for(slider in sliders) { sliders[slider].reset(); }
          };
          addEvent(window, "load", function() { setTimeout(onload, 200) });
          /*@end@*/

        return {
            create:                 function(elem) { init(elem) },
                destroyAll:             function() { destroyAllsliders(); },
                destroySlider:          function(id) { return destroySingleSlider(id); },
                redrawAll:              function() { resize(); },
                increment:              function(id, numSteps) { if(!(id in sliders)) { return false; }; sliders[id].increment(numSteps); },
                addEvent:               addEvent,
                removeEvent:            removeEvent,
                stopEvent:              stopEvent,
                disableMouseWheel:      function() { removeMouseWheelSupport(); },
                removeOnLoadEvent:      function() { removeOnloadEvent(); }
        }
    })();
