/*
 * jQuery Selectbox plugin 0.1.3
 *
 * Copyright 2011, Dimitar Ivanov (http://www.bulgaria-web-developers.com/projects/javascript/selectbox/)
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 * 
 * Date: Wed Jul 29 23:20:57 2011 +0200
 */
(function ($, undefined) {
    var PROP_NAME = "selectbox", FALSE = false, TRUE = true;

    function Selectbox() {
        this._state = [];
        this._defaults = {classHolder:"sbHolder", classHolderDisabled:"sbHolderDisabled", classSelector:"sbSelector", classOptions:"sbOptions", classGroup:"sbGroup", classSub:"sbSub", classDisabled:"sbDisabled", classToggleOpen:"sbToggleOpen", classToggle:"sbToggle", speed:200, effect:"slide", onChange:null, onOpen:null, onClose:null}
    }

    $.extend(Selectbox.prototype, {_isOpenSelectbox:function (target) {
        if (!target) {
            return FALSE
        }
        var inst = this._getInst(target);
        return inst.isOpen
    }, _isDisabledSelectbox:function (target) {
        if (!target) {
            return FALSE
        }
        var inst = this._getInst(target);
        return inst.isDisabled
    }, _attachSelectbox:function (target, settings) {
        if (this._getInst(target)) {
            return FALSE
        }
        var $target = $(target), self = this, inst = self._newInst($target), sbHolder, sbSelector, sbToggle, sbOptions, s = FALSE, optGroup = $target.find("optgroup"), opts = $target.find("option"), olen = opts.length;
        $target.attr("sb", inst.uid);
        $.extend(inst.settings, self._defaults, settings);
        self._state[inst.uid] = FALSE;
        $target.hide();
        function closeOthers() {
            var key, uid = this.attr("id").split("_")[1];
            for (key in self._state) {
                if (key !== uid) {
                    if (self._state.hasOwnProperty(key)) {
                        if ($(":input[sb='" + key + "']")[0]) {
                            self._closeSelectbox($(":input[sb='" + key + "']")[0])
                        }
                    }
                }
            }
        }

        sbHolder = $("<div>", {id:"sbHolder_" + inst.uid, "class":inst.settings.classHolder});
        sbSelector = $("<a>", {id:"sbSelector_" + inst.uid, href:"#", "class":inst.settings.classSelector, click:function (e) {
            e.preventDefault();
            closeOthers.apply($(this), []);
            var uid = $(this).attr("id").split("_")[1];
            if (self._state[uid]) {
                self._closeSelectbox(target)
            } else {
                self._openSelectbox(target)
            }
        }});
        sbToggle = $("<a>", {id:"sbToggle_" + inst.uid, href:"#", "class":inst.settings.classToggle, click:function (e) {
            e.preventDefault();
            closeOthers.apply($(this), []);
            var uid = $(this).attr("id").split("_")[1];
            if (self._state[uid]) {
                self._closeSelectbox(target)
            } else {
                self._openSelectbox(target)
            }
        }});
        sbToggle.appendTo(sbHolder);
        sbOptions = $("<ul>", {id:"sbOptions_" + inst.uid, "class":inst.settings.classOptions, css:{display:"none"}});
        $target.children().each(function (i) {
            var that = $(this), li, config = {};
            if (that.is("option")) {
                getOptions(that)
            } else {
                if (that.is("optgroup")) {
                    li = $("<li>");
                    $("<span>", {text:that.attr("label")}).addClass(inst.settings.classGroup).appendTo(li);
                    li.appendTo(sbOptions);
                    if (that.is(":disabled")) {
                        config.disabled = true
                    }
                    config.sub = true;
                    getOptions(that.find("option"), config)
                }
            }
        });
        function getOptions() {
            var sub = arguments[1] && arguments[1].sub ? true : false, disabled = arguments[1] && arguments[1].disabled ? true : false;
            arguments[0].each(function (i) {
                var that = $(this), li = $("<li>"), child;
                if (that.is(":selected")) {
                    sbSelector.text(that.text());
                    s = TRUE
                }
                if (i === olen - 1) {
                    li.addClass("last")
                }
                if (!that.is(":disabled") && !disabled) {
                    child = $("<a>", {href:"#" + that.val(), rel:that.val(), text:that.text(), click:function (e) {
                        e.preventDefault();
                        var t = sbToggle, uid = t.attr("id").split("_")[1];
                        self._changeSelectbox(target, $(this).attr("rel"), $(this).text());
                        self._closeSelectbox(target)
                    }});
                    if (sub) {
                        child.addClass(inst.settings.classSub)
                    }
                    child.appendTo(li)
                } else {
                    child = $("<span>", {text:that.text()}).addClass(inst.settings.classDisabled);
                    if (sub) {
                        child.addClass(inst.settings.classSub)
                    }
                    child.appendTo(li)
                }
                li.appendTo(sbOptions)
            })
        }

        if (!s) {
            sbSelector.text(opts.first().text())
        }
        $.data(target, PROP_NAME, inst);
        sbSelector.appendTo(sbHolder);
        sbOptions.appendTo(sbHolder);
        sbHolder.insertAfter($target)
    }, _detachSelectbox:function (target) {
        var inst = this._getInst(target);
        if (!inst) {
            return FALSE
        }
        $("#sbHolder_" + inst.uid).remove();
        $.data(target, PROP_NAME, null);
        $(target).show()
    }, _changeSelectbox:function (target, value, text) {
        var inst = this._getInst(target), onChange = this._get(inst, "onChange");
        $("#sbSelector_" + inst.uid).text(text);
        $(target).find("option[value='" + value + "']").attr("selected", TRUE);
        if (onChange) {
            onChange.apply((inst.input ? inst.input[0] : null), [value, inst])
        } else {
            if (inst.input) {
                inst.input.trigger("change")
            }
        }
    }, _enableSelectbox:function (target) {
        var inst = this._getInst(target);
        if (!inst || !inst.isDisabled) {
            return FALSE
        }
        $("#sbHolder_" + inst.uid).removeClass(inst.settings.classHolderDisabled);
        inst.isDisabled = FALSE;
        $.data(target, PROP_NAME, inst)
    }, _disableSelectbox:function (target) {
        var inst = this._getInst(target);
        if (!inst || inst.isDisabled) {
            return FALSE
        }
        $("#sbHolder_" + inst.uid).addClass(inst.settings.classHolderDisabled);
        inst.isDisabled = TRUE;
        $.data(target, PROP_NAME, inst)
    }, _optionSelectbox:function (target, name, value) {
        var inst = this._getInst(target);
        if (!inst) {
            return FALSE
        }
        inst[name] = value;
        $.data(target, PROP_NAME, inst)
    }, _openSelectbox:function (target) {
        var inst = this._getInst(target);
        if (!inst || inst.isOpen || inst.isDisabled) {
            return
        }
        var el = $("#sbOptions_" + inst.uid), viewportHeight = parseInt($(window).height(), 10), offset = $("#sbHolder_" + inst.uid).offset(), scrollTop = $(window).scrollTop(), height = el.prev().height(), diff = viewportHeight - (offset.top - scrollTop) - height / 2, onOpen = this._get(inst, "onOpen");
        el.css({top:height + "px", maxHeight:(diff - height) + "px"});
        inst.settings.effect === "fade" ? el.fadeIn(inst.settings.speed) : el.slideDown(inst.settings.speed);
        $("#sbToggle_" + inst.uid).addClass(inst.settings.classToggleOpen);
        this._state[inst.uid] = TRUE;
        inst.isOpen = TRUE;
        if (onOpen) {
            onOpen.apply((inst.input ? inst.input[0] : null), [inst])
        }
        $.data(target, PROP_NAME, inst)
    }, _closeSelectbox:function (target) {
        var inst = this._getInst(target);
        if (!inst || !inst.isOpen) {
            return
        }
        var onClose = this._get(inst, "onClose");
        inst.settings.effect === "fade" ? $("#sbOptions_" + inst.uid).fadeOut(inst.settings.speed) : $("#sbOptions_" + inst.uid).slideUp(inst.settings.speed);
        $("#sbToggle_" + inst.uid).removeClass(inst.settings.classToggleOpen);
        this._state[inst.uid] = FALSE;
        inst.isOpen = FALSE;
        if (onClose) {
            onClose.apply((inst.input ? inst.input[0] : null), [inst])
        }
        $.data(target, PROP_NAME, inst)
    }, _newInst:function (target) {
        var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, "\\\\$1");
        return{id:id, input:target, uid:Math.floor(Math.random() * 99999999), isOpen:FALSE, isDisabled:FALSE, settings:{}}
    }, _getInst:function (target) {
        try {
            return $.data(target, PROP_NAME)
        } catch (err) {
            throw"Missing instance data for this selectbox"
        }
    }, _get:function (inst, name) {
        return inst.settings[name] !== undefined ? inst.settings[name] : this._defaults[name]
    }});
    $.fn.selectbox = function (options) {
        var otherArgs = Array.prototype.slice.call(arguments, 1);
        if (typeof options == "string" && options == "isDisabled") {
            return $.selectbox["_" + options + "Selectbox"].apply($.selectbox, [this[0]].concat(otherArgs))
        }
        if (options == "option" && arguments.length == 2 && typeof arguments[1] == "string") {
            return $.selectbox["_" + options + "Selectbox"].apply($.selectbox, [this[0]].concat(otherArgs))
        }
        return this.each(function () {
            typeof options == "string" ? $.selectbox["_" + options + "Selectbox"].apply($.selectbox, [this].concat(otherArgs)) : $.selectbox._attachSelectbox(this, options)
        })
    };
    $.selectbox = new Selectbox();
    $.selectbox.version = "0.1.3"
})(jQuery);
