﻿//+
//- DropDownTextBox -//
if (typeof Instinct == 'undefined') { Instinct = {}; }
Instinct.DropDownTextBox = Class.create({
    //- main -//
    initialize: function(init) {
        this.minCharacterCount = 3;
        this.maxRecords = -1;
        this.maxHeight = 200;
        if (init) {
            if (init.textBoxId) {
                this.textBoxId = init.textBoxId;
                if (!$(this.textBoxId)) {
                    throw 'TextBox (input) with ID ' + this.textBoxId + ' was not found.';
                }
            } else {
                throw 'textBoxId is required.';
            }
            if (init.endpointId) {
                this.endpointId = init.endpointId;
            } else {  
                throw 'endpointId is required.';
            }
            if (init.minCharacterCount) {
                this.minCharacterCount = init.minCharacterCount;
            }
            if (init.maxRecords) {
                this.maxRecords = init.maxRecords;
            }
            if (init.maxHeight) {
                this.maxHeight = init.maxHeight;
            }
        }
        else {
            throw 'init is required.';
        }
        this.DOMElement = $(this.textBoxId);
        //+ wire events
        this._cachedRouteKeyEvent = this._routeKeyEvent.bind(this);
        this._cachedHideData = this._hideData.bind(this);
        Event.observe(this.DOMElement, 'keyup', this._beginUpdateListData.bind(this));
    },
    
    //- beginUpdateListData -//
    _beginUpdateListData: function(evt) {
        if (evt) {
            if ((evt.keyCode == Event.KEY_ESC)
                || (evt.keyCode == Event.KEY_DOWN)
                || (evt.keyCode == Event.KEY_UP)
                || (evt.keyCode == Event.KEY_TAB)) {
                return;
            }
        }
        if ($F(this.textBoxId).length >= this.minCharacterCount) {
            if (!!this.timerStarted == false) {
                if (this.timerId) {
                    clearTimeout(this.timerId);
                }
                //+
                this.timerId = setTimeout((function() {
                    this._updateListData();
                }).bind(this), 250);
            };
        } else {
            this._hideData();
        }
    },
    
    //- _updateListData -//
    _updateListData: function() {
        document.stopObserving('keyup', this._cachedRouteKeyEvent);
        //+ for use with the callbackendpoint control
        var endpoint = window.registeredEndpointList[this.endpointId];
        if (endpoint) {
            var query = $F(this.textBoxId);
            endpoint.executeContract(this._showData.bind(this), 'Hash~cQuery', query);
        }
    },
    
    //- _hideData -//
    _hideData: function() {
        if (this.box) {
            this.box.setStyle({
                visibility: 'hidden'
            });
            this.box.childElements().invoke('remove');
        }
        document.stopObserving('click', this._cachedHideData);
    },
    
    //- _showData -//
    _showData: function(data, context) {
        if (!this.box) {
            var dimensions = this.DOMElement.getDimensions();
            this.box = new Element('div').setStyle({
                position: 'absolute',
                overflow: 'auto',
                backgroundColor: '#F2F2FA',
                border: '1px solid #333',
                width: dimensions.width + 'px',
                maxHeight: this.maxHeight + 'px'
            });
            document.getElementsByTagName('body')[0].appendChild(this.box);
        }
        //+
        this._setBoxPosition();
        this._drawData(data);
        this._wireEvents();
    },
    
    //- _setBoxPosition -//
    _setBoxPosition: function() {
        var offset = this.DOMElement.cumulativeOffset();
        var dimensions = this.DOMElement.getDimensions();
        this.box.setStyle({
            left: offset.left + 'px',
            top: (offset.top + dimensions.height) + 'px'
        });
    },
    
    //- _drawData -//
    _drawData: function(response) {
        //+ remove from envelope and remove unicode serialization artifact
        eval('response=' + response);
        var data=(response.data || {}).replace('\\u0002','\\2');
        //+
        this.box.childElements().invoke('remove');
        var sections = data.split('\2');
        if (sections.length == 2 && parseInt(sections[1]) > 0) {
            this.box.insert(this.dataList = new Element('ul').setStyle({
                listStyleType: 'none',
                padding: 0,
                margin: 0
            }));
            this.box.scrollTop = 0;
            this._totalRowCount = parseInt(sections[1]);
            data = sections[0].split(',');
            this._displayRowCount = data.length;
            if (this._displayRowCount > 0) {
                data.each((function(p, i) {
                    var dataParts = p.split('|');
                    var li;
                    this.dataList.insert(li = new Element('li')
                        .addClassName(i % 2 == 0 ? 'dropDownTextBoxListItem' : 'dropDownTextBoxListItemAlternate')
                        .setStyle({
	                        margin: 0,
	                        cursor: 'pointer'
                        })
                        .update(dataParts[1])
                        .observe('click', (function() {
                            $(this.textBoxId).value = li.innerHTML
                            this._hideData();
                        }).bind(this))
                        .observe('mouseover', (function(evt) {
                            this.rowPointer = li.index;
                            this._updatePointedRow();
                        }).bind(this))
                    );
                    li.key = dataParts[0];
                    li.index = i;
                    li.id = 'listItem' + i;
                }).bind(this));
            }
            //+ is only a subset of data shown?
            if (this._totalRowCount > this._displayRowCount) {
                var diff = this._totalRowCount - this._displayRowCount;
                this.box.insert(new Element('div')
                    .setStyle({
                        fontWeight: 'bold'
                    })
                    .update('There are #{count} more record#{plural}.'.interpolate({
                        count: diff,
                        plural: diff > 1 ? 's' : ''
                    }))
                );
            }
            //+
            this.rowPointer = 0;
        } else {
            this.box.insert(new Element('h4').update('No data returned.'));
        }
        this._updatePointedRow();
        this.box.setStyle({
            visibility: 'visible'
        });
    },

    //- _wireEvents -//
    _wireEvents: function() {
        document.stopObserving('click', this._cachedHideData);
        document.stopObserving('keyup', this._cachedRouteKeyEvent);
        //+
        document.observe('keyup', this._cachedRouteKeyEvent);
        document.observe('click', this._cachedHideData);
        //+
        Event.observe(window, 'resize', this._setBoxPosition.bind(this));
    },

    //- _routeKeyEvent -//
    _routeKeyEvent: function(evt) {
        Event.stop(evt);
        if (evt.keyCode == Event.KEY_DOWN) {
            if (this.rowPointer < this._displayRowCount - 1) {
                this.rowPointer++;
            }
            this._updatePointedRow();
        } else if (evt.keyCode == Event.KEY_UP) {
            if (this.rowPointer > 0) {
                this.rowPointer--;
            }
            this._updatePointedRow();
        } else if ((evt.keyCode == Event.KEY_TAB) || (evt.keyCode == Event.KEY_RETURN)) {
            var li = $('listItem' + this.rowPointer);
            if (li) {
                $(this.textBoxId).value = li.innerHTML;
            }
            this._hideData();
        }
    },

    //- _updatePointedRow -//
    _updatePointedRow: function() {
        if (this.dataList) {
            this.dataList.select('li.dropDownTextBoxListItemSelected').invoke('removeClassName', 'dropDownTextBoxListItemSelected');
            //+
            var li = $('listItem' + this.rowPointer);
            if (li) {
                //+
                var boxDimensions = this.box.getDimensions();
                var boxOffset = this.box.cumulativeOffset();
                var liDimensions = li.getDimensions();
                var liOffset = li.cumulativeOffset();
                //+ debug
                /*
                var buffer = new Intranet.Trace.Buffer();
                buffer.write({
                    'liOffset.top': liOffset.top,
                    'liOffset.height': liOffset.height,
                    'boxDimensions.height': boxDimensions.height,
                    'this.box.scrollTop': this.box.scrollTop,
                    'lT + lH': liOffset.top + liDimensions.height,
                    'bT + bH + sT': boxOffset.top + boxDimensions.height + this.box.scrollTop
                });
                buffer.flush();*/
                //+
                if ((liOffset.top + liDimensions.height) > (boxOffset.top + boxDimensions.height + this.box.scrollTop)) {
                    var top = liOffset.top - (boxOffset.top + boxDimensions.height + this.box.scrollTop);
                    this.box.scrollTop += liDimensions.height;
                }
                if (liOffset.top < (boxOffset.top + this.box.scrollTop)) {
                    var top = liOffset.top - (boxOffset.top / 2);
                    this.box.scrollTop -= liDimensions.height;
                }
                li.addClassName('dropDownTextBoxListItemSelected');
            }
        }
    }
});