/** 
 * INFORMATION
 * -----------
 * Author: Zaenal Muttaqin - http://blog.lokamaya.net
 * Class: AutoField [autoTextField] for Prototype 1.6.0
 * Purpose: Facebook like auto-textfield
 *
 * BASE ON: 
 * - AutoComplete by Antonio Ramirez - http://webeaters.blogspot.com (GMail like)
 * - Referenced from AutoSuggest by Timothy Groves - http://www.brandspankingnew.net
 *
 * Released for free
*/

var AutoField = Class.create();

AutoField.prototype = {
    Version: '1.3.1',
    REQUIRED_PROTOTYPE: '1.6.0',
    openContainer:null,
    openTimeout:null,
    ajaxTimeout:null,
    Options: {
        valueSeparator: ';',
        minchars:1,
        method:"get",
        className:"autofield",
        timeout:3000,
        delay:100,
        shownoresults: true,
        noresults: "No results were found.",
        cache: true,
        maxentries: 25,
        onAjaxError:null,
        setWidth: false,
        minWidth: 100,
        maxWidth: 300,
        maxHeight: 250,
        offsetField:4, //for auto width field: calcculate padding, border, margin
        offsetW:4, //for auto width field: calcculate padding, border, margin
        offsetY:0,
        offsetX:0,
        script: null,
        scriptParam: null
    },
    
    initialize: function (field, options) {
        this.field = $(field);
        this.primeField = null;
        if (!this.field) {
            throw("AutoComplete requires a field id to initialize");
            return;
        }
        this.fieldName = this.field.name || field;
        // Init variables
        this.fieldValue = "";
        this.fieldValueText = [];
        this.fieldValueId = [];
        this.divContainer = null;
        this.fieldValueLength = 0;
        this.autoSuggest = [];
        this.currentSuggest = [];
        this.highlighted = 0;
        this.highlightedLast = 0;
        this.mouseOvered = false;

        var Options = this.Options;
        new Hash(options || {}).each(function(pair) {Options[pair.key] = pair.value;});
        this.options = Options;

        this.initializeField();
    },
    
    initializeField: function() {
        var that = this;
        this.divSelectOption = new Element('div', {id:this.fieldName+'_selectlist'}).addClassName(this.options.className+'_selectlist').setStyle({position:'absolute',overflow:'auto','max-height':this.options.maxHeight+'px',maxHeight:this.options.maxHeight+'px'}).hide();
        document.getElementsByTagName("body")[0].appendChild(this.divSelectOption);
        Event.observe(this.divSelectOption, 'mouseover', function(){that.killTimeout()});
        Event.observe(this.divSelectOption, 'mouseout', function(){that.resetTimeout(true)});
        
        this.divContainer = new Element('div', {id:this.fieldName+'_container', style:"float:left"}).addClassName(this.options.className+'_container').addClassName(this.field.className);
        this.field.insert({before:this.divContainer}).insert({after:'<div style="clear:left; width:1px; height:1px; font-size:1px; line-height:1px; padding:0px; margin:0px; margin-top:-5px;"></div>'});
        this.divContainer.insert('<label for="_'+this.field.id+'" id="'+this.fieldName+'_holder"></label><div style="float:left"><input type="text" name="_'+this.fieldName+'" id="_'+this.fieldName+'" class="'+this.options.className+'" /></div>');
        this.field.setStyle({float:'left', width:'1px', height:'1px', visibility:'hidden', border:'none', background:'transparent', position:'absolute'});
        this.primeField = this.field;
        this.primeField.className = '';
        this.field = $('_'+this.fieldName);
        
        Event.observe(this.field, 'keydown', function(e){that.onKeyPressed(e);});
        Event.observe(this.field, 'keyup', function(e){that.onKeyUped(e);});
        Event.observe(this.field, 'blur', function(){that.onBlured();});
        this.field.setAttribute("AutoComplete","off");
        this.field.addClassName(this.options.className+'_notifier');
        try {
            this.field.up('form').select('label[for="'+this.fieldName+'"]').each(function(item) {item.writeAttribute('for', that.field.id);});
        } catch(e){};

        //that.autoWidthField();
        //Event.observe(window, 'load', function() {that.autoWidthField();});
        that.autoWidthField();
        if (this.field.getDimensions().width < 1) {
            this.field.style.width = this.options.maxWidth + 'px';
        }
    },
    
    autoWidthField: function() {
        this.field.style.width = this.options.minWidth + 'px';
        var posField  = this.field.positionedOffset();
        var posHolder = this.divContainer.positionedOffset();
        var dimHolder = this.divContainer.getDimensions();
        this.field.style.width = (dimHolder.width - (posField.left - posHolder.left)) - this.options.offsetField + 'px';
    },
    
    createList:	function(arr) {
        this.killTimeout();
        this.currentSuggest = [];
        // if no results, and showNoResults is false, do nothing
        if (arr.length == 0 && !this.options.shownoresults) return false;
        
        if (this.openSelectOption) {
            $(this.openSelectOption).hide();
            this.openSelectOption = null;
        }
        this.divSelectOption.hide().update('');
        
        var ul	= new Element('ul').setStyle({position:'relative'}); // create and populate ul
        this.divSelectOption.appendChild(ul);
        
        var that 	= this;
        var j = 0;
        var regReplace = new RegExp("("+this.field.value+")", "i");
        for (var i=0,l = arr.length; i<l; i++) {
            if (this.fieldValueId.indexOf(arr[i].id+'') < 0) {
                var val 	= arr[i].value;
                if (regReplace.test(val)) {
                    j++;
                    this.currentSuggest.push(arr[i]);
                    val = '<span>' + val.sub(regReplace, function(match) {return '<em>' + match[1] + '</em>';}) + '<br /><small>'+arr[i].info+'</small></span>';
                    var a 	= new Element('a',{href:'#', name: (j)}).update(val).observe('click',function(e){that.getHighlightedValue();Event.stop(e);}).observe('mouseover',function(e){that.setHighlightOver(this);Event.stop(e);});
                    ul.appendChild(new Element('li').addClassName(this.options.className+'_default').insert(a));
                }
            }
        }
        // no results?
        if (j == 0 && this.options.shownoresults) ul.appendChild(new Element('li').addClassName(this.options.className+'_warning').update(this.options.noresults));
        
        this.showContainer();
    },
    
    
    // set responses to keypress events in the field
    // ESCAPE, RETURN, UP/DOWN move around the list
    
    onKeyPressed: function (e) {
        if (!e) e = window.event;
        var key	= e.keyCode || e.wich;
        //alert(key);
        switch(key) { 
        case Event.KEY_RETURN:
            if (this.highlighted > 0) {
                this.getHighlightedValue();
            } else {
                this.getSuggestions();
            }
            Event.stop(e);
            break;
        case Event.KEY_ESC:
            if (this.highlighted > 0) {
                this.clearSuggestions(0.05);
            }
            Event.stop(e);
            break;
        case Event.KEY_TAB:
            if (this.highlighted > 0) {
                this.setHighlight(this.highlighted +1);
                Event.stop(e);
            }
            break;
        case Event.KEY_HOME:
        case Event.KEY_END:
        case Event.KEY_UP:
        case Event.KEY_DOWN:
            if (this.highlighted > 0) {
                var n = this.highlighted;
                if (key == Event.KEY_END) n = -1;
                else if (key == Event.KEY_UP) {n = this.highlighted -1; if (n==0) n = -1}
                else if (key == Event.KEY_DOWN) n = this.highlighted +1;
                else n = 1;
                this.setHighlight(n);
            } else {
                this.getSuggestions();
            }
            Event.stop(e);
            break;
        }
        return true;
    },
    
    onKeyUped: function (e) {
        if (!e) e = window.event;
        var key = e.keyCode || e.wich;
        switch(key) { 
        case Event.KEY_RETURN:
            Event.stop(e);
            break;
        case Event.KEY_TAB:
            if (this.highlighted > 0) {
                //this.setHighlight(this.highlighted +1);
                Event.stop(e);
            }
            break;
        case Event.KEY_ESC:
        case Event.KEY_HOME:
        case Event.KEY_END:
        case Event.KEY_UP:
        case Event.KEY_DOWN:
            break;
        default:
            this.getSuggestions();
            break;
        }
        return true;
    },
    
    onBlured: function() {
        if (!this.mouseOvered) {
            //this.field.value = '';
            this.clearSuggestions();
        }
    },
    
    showContainer: function() {
        var pos = this.divContainer.cumulativeOffset();
        var dim = this.divContainer.getDimensions();
        this.divSelectOption.style.left 	= pos.left + this.options.offsetX + "px";
        this.divSelectOption.style.top = pos.top + dim.height + this.options.offsetY + "px";
        this.divSelectOption.style.width = dim.width - this.options.offsetW + "px";

        this.openSelectOption = this.divSelectOption;
        this.resetTimeout();
        this.divSelectOption.show();
        this.highlighted = 0;
        this.setHighlight(this.highlighted);
        //this.divSelectOption.scrollTop = 0;
    },
    
    getLastInput: function(str) {
        return str.strip();
    },
    
    doAjaxRequest: function () {
        if(!this.options.script) return alert('You did not provide any url/script for Ajax request!');
        
        var url = this.options.script;
        var param = {};
        if (typeof this.options.script == 'function') {
            url = this.options.script(encodeURIComponent(this.fieldValue), this.options.scriptParam);
        } else {
            param = (this.options.scriptParam ? this.options.scriptParam : '').toQueryParams();
            param[this.fieldName] = this.fieldValue;
        }
        
        var that = this;
        var options = {
            method: that.options.method, 
            parameters: param,
            onSuccess: function (req) {that.ajaxRequestSuccess(req, that)},
            onFailure: function (status) {that.ajaxRequestSuccess(status, that)}
        }
        // make new ajax request
        new Ajax.Request(that.options.script, options);
        this.field.addClassName(this.options.className+'_notifier_busy');
    },
    
    ajaxRequestSuccess: function(req, p) {
        p.field.removeClassName(this.options.className+'_notifier_busy');
        p.setSuggestions(req);
    },
    
    ajaxRequestError: function(status, p) {
        p.field.removeClassName(this.options.className+'_notifier_busy');
        if(typeof p.options.onAjaxError == 'function') {
            p.options.onAjaxError(status);
        } else {
            alert("AJAX error: "+status);
        }
    },
    
    getSuggestions: function(force) {
        var val = this.getLastInput(this.field.value);
        this.fieldValueLength = val.length;
        
        if (this.fieldValueLength < this.options.minchars) {
            this.clearSuggestions(0.1);
            return false;
        }
        if (val.startsWith(this.fieldValue) && (this.autoSuggest.length > 0 || force)) {
            //force = to avoid hit to the server
            this.createList(this.autoSuggest);
            return true;
        }
        this.fieldValue = val;
        var that = this;
        if (this.ajaxTimeout) clearTimeout(this.ajaxTimeout); // ajax id timer
        this.ajaxTimeout = setTimeout( function () {that.doAjaxRequest()}, that.options.delay);

        document.helper = this;
        return true;
    },
    
    setSuggestions: function (req) {
        this.autoSuggest = []
        this.currentSuggest = [];
        this.ajaxTimeout = null;
        if(this.options.json) {
            var jsondata = eval('(' + req.responseText + ')');
            this.autoSuggest = jsondata.results;
        } else {
            var results = req.responseXML.getElementsByTagName('results')[0].childNodes;
            for(var i=0;i<results.length;i++) {
                if(results[i].hasChildNodes())
                    this.autoSuggest.push(  { 'id':results[i].getAttribute('id'), 'value':results[i].childNodes[0].nodeValue, 'info':results[i].getAttribute('info') }  );
            }
        }
        this.highlighted = 0;
        if (this.fieldValue == this.getLastInput(this.field.value)) {
            return this.createList(this.autoSuggest);
        } else {
            this.getSuggestions(1);
        }
    },
    
    setHighlightOver: function(obj) {
        this.mouseOvered = true;
        return this.setHighlight((obj.name * 1), this.mouseOvered);
    },
    
    setHighlight: function(n, noScroll) {
        if (!noScroll) this.mouseOvered = false;
        if (this.currentSuggest.length < 1) return;
        
        if (n==0) n = 1;
        else if (n<0) n = this.currentSuggest.length;
        if (n>this.currentSuggest.length) n=1;

        var list = this.divSelectOption.down('ul');
        if (!list) {
            return;
        }
        var that = this;
        $(list).select('li').each(function(item, k) {
                var scrolleds = false;
                if (n - 1 == k) {
                    item.addClassName(that.options.className+'_highlight');
                    that.highlighted = n;
                    that.resetTimeout();
                    if (n==1) {
                        that.divSelectOption.scrollTop = 0;
                        scrolleds = true;
                    } else if (n==that.currentSuggest.length) {
                        that.divSelectOption.scrollTop = that.divSelectOption.scrollHeight;
                        scrolleds = true;
                    } else if (!noScroll) {
                        try {
                            //var oScroll  = that.divSelectOption.scrollTop;
                            //var divDim   = (that.divSelectOption.getDimensions()).height;
                            var divTop   = (that.divSelectOption.positionedOffset()).top;
                            var divBot   = divTop + (that.divSelectOption.getDimensions()).height;
                            
                            //var itemTop  = (item.positionedOffset()).top;
                            var itemDim  = (item.getDimensions()).height;
                            //var itemOffset = (item.cumulativeOffset()).top;
                            //var itemScroll = (item.cumulativeScrollOffset()).top;
                            
                            var offset = (item.cumulativeOffset()).top - that.divSelectOption.scrollTop;
                            if (offset < divTop) {
                                that.divSelectOption.scrollTop = (item.positionedOffset()).top;
                            } else if ((offset + itemDim + 5) > divBot) {
                                while ((offset + itemDim + 5) > divBot) {
                                    that.divSelectOption.scrollTop += 1;
                                    offset--;
                                }
                            }
                        } catch(e) {}
                    }
                } else {
                    item.removeClassName(that.options.className+'_highlight');
                }
        });
    },
    
    removeSelected: function(e, i) {
        this.field.style.width = this.options.minWidth + 'px';
        i = i || Event.element(e).up('div').down('input').value;
        var x = this.fieldValueId.indexOf(i+'');
        if (x >= 0) {
            this.fieldValueText = this.fieldValueText.toArray().uniq().without(this.fieldValueText[x]);
            this.fieldValueId = this.fieldValueId.toArray().uniq().without(this.fieldValueId[x]);
            Event.element(e).up('div').remove();
            this.primeField.value = (this.fieldValueText.toArray()).join(this.options.valueSeparator);
        }
        this.highlighted = 0;
        this.autoWidthField();
        Event.stop(e);
    },
    
    getHighlightedValue: function() {
        if (this.highlighted > 0 && this.currentSuggest.length > 0) {
            var response = this.currentSuggest[this.highlighted-1];
            if (!response.value) return false;
            this.clearSuggestions(0.05);
            
            this.getHighlightedMultiple(response);
            this.field.value = '';
            this.field.focus();
            var x = this.highlighted;
            // pass selected object to callback function, if exists
            if (typeof this.options.callback == 'function')
                this.options.callback(this.currentSuggest[x]); 
            // the object has the properties we want, it will depend of
            this.currentSuggest = [];
            this.highlighted = 0;
            return true;
        }
        return false;
    },
    
    getHighlightedMultiple: function(response) {
        this.field.style.width = this.options.minWidth + 'px';
        
        // HERE WE NEED TO IMPLEMENT THE FACEBOOK LIKE SPLITTED VALUE
        var aClick = this.removeSelected.bind(this);
        var newDiv = new Element('div', {'class':this.options.className+"_value"}).update(response.value+"<input type=\"hidden\" name=\""+this.fieldName+"_id[]\" value=\""+response.id+"\" />&nbsp;").insert(
            new Element('a', {href:"#", title:"remove"}).update("<span>x</span>").observe('click', function(e) {aClick(e, response.id); return false;})
            );
        $(this.fieldName+'_holder').insert(newDiv);
        
        this.fieldValueText.push(response.value);
        this.fieldValueId.push(response.id+'');
        this.primeField.value = (this.fieldValueText.toArray()).join(this.options.valueSeparator);
        this.autoWidthField();
    },
        
    killTimeout: function() {
        if (this.openTimeout) clearTimeout(this.openTimeout);
        this.openTimeout = null;
    },
    
    resetTimeout: function(mouseOut) {
        if (mouseOut) this.mouseOvered = false;
        this.killTimeout();
        var that = this;
        this.openTimeout = setTimeout(function () {that.clearSuggestions();}, that.options.timeout);
    },
    
    clearSuggestions: function (n) {
        if (!this.openSelectOption) return;
        n = n || 0.4;
        this.killTimeout();
        this.currentSuggest = [];
        this.highlighted = 0;
        if (this.openSelectOption != this.divSelectOption) $(this.openSelectOption).hide();
        if (typeof Effect == 'object') {
            new Effect.Fade(this.divSelectOption, {duration: n});
        } else {
            this.divSelectOption.hide();
        }
        this.openSelectOption = null;
    },
    
	getSelectedLocations: function() {
		var cities = [];
		var regions = [];
		var countries = [];
		var inputs = document.getElementsByName(this.fieldName+"_id[]");
		for(i=0; i<inputs.length; i++) {
			var id = $F(inputs[i]);
			var ids = id.substring(id.indexOf('_')+1).split(',');
			if(!ids[0].blank())
				cities.push(ids[0]);
			else if(!ids[1].blank())
				regions.push(ids[1]);
			else if(!ids[2].blank())
				countries.push(ids[2]);
		}
		var loc = new Object();
		loc.cities = cities;
		loc.regions = regions;
		loc.countries = countries;
		return loc;
	}
}


