if ( !framework.isDeclared("org.iit.SuggestInput") ){

framework.declareClass("org.iit.SuggestInput", org.iit.Widget, 
    function(config) {
        org.iit.SuggestInput.superclass.constructor.apply(this, arguments);

        this.name = config.name;
        this.canHaveCustomValue = config.canHaveCustomValue||false;
        
        this._combo = null;
        this._comboMaxHeight = config.comboMaxHeight||200;
        this._input = null;
        this._hiddenInput = null;
        this._comboVisible = false;
        this._itemTemplate = config.itemTemplate?org.iit.Widget.get(config.itemTemplate):new org.iit.Template({templateText: "<a class='item'>{this.makeBold(get(this.parent._nameField), this.search)}</a>"});
        this._itemTemplate.parent = this;
        
        this.value = null;
        this.text = "";

        this.store = org.iit.data.DataStore.get(config.store);
        this._nameField = config.nameField||"name";
        this._valueField = config.valueField||"id";

        if ( config.criteria ) {
            switch ( config.criteria ) {
                case "contains":
                    this._criteria = new org.iit.ContainsCriteria("name", "");
                    break;
                case "beginsWith":
                    this._criteria = new org.iit.BeginsWithCriteria("name", "");
                    break;
                default:
                    this._criteria = new org.iit.ContainsCriteria("name", "");
                    break;
            }
        } else {
            this._criteria = new org.iit.ContainsCriteria("name", "");
        }
        this._queryResultSet = [];
        this._comboItems = [];
        this._selectedIndex = -1;
        this._comboSize = {};
        this._elementSize = {};
        this._comboOpenedUp = false;
        this._queryDfd = null;
        this._shadow = null;

        this.initialize();
    },
    {
        initialize: function(){
            if ( framework.isDeclared("org.iit.BoxShadow") ) {
                this._shadow = new org.iit.BoxShadow();
            }
            var elements = this.createElements("<div class='suggest-input"
                    + (this._element.className!=""?" "+this._element.className:"") + "' id='"+this.id+"'"
                    + (this._element.style.cssText!=""?" style='"+this._element.style.cssText+"'":"") + ">\n"
                    + "<input type='hidden' name='"+this.name+"'/>\n"
                    + "<div class='arrow-button'></div>\n"
                    + "<input type='text' class='text-value' />\n"
                    + "</div>"
                    );
            
            this._element.style.display = "none";
            var e = this._element;
            this._element = e.parentNode.insertBefore(elements[0], e);
            e.parentNode.removeChild(e);

            this._combo = document.createElement("div");
            this._combo.className = "suggest-input-combo";
            this._combo.style.display = "none";
            document.body.appendChild(this._combo);
            
            this._input = framework.query("input[type='text']", this._element)[0];
            this._hiddenInput = framework.query("input[type='hidden']", this._element)[0];
            this._button = framework.el(framework.query("div.arrow-button", this._element)[0]);
            framework.event.addEventListener(this._button.element, "mouseover", framework.runInScope(this, function(){ this._button.addClass("over"); }));
            framework.event.addEventListener(this._button.element, "mouseout", framework.runInScope(this, function(){ this._button.removeClass("over"); }));
            framework.event.addEventListener(this._button.element, "click", framework.runInScope(this, function() {
                if ( !this._button.hasClass("wait") ) {
                    if ( this._comboVisible ){
                        this.hideCombo();
                    } else {
                        this._getData(1);
                    }
                }
            }));
            
            if ( !this._itemTemplate.makeBold ) {
                this._itemTemplate.makeBold = function(source, search) {
                    if ( search ) {
                        var r = new RegExp("("+search+")", "gi");
                        return source.replace(r, "<b>$1</b>");
                    } else {
                        return source;
                    }
                };
            }

            if ( this._itemTemplate._element ){
                this._itemTemplate._element.parentNode.removeChild(this._itemTemplate._element);
                this._itemTemplate._element = null;
            }
            
            switch ( e.tagName ) {
                case "SELECT":
                    if ( e.selectedIndex > -1 && e.options[e.selectedIndex].selected ) {
                        this._hiddenInput.value = this.value = e.options[e.selectedIndex].value;
                        this._input.value       = this.text  = e.options[e.selectedIndex].text;
                    }
                    break;
                case "INPUT":
                    if ( e.value != "" ){
                        this._hiddenInput.value = this.value = e.value; 
                        this._input.value =       this.text  = e.value;
                    }
                    break;
            }
            
            var hideFunc = framework.runInScope(this, function(){
                if ( this._comboVisible ){
                    this.hideCombo();
                    this._setValue();
                } else if ( this.canHaveCustomValue ) {
                    this._setValue();
                }
            });
            
            framework.event.addEventListener(this._element, "click", framework.runInScope(this, function(evnt) { framework.event.stopPropagation(evnt); }));
            framework.event.addEventListener(this._combo, "click", framework.runInScope(this, function(evnt) { framework.event.stopPropagation(evnt); }));
            framework.event.addEventListener(document, "click", framework.runInScope(this, function(evnt) { hideFunc(); }));

            framework.event.addEventListener(this._input, "keyup", framework.runInScope(this, this.onKeyUp));
            framework.event.addEventListener(this._input, "keypress", framework.runInScope(this, this.onKeyPress));
            if ( framework.isIE || framework.isWebKit ) {
                framework.event.addEventListener(this._element, "keydown", framework.runInScope(this, function(evnt) {
                    this.onKeyPress(evnt);
                }));
            }
            
            this.afterRender.add(framework.runInScope(this, function(){ this._recalcSize(); }));
        },
        _recalcSize: function() {
            this._elementSize.box = framework.dom.getBox(this._element);
            this._elementSize.b = framework.dom.getBorders(this._element);
            this._elementSize.p = framework.dom.getPaddings(this._element);
            
            var buttonSize = framework.dom.getBox(this._button.element);
            var inputBP = framework.dom.getBP(this._input);
            if ( this._elementSize.box.w ) {
                this._input.style.width = (this._elementSize.box.w-this._elementSize.p.w-this._elementSize.b.w-buttonSize.w-inputBP.w) + "px";
            }
        },
        onSetSize: function(w, h){
            org.iit.SuggestInput.superclass.onSetSize(w, h);
            this._recalcSize();
        },
        onKeyPress: function(evnt){
            //if ( window.console && console.log ) console.log("keyPress", evnt.keyCode, evnt);
            switch ( evnt.keyCode ) {
                case 13:
                    if ( this._comboVisible ){
                        this.setSelected(this._queryResultSet[this._selectedIndex]);
                        framework.event.preventDefault(evnt);
                        this.hideCombo();
                    }
                    break;
                case framework.keys.UP:
                    if ( this._comboVisible ){
                        if ( this._selectedIndex > 0 ){
                            this._comboItems[this._selectedIndex].removeClass("over");
                            this._selectedIndex--;
                            this._comboItems[this._selectedIndex].addClass("over");
                            this._itemScrollIntoView(this._selectedIndex);
                        }
                        framework.event.preventDefault(evnt);
                    }
                    break;
                case framework.keys.DOWN:
                    if ( this._comboVisible ){
                        if ( this._selectedIndex < this._comboItems.length-1 ){
                            if (this._selectedIndex >= 0) {
                                this._comboItems[this._selectedIndex].removeClass("over");
                            }
                            this._selectedIndex++;
                            this._comboItems[this._selectedIndex].addClass("over");
                            this._itemScrollIntoView(this._selectedIndex);
                        }
                    } else {
                        if( this._queryResultSet.length > 0 ) {
                            this.showCombo();
                            this._input.focus();
                        }
                    }
                    framework.event.preventDefault(evnt);
                break;
            }
        },
        onKeyUp: function(evnt){
            
            //if ( window.console && console.log ) console.log("keyUp", evnt.keyCode, evnt);

            switch ( evnt.keyCode ) {
                /*
                case 13:
                    if ( this._comboVisible ){
                        this._input.value = this._queryResultSet[this._selectedIndex].get(this._nameField);
                        this._hiddenInput.value = this._queryResultSet[this._selectedIndex].get(this._valueField);
                        framework.event.preventDefault(evnt);
                        this.hideCombo();
                    }
                    break;
                */
                case framework.keys.BACKSPACE:
                case framework.keys.DELETE:
                    if ( this._input.value.length > 0 ) {
                        this._search(this._input.value);
                    } else {
                        if ( this._comboVisible ){
                            this.hideCombo();
                        }
                    }
                    break;
                case 27:
                    if ( this._comboVisible ){
                        this.hideCombo();
                    }
                    this._setValue(true);
                    break;
                default:
                    var k = evnt.keyCode
                    if ( (k==32||k>=48&&k<=90||k>=96&&k<=111||k>=186&&k<=192||k>=219&&k<=222)
                            && this._input.value.length > 0 ){
                        if ( !(this._queryResultSet.length == 0 && this._criteria.getString() != "" && this._input.value.indexOf(this._criteria.getString()) != -1 ) ) {
                            this._search(this._input.value);
                        }
                    }
                    break;
            }
        },
        _search: function(str){
            if ( this._queryDfd ){
                var searchFunc = framework.runInScope(this, function(){ this._search(this._input.value); });
                if ( this._queryDfd.chain.length == 1 ){
                    this._queryDfd.addCallback(searchFunc);
                }
            } else {
                this._button.addClass("wait");
                this._criteria.setString(str);
                (this._queryDfd = this.store.search(this._criteria)).addCallback(framework.runInScope(this, this._handleSearch, str));
            }
        },
        _handleSearch: function(resultSet, searchStr){
            this._button.removeClass("wait");
            this._queryResultSet = resultSet;
            this._selectedIndex = -1;
            this._comboItems = [];
            this._combo.innerHTML = "";
            this._queryDfd = null;
            
            this._itemTemplate.search = searchStr;
            
            for ( var i = 0, l = resultSet.length; i < l; i++ ){
                this._comboItems.push(framework.el(this.createElements(this._itemTemplate.applyTemplate(resultSet[i]))[0]));
                this._combo.appendChild(this._comboItems[i].element);
                framework.event.addEventListener(this._comboItems[i].element, "mouseenter", framework.runInScope(this, function(evnt, index) {
                    this._comboItems[this._selectedIndex].removeClass("over");
                    this._comboItems[index].addClass("over");
                    this._selectedIndex = index;
                }, i));
                framework.event.addEventListener(this._comboItems[i].element, "click", framework.runInScope(this, function(evnt, index) {
                    this._selectedIndex = index;
                    this.setSelected(this._queryResultSet[index]);
                    this.hideCombo();
                }, i));
            }
            if ( resultSet.length > 0 ){
                this._selectedIndex = 0;
                this._comboItems[0].addClass("over");
                if ( !this._comboVisible ){
                    this.showCombo();
                } else {
                    this.adjustComboSize();
                }
            } else if ( this._comboVisible ) {
                this.hideCombo();
            }
        },
        _getData: function(page) {
            if ( !this.store.isLoaded()  ) {
                if ( this._queryDfd ){
                    var getDataFunc = framework.runInScope(this, function(){ this._getData(page); });
                    if ( this._queryDfd.chain.length == 1 ){
                        this._queryDfd.addCallback(getDataFunc);
                    }
                } else {
                    this._button.addClass("wait");
                    (this._queryDfd = this.store.load(page)).addCallback(framework.runInScope(this, this._handleData));
                }
            } else {
                if ( page && this.store.getAdapter() && this.store.getAdapter().supportPagination ) {
                    this._handleData(this.store.getPage(page));
                } else {
                    this._handleData(this.store.records);
                }
            }
        },
        _handleData: function(resultSet) {
            this._button.removeClass("wait");
            this._queryResultSet = resultSet;
            this._selectedIndex = -1;
            this._comboItems = [];
            this._combo.innerHTML = "";
            this._queryDfd = null;
            
            this._itemTemplate.search = "";
            
            for ( var i = 0, l = resultSet.length; i < l; i++ ){
                this._comboItems.push(framework.el(this.createElements(this._itemTemplate.applyTemplate(resultSet[i]))[0]));
                this._combo.appendChild(this._comboItems[i].element);
                framework.event.addEventListener(this._comboItems[i].element, "mouseenter", framework.runInScope(this, function(evnt, index) {
                    this._comboItems[this._selectedIndex].removeClass("over");
                    this._comboItems[index].addClass("over");
                    this._selectedIndex = index;
                }, i));
                framework.event.addEventListener(this._comboItems[i].element, "click", framework.runInScope(this, function(evnt, index) {
                    this._selectedIndex = index;
                    this.setSelected(this._queryResultSet[index]);
                    this.hideCombo();
                }, i));
            }
            if ( resultSet.haveToPaginate ) {
                if ( resultSet.currentPage != 1 ) {
                    var prevItems = framework.el(this.createElements("<div class='item'>Предыдущие</div>")[0]);
                    this._combo.insertBefore(prevItems.element, this._combo.firstChild);
                    framework.event.addEventListener(prevItems.element, "mouseenter", framework.runInScope(this, function(evnt) {
                        this._comboItems[this._selectedIndex].removeClass("over");
                        prevItems.addClass("over");
                    }));
                    framework.event.addEventListener(prevItems.element, "mouseleave", framework.runInScope(this, function(evnt) {
                        prevItems.removeClass("over");
                    }));
                    framework.event.addEventListener(prevItems.element, "click", framework.runInScope(this, function(evnt) {
                        this._getData(resultSet.currentPage-1);
                    }));
                }
                if ( resultSet.currentPage != resultSet.lastPage ) {
                    var nextItems = framework.el(this.createElements("<div class='item'>Следующие</div>")[0]);
                    this._combo.appendChild(nextItems.element);
                    framework.event.addEventListener(nextItems.element, "mouseenter", framework.runInScope(this, function(evnt) {
                        this._comboItems[this._selectedIndex].removeClass("over");
                        nextItems.addClass("over");
                    }));
                    framework.event.addEventListener(nextItems.element, "mouseleave", framework.runInScope(this, function(evnt) {
                        nextItems.removeClass("over");
                    }));
                    framework.event.addEventListener(nextItems.element, "click", framework.runInScope(this, function(evnt) {
                        this._getData(resultSet.currentPage+1);
                    }));
                }
            }
            if ( resultSet.length > 0 ){
                this._selectedIndex = 0;
                this._comboItems[0].addClass("over");
                if ( !this._comboVisible ){
                    this.showCombo();
                } else {
                    this.adjustComboSize();
                }
            } else if ( this._comboVisible ) {
                this.hideCombo();
            }
        },
        showCombo: function() {
            if ( this._comboVisible ) return;
            this._elementSize.box = framework.dom.getBox(this._element);
            var box = this._elementSize.box;
            var v = framework.dom.getViewport();
            this._combo.style.display = "block";
            this._combo.style.width = "auto";
            this._comboVisible = true;
            
            this._comboSize.box = framework.dom.getBox(this._combo);
            this._comboSize.p   = framework.dom.getPaddings(this._combo);
            this._comboSize.b   = framework.dom.getBorders(this._combo);
            
            if ( box.t+box.h+this._comboMaxHeight < v.scrollTop + v.height ) {//down
                this._combo.style.top = (box.t+box.h) + "px";
                this._comboOpenedUp = false;
            } else if ( box.t-this._comboMaxHeight > v.scrollTop ) {//up
                this._comboOpenedUp = true;
            } else {//down
                this._combo.style.top = (box.t+box.h) + "px";
                this._comboOpenedUp = false;
            }
            this._combo.style.left = box.l + "px";
            this._combo.style.width = "auto";
            /*
            if ( this._combo.scrollWidth > this._combo.offsetWidth ) {
                this._combo.style.width = "auto";
            } else {
                this._combo.style.width = (box.w-this._comboSize.p.w-this._comboSize.b.w) + "px";
            }
            */
            this.adjustComboSize();
            this._itemScrollIntoView(this._selectedIndex);
        },
        hideCombo: function(){
            if ( !this._comboVisible ) return;
            if ( this._shadow ) {
                this._shadow.display(false);
            }
            this._comboVisible = false;
            this._combo.style.display = "none";
        },
        adjustComboSize: function() {
            var p = this._comboSize.p;
            var b = this._comboSize.b;
            var height;
            if ( this._combo.scrollHeight+this._comboSize.b.h == this._combo.offsetHeight ){
                this._combo.style.height = "auto";
            }
            if ( this._combo.scrollHeight > this._comboMaxHeight-b.h ) {
                this._combo.style.height = ((height = this._comboMaxHeight)-b.h-p.h) + "px";
            } else {
                this._combo.style.height = ((height = this._combo.scrollHeight)-p.h) + "px";
                height += b.h;
            }
            if ( this._comboOpenedUp ){
                this._combo.style.top = (this._elementSize.box.t - height) + "px"; 
            }
            if ( this._combo.offsetWidth < this._elementSize.box.w ) {
                this._combo.style.width = (this._elementSize.box.w-this._comboSize.p.w-this._comboSize.b.w) + "px";
            }
            if ( this._shadow ) {
                this._shadow.setBox(this._combo);
                this._shadow.display(true);
            }
        },
        _itemScrollIntoView: function(index){
            var el = this._comboItems[index].element, h; 
            if ( el.offsetTop < this._combo.scrollTop ) {
                this._combo.scrollTop = el.offsetTop;
            } else if ( el.offsetTop + el.offsetHeight > this._combo.scrollTop + (h = this._combo.offsetHeight-this._comboSize.p.h-this._comboSize.b.h) ) {
                this._combo.scrollTop = el.offsetTop + el.offsetHeight - h;
            }
        },
        _setValue: function(old) {
            if ( this.canHaveCustomValue ) {
                if ( old ) {
                    this._hiddenInput.value = this.value;
                    this._input.value       = this.text;
                } else {
                    this._hiddenInput.value = this.value = this.text = this._input.value;
                }
            } else {
                this._hiddenInput.value = this.value;
                this._input.value = this.text;
            }
        },
        setSelected: function(record) {
            this._input.value = this.text = record.get(this._nameField);
            this._hiddenInput.value = this.value = record.get(this._valueField);
        }
    },
    {
        createFromHtml: function(element){
            var config = {
                id: element.id,
                element: element,
                store: element.getAttributeNode("store").value
            };
            var attr;
            if ( attr = element.getAttributeNode("comboMaxHeight") ) config.comboMaxHeight = Number(attr.value);
            if ( attr = element.getAttributeNode("itemTemplate") ) config.itemTemplate = attr.value;
            if ( attr = element.getAttributeNode("nameField") ) config.nameField = attr.value;
            if ( attr = element.getAttributeNode("valueField") ) config.valueField = attr.value;
            if ( attr = element.getAttributeNode("name") ) config.name = attr.value;
            if ( attr = element.getAttributeNode("canHaveCustomValue") ) config.canHaveCustomValue = String(attr.value).toLowerCase() == "true";
            if ( attr = element.getAttributeNode("criteria") ) config.criteria = attr.value.toLowerCase();
            var input = new org.iit.SuggestInput(config);
            return input;
        }
    }
);


}
