        ////////////////////////////////////////////////////////
      //   Suggest Class - class for XHR unobtrusive layer  //
    //           implemented in prototype 1.5.1           //
  //                Last updated: 11/15/07              //
////////////////////////////////////////////////////////
var Suggest = Class.create();

Suggest.prototype = {
    
    /*-----------------------------------------------------------------------------
      constructor now uses fancy JSON object instead of a bunch of passed params
      
      Syntax:
      var paramObject = {
                     inputBox : $('searchOnThisParish'),
                     url : '/cfm/SearchSuggestParish.cfm',
                     requiredSearchLength : 2,
                     submitString : ['ParishID'],
                     id : 'parish',
                     clickSubmit : true,
                     clickSubmitURL : '/parish/?parishID=',
                     dropDown : '',
                     noResultsDisplayID : 'pname'
       };
      -----------------------------------------------------------------------------
    
    
    inputBox:HTMLelemnt     - text field element -- which input is used to text capture
                              also dtermines where the auto-built drop down will be placed in the dom
            
    url:String              - url to send XHR to  
    
    requiredSearchLength    - The number of characters required to be entered into the text box before 
    :Number                   a request will be made
    
    submitString:Array      - javaScript array of strings (ie ['string1', 'string2'] )
                              The values in the array correspond to specific names in the name:value paries
                              that the XHR returns as its response. The names in the array indicate which
                              which values you want to be put into the inputBox on click and/or which
                              values you want in the GET query
    
    
    ---- the remaining params are optional, and will get the commonly used defaults if you don''t set them ----

    id:String               - String to which the ID of the dropDown is set                 

    clickSubmit:Boolean     - Should clicking on one of the drop down results submit the clicked value
                              via a GET query string (true) -- or -- populate the input textbox with
                              the click on value (false)

    
    dropDown:HTMLObject     - optional - If the drop down needs to be built in a specific location,
                              this param will override the default behavior of placing directly below
                              the input box and build the drop down inside the elemennt that is passed
                              -- not really sure if you would ever want to do this, but if you do...
                            
    
    ----------------------------------------------------------------------------*/
    
    initialize: function(paramObject){  

        this.inputBox = paramObject.inputBox;
        
        this.url = paramObject.url;
        
        this.requiredSearchLength = paramObject.requiredSearchLength;
        
        //-- keep track of which values are submitted via GET, if that feature is being used
        this.submitStrings = new Array;
        this.submitStrings = paramObject.submitString;

        
        this.noResultsDisplayID = paramObject.noResultsDisplayID;
        
        if(paramObject.id){
            this.id = paramObject.id;
        }else{
            this.id = '';
        }
        
        if(paramObject.clickSubmit){
            this.clickSubmit = paramObject.clickSubmit;
            this.clickSubmitURL = paramObject.clickSubmitURL;
        }else{ 
            this.clickSubmit = false;
        }
        
        if(paramObject.dropDown && $(paramObject.dropDown)!=null){
            this.dropDown = paramObject.dropDown;
        }else{
            this.dropDown = null;
        
        }
        
        this.spans = null;
        
        this.allowSearch = true;
        this.isOpen = false;
        
        //-- object as associative array for caching results
        this.resultCache = new Object;
        
        //-- value in the input field
        this.data = '';
        
        //-- name/value pair of data
        this.query = '';
        
        //-- the text that gets displayed when no results are found
        this.noResults = 'No results for your query';
        this.noReusltsJSON = new Object;
        this.noResultnonJSON =  '';
        this.noResultBool = false;
        this.parseNoResults();
        
        this.numResults = 11;
        
        //the first span in the drop down -- so we can apply a specific style when no results are shown
        this.messageSpan;
        
        //last span to show 'view all' link
        this.viewAll;
        
        //-- current json object being used to populate dropdown array
        this.json = new Object;
        
        //-- how many results were returned for the current query
        this.jsonLength = 0;
        
        //-- string of un-evaluated json -- used for storing in results chache
        this.nonJSON = '';
        
        //-- how many items to be displayed in the the dropDown, used in sql query
        this.numDropDowns = 10;
        
        this.createDropdown();
        
        this.theForm = $(this.inputBox).up('form');
        
        //--watch keyDown events
        Event.observe(this.inputBox, 'keydown', this.captureKeys.bindAsEventListener(this));
        
        Event.observe(this.theForm, 'submit', this.stopSubmit.bindAsEventListener(this));
        //--timed observer -- polls the text box every x seconds to see if it has changed since the last poll
        //--if it has, fire keyUpCheck
        new Form.Element.Observer(this.inputBox, 0.5, this.submitCheck.bind(this));
       
        this.theSubmit = $(this.theForm).getElementsBySelector('input').find(function(el){
			var elm = $(el);
			if(elm.readAttribute('value') !== null){
            	if(elm.readAttribute('value').toLowerCase() === 'submit'){ 
	                return true;
	            }
			}
            
        });

        Event.observe(this.theSubmit, 'mouseup', this.submitForm.bindAsEventListener(this), true);
        Event.observe(this.theForm, 'keydown', this.captureKeysForm.bindAsEventListener(this), true);
    },
    
    stopSubmit: function(event){
        Event.stop(event);
    },
    
    submitForm: function(event){
        this.theForm.submit();  
    },
        
    /* --- create a json string used to populate the drop down with a 'no results message' --- */
    parseNoResults: function(){
        this.noResultnonJSON = '{\"resultArray\": [{\"'+this.noResultsDisplayID+'\": \"'+this.noResults+'\"}]}';    
    },
    
    /* --- on key press, determine how to handle the pressed key --- */
    captureKeys: function(event){
        this.allowSearch = true;
        var key = event.which || event.keyCode;
        
        if(key == Event.KEY_TAB){
            $$('html .searchDrop span').each(function(el) {
                el.setStyle({
                    display: 'none'
                });
            });
        }else if(key == Event.KEY_UP){
            this.updateDropSelectionKey(-1);
        }else if(key == Event.KEY_DOWN){
            this.updateDropSelectionKey(1);
        }else if(key == Event.KEY_ESC){
            this.hideDrop();
        }else if(key == Event.KEY_RETURN){
			var kind = (this.clickSubmit == true) ? 'selectedUnderline' : 'selected';
            if ($$('span#'+kind)[0].hasClassName('viewAll')){
                //alert(event);
                this.submitForm();
            }else{
                this.setInputBoxText(event);
            }
        }
    },
    
    captureKeysForm: function(event){
        var key = event.which || event.keyCode;
        //console.log(key);
        if(key == Event.KEY_RETURN){
            //Event.stop(event);
            //console.log(key, Event.KEY_RETURN);
            this.submitForm();
        }
    },
    
    /* --- check to see if the inputbox meets all criteria to initiate a search --- */
    submitCheck: function(){
        
        //if enough characters have been entered and [something] 
            //grab the text in the inputbox and begin search
        var re = /[A-Za-z-\'\s]+/;
        var cleanString;
        
        //DIE if NOTHING is in the box
        if(this.inputBox.value.match(re) === null){
            this.hideDrop();
            return;
        }
        
        cleanString = this.inputBox.value.match(re)[0];
        
        //continue testing the values before sending, assume the string is clean.
        if(cleanString.length >= this.requiredSearchLength && this.allowSearch == true){
            this.data = cleanString;
            this.query = 'q=' + this.data+'&num='+this.numDropDowns;
            this.prepareUpdate();
            
        }else{
        //don't show the drop down if not enough characters have been entered   
            this.hideDrop();
        }
        
    },
    
    /* --- determine if a cached response set can be used, if not, hit the server --- */
    prepareUpdate: function(){
        
        // check to see if there is a cachhed result
        if(this.resultCache[this.data] != null){
            this.json = this.resultCache[this.data].evalJSON();
            this.nonJSON = this.resultCache[this.data];
            this.update();
        }else{
        // if not hit the server    
        var request = new Ajax.Request(
            this.url, 
            {
                method: 'post', 
                parameters: this.query, 
                onSuccess: this.parseResponse.bindAsEventListener(this)
            });
        }
    },
    
    /* --- handle variable house keeping after ajax --- */
    parseResponse: function(response){
        this.nonJSON = response.responseText;
        this.json = response.responseText.evalJSON();
        this.update();
    },
    
    /* --- determine if any results we returned/found in cache
           if no results display message, if there are -- display results --- */
    update: function(){
        this.numResults = this.json.numResults;
        //console.log(this.numResults);
        this.jsonLength = this.json.resultArray.length;
    
        //-- if no results are returned 
        if(this.json.resultArray.length == 0){

            this.nonJSON = this.noResultnonJSON;
            //console.log(this.nonJSON);
            this.json = this.nonJSON.evalJSON();
            this.noResultBool = true;
            $(this.messageSpan).addClassName('message');
            //-- even though the result of the query is zero results, we still have to display the error text - thus 1
            this.jsonLength = 1;
        }else{
                this.noResultBool = false;
                $(this.messageSpan).removeClassName('message');
        }
        
        //this.json = response.responseText.evalJSON();
        //console.log(this.nonJSON);
        this.updateCache();
        this.updateDropdown();
    },
    
    /* --- add latest result set to the cache -- we already check to see if we could use the cache, so no need to check again --- */
    updateCache: function(){
        this.resultCache[this.data] = this.nonJSON;
    },
    
    createDropdown: function(){
        //-- if no dropDown element is passed to the constructor -- create one;
        if(!this.dropDown){
            this.createDropDownWrapper();
        }
        //-- one span per line of the drop down box, number of drop down items passed to constructor
        for(var k=0; k<this.numDropDowns; k++){
            
            //-- wrapper element
            var s = document.createElement('span');
            this.dropDown.appendChild(s);   
            //create the divs here, and avoid them later.       
            this.createClearer(s);
            if(k ==0){
                this.messageSpan = s;
            }
        }
        
        this.createViewAll();
        //-- set spans collection array for access later on
        this.spans = this.dropDown.getElementsByTagName('span');
        
        //-- set default state on drop down to be hidden
        this.hideDrop();
        
        this.attachDropEvents();    
    }, 
    
    /* --- if no element for the drop down is passed in, we need to make one --- */
    createDropDownWrapper: function(){
        //console.log('creating dropdown on the fly');
        var d = document.createElement('div');
        
        this.createClearer(this.inputBox.parentNode);
        this.inputBox.parentNode.appendChild(d);
        
        d.setAttribute('id', this.id);
        $(d).addClassName('searchDrop');
//      d.setAttribute('class', 'searchDrop');
        this.dropDown = d;
        
        this.dropDown.style.zIndex = '1';
    },
    
    createClearer: function(where){
        var c = document.createElement('div');
        
        var t4 = document.createTextNode('\u00a0');

        c.appendChild(t4);
        where.appendChild(c);
        //console.log(where);
        $(c).addClassName('clearer');
    },
    removeClearer: function(where){
        //var clearer = where.firstChild('clearer')[0];
        //console.log(where.down(1));
        
    },
    
    /* --- build the view all span at the bottom of the dropdown --- */
    createViewAll: function(){
        new Insertion.Bottom (this.dropDown, '<span class="viewAll off"><p class="showViewAll ">view all ' + this.numResults + ' results</p></span>');
        this.viewAll = $(this.dropDown).getElementsByClassName('viewAll')[0];
        this.createClearer(this.viewAll);
    //console.log(this.viewAll.down(0));
    },
    
    /* --- update the view all display --- */ 
    updateViewAll: function(){
        //console.log('view all ' + this.numResults + ' results');
        //console.log(this.viewAll.down(0));
        this.viewAll.down(0).update('view all ' + this.numResults + ' results');
    },
    
    
    /* --- attach two mouse events to each drop down span --- */
    attachDropEvents: function(){
        for(var i = 0; i<this.spans.length-1; i++){
            Event.observe(this.spans[i], 'mouseup', this.setInputBoxText.bindAsEventListener(this));
            Event.observe(this.spans[i], 'mouseover', this.updateDropSelectionMouse.bindAsEventListener(this));
        }   
        
        Event.observe(this.spans[10], 'mouseup', this.submitForm.bindAsEventListener(this));
        Event.observe(this.spans[10], 'mouseover', this.updateDropSelectionMouse.bindAsEventListener(this));
    },
    
    /* --- take the value in the clicked drop down item and replace the value in the inputBox --- */
    setInputBoxText: function(event){
        Event.stop(event);
        //console.log(this);
        if(this.isOpen == false){return}
        //console.log(Event.element(event)); 
        this.allowSearch = false;
        //-- check to see if there are any results. if there are, fill the input box with the value clicked.
        //-- don't want to fill the text box with the noResult default text
        if(this.noResultBool == false){
            
            if(this.clickSubmit == true){
                var submitID =  new Array;
            }
            
            //-- s = the span direcectly above (hierarchically) from the element that fired the mouseUp event
            //var s = $(Event.element(event)).up('span');
            var s = $(this.dropDown).getElementsBySelector('span#selected', 'span#selectedUnderline');
            /*$(s).each(function(el,index){
                //console.log(el);
                
            });*/
            var p = ($A(s)[0].childNodes);

//          var p = s.getElementsBySelector('p');
//          //console.log(s);
//          //console.log(s[0]);
            //var p = s.childNodes;
            
            this.inputBox.value =  '';
            
            for(var i=0; i<p.length; i++){
                for( var l = 0; l<this.submitStrings.length; l++){
                    if(p[i].className === this.submitStrings[l]){
                        //console.log(this.clickSubmit);
                        
                        if(this.clickSubmit == false){
                            
                            this.inputBox.value += p[i].firstChild.nodeValue + ' ';
                        }else if(this.clickSubmit == true){
                            submitID[i] = p[i].firstChild.nodeValue;
                            //console.log(submitID[i]);
                            //this.inputBox.value += p[i].firstChild.nodeValue + ' ';
                        }
                        //console.log(submitID);
                    }
                }
            }
        }

        //this.fillHiddenField(s);
        //this.hideDrop();
        if(this.clickSubmit == true){
            
            this.submitURL = this.buildURL(submitID); 
            window.location = this.submitURL;
        }
    },
    
    buildURL: function(passedID){
        var returnString = '';
        for(var i = 0; i < passedID.length; i++){
            if(passedID[i] != undefined){
                returnString += passedID[i];        
            }
        }
        return this.clickSubmitURL + returnString;
    },
    
    /* --- handle onmouseover of drop down elements -- was :hover, using JS lets it play nice with the 
           arrow keys being used to change the selection --- */
    updateDropSelectionMouse: function(event){
        var type = (this.clickSubmit == true) ? 'selectedUnderline' : 'selected';
        
        var elt = $(Event.element(event)).up('span');
        var current = document.getElementById(type);
        if(current) current.id= '';
        if(elt) elt.id = type;      
    },
    
    /* --- function that handles up and down arrow key presses from within the input box -- moves the highlight up and down --- */
    updateDropSelectionKey: function(direction){
        var next = (direction == 1) ? 'nextSibling' : 'previousSibling';
        var type = (this.clickSubmit == true) ? 'selectedUnderline' : 'selected';
        
        var current = $(type);

        var first = this.spans[0];
        var last = this.spans[this.jsonLength-1];
        
        var wrap = (direction == 1) ? last : first;
    
        if(current != null){current.id= '';}
        if(current == null || current[next] == null || current == undefined || current == wrap){
            if(direction == 1){
                first.id= type;
            }else{
                last.id= type;
            }
        }else if (current[next] == wrap){
            wrap.id = type; 
        }else{
            current[next].id = type;
        }
        
    },

    /* --- updated the text in the dropdown -- and if enabeled, build the submit string--- */
    updateDropdown: function(){
        $(this.dropDown).addClassName('open');
        this.isOpen = true;
        //console.log(this.jsonLength);
        this.updateViewAll();
        //console.log(this.spans[1].childNodes);
        for(var k=0; k<this.jsonLength; k++){
            this.spans[k].show();
            
            /*-- abstracted so any number of values you can returned
                -- pair.key and pair.value to access each value in the hash
                -- index is numeric index of each paiir
                
                convert to hash, grab spans because this scope gets broken
            --*/
            var h = $H(this.json.resultArray[k]);
            //grab spans because this scope gets broken
            var s = this.spans;
            
            var theClearerDiv = this.spans[k].down('div');
            var create = this.spans[k].firstDescendant().hasClassName('clearer');
            //console.log(theClearerDiv);
            h.each(function(pair, index){       
                //this.removeClearer(s[k].childNodes[index].firstChild);
                //console.log(index,pair);
                if(s[k].childNodes[index].hasClassName('clearer')){
                    var p = document.createElement('p');
                    var t = document.createTextNode('\u00a0');
                    
                    //console.log(theClearerDiv);
                    p.appendChild(t);
                    blah = theClearerDiv.insert({before:p});//console.log(blah);

                }
                
                //
                s[k].childNodes[index].firstChild.nodeValue = pair.value;


                /* --- there was an elegant pure css solution here. but IE likes to invent how to display css as it goes.
                       IE was mis-interpreting the css to hide an empty piece of data.
                    
                       This explicitly adds or removes the data's class to make sure it gets the default state of hidden 
                       if it needs to be hidden.
                 --- */
                var count = 0;
                //console.log('line 530');
                $(s[k]).getElementsBySelector('p').each(function(el){
                    el = $(el);
                    if(el.hasClassName(pair.key)){
                        el.removeClassName(pair.key);
                    }
                    
                    if((s[k].hasClassName('message'))){
                        if(el.hasClassName('clearer')){
                            el.className = 'clearer';
                        }else{
                            el.className = '';
                        }                       
                    }

                    if(pair.value.length > 0 && count==index){
                        el.addClassName(pair.key);
                    }
                    count += 1; 
                });
            });
            
        }
        //-- if there is no data to display in the span[x]
        //   hide span
        for(var l = this.jsonLength; l<this.numDropDowns; l++){
            $(this.spans[l]).hide();
        }
    /*  if(create){
            this.createViewAll();
        }*/
        //console.log(this.numResults);
        if(this.numResults > 10){
        //console.log(this.viewAll);
            this.viewAll.removeClassName('off');
        }else{
            this.viewAll.addClassName('off');
        }
        
        //-- because of conveluted binding/closuers in prototype, we have to set the bound 
        //   event to a variable to be able to unset the event
        //-- When the dropDown is open, this event is set.
        this.cachedCheckBlur = this.checkBlur.bindAsEventListener(this);
        Event.observe(document.body, 'click', this.cachedCheckBlur);
    },
    
    /* --- runs through all dropDown spans and hides them (display:none) --- */
    hideDrop: function(){
        $(this.dropDown).removeClassName('open');
        this.isOpen = false;
        //console.log(this.numDropDowns);
        for(var i = 0; i<this.numDropDowns; i++){
            $(this.spans[i]).hide();
        } 
        this.viewAll.addClassName('off');
    },
    
    /* --- This function is bound to a document wide onClick event handler. 
           When the dropDown is open, this function watches all clicks. If a click is on the drop down,
           it updates the drop down via setInputBoxText(); if not, we just close the dropDown with out
           updating the text box. In either case, the event handler is removed
        
           This is basically a work around to get the effects of onBlur. When using onBlur, the blur event
           was firing and closing the dropDown, before the click event was sent to the dropDown to register 
           that the update should happen --- */
    checkBlur: function(event){
            var element = Event.element(event);
            var ancestors = element.ancestors();
            
            if(ancestors[1] == 'div.searchDrop'){
                this.setInputBoxText();
                
            }else{
                this.hideDrop();
            }
            Event.stopObserving(document.body, 'click', this.cachedCheckBlur);

            //console.log(element.ancestors());
            

    
    }
}


function prepareClegyForm(){
	var paramObject = {
					 inputBox : $('searchOnThis'),
					 url : '/cfm/SearchSuggest.cfm',
					 requiredSearchLength : 2,
					 submitString : ['ClergyID'],
 					 id : 'clergy',
					 clickSubmit : true,
 					 clickSubmitURL : '/clergy/?clergyID=',	
					 dropDown : '',
					 noResultsDisplayID : 'name'
	};
	var suggest = new Suggest(paramObject);
}

function prepareLocationForm(){	
	var paramObject = {
					 inputBox : $('searchOnThisLocation'),
					 url : '/cfm/SearchSuggestLocation.cfm',
					 requiredSearchLength : 2,
					 submitString : ['location'],
 					 id : 'location',
					 clickSubmit : false,
					 dropDown : '',
					 noResultsDisplayID : 'location'
	};
	var suggest3 = new Suggest(paramObject);
}


function prepareParishForm(){
	var paramObject = {
					 inputBox : $('searchOnThisParish'),
					 url : '/cfm/SearchSuggestParish.cfm',
					 requiredSearchLength : 2,
					 submitString : ['ParishID'],
 					 id : 'parish',
					 clickSubmit : true,
					 clickSubmitURL : '/parish/?parishID=',
					 dropDown : '',
					 noResultsDisplayID : 'pname'
	};	
	var suggest4 = new Suggest(paramObject);
}

function prepareLocationForm2(){
	var paramObject = {
					 inputBox : $('searchOnThisLocation2'),
					 url : '/cfm/SearchSuggestLocation2.cfm',
					 requiredSearchLength : 2,
					 submitString : ['plocation2'],
 					 id : 'plocation2',
					 clickSubmit :  false,
					 clickSubmitURL : '',
					 dropDown : '',
					 noResultsDisplayID : 'plocation2'
	};	
	var suggest5 = new Suggest(paramObject);
}
