// --- mosaic.inputs.combobox ---
// version of june 17, 2011

// later

// todo: использовать mosaic.widgets.suggest

// todo: dropdown button / http://wiki.kirkazan.ru/pages/viewpage.action?pageId=42043895
// todo: opera: неправильная вертикальная позиция текста в background
// todo: IE8: неправильная вертикальная позиция текста в background
// todo: IE8: текст не выделяется при клике
// todo: IE7: неправильная вертикальная позиция текста в background
// todo: IE7: текст не выделяется при клике


(function($) {

    // --- PRE-PHASE ---

    if (!window.mosaic) { throw('global object [mosaic] not found'); }
    if (!window.mosaic.widgets) { throw('global object [mosaic.widgets] not found'); }
    if (!window.mosaic.widgets.dropdown) { throw('global object [mosaic.widgets.dropdown] not found'); }
    if (!window.mosaic.inputs) { throw('global object [mosaic.inputs] not found'); }

    if (window.mosaic.inputs.combobox) { return false; }

    // --- end of PRE-PHASE




    // --- PRIVATE GLOBAL FIELDS ---
    // any private field should start from "_" prefix
        //var

            // end of PRIVATE GLOBAL FIELDS ---
            //;




    // --- PSEUDO-CONSTRUCTOR ---

    // idNamespace points to:
    //      - id/name of the central DOM element of control
    //      - any DOM element inside control
    //      - any jQuery-object inside control
    window.mosaic.inputs.combobox = function(idCombobox) {
        var
                _context        = {}
            ,   _constructor    = function() {  }
            ,   _self
            ;

        // saving id
        _context.idCombobox = idCombobox;

        // search of container for control
        // any jQuery-object fields should start from "$" prefix
        _context.$container     = _container(idCombobox); if (!_context.$container) { return null; }

        _context.$value         = _context.$container.find('.mosaic-inputs-combobox-value');
        _context.$editable      = _context.$container.find('.mosaic-inputs-combobox-editable-area');
        _context.$background    = _context.$container.find('.mosaic-inputs-combobox-editable-area-background');
        _context.$hidden        = _context.$container.find('input[type=hidden]');
        _context.$edit          = _context.$container.find('input[type=text]');
        _context.$btn           = _context.$container.find('.mosaic-inputs-combobox-button');
        _context.dropdown       = mosaic.widgets.dropdown(_dropdownBoxId(idCombobox));

        // create object using fake function
        _constructor.prototype  = _proto;
        _self                   = new _constructor();

        // create function returning context inside created object
        _self.context = function() {
            return _context;
        };

        // finish pseudo-constructor
        return _self;
    };

    // --- end of PSEUDO-CONSTRUCTOR ---




    // --- SHORTCUT ---

    var
            combobox = window.mosaic.inputs.combobox

        // --- end of SHORTCUT ---
        ;




    // --- PUBLIC LOCAL METHODS  ---
    var _proto = {

            // must be invoked only at first time!
            // this method should run any initialization
            // required at the first time
            // like event handling
            apply: function(settings) {
                var
                        context = this.context()
                    ;
                
                // apply settings to the element
                _settings(this, settings);

                // default dropdown settings
                context.dropdown.settings({
                        'filter': function(selfDropdown, handler) {
                            inside.core.js.executeSafe(
                                    _settings(this).filter
                                ,   function(items) { handler(items); }
                            )(this, this.context().$edit.val());
                        }.bind(this)
                    ,   'onselect': function(selfDropdown, label, data) {
                            _select(this, label, data);
                        }.bind(this)
                    ,   'oncancel': function(selfDropdown) {
                            _hide(this);
                        }.bind(this)
                    ,   'onfocus': function(selfDropdown, $liChosen) {
                            _tryFocus(this, $liChosen);
                        }.bind(this)
                    ,   'preventHideOnClick': function(selfDropdown, e) {
                            return $(e.target).closest('.mosaic-inputs-combobox-container').size() > 0
                        }.bind(this)
                    ,   'getOffset': function(selfDropdown) {
                            return _getOffset(this);
                        }.bind(this)
                    ,   'width': context.$container.outerWidth() + 'px'
                });

                context.$value.click(function(e) {
                    if (_hidden(this)) { _show(this); }
                }.bind(this));
                context.$btn.keypress(function(e) {
                    _checkExitByTab(this, e);
                }.bind(this));
                context.$btn.click(function() {
                    if (_hidden(this)) { _show(this); }
                }.bind(this));
                $(function() {
                    KIR.events(context.$edit).typing(function(e, namespace, params) {
                        context.dropdown.call();
                    }.bind(this)).change(function(e, namespace, params) {
                        _checkBackgroundMatchWithInput(this);
                    }.bind(this));
                }.bind(this));

                return this;
            } // end of apply

            // shortcut to private "_settings"
        ,   settings: function(settings) {
                return _settings(this, settings);
            } // end of settings

            // ANY OTHER METHODS HERE

    }; // --- end of PUBLIC LOCAL METHODS  ---




    // --- PRIVATE CONSTS ---
    var
            // key for storing settings
            _STATE_SETTINGS             = 'state-namespace-settings'

            // class for value under edit
        ,   _EDITABLE_CLASSNAME         = 'mosaic-inputs-combobox-editable-area-active'

            // class for value when no text inside
        ,   _VALUE_HINT_CLASSNAME       = 'mosaic-inputs-combobox-value-hint'

            // hint
        ,   _SETTINGS_HINT              = 'hint'

        // --- end of PRIVATE CONSTS ---
        ;




    // --- PRIVATE GLOBAL METHODS ---
    var
            // find container for given namespace
            // finds object with id/name=idNamespace and then
            // its closest parent with selector = ".container-for-idNamespace-id-object"
            _container = function(idCombobox) {
                return inside.core.util.object(idCombobox, '.mosaic-inputs-combobox-container')
            } // end of container

            // returns id for dropdown box attached to input
        ,   _dropdownBoxId = function(idCombobox) {
                return _container(idCombobox).find('input[type=hidden]').attr('name') + 'DropdownBox';
            }

        ; // --- end of PRIVATE GLOBAL METHODS ---




    // --- PRIVATE LOCAL METHODS ---

    var
            _settings = function(self, settings) {
                var
                        _settings = _stateSettings(self)
                    ;
                _settings = 'undefined' != typeof _settings ? _settings : {};

                if (settings) {
                    for (var key in settings) {
                        _settings[key] = settings[key];

                        if (_SETTINGS_HINT == key) {
                            _hint(self, settings[key]);
                        }
                    }

                    _stateSettings(self, _settings);
                }

                return _settings;
            } // end of _settings

            // method for retrieving or setting inner property of namespace
            // binded to concrete instance on the page
        ,   _state = function(self, name, value) {
                if (undefined == value) { return self.context().$container.data(name); }
                    else { self.context().$container.data(name, value); }
            } // end of _state

            // method for setting or retrieving settings of namespace
        ,   _stateSettings = function(self, settingsValue) {
                return _state(self, _STATE_SETTINGS, settingsValue);
            } // end of _state_settings

        ,   _show = function(self) {
                var
                        context = self.context()
                    ,   input = context.$edit.get(0)
                    ;

                context.$editable.addClass(_EDITABLE_CLASSNAME);

                _toggleHint(self);

                context.$edit.focus().val(context.$value.html());
                context.$background.html(context.$value.html());

                // select all the value by text range
                if (input.createTextRange) {
                    var
                            range = input.createTextRange()
                        ;
                    range.collapse(false);
                    range.select();
                } else if (input.selectionStart) {
                    input.setSelectionRange(0, context.$edit.val().length);
                }

                context.dropdown.call();
            }

        ,   _hide = function(self) {
                self.context().$editable.removeClass(_EDITABLE_CLASSNAME);

                _toggleHint(self);
            }

        ,   _shown = function(self) {
                return self.context().$editable.hasClass(_EDITABLE_CLASSNAME);
            }

        ,   _hidden = function(self) {
                return !self.context().$editable.hasClass(_EDITABLE_CLASSNAME);
            }

        ,   _select = function(self, label, data) {
                self.context().$value.html(label);
                self.context().$hidden.val(data.value);

                _hide(self);

                inside.core.js.executeSafe(_settings(self).onselect)(
                        self
                    ,   label
                    ,   data
                );
            }

        ,   _checkExitByTab = function(self, e) {
                if (inside.code.KEY_TAB == e.keyCode) {
                    self.context().dropdown.cancel();
                }
            }

        ,   _checkBackgroundMatchWithInput = function(self) {
                if (0 != self.context().$background.html().indexOf(self.context().$edit.val())) {
                    self.context().$background.html('');
                }
            }

        ,   _getOffset = function(self) {
                var
                        offset = self.context().$container.offset()
                    ;
                offset.top += self.context().$container.outerHeight();

                return offset;
            }

        ,   _tryFocus = function(self, $liChosen) {
                if (!$liChosen.size()) { return false; }

                self.context().$background.html($liChosen.find('a').html());
            }

        ,   _toggleHint = function(self) {
                // if value is the same as the hint
                // then clear value and remove hint class
                if (_settings(self)[_SETTINGS_HINT] == self.context().$value.html()) {
                    self.context().$value
                            .html('')
                            .removeClass(_VALUE_HINT_CLASSNAME);
                } else if (_settings(self)[_SETTINGS_HINT] && !self.context().$value.html().length) {
                    // if we have the hint and no value applied
                    // then show hint
                    self.context().$value
                            .html(_settings(self)[_SETTINGS_HINT])
                            .addClass(_VALUE_HINT_CLASSNAME);
                }
            }

        // --- end of PRIVATE LOCAL METHODS ---
        ;




    // --- SETTINGS ---

    var
            _hint = function(self, value) {
                var
                        oldHint = _settings(self)[_SETTINGS_HINT] ? _settings(self)[_SETTINGS_HINT] : ''
                    ;

                // if hint is not present in input field, make no changes
                if (oldHint != self.context().$value.html()) { return undefined; }

                // if new hint is empty, clear old hint
                if ('undefined' == typeof value || '' == value) {
                    self.context().$value
                            .removeClass(_VALUE_HINT_CLASSNAME);
                }

                // if new hint is not empty, put classname
                if ('string' == typeof value && '' != value) {
                    self.context().$value
                            .addClass(_VALUE_HINT_CLASSNAME);
                }

                // put new hint
                self.context().$value.html(value);
            }

        // --- end of SETTINGS ---
        ;

    // --- GLOBAL EVENT HANDLERS ---

    $(document).click(function(e) {
        // place your code for event handling like this
    });

    // --- end of GLOBAL EVENT HANDLERS ---

})(jQuery);
