/*
 * jQuery selectbox plugin
 *
 * Copyright (c) 2007 Sadri Sahraoui (brainfault.com)
 * Licensed under the GPL license and MIT:
 *   http://www.opensource.org/licenses/GPL-license.php
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * The code is inspired from Autocomplete plugin (http://www.dyve.net/jquery/?autocomplete)
 *
 * Revision: $Id$
 * Version: 0.6
 * 
 * Changelog :
 *  Version 0.6
 *  - Fix IE scrolling problem
 *  Version 0.5 
 *  - separate css style for current selected element and hover element which solve the highlight issue 
 *  Version 0.4
 *  - Fix width when the select is in a hidden div   @Pawel Maziarz
 *  - Add a unique id for generated li to avoid conflict with other selects and empty values @Pawel Maziarz
 */
 
 /* jQuery extension for change event fix in IE */
jQuery.fn.extend({
    fire: function(evttype){ 
        el = this.get(0);
        if (document.createEvent) {
            var evt = document.createEvent('HTMLEvents'); 
            evt.initEvent(evttype, false, false); 
            el.dispatchEvent(evt); 
            
        } else if (document.createEventObject) { 
            //PK: If we have hooked up the event trigger at the dom level then fire the event with IE "fireEvent" function
            if(el.onchange)
            {
                el.fireEvent('on' + evttype); 
            }
            else
            {//PK: we have only hooked up to the "change" event with jQuery. This means the "fireEvent" doesn't fire, so call the jQuery "change" event
                $(el).change();
            }
        }
        return this;
    }
});

/* jQuery extension for Select box */ 
jQuery.fn.extend({
	selectbox: function(options) {
		return this.each(function() {
			new jQuery.SelectBox(this, options);
		});
	}
});

/* pawel maziarz: work around for ie logging */
if (!window.console) {
	var console = {
		log: function(msg) { 
	 }
	}
}
/* */

/* begin jquery selectbox logix */
jQuery.SelectBox = function(selectobj, options) {
	
	var opt = options || {};
	opt.inputClass = opt.inputClass || "selectbox";
	opt.containerClass = opt.containerClass || "selectbox-wrapper";
	opt.hoverClass = opt.hoverClass || "current";
	opt.currentClass = opt.selectedClass || "selected"
	opt.debug = opt.debug || false;
	opt.maxLength = opt.maxLength || 0;
	
	debug("SelectBox jquery called");
		
	var elm_id = selectobj.id;
	var active = 0;
	var inFocus = false;
	var hasfocus = 0;
	//jquery object for select element
	var $select = $(selectobj);
	// jquery container object
	var $container = setupContainer(opt);
	//jquery input object 
	var $input = setupInput(opt);
	
	//PK variables
	var _sDropdownFilter = "";
	var _bSkipShowContainerOnFocusEvent = false;
	var $label = $("label[for=" + $select.attr("id") + "]");
	var _objActiveElementFix = null;
	var _dateTimeLastKeyPress = new Date();
	//PK variables stop
	
	// hide select and append newly created elements
	$select.hide().before($input).before($container);
	
	init();
	
	debug("Binding input events start");
	//PK: Not working in opera
	$label.click(function(e)
	    {   
	        debug('label clicked');     
	        //PK:Hide all other controls lists
	        hideAllListOnPage();
	        
	        //PK: Focus on the input, false = fire full event
	        focusOnInput(false);
	        
	        //PK: need this to stop firefox from taking focus from the input and putting on the label
	        return false;
	    }
	);
		
	$input
	.mousedown(function(e){
	    //PK: Must be mousedown event. This fires before the input gets focus. If I use click event the input focus fires before the click which 
	    //causes the following problem;
	    //The drop down list container is show in the focus event but then hidden in the click event, so when you first click on the input,
	    //the drop down list is shown then hiden again!
        debug("input click event. Container is " + $container.is(':visible'))
        
	    //PK: If we are visible and haven't just shown the container in the focus event then we need to hide the container
        if ($container.is(':visible'))  
        {
            hideMe();
	    }
	    else if (!$container.is(':visible'))
	    {
	        showMe();
	    }       
	})
	.focus(function(e){
	   //alert("start focus");
	   //PK: Clear filtered so user can start typing from scratch
	   setDropdownFilter("");
	   
	   //PK: We have to make the textbox readonly on focus. We cannot do this when we create the form input because safari, chrome and opera
	   //won't table to a readonly text box
	   $input.attr("readonly", "readonly");
	   
	   //set opt.debug to true at top of class
	   debug('input got focus: '+ $input.attr("id") +'. Skip show container on focus: ' + _bSkipShowContainerOnFocusEvent + '. Container visible: ' + $container.is(':visible'));

	   if (!$container.is(':visible') && !_bSkipShowContainerOnFocusEvent) {
	       inFocus = true;
	       //PK:Need to rest the _bSkipShowContainerOnFocusEvent flag
	        _bSkipShowContainerOnFocusEvent = false;
	       //PK: Show the list of li's
	       showMe();
	       
	       debug('show list. Skip focus: ' + _bSkipShowContainerOnFocusEvent);
	   }
	   
	   //alert("end focus");
	})
	.keydown(function(event) {	   
		switch(event.keyCode) {
			case 38: // up
				event.preventDefault();
				moveSelect(-1);
				
				break;
			case 40: // down
				event.preventDefault();
				moveSelect(1);
				
				break;
			case 9:  // tab   
			    debug("key pressed: tab");
	            finaliseLiSelection();			
				
			    break;
			case 13: // return
			    event.preventDefault(); // seems not working in mac !
			    
				finaliseLiSelection();
				
				break;
			case 27: //escape
			    finaliseLiSelection();
			    hideMe();
			    break
			case 9:
			    break;  
			default:
                //PK placed my login in function
                //when this calls moveSelect this can't get offsetTop
                inputOnKeyDown(event);
                break;
		}
		
		
	})
	.blur(function() {
	    
	    debug('input blur');
	    
	    //PK: Remove the readonly attibute that we added on focus
	    $input.removeAttr("readonly");
	    
		if ($container.is(':visible') && hasfocus > 0 ) {
			debug('container visible and has focus');
		} 
		else 
		{
		    // Workaround for ie scroll - thanks to Bernd Matzner
		    if($.browser.msie || $.browser.safari || $.browser.opera)
		    { // check for safari too - workaround for webkit
		    
		        var activeElement;
		        var activeElementID;
    		     
		        //PK: Fix for chrom or safari or opera. These browsers don't support active element so I have to simulate!
		        if($.browser.safari || $.browser.opera)
		        {
		            activeElement = _objActiveElementFix;
		        }
		        else
		        {
		            activeElement = document.activeElement;
		        }
    		   
		        //PK: check for null. Active element can return null
		        if(activeElement != null) 
		        {   
		            //PK: IE8 Fix. IE8 returns null if the id attribute hasn't been set other browsers return a empty string
		            if(activeElement.getAttribute('id') == null)
		            {
		                activeElementID = "";
		            }
		            else
		            {
		                activeElementID = activeElement.getAttribute('id');
		            }
		             		    
                    if(activeElementID.indexOf('_container')==-1)
                    {
                        debug("blur hideMe():1");
                        hideMe();
                    } 
                    else 
                    {
                        debug("blur trying to refocus on input. Active element: " + activeElementID);
                        focusOnInput(true);
                    }
                }
                else
                {
                    debug("blur hideMe():2");
                    hideMe();
                }
            } 
            else 
            {
                debug("blur hideMe():3");
                hideMe();
            }
		}
		
		//PK: Reset if the textbox has just got form focus
		_bFocusEventJustShownControl = false;
	});
	
	debug("Binding input events end");
	
	/* Init functions */
	
	function init() {
	    debug("init() start");
	    var childUL = getSelectOptions($input.attr('id'));
	    
	    if(childUL != null)
	    {
		    $container.append(childUL);
		}
		
		$container.hide();
		var width = $input.css('width');
		$container.width(width);
		debug("init() end");
    }
    
    /* Setup functions */
	
	function setupContainer(options) {
	    debug("setupContainer() start");
	    var containerID = elm_id+'_container';
	    var container = null;
	    
	    //PK: If we bind to a dropdown that we have already been bound to e.g in a form reset then we need to destroy the contanier first
	    //destroy is needed and not removeChild as this messes up events clicks in every other browser than IE
	    if($("#" + containerID).length > 0)
	    {   
            container = $("#" + containerID);
            //remove children
            container.find('ul li').detach();
	        container.find('ul').detach();
            container.detach();
	    }
	    
	    container = document.createElement("div");
	    $container = $(container);
	    $container.attr('id', elm_id+'_container');
	    $container.addClass(options.containerClass);		
		
	    //PK: Fix for chrom or safari. These browsers don't support active element so I have to simulate!
	    //Used in the input blur event
	    $container.mouseover(function(event)
	        {
	            _objActiveElementFix = this;
	        }
	    )
	    .mouseout(function(event)
	        {
                _objActiveElementFix = null;
	        }
	    )	
		
		debug("setupContainer() end");
		return $container;
	}
	
	function setupInput(options) {
	    debug("setupInput() start");
	    var inputID = elm_id+"_input";
	    var input = null;
	    
        //PK: If we bind to a dropdown that we have already been bound to e.g in a form reset then we need to destroy the contanier first
	    //destroy is needed and not removeChild as this messes up events clicks in every other browser than IE
	    if($("#" + inputID).length > 0)
	    {  
            input = $("#" + inputID);
            debug("destroy input: #" + inputID);
            input.detach();
	    }
	    
	    if($("#" + inputID).length == 0)
	    {
		    input = document.createElement("input");
		    $input = $(input);
		    $input.attr("id", inputID);
		    $input.attr("type", "text");
		    $input.addClass(options.inputClass);
		    $input.attr("autocomplete", "off");
		    //PK: cannot tab to a readonly textbox in safari or chrome
		    //$input.attr("readonly", "readonly");
		    $input.attr("tabIndex", $select.attr("tabindex")); // "I" capital is important for ie
		}
		else
		{
		    $input = $("#" + inputID);
		}
		
		debug("setupInput() end");
		return $input;	
	}
	
	/* Helper functions */
	
	//PK: On prepare filter to filter items in select list
    function inputOnKeyDown(event)
    {
        var character = String.fromCharCode(event.keyCode);
        
        processTimeSinceLastKeyPress();
               
        //taken from character codes at http://www.scottklarr.com/topic/126/how-to-create-ctrl-key-shortcuts-in-javascript/
        if((event.keyCode >= 48 && event.keyCode <= 90) || event.keyCode == 32) 
        {
            //by default cancel event action
            event.preventDefault();
                
            //alpha numerics or space
            setDropdownFilter(_sDropdownFilter + character);
            
            filterList(_sDropdownFilter);
            
            showMe();
            //$container.show();
        }
        else if (event.keyCode >= 96 && event.keyCode <= 105)
        {
            //by default cancel event action
            event.preventDefault();
                
            //number page keys
            switch(event.keyCode)
            {
                case 96:
                    setDropdownFilter(_sDropdownFilter + "0");
                    break;
                case 97:
                    setDropdownFilter(_sDropdownFilter + "1");
                    break;
                case 98:
                    setDropdownFilter(_sDropdownFilter + "2");
                    break;
                case 99:
                    setDropdownFilter(_sDropdownFilter + "3");
                    break;
                case 100:
                    setDropdownFilter(_sDropdownFilter + "4");
                    break;
                case 101:
                    setDropdownFilter(_sDropdownFilter + "5");
                    break;
                case 102:
                    setDropdownFilter(_sDropdownFilter + "6");
                    break;
                case 103:
                    setDropdownFilter(_sDropdownFilter + "7");
                    break;    
                case 104:
                    setDropdownFilter(_sDropdownFilter + "8");
                    break;
                case 105:
                    setDropdownFilter(_sDropdownFilter + "9");
                    break;
            }
            
            filterList(_sDropdownFilter);
        }
        else if (event.keyCode == 8)
        {
            //cancel event action
            event.preventDefault();
                
            //delete key
            if(_sDropdownFilter.length > -1)
            {
                //trim last character off
                setDropdownFilter(_sDropdownFilter.substring(0,_sDropdownFilter.length - 1));
                //redisplay list
                filterList(_sDropdownFilter);
            }
        }
    }

    //PK: Filter items in select list
    function filterList(filter)
    {
        var bMatched = false;
        var list = $container.get(0);

        if(filter != "")
        {
            $("li", $container).each(
                function(index)
                {
                    var li = $(this);
                    var text = li.text();
                    var optionText = text.substring(0,filter.length);
                    
                    //not working properly!
                    if(optionText.toLowerCase() == filter.toLowerCase() && !bMatched)
                    {
                        var numberOfStepsToMove = index - active;
                        //move to the selected li
                        //this is flacey... when you try to alert to debug, it will just stop getting the offsetTop ot scrollTop information!!!
                        //if you take out alerts it works fine!
                        moveSelect(numberOfStepsToMove);
                        
                        //I can debug after the moveSelect function!!!
                        //alert(this.offsetTop);
                        
                        //clear selected classes
                        $('li.'+opt.currentClass, $container).removeClass(opt.currentClass);
                        //set as the current item
                        li.addClass(opt.currentClass);
                        
                        bMatched = true;
                    }
                    
                }
            );
        }
        else
        {
            //go to the start of the list
            active = 0;
            moveSelect(1);
        }
    }

    //PK: set the dropdown filter value
	function setDropdownFilter(value)
	{
	    _sDropdownFilter = value;
	}

    //PK: show the list container
    function showMe()
    {
        debug("called showMe()");
        //alert("showMe()");
        $container.show();
	       
        //PK: need to maintain scoll position for chrome
        moveSelect(0); 
        //alert("end showMe()");
    }

	function hideMe() { 
	    debug("called hideMe() called on: " + $container.attr("id"));
	    debug("called hideMe() $container element length: " + $container.length);
		hasfocus = 0;
		$container.hide(); 
	}
	
	function moveSelect(step) {
	    
		var lis = $("li", $container);
		if (!lis || lis.length == 0) return false;
		active += step;
        //loop through list
		if (active < 0) 
		{
			active = lis.size() -1; //PK: change this to minus one because the array is 0 based
		} 
		else if (active >= lis.size()) //PK: canot be equal to size of list
		{
			active = 0;
		}
    
        scroll(lis, active);
		lis.removeClass(opt.currentClass);

		$(lis[active]).addClass(opt.currentClass);
		
		
		//PK: If the container is visible then the user is just scrolling through the options and hasn't selected one
		// if the container isn't visible and the user is scrolling then each change should be a selection
		if(!$container.is(":visible"))
		{
		    setCurrent();
		}
	}
	
	function scroll(list, active) {
      var el = $(list[active]).get(0);
      var list = $container.get(0);
      
      //works when I call move from the main event
      if (el.offsetTop + el.offsetHeight > list.scrollTop + list.clientHeight) {
        list.scrollTop = el.offsetTop + el.offsetHeight - list.clientHeight;      
      } else if(el.offsetTop < list.scrollTop) {
        list.scrollTop = el.offsetTop;
      }
	}
	
	function setCurrent() {	
	    debug("setCurrent()");
	    //PK: need to trigger changed event for .net
		var li = $("li."+opt.currentClass, $container).get(0);
		var sCurrentDropdownValue = getCurrentSelected();
		var el = getUserSelectedValue();
		
		$select.val(el);
		setInputValue($(li).text());

		if(checkIfSelectedItemHasChanged(sCurrentDropdownValue,el))
		{
		    fireSelectedItemChangedEvent();
		}
		
		return true;
	}
	
	//PK: check if value changed
	function checkIfSelectedItemHasChanged()
	{
	    var sCurrentDropdownValue = getCurrentSelected();
		var sUserSelectedValue = getUserSelectedValue();
		
		return checkIfSelectedItemHasChanged(sCurrentDropdownValue, sUserSelectedValue);
	}
	
	//PK: check if value changed
	//PARAM sCurrentDropdownValue = current value in the $select.
	//PARAM sUserSelectedValue = value selected by the user in the styled dropdown
	function checkIfSelectedItemHasChanged(sCurrentDropdownValue, sUserSelectedValue)
	{
	    //PK: fire the list changed event
		debug("User user selected value: " + sUserSelectedValue + ". Current dropdown value: " + sCurrentDropdownValue)
	    if(sUserSelectedValue != sCurrentDropdownValue)
	    {
	        debug("checkIfSelectedItemHasChanged() returns true");
		    return true;
		}
		else
		{
		    return false;
		}
	}
	
	//PK: fire select box changed events
	function fireSelectedItemChangedEvent()
	{
	    if($.browser.msie)
	    {
	        //PK: for .NET validators to work we need to fire the change event with this custom jQuery event fireing
	        //got from http://stackoverflow.com/questions/168596/programmatically-triggering-events-in-javascript-for-ie-using-jquery
	        $select.fire("change").blur();
	    }
	    else
	    {
		    $select.change();
		    //$select.get(0).blur();
		}
	}
	
	//PK: Gets the value selected by the user in the styled dropdown
	function getUserSelectedValue()
	{
	    var li = $("li."+opt.currentClass, $container).get(0);
		var ar = (''+li.id).split('_');
		var el = ar[ar.length-1];
		return el;
	}
	
	// select value
	function getCurrentSelected() {
		return $select.val();
	}
	
	// input value
	function getCurrentValue() {
		return $input.val();
	}
	
	//PK: input value
	function setInputValue(value) 
	{
	    debug("setInputValue: " + value);
	    if(opt.maxLength != null && opt.maxLength > 0 && value != null && value.length > 0)
	    {
	        $input.val(value.substring(0,opt.maxLength));
	    }
	    else
	    {
		    $input.val(value);
		}
		debug("setInputValue: value has been set to: " + $input.val());
	}
	
	function getSelectOptions(parentid) {
	    debug("getSelectOptions");
	    
		var select_options = new Array();
		var ul = null;
		
	    ul = document.createElement('ul');
	    $select.children('option').each(function(index) {
		    var li = document.createElement('li');
		    li.setAttribute('id', parentid + '_' + $(this).val());		
		    li.innerHTML = $(this).html();
		    if ($(this).is(':selected')) {
			    setInputValue($(this).text());
			    $(li).addClass(opt.currentClass);
			    //PK: set the list step
			    active = index;
		    }
		    ul.appendChild(li);
		    $(li)
		    .mouseover(function(event) {
			    hasfocus = 1;
			    debug('over on : '+this.id);
			    jQuery(event.target, $container).addClass(opt.hoverClass);
		    })
		    .mouseout(function(event) {
			    hasfocus = -1;
			    debug('out on : '+this.id);
			    jQuery(event.target, $container).removeClass(opt.hoverClass);
		    })
		    .click(function(event) {
				
			    //PK: Not used and is causing error
                //var fl = $('li.'+opt.hoverClass, $container).get(0);
		        
	            debug('click on :'+li.id);
	            $('li.'+opt.currentClass).removeClass(opt.currentClass); 
	            $(li).addClass(opt.currentClass);
        		
	            //PK: need to set the active step property
	            active = $("li", $container).index($(li));
        		
    		    //PK: TEST 1
	            //setCurrent();
		        
	            //PK:extracted to function    
	            finaliseLiSelection(this);
		    });
	    });
		
		return ul;
	}
	
	function finaliseLiSelection()
	{
	    var currentLI = $('li.'+opt.hoverClass);			
				
		//PK: get the currently selected li
		if(currentLI.length == 0)
		{
		    currentLI = $('li.'+opt.currentClass);
		}
		else
		{
		    currentLI.removeClass(opt.hoverClass);
		    currentLI.addClass(opt.hoverClass);
		}
		finaliseLiSelection(currentLI.get(0));
	}
	
	//PK: finialises the li selection
	//PARAMTER: li as html object NOT jQuery
	function finaliseLiSelection(li)
	{
	    //PK: this must be above setCurrent();
	    hideMe();   
	    
	    //set the selection to the dropdown list
	    setCurrent();
	   	
		//PK: Once we have made a selection we need to clear the filter string
		setDropdownFilter("");
		
		//PK: Stops us showing the container again on focus. This is fox for IE
		focusOnInput(true);
	}
	
	//PK: calls the input focus event.
	//PARAM: true if we want to show the list on focus
	function focusOnInput(bSkipShowContainerOnFocus)
	{
	    //PK: If _bSkipShowContainerOnFocusEvent is true it stops us showing the container again on focus
	    _bSkipShowContainerOnFocusEvent = bSkipShowContainerOnFocus;
	    $input.focus();
	}
	
	//PK: hide all list that have the same style
	function hideAllListOnPage()
	{
	    debug("hideAllListOnPage()");
	    $("."+opt.containerClass).hide();
	}
	
	//PK: If 2 seconds has elapsed since the last key press then we want to restart the dropdown list filter
	//this is the same functionality as a html dropdown box
	function processTimeSinceLastKeyPress()
	{
	    var currentTime = new Date();
        var timeDiffSeconds = ((currentTime.getTime() - _dateTimeLastKeyPress.getTime())/1000);
        
        //if it has been 3 seconds since the last filter then we need to start the filter gain
        if(timeDiffSeconds > 1)
        {
            setDropdownFilter("");
        }
        
        debug('time since last keypress: ' + timeDiffSeconds);
        _dateTimeLastKeyPress = currentTime;
	}
	
	//PK: put debuging into function
	function debug(text)
	{
	    if(opt.debug) 
	    {
	        console.log(text);
	    }
	}
	
	debug("Selectbox end");
};

