String.prototype.format = function() {
    var s = this;
    var aRE = String.prototype.format.aRegExp;
    for (var i = 0; i < arguments.length; i++)
    {
        if(!aRE[i])
            aRE[i] = new RegExp("\\{"+i+"\\}", "g");
        s = s.replace(aRE[i], arguments[i]);
    }
    return s;
};
String.prototype.format.aRegExp = new Array();
String.format = function(T) {
    var result = "";
    for (var i = 0; true;)
    {
        var next = T.indexOf("{",i);
        if (next < 0)
        {
            result += T.slice(i);
            break;
        }
        result += T.slice(i, next);
        i = next + 1;
        if (T.charAt(i) == "{")
        {
            result += "{";
            i++;
            continue;
        }
        var next = T.indexOf("}", i);
        var brace = T.slice(i, next).split(":");
        var argNumber = Number.parse(brace[0]) + 1;
        var arg = arguments[argNumber];
        if (arg == null)
            arg = "";
        if (arg.toFormattedString)
            result += arg.toFormattedString(brace[1] ? brace[1] : "");
        else
            result += arg.toString();
        i = next + 1;
    }
    return result;
};
String.localeFormat = function(U) {
    for(var i = 1; i < arguments.length; i++)
    {
        var arg = arguments[i];
        if(arg == null)
            arg = "";
        U = U.replace("{"+(i-1)+"}", arg.toLocaleString());
    }
    return U;
};
String.prototype.stripTags = function() {
    return this.replace(/<\/?[^>]+>/gi, ''); };

window.UI = new Object();

UI.getLayoutRoot = function() {
    return $((document.compatMode == "CSS1Compat") ? document.documentElement : document.body);
}

UI.Point = function(pX, pY) {
    return {
        x: pX,
        y: pY,
        offset: function(x, y)
        {
            this.y += y;
            this.x += x;
            return this;
        },
        add: function(x, y)
        { return this.offset(x, y); },
        subtract: function(x,y)
        { return this.offset(-x, -y); },
        toString: function()
        { return "(x={0}, y={1})".format(this.x, this.y); }
    }
}
UI.Size = function(w, h) {
    return {
        width: w,
        height: h,
        toString: function() {
            return "(width={0}, height={1})".format(this.width, this.height); }
    }
}
UI.Rect = function(x, y, w, h) {
    return {
        x: x,
        y: y,
        width: w,
        height: h,
        right: x + w,
        bottom: y + h,
        toString: function() {
            return "(x:{0}, y:{1}, width:{2}, height:{3}, right: {4}, bottom: {5})".format(this.x, this.y, this.width, this.height, this.right, this.bottom); },
        inflate: function(width, height) {
            this.width += width;
            this.height += height;
            this.right = this.x + this.width;
            this.bottom = this.y + this.height;
        },
        offset: function(x, y) {
            this.x += x;
            this.y += y;
            this.right += x;
            this.bottom += y;
        },
        equals: function(r) {
            return this.x == r.x && this.y == r.y && this.width == r.width && this.height == r.height; }
    };
}
UI.coerceInt = function(v) {
    v = parseInt(v);
    return isNaN(v) ? 0 : v;
}
UI.getMouseOffset = function(e) {
    e = new Event(e);
    try
    {
        if (e.target.style.position == "relative")
            return new Point(e.event.x, e.event.y);
        else
            return new Point(e.event.offsetX, e.event.offsetY);
    }
    catch (ex) { }
    return new UI.Point(0, 0);
}
UI.Rect.Empty = new UI.Rect(0, 0, 0, 0);

Element.extend({
    getBounds: function(elScope) {
        var loc = new UI.Rect(0, 0, 0, 0);
        var cs = this.currentStyle || this.style;
        var sides = new Array("Top", "Left", "Right", "Bottom");
        var restore = {
            visibility: (cs.visibility || "visible"),
            display: (cs.display || "")
        };

        this.style.visibility = "hidden";
        this.style.display = "";

        for (var i = 0; i < sides.length; i++)
        {
            var side = sides[i];
            loc["margin" + side] = UI.coerceInt(cs["margin" + side] || 0);
            loc["border" + side] = UI.coerceInt(cs["border" + side + "Width"] || 0);
            loc["padding" + side] = UI.coerceInt(cs["padding" + side] || 0);
        }

        loc.offset(this.offsetLeft || 0, this.offsetTop || 0);
        loc.inflate(this.offsetWidth || 0, this.offsetHeight || 0);

        var el = this.offsetParent || null;
        var root = UI.getLayoutRoot();

        while (el && (!elScope || elScope != this))
        {
            loc.offset(el.offsetLeft - ((root != el) ? UI.coerceInt(el.scrollLeft) : 0),
                el.offsetTop - ((root != el) ? UI.coerceInt(el.scrollTop) : 0));
            el = el.offsetParent;
        }
        if (document.compatMode == "CSS1Compat")
        {
            loc.contentWidth = this.offsetWidth - loc.borderLeft - loc.borderRight - loc.paddingLeft - loc.paddingRight - loc.marginLeft - loc.marginRight;
            loc.contentHeight = this.offsetHeight - loc.borderTop - loc.borderBottom - loc.paddingTop - loc.paddingBottom - loc.marginTop - loc.marginBottom;
        }
        else
        {
            loc.contentWidth = this.offsetWidth;
            loc.contentHeight = this.offsetHeight;
        }
        loc.bottom -= loc.marginBottom;
        loc.right -= loc.marginRight;

        this.style.display = restore.display;
        this.style.visibility = restore.visibility;

        return loc;
    }
});

UI.HDock = new Abstract({ 'Left': 1, 'Center': 2, 'Right': 3, 'Screen': 4 });
UI.VDock = new Abstract({ 'Top': 1, 'Middle': 2, 'Bottom': 3, 'Screen': 4 });
UI.HAnchor = new Abstract({ 'None': 0, 'Left': 1, 'Center': 2, 'Right': 3 });
UI.VAnchor = new Abstract({ 'None': 0, 'Top': 1, 'Middle': 2, 'Bottom': 3 });

UI.Popup = function(pClassName)
{
    var _this = this;
    var _state = null;

    initialize();
    _this.initialize = initialize;

    function initialize()
    {
        $extend(_this, new Events);

        pClassName = pClassName || 'ui';

        _state = {};

        _state.HDock = UI.HDock.Screen;
        _state.VDock = UI.VDock.Screen;
        _state.HAnchor = UI.HAnchor.Center;
        _state.VAnchor = UI.VAnchor.Middle;
        _state.isRelocate = true;
        _state.offsetX = 0;
        _state.offsetY = 0;
        _state.popup = new Element('div', {
            'class': pClassName + '-popup',
            'styles': {
                'position': 'absolute',
                'visibility': 'hidden',
                'top': '0',
                'left': '0',
                'zIndex': 10000 } });

        _state.popup.injectTop(document.body); //use without moottools: document.body.insertBefore(_state.popup, document.body.firstChild);
    }
    function _keepPositionInternal()
    {
        _state.popup.removeEvent("resize", _keepPositionInternal);
        _this.recalc();
        _wire("int");
    };
    function _keepPositionExternal()
    {
        window.removeEvent("resize", _keepPositionExternal);
        window.removeEvent("scroll", _keepPositionExternal);
        _this.recalc();
        _wire("ext");
    }
    function _wire(type)
    {
        if (type == "int")
        {
            _state.popup.addEvent("resize", _keepPositionInternal);
        }
        else
        {
            window.addEvent("resize", _keepPositionExternal);
            /*
            if (_state.VDock == UI.VDock.Screen || _state.HDock == UI.HDock.Screen)
                window.addEvent("scroll", _keepPositionExternal);
                */
        }
    }
    this.show = function()
    {
        _this.recalc();
        _wire("ext");
        _state.popup.setStyle('visibility', 'visible');
        _this.fireEvent('onShow', [_state.popup]);
    }
    this.hide = function()
    {
        _state.popup.setStyle('visibility', 'hidden');
        _this.fireEvent('onHide', [_state.popup]);
    }
    this.dockTo = function(pOwner, pIsRelocate)
    {
        pOwner = $(pOwner);
        if (pOwner && pOwner.htmlElement)
        {
            _state.owner = pOwner;
            _state.isRelocate = (pIsRelocate) ? true : false;
            _this.recalc();
        }
    }
    this.setPosition = function(pPosition)
    {
        var bounds = _state.popup.getBounds();
        if (bounds.x == pPosition.x && bounds.y == pPosition.y)
            return;
        _state.popup.setStyles({ 'top': pPosition.y, 'left': pPosition.x });
    }
    this.setOffset = function(pX, pY)
    {
        _state.offsetX = pX || _state.offsetX;
        _state.offsetY = pY || _state.offsetY;
        this.recalc();
    }
    this.setDock = function(pHDock, pVDock)
    {
        _state.HDock = pHDock || _state.HDock;
        _state.VDock = pVDock || _state.VDock;
        _this.recalc();
    }
    this.setAnchor = function(pHAnchor, pVAnchor)
    {
        _state.VAnchor = pVAnchor || _state.VAnchor;
        _state.HAnchor = pHAnchor || _state.HAnchor;
        _this.recalc();
    }
    this.setContent = function(pContent, pAsText)
    {
        _state.popup.empty();
        if ($type(pContent) == 'element')
        {
            $(pContent).inject(_state.popup);
        }
        else
        {
            if (pAsText) _state.popup.setText(pContent);
            else _state.popup.setHTML(pContent);
        }
        _wire("int");
    }
    this.getContent = function()
    {
        return _state.popup;
    }
    this.getDockElement = function()
    {
        return _state.owner;
    }
    this.getDockPosition = function()
    {
        if (!_state.owner && _state.HDock != UI.HDock.Screen && _state.VDock != UI.VDock.Screen)
            return UI.Rect.Empty;

        var pos;
        var size = _state.popup.getBounds();
        var dock = new UI.Point(0, 0);
        var anchor = new UI.Point(0, 0);
        var root = UI.getLayoutRoot();

        if (_state.HDock != UI.HDock.Screen || _state.VDock != UI.VDock.Screen)
            pos = _state.owner.getBounds();

        switch (_state.HDock)
        {
            case UI.HDock.Left:
                dock.x = pos.x; break;
            case UI.HDock.Center:
                dock.x = (pos.x + pos.right) / 2; break;
            case UI.HDock.Right:
                dock.x = pos.right; break;
            case UI.HDock.Screen:
                dock.x = root.scrollLeft + (root.clientWidth / 2); break;
        }
        switch (_state.VDock)
        {
            case UI.VDock.Top:
                dock.y = pos.y; break;
            case UI.VDock.Middle:
                dock.y = (pos.y + pos.bottom) / 2; break;
            case UI.VDock.Bottom:
                dock.y = pos.bottom; break;
            case UI.VDock.Screen:
                dock.y = root.scrollTop + (root.clientHeight / 2); break;
        }
        switch (_state.HAnchor)
        {
            case UI.HAnchor.None:
            case UI.HAnchor.Left:
                anchor.x = dock.x; break;
            case UI.HAnchor.Center:
                anchor.x = dock.x - (size.width / 2); break;
            case UI.HAnchor.Right:
                anchor.x = dock.x - size.width; break;
        }
        switch (_state.VAnchor)
        {
            case UI.VAnchor.None:
            case UI.VAnchor.Top:
                anchor.y = dock.y; break;
            case UI.VAnchor.Middle:
                anchor.y = dock.y - (size.height / 2); break;
            case UI.VAnchor.Bottom:
                anchor.y = dock.y - size.height; break;
        }
        anchor.x += _state.offsetX;
        anchor.y += _state.offsetY;
        if (_state.isRelocate)
        {
            if (anchor.x + size.width > root.scrollLeft + root.clientWidth)
                anchor.x = root.scrollLeft + root.clientWidth - size.width;
            if (anchor.y + size.height > root.scrollTop + root.clientHeight)
                anchor.y = root.scrollTop + root.clientHeight - size.height;
            if (anchor.x < root.scrollLeft)
                anchor.x = root.scrollLeft;
            if (anchor.y < root.scrollTop)
                anchor.y = root.scrollTop;
        }
        anchor.x = Math.round(anchor.x);
        anchor.y = Math.round(anchor.y);
        return anchor;
    }
    this.recalc = function()
    {
        if (!_state.owner && _state.HDock != UI.HDock.Screen && _state.HDock != UI.VDock.Screen)
            return;
        _this.setPosition(_this.getDockPosition());
    }
    this.toString = function()
    {
        return "[Object instance of UI.Popup]";
    }
}
/*
var mySelect = new UI.ComboBox('my_select', 'divHolder');
Output html:
=====
<div id="divHolder">
    <input type="hidden" name="my_select" id="divHolder_value" />
    <div id="divHolder_ComboBox" class="combobox" style="width: 100%; cursor: pointer;">
        <table width="100%">
            <tr>
                <td width="100%"></td>
                <td class="but"></td>
            </tr>
        </table>
    </div>
</div>
*/
UI.ComboBox = function (pName, pElHolder)
{
    var _this = this;
    var _state = null;

    initialize();
    _this.initialize = initialize;

    function initialize()
    {
        _state = {};
        _state.isOpened = false;
        _state.tmrClosing = null;
        _state.popupHeight = 0;
        _state.itemIndex = 0;
        _state.owner = $(pElHolder);

        if (!$defined(_state.owner))
        {
            throw new Error('Initialize instance of UI.ComboBox class failed.\r\nCould not find the holder...');
        }

        $extend(_this, new Events);
        //_state.owner.setStyle('position', 'relative');
        _state.owner.empty();

        _this.Name = pName;
        _this.Value = 0;
        _this.SelectedIndex = -1;
        _this.BackColor = '';
        _this.ForeColor = '';
        _this.SelectedBackColor = '';
        _this.SelectedForeColor = '';

        _state.elHidden = new Element('input', {
            'id': _state.owner.id + "_value",
            'name': _this.Name,
            'type': 'hidden' }).inject(_state.owner);

        _state.comboBox = new Element('div', {
            'id': _state.owner.id + '_ComboBox',
            'styles': {
                'width': '100%',
                'cursor': 'pointer' },
            'class': 'combobox' }).inject(_state.owner);

        _state.comboBox.addEvent('click', _OnClick);
        _state.comboBox.addEvent('mouseenter', _OnMouseOver);
        _state.comboBox.addEvent('mouseleave', _OnMouseOut);

        var oTable = new Element('table', { 'width': '100%' });
        oTable.inject(_state.comboBox);

        var tr = $(oTable.insertRow(oTable.rows.length));

        _state.tdText = $(tr.insertCell(tr.cells.length));
        _state.tdText.setProperty('width', '100%');

        _state.tdButton = $(tr.insertCell(tr.cells.length));
        _state.tdButton.addClass('but');

       _state.ctrlPopup = new UI.Popup(_state.owner.getProperty('class'));

       _state.ctrlPopup.setAnchor(UI.HAnchor.Left, UI.VAnchor.Top);
       _state.ctrlPopup.setDock(UI.HDock.Left, UI.VDock.Bottom);
       _state.ctrlPopup.dockTo(_state.comboBox, false);
       _state.ctrlPopup.getContent().addEvent('mouseenter', _OnMouseOver);
       _state.ctrlPopup.getContent().addEvent('mouseleave', _OnMouseOut);
       _state.ctrlPopup.addEvent('onShow', popup_OnShow.bind(_this));
       _state.ctrlPopup.addEvent('onHide', popup_OnHide.bind(_this));

       _state.fx = new Fx.Style(_state.ctrlPopup.getContent(), 'height', { transition: Fx.Transitions.Quart.easeOut, wait: false });

       _state.fx.addEvent('onStart', function(popup) { popup.setStyle('overflow-y', 'hidden'); });
       _state.fx.addEvent('onComplete', function(popup) { popup.setStyle('overflow-y', 'auto'); });

        if (window.$comboBoxs == null)
            window.$comboBoxs = new Array();
        window.$comboBoxs.include(_state.ctrlPopup);
    }
    function _OnMouseOver(e)
    {
        if ($defined(_state.tmrClosing))
            _state.tmrClosing = $clear(_state.tmrClosing);
    }
    function _OnMouseOut(e)
    {
        if (!$defined(_state.tmrClosing))
            _state.tmrClosing = _state.ctrlPopup.hide.delay(1000, _state.ctrlPopup);
    }
    function _OnClick(e)
    {
        if (_state.isOpened) _state.ctrlPopup.hide();
        else _state.ctrlPopup.show();
    }
    function popup_OnShow(popup)
    {
        window.$comboBoxs.each(function (item, i) {
            if (item != _state.ctrlPopup)
                item.hide();
        });

         _state.isOpened = true;
         _state.fx.set(1);
         _state.fx.start(1, _state.popupHeight);
    }
    function popup_OnHide(popup)
    {
        if (_state.isOpened)
        {
            _state.fx.stop(false);
            _state.isOpened = false;
            if ($defined(_state.tmrClosing))
                _state.tmrClosing = $clear(_state.tmrClosing);
        }
    }
    function item_OnClick(e)
    {
        _state.ctrlPopup.hide();
        if (_this.SelectedIndex != this.Index)
        {
            _this.SelectedIndex = this.Index;
            _this.Value = this.Value;

            _state.elHidden.value = this.Value;
            _state.tdText.setHTML(this.innerHTML);
            _this.fireEvent('onChange', [_this]);
        }
    }
    function item_OnMouseOver(e)
    {
        e = new Event(e);
        this.setStyles({ 'color': _this.SelectedForeColor, 'background-color': _this.SelectedBackColor });
        if ($defined(_state.tmrClosing))
            _state.tmrClosing = $clear(_state.tmrClosing);
        e.stop();
    }
    function item_OnMouseOut(e)
    {
        e = new Event(e);
        this.setStyles({ 'color': _this.ForeColor, 'background-color': _this.BackColor });
        if (!$defined(_state.tmrClosing))
            _state.tmrClosing = _state.ctrlPopup.hide.delay(1000, _state.ctrlPopup);
        e.stop();
    }
    this.setItem = function(pIndex)
    {
        var item = $(_state.comboBox.id + '_Item_' + pIndex);
        if ($type(item) == 'element')
        {
            _this.SelectedIndex = item.Index;
            _this.Value = item.Value;

            _state.elHidden.value = item.Value;
            _state.tdText.setHTML(item.innerHTML);

            return true;
        }

        return false;
    }
    this.setButton = function(pValue)
    {
        _state.tdButton.setHTML(pValue);
    }
    this.addItem = function(pName, pValue, pIsSelected)
    {
        if (!$defined(pIsSelected))
            pIsSelected = false;

        var divItem = new Element('div', { 'id': _state.comboBox.id + '_Item_' + _state.itemIndex });

        if (pIsSelected || _state.itemIndex == 0)
        {
            _state.tdText.setHTML(pName);
            _this.Value = pValue;
            _this.SelectedIndex = _state.itemIndex;
            _state.elHidden.value = pValue;
        }

        divItem.Owner = _this;
        divItem.setHTML(pName);
        divItem.Value = pValue;
        divItem.Index = _state.itemIndex;
        divItem.addEvent('click', item_OnClick);
        divItem.addEvent('mouseenter', item_OnMouseOver);
        divItem.addEvent('mouseleave', item_OnMouseOut);
        divItem.inject(_state.ctrlPopup.getContent());

        _state.itemIndex++;

        if (_state.popupHeight < 250)
        {
            var bounds = _state.ctrlPopup.getContent().getBounds();
            _state.popupHeight = (bounds.height > 250) ? 250 : bounds.height;
        }
    }
    this.toString = function()
    {
        return "[Object instance of UI.ComboBox]";
    }
}