/*--------------------------------------------------------------------*
*                                                                     *
*  autocompleter.js                                                   *
*                                                                     *
*  Implements 'auto-complete/suggestion behavior' on a text input.    *
*  Uses Ajax to get a list of possible completions to a given initial * 
*  input value and displays those in a dropdown-like way below the    *
*  text input.                                                        *
*                                                                     *
*  Usage: add 'onkeyup' attribute to the text input                   *
*                                                                     *
*  onkeyup="AutoCompleter.TextChange(this,                            *
*                                       <SCRIPT URL>,                 *
*                                       <PARAM LIST>,                 *
*                                       <TEXT PARAMNAME>,             *
*                                       <ID PARAMNAME>,               *
*                                       <THRESHOLD>,                  *
*                                       <VALUE INPUTID?>,             *
*                                       event)"                       *
*                                                                     *
*                                                                     *
* <SCRIPT URL> the (relative) URL of the server-side ajax script that *
* will return values <PARAM LIST> a list of (fixed) script params     *
* that the script may require <TEXT PARAMNAME> the name of the script *
* param that holds the text types sofar <ID PARAMNAME> the name of    *
* the script param that holds the id of the text input that the event *
* originates from (the callback needs that) <THRESHOLD> the minimum   *
* length at which autocomplete starts: this is where the ajax call    *
* will be done. For longer inputs, the script will use client-side    *
* filtering <VALUE INPUTID?> (optional, put in NULL if not needed)    *
* the id of the form element that will hold the ID of the selected    *
* value.                                                              *
*                                                                     *
* Format of the response expected:                                    *
*                                                                     *
* The response is a string of the form:                               *
*                                                                     *
* <INPUT_ID>/<PREFIX>:<TEXT_1>;<ID_1>|...|<TEXT_n>;<ID_n>             *
*                                                                     *
* <INPUT_ID> is the ID of the text element that the event handler is  *
* bound to (the value of the request param ,<ID PARAMNAME>) <PREFIX>  *
* is the text typed sofar (so, the 'cache key' for this list), which  *
* is the value of <TEXT PARAMNAME>                                    *
*                                                                     *
* The possible values are to be returned as a pipe-delimited list of  *
* semi-colon separated pairs.                                         *
*                                                                     *
*---------------------------------------------------------------------*/       
       
       var AutoCompleter = 
       {       
            // Maximum nr of results
            // don't show the option list if there are more than this.
            maxResults: 40,
       
            StoreListPrefix : function(txtElmId,sValue)
            {    
                 //Debug.Print( "StoreListPrefix( [" + txtElmId  + "], [" + sValue + "] )" );
                 eval('window.acpr_' + txtElmId + '=\'' + sValue.toLowerCase() + '\'');
            },   
                         
            GetListPrefix : function(txtElmId)
            {
            
                var result = typeof(eval('window.acpr_' + txtElmId)) != 'undefined' ? eval('window.acpr_' + txtElmId) : '';
                //Debug.Print( "GetListPrefix: [" + result + "]" );
                return result;
            },     
                       
            StoreValueInputId : function(txtElmId, sValue)
            {
                 eval('window.vali_' + txtElmId + '=\'' + sValue + '\'');
            },       
                     
            GetValueInputId : function(txtElmId)
            {
                return typeof(eval('window.vali_' + txtElmId)) != 'undefined' ? eval('window.vali_' + txtElmId) : null;
            },      
                      
            StoreSelectedIndex : function(txtElmId, intValue)
            {
                 eval('window.seli_' + txtElmId + '=' + intValue);
            },       
                     
            GetSelectedIndex : function(txtElmId)
            {
                return typeof(eval('window.seli_' + txtElmId)) != 'undefined' ? eval('window.seli_' + txtElmId) : -1;
            },      
                      
            StoreOptionsArray : function(txtElmId,optArray)
                {
                    eval('acoa_' + txtElmId + '= optArray');
                },                
            GetOptionsArray : function(txtElmId)
                {
                    return eval('window.acoa_' + txtElmId);
                },                
            GetOptionsList : function(txtElmId)
                {
                    var optList = $(txtElmId + '_acoptlist');
                    return optList;
                },                          

            HideOptionsList : function(txtElmId)
            {
                var optList = this.GetOptionsList(txtElmId);
                
                if (optList)
                {                    
                    Debug.Print( "Hide option list" );
                    Element.hide(optList);
                }
            },
            
            ShowOptionsList : function(txtElmId)
            {           
                var txtElm = $(txtElmId);                              
                var arrOptions = this.GetOptionsArray(txtElmId);
                
                var sCurrentText = txtElm.value.toLowerCase()
                // insert the matching options list into the DL
                var sHTML = '';
                var optText = '';
                var optValue = '';
                var a;
                var intJ = 0;
                
                var optionsToShow = new Array();
                
                for (var intI = 0; intI < arrOptions.length; intI ++)
                {               
                    a = arrOptions[intI].split(';');
                    optText = a[0];
                    optValue = a[1];
                    if (optText.toLowerCase().substring(0,sCurrentText.length) == sCurrentText)
                    {
                        optionsToShow[intJ++] = optText; 
                    };
                };                
                
                Debug.Print( "Nr of options: " + intJ );
                
                if( intJ == 0 || intJ > this.maxResults ) {
                    this.HideOptionsList(txtElmId);
                    return;
                }
                
                 // if optList (DL) does not exist, create the DL now
                var optList = this.GetOptionsList(txtElmId);
                if (!optList)
                {
			        var locSelect = document.createElement("select");
	                
			        locSelect.setAttribute("style", "position:absolute;left:-1000px;top:0px;display:none;");
			        locSelect.setAttribute("id", txtElmId + '_acoptlist');
			        locSelect.setAttribute("size", "4");
    				
    				locSelect.onchange = function(e) {
		                AutoCompleter.Select( txtElmId );
	                };
    				
			        document.body.appendChild(locSelect);
			        optList = this.GetOptionsList(txtElmId);
			    }                      
                
                // remove all existing options
                optList.options.length = 0;
                
                for( var i = 0; i < intJ; i++ ) { 
                    optList.options[i] = new Option( optionsToShow[i], optionsToShow[i] );
                }
                
                // do not choose anything, otherwise the onChange doesn't fire
                optList.selectedIndex = -1;
              
                // position the optionslist underneath the text element
                var txtPos = Position.cumulativeOffset(txtElm);
                Element.setStyle(optList,
                    {
                        'left': txtPos[0]  + 'px', 
                        'top' : 23 + txtPos[1] + 'px' , 
                        'display':'block',                
                        'position':'absolute'
                    }
                );
     
            },
            
            TextChange: function(txtElm, iMinLength, strValueInputId, e)
            {
            
                var code;
                        
                if (e.keyCode) code = e.keyCode;
                else if (e.which) code = e.which;             
               
                var txtElmId = txtElm.id;

                if (strValueInputId != null)
                {
                    this.StoreValueInputId(txtElmId,strValueInputId);
                };

                if (txtElm.value.length >= iMinLength)
                {                  
                    // see if we have an option list that still matches the prefix
                    var sCurrentPrefix = this.GetListPrefix(txtElmId);                    
                    
                    if ( sCurrentPrefix == '' ||  txtElm.value.substring(0,sCurrentPrefix.length).toLowerCase() != sCurrentPrefix )
                        {
                          this.SetLocationList( txtElmId );
                          return 0;
                        };                     
                    
                    var optList = this.GetOptionsList(txtElmId);                    
                   
                    if( optList == null ) { 
                        this.ShowOptionsList(txtElmId);
                        return 0;
                    }
                   
                    //var lastOptIndex = optList.childNodes.length - 1;
                    
                    var seli = this.GetSelectedIndex(txtElmId);
                   
                    Debug.Print('keycode = '+ code);
                    // handle 'cursor' keys
                    switch(code)
                       {
                           case 27 : // esc
                                this.Exit(txtElmId, null);                           
                                break;
                           case 13 : // enter
                               if (seli != -1)
                                {                                   
                                    this.Select(txtElmId);
                               };
                                break;
                           case 36 : // end
                           case 33 : // page up
                               if (seli != 0)
                                {
                                    //this.OptionHover(txtElmId, 0);                                   
                                };
                                break;
                           case 35 : // home
                           case 34 : // page down
                               //if (seli != lastOptIndex)
                               // {
                                    //this.OptionHover(txtElmId, lastOptIndex);
                               // };
                                break;
                           case 38 : // arrow up
                               if (seli > 0)
                                {
                                    //this.OptionHover(txtElmId, seli - 1);
                                };
                                break;
                           case 40 : // arrow down
                               //if (seli < lastOptIndex)
                               // {
                                    //this.OptionHover(txtElmId, seli + 1);
                                //};
                                break;
                           default : 
                                this.StoreSelectedIndex(txtElmId,-1);
                                this.ShowOptionsList(txtElmId); 
                                break;
                    };
                    
                }
                else
                 {
                   this.HideOptionsList(txtElmId)
                 }
               
                
            },        
            
            
            SetLocationList: function( txtElmId ) { 
            
                var pubCode = $F('ddlLocation');
                var locPrefix = $F(txtElmId);
                
                    
                this.SetOptionsList( txtElmId, locPrefix, Locations.getByPrefix( pubCode, locPrefix) );
            },
            
            clear: function( txtElmId ) { 
              
                var pubCode = $F('ddlLocation');
                
                var currentLocation = $F(txtElmId);
                
                // TODO: some smarter code to deal with current location?
                
                if( !Locations.hasPrefix( pubCode, currentLocation ) ) {
                    $(txtElmId).value = '';
                }
                             
                this.SetLocationList( txtElmId);
            },
            
            SetOptionsList: function( txtElmId, prefix, list) { 
               
                if( list.length == 0 ) {
                     $(txtElmId).disabled = true;
                     return;
                }
               
                $(txtElmId).disabled = false;
                
                AutoCompleter.StoreListPrefix(txtElmId, prefix);
                
                // save the options (for this input) 
                AutoCompleter.StoreOptionsArray(txtElmId, list );
                  
                // fill the options list with these new options  
                AutoCompleter.ShowOptionsList(txtElmId);             
            },
            
            Exit: function(txtElmId, e)
            {
                var code;
                
                if (e)
                {
                    if (e.keyCode) code = e.keyCode;
                    else if (e.which) code = e.which;
                };
                
                var optList = this.GetOptionsList(txtElmId);
                var seli = this.GetSelectedIndex(txtElmId);
                if (seli != -1)
                {
                    optList.childNodes[seli].firstChild.className  = 'acOut';
                };
                seli = -1;
                eval('seli_' + txtElmId + '=' + seli);
                Element.hide(optList);
           },
           
           
            Select: function( txtElmId)
            {
                var select = $(txtElmId + '_acoptlist');
                
                Debug.Print( "Selected index: [" + select.options.selectedIndex + "]");
                
                var option = select.options[select.selectedIndex];
                
                Debug.Print( "option: [" + option + "]" );
                
                $(txtElmId).value = option.text;
                          
                var vali = typeof(eval('window.vali_' + txtElmId)) != 'undefined' ? eval('window.vali_' + txtElmId) : null;
               
                if (vali != null)
                {
                    $(vali).value =  option.value;    
                };
                
                this.Exit(txtElmId, null);
           }           
           
       }; 
       
       
    var Debug = 
    {
        Enabled : false,
        
        Print : function(sMessage)
        
        {
            if (!this.Enabled)
                {
                    return 0;
                };
            if (!$('pipodebug'))
            {
		        var dta = document.createElement("textarea");
		        dta.setAttribute("rows", "16");
		        dta.setAttribute("cols", "80");
		        dta.setAttribute("id", 'pipodebug');
		        dta.setAttribute("style", 'position:absolute;top:400px;left:400px;background-color:black;color:#44ff44;font-weight:bold');
				
		        document.body.appendChild(dta);
		        Element.setStyle('pipodebug',
                    {
                    'position': 'absolute', 
                    'top': '400px', 
                    'left': '400px', 
                    'background-color': 'black', 
                    'color': '#44ff44' 
                    }
                );
            };
            $('pipodebug').value = sMessage + '\n' + $('pipodebug').value;
        }
    };       
