// global storage object for type-ahead info, including reset() method
var typeAheadInfo = {last:0, 
                     accumString:"", 
                     delay:2000,
                     timeout:null, 
                     nearest:null, 
                     reset:function() {this.last=0; this.accumString=""}
                    };

function DoNothing(evt)
{
   evt.cancelBubble = true;
   evt.returnValue = false;
   typeAheadInfo.reset();
   return false;
}

function PassThrough(evt)
{
   evt.cancelBubble = false;
   evt.returnValue = true;
   typeAheadInfo.reset();
   return false;
}

var navkey;
// function invoked by select element's onkeydown event handler
function typeAhead(event) {
   if (navigator.appName == 'Netscape') {
     return false;
   }

  var theEvent = window.event || arguments.callee.caller.arguments[0];
   // get reference to the select element

   var selectElem = theEvent.srcElement;// || theEvent.target;

   if(theEvent.keyCode > 127){
// it's not a recognized keycode so ignore it
      return PassThrough(theEvent);
   }
   if(theEvent.keyCode > 95){
      // handle the keypad 
      theEvent.keyCode = theEvent.keyCode - 48;
   }
   if (theEvent && !theEvent.ctrlKey) {

      // should be a valid key, so reset the timeout
      clearTimeout(typeAheadInfo.timeout);

      typeAheadInfo.timeout = setTimeout("typeAheadInfo.reset()", typeAheadInfo.delay);

      switch(theEvent.keyCode){
         case 13://enter
            typeAheadInfo.reset();
            if(event){
    			var callargs = "event(" 
	    		var args = new Array();
    	    	for(i=1;i < arguments.length;++i) {
    		       args[i-1] = arguments[i];
    		       callargs=callargs+"args["+(i-1)+"]";
    			   if(i < arguments.length-1) callargs+=",";
	    		}
		    	callargs = callargs + ")";
    			eval(callargs);
            }else if(selectElem.onchange){
				if(navkey == false){
                   // ok this is a total and complete hack, for some reason, now that I'm letting 
				   // IE control pgup/pgdown and the arrows it causes a double onchange() event call
                   // when i press enter.  i need to keep this here because when a user types in letters
                   // for a search followed by <enter> this must be called. *sigh*
        		   selectElem.onchange();
				}
            }
            return true;
         case 27:  // escape
  			navkey = false;
		    typeAheadInfo.accumString="";
    		return DoNothing(theEvent);

         case 36: // home
  			navkey = false;
            selectElem.selectedIndex = 0;
            return DoNothing(theEvent);

         case 35: // end
  			navkey = false;
            selectElem.selectedIndex = selectElem.options.length-1;
            return DoNothing(theEvent);


         case 40: // down arrow
         case 38: // up arrow
         case 34: // pgdown
         case 33: // pgup
  			navkey = true;
			return PassThrough( theEvent );

//          case 40:  // down arrow
//        	    if(selectElem.selectedIndex < (selectElem.options.length-1)){
//               selectElem.selectedIndex = selectElem.selectedIndex + 1;
//             }
//             return DoNothing(theEvent);

//          case 38: // up arrow
//             selectElem.selectedIndex = selectElem.selectedIndex - 1;
//         	if(selectElem.selectedIndex < 0){
//     	      selectElem.selectedIndex = 0;
//             }
//           return DoNothing(theEvent);


//          case 34: // pgdown
// //alert (selectElem.size+", " +selectElem.options.size);
//              selectElem.selectedIndex = selectElem.selectedIndex + 10;
//         	    if(selectElem.selectedIndex < 0){
//                selectElem.selectedIndex = selectElem.options.length-1;
//              }
//              return DoNothing(theEvent);

//           case 33: // pgup
//  //alert (selectElem.size+", " +selectElem.options.size);
//             selectElem.selectedIndex = selectElem.selectedIndex - 10;
//          	if(selectElem.selectedIndex < 0){
//      	      selectElem.selectedIndex = 0;
//              }
//              return DoNothing(theEvent);

//		  default:
//			return PassThrough(theEvent);
      }
      navkey = false;
      // timer for current event
      var now = new Date();
      // process for an empty accumString or an event within [delay] ms of last
	  var delay = now - typeAheadInfo.last;	  
      typeAheadInfo.last = now;

      if(delay > typeAheadInfo.delay){
	    typeAheadInfo.accumString="";
      }

      if (typeAheadInfo.accumString == "" ||  delay < typeAheadInfo.delay) {
         // append new character to accumString storage
		 if(theEvent.keyCode == 8){ // backspace
            if(typeAheadInfo.accumString.length > 0){
	            var tmpstr = typeAheadInfo.accumString.substring(0, typeAheadInfo.accumString.length-1);
				typeAheadInfo.accumString = tmpstr;
            }
         }else{
		     // get typed character ASCII value
		     var charCode = theEvent.keyCode;
             // get the actual character, converted to uppercase
             var newChar =  String.fromCharCode(charCode).toUpperCase();
             typeAheadInfo.accumString += newChar;
         }

         // grab all select element option objects as an array
         var selectOptions = selectElem.options;

         // prepare local variables for use inside loop
         var txt;
         // look through all options for a match starting with accumString
         for (var i = 0; i < selectOptions.length; i++) {
            // convert each item's text to uppercase to facilitate comparison
            // (use value property if you want match to be for hidden option value)
            txt = selectOptions[i].text.toUpperCase();

            if (txt.indexOf(typeAheadInfo.accumString) == 0) {
               // visibly select the matching item
               selectElem.selectedIndex = i;

               // prevent default event actions and propagation
               theEvent.cancelBubble = true;
               theEvent.returnValue = false;

               // exit function
               typeAheadInfo.nearest = i;
               return false;   
            }            
         }
         // if a next lowest match exists, select it
         if (typeAheadInfo.nearest != null) {
            selectElem.selectedIndex = typeAheadInfo.nearest;
         }
     }
   } else {
         // not a desired event, so clear timeout
         clearTimeout(typeAheadInfo.timeout);
   }

   theEvent.cancelBubble = true;
   theEvent.returnValue = false;

   return false;
}
