/* jQuery Autocomplete
 * Version 1.0
 * Written by Yehuda Katz (wycats@gmail.com) and Rein Henrichs (reinh@reinh.com)
 * @requires jQuery v1.2, jQuery dimensions plugin
 *
 * Copyright 2007 Yehuda Katz, Rein Henrichs
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 */

/*
 * @description Form autocomplete plugin using preloaded or Ajax JSON data source 
 *
 * @example $('input#user-name').autocomplete({list: ["quentin", "adam", "admin"]})
 * @desc Simple autocomplete with basic JSON data source
 *
 * @example $('input#user-name').autocomplete({ajax: "/usernames.js"})
 * @desc Simple autocomplete with Ajax loaded JSON data source
 *
 */

/*extern document, jQuery, window, activate */ 

(function(jQuery) {
  
  jQuery.autocomplete = jQuery.autocomplete || {}; 
  var active;
  
  var acopt;
  
  var KEY = {
    ESC: 27,
    RETURN: 13,
    TAB: 9,
    BS: 8,
    DEL: 46,
    UP: 38,
    DOWN: 40
  };
  
  function preventReturnInAutocompleteMode(e) {
    //if (!acopt || (acop && acopt.catchReturn)) {
      var k = e.which || e.keycode;
      if (jQuery.data(document.body, "autocompleteMode") && k == KEY.RETURN) {
        e.preventDefault();
      }
    //}
  }
	  
  jQuery.fn.autocompleteMode = function(container, input, size, opt) {
    var original = input.val(); 
    var selected = -1; 
    var self = this;
    
    jQuery.data(document.body, "autocompleteMode", true);

    jQuery("body").one("cancel.autocomplete", function() { 
      input.trigger("cancel.autocomplete"); 
      jQuery("body").trigger("off.autocomplete"); 
      input.val(original); 
    });
    
    jQuery("body").one("activate.autocomplete", function() {
      if (active && active[0]) { 
        activate();
        input.trigger("activate.autocomplete", [jQuery.data(active[0], "originalObject")]); 
      }
      jQuery("body").trigger("off.autocomplete");
    });
    
    jQuery("body").one("off.autocomplete", function(e, reset) {
      container.remove();
      jQuery.data(document.body, "autocompleteMode", false);
      input.unbind("keydown.autocomplete");
      
      jQuery("body").add(window)
        .unbind("click.autocomplete")
        .unbind("cancel.autocomplete")
        .unbind("activate.autocomplete");
    });
    
    // If a click bubbles all the way up to the body, close the autocomplete 
    // - this was originally binded to the window object, however this was 
    // not working correctly in IE7
    jQuery("body").bind("click.autocomplete", function() {
      jQuery("body").trigger("cancel.autocomplete"); 
    });
    
    var select = function() {
      active = jQuery("> *", container).removeClass("active").slice(selected, selected + 1).addClass("active");
      input.trigger("itemSelected.autocomplete", [jQuery.data(active[0], "originalObject")]);
    };
    
    var activate = function() {
      active = jQuery("> *", container).removeClass("active").slice(selected, selected + 1).addClass("active");
      if (active && active[0]) {
        input.val(opt.displayText(jQuery.data(active[0], "originalObject")));
        if (opt.idfield) {
          jQuery(opt.idfield).val(jQuery.data(active[0], "originalObject").id);
          if (opt.afterSelect){
        	  opt.afterSelect(jQuery.data(active[0], "originalObject"));
          }
        }
        if (opt.afterSelect){
      	  opt.afterSelect(jQuery.data(active[0], "originalObject"));
      }
      }
      
    };
    
    container.mouseover(function(e) {
      // If you hover over the container, but not its children, return
      if (e.target == container[0])  { return; }
      
      // Set the selected item to the item hovered over and make it active
      selected = jQuery("> *", container).index(jQuery(e.target).is('li') ? jQuery(e.target)[0] : jQuery(e.target).parents('li')[0]);
      select();
    }).bind("click.autocomplete", function(e) {
      jQuery("body").trigger("activate.autocomplete"); 
      jQuery.data(document.body, "suppressKey", false); 
    });
    
    input.bind("keydown.autocomplete", function(e) {
        if (e.which == KEY.ESC ) { 
          jQuery("body").trigger("cancel.autocomplete"); 
        } else if (e.which == KEY.RETURN) {
          jQuery("body").trigger("activate.autocomplete");  
          return false;
        } else if (e.which == KEY.TAB) { 
          jQuery("body").trigger("activate.autocomplete");  
          preventReturnInAutocompleteMode(e);
          return false;
        } else if(e.which == KEY.UP || e.which == KEY.DOWN) {
          switch(e.which) {
            case KEY.DOWN: 
              selected = selected >= size - 1 ? 0 : selected + 1; 
              if (jQuery.browser.msie && jQuery.browser.version == 6) {
                selected += 1;
              }
              break;
            case KEY.UP:
              selected = selected <= 0 ? size - 1 : selected - 1; 
              if (jQuery.browser.msie && jQuery.browser.version == 6) {
                selected += 1;
              }
              break;
            default: break;
          }
          select();
        } else { return true; }
        jQuery.data(document.body, "suppressKey", true);
      });
  };
  
  jQuery.fn.autocomplete = function(opt) {
    
    opt = jQuery.extend({}, {
      timeout: 1000,
      getList: function(input) { input.trigger("updateList", [opt.list]); },
      template: function(str) { return "<li>" + opt.insertText(str) + "</li>"; },
      insertText: function(str) { return str; },
      displayText: function(str) { return str.name; },
      wrapper: "<ul class='jq-ui-autocomplete'></ul>", 
      minChar: 2,
      cacheSize: 20,
      ie6width: '400px',
      catchReturn: false 
    }, opt);

    acopt = opt;
    
    if (jQuery.autocomplete.ext) {
      for (var ext in jQuery.autocomplete.ext) {
        if(opt[ext]) {
          opt = jQuery.extend(opt, jQuery.autocomplete.ext[ext](opt));
          delete opt[ext];
        }
      } 
    }
    
    if (jQuery.jCache && opt.cacheSize > 0) {
      jQuery.jCache.maxSize = opt.cacheSize;
    }

	  function preventTabInAutocompleteMode(e) {
	    var k = e.which || e.keycode;
	    if (jQuery.data(document.body, "autocompleteMode") && k == KEY.TAB) {
	      e.preventDefault();
	    }
	  }
	
	  function startTypingTimeout(e) {
	    jQuery.data(this, "typingTimeout", window.setTimeout(function() {
	      jQuery(e.target || e.srcElement).trigger("autocomplete");
	    }, opt.timeout));
	  }

    return this.each(function() {
  
      jQuery(this)
        .keydown(function(e) {
          if ((jQuery.browser.safari || jQuery.browser.msie) && e.which == KEY.BS) {
            startTypingTimeout(e);
          }
          preventTabInAutocompleteMode(e);
          preventReturnInAutocompleteMode(e);
        })
        .keyup(function(e) {
          var k = e.which || e.keycode;
          if (!jQuery.data(document.body, "autocompleteMode") 
              && (k == KEY.UP || k == KEY.DOWN) 
              && !jQuery.data(this, "typingTimeout")) {
            startTypingTimeout(e);
          } else {
            preventTabInAutocompleteMode(e);
            preventReturnInAutocompleteMode(e);
          }
        })
        .keypress(function(e) { 
          var typingTimeout = jQuery.data(this, "typingTimeout");
          var k = e.keyCode || e.which; // keyCode == 0 in Gecko/FF on keypress
          if(typingTimeout) { window.clearInterval(typingTimeout); }

          if(jQuery.data(document.body, "suppressKey")) {
            return jQuery.data(document.body, "suppressKey", false);
          } else if (jQuery.data(document.body, "autocompleteMode") && k < 32 && k != KEY.BS && k != KEY.DEL) { 
            return false; 
          } else if (k > 32 || k == KEY.BS) { 
            // more than ESC and RETURN and the like
            startTypingTimeout(e);
          }
        })
        .bind("autocomplete", function() {
          var self = jQuery(this);
          
          if (self.val().length < opt.minChar) {
            jQuery("body").trigger("off.autocomplete");
            return;
          }
          
          self.one("updateList", function(e, list) {
          opt.searchstring = self.val();
          if (opt.match) {
            list = jQuery(list)
              .filter(function() { return opt.match.call(this, self.val()); })
              .map(function() {
                var node = jQuery(opt.template(this))[0];
                jQuery.data(node, "originalObject", this);
                return node; 
              });
            } else {
              list = jQuery(list)
              .map(function() {
                var node = jQuery(opt.template(this))[0];
                jQuery.data(node, "originalObject", this);
                return node; 
              });
            } 
          
            jQuery("body").trigger("off.autocomplete");
            if (!list.length) { return false; }
          
            var container = list.wrapAll(opt.wrapper).parents(":last").children();
            // IE seems to wrap the wrapper in a random div wrapper so
            // drill down to the node in opt.wrapper.
            var wrapper_tagName = jQuery(opt.wrapper)[0].tagName;
            for (;container[0].tagName !== wrapper_tagName; container = container.children(':first')) {}
            
            var offset = self.offset();
            
            if ( (jQuery.browser.msie) && (jQuery.browser.version==6) ) {
	            opt.container = container
	              .css({top: offset.top + self.outerHeight(), left: offset.left, width: opt.ie6width})
	              .appendTo("body");
	          } else {
	            opt.container = container
	              .css({top: offset.top + self.outerHeight(), left: offset.left, minWidth: self.width()})
	              .appendTo("body");
	          }
	          
	          
	          jQuery(".jq-ui-autocomplete li").css({width: container.width()});
            container.bgiframe();
            jQuery("body").autocompleteMode(container, self, list.length, opt);
          });

          opt.getList(self);
        });

    });
  };
  
})(jQuery);

/*
 * jQuery UI Autocomplete
 * version: 1.0 (1/2/2008)
 * @requires: jQuery v1.2 or later, dimensions plugin
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Copyright 2007 Yehuda Katz, Rein Henrichs
 */
/*extern found jQuery*/

(function(jQuery) {
 
  jQuery.autocomplete = jQuery.autocomplete || {};
  jQuery.autocomplete.ext = jQuery.autocomplete.ext || {};
  
  jQuery.autocomplete.ext.ajax = function(opt) {
    var ajax = opt.ajax;
    return { getList: function(input) { 
      found = false;
      if (jQuery.jCache && opt.cacheSize > 0) {
        if (jQuery.jCache.hasItem(input.val())) {
          found = true;
          input.trigger("updateList", [jQuery.jCache.getItem(input.val())]);
        } 
      }  
      if (!found) {
	      jQuery.getJSON(ajax, "s=" + encodeURIComponent(input.val()), function(json) {
	        if (jQuery.jCache && opt.cacheSize > 0) {
	          jQuery.jCache.setItem(input.val(), json);
	        }
	        input.trigger("updateList", [json]); 
	      }); 
	    }
	  }};
  };
  
  jQuery.autocomplete.ext.templateText = function(opt) {
    var template = jQuery.makeTemplate(opt.templateText, "<%", "%>");
    return { template: function(obj) { return template(obj); } };
  };
  
})(jQuery);

/* Copyright (c) 2006 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * $LastChangedDate: 2007-07-21 18:44:59 -0500 (Sat, 21 Jul 2007) $
 * $Rev: 2446 $
 *
 * Version 2.1.1
 *
 * 
 */
 
/*extern document, jQuery, navigator */
(function(jQuery){

/**
 * The bgiframe is chainable and applies the iframe hack to get 
 * around zIndex issues in IE6. It will only apply itself in IE6 
 * and adds a class to the iframe called 'bgiframe'. The iframe
 * is appeneded as the first child of the matched element(s) 
 * with a tabIndex and zIndex of -1.
 * 
 * By default the plugin will take borders, sized with pixel units,
 * into account. If a different unit is used for the border's width,
 * then you will need to use the top and left settings as explained below.
 *
 * NOTICE: This plugin has been reported to cause perfromance problems
 * when used on elements that change properties (like width, height and
 * opacity) a lot in IE6. Most of these problems have been caused by 
 * the expressions used to calculate the elements width, height and 
 * borders. Some have reported it is due to the opacity filter. All 
 * these settings can be changed if needed as explained below.
 *
 * @example $('div').bgiframe();
 * @before <div><p>Paragraph</p></div>
 * @result <div><iframe class="bgiframe".../><p>Paragraph</p></div>
 *
 * @param Map settings Optional settings to configure the iframe.
 * @option String|Number top The iframe must be offset to the top
 * 		by the width of the top border. This should be a negative 
 *      number representing the border-top-width. If a number is 
 * 		is used here, pixels will be assumed. Otherwise, be sure
 *		to specify a unit. An expression could also be used. 
 * 		By default the value is "auto" which will use an expression 
 * 		to get the border-top-width if it is in pixels.
 * @option String|Number left The iframe must be offset to the left
 * 		by the width of the left border. This should be a negative 
 *      number representing the border-left-width. If a number is 
 * 		is used here, pixels will be assumed. Otherwise, be sure
 *		to specify a unit. An expression could also be used. 
 * 		By default the value is "auto" which will use an expression 
 * 		to get the border-left-width if it is in pixels.
 * @option String|Number width This is the width of the iframe. If
 *		a number is used here, pixels will be assume. Otherwise, be sure
 * 		to specify a unit. An experssion could also be used.
 *		By default the value is "auto" which will use an experssion
 * 		to get the offsetWidth.
 * @option String|Number height This is the height of the iframe. If
 *		a number is used here, pixels will be assume. Otherwise, be sure
 * 		to specify a unit. An experssion could also be used.
 *		By default the value is "auto" which will use an experssion
 * 		to get the offsetHeight.
 * @option Boolean opacity This is a boolean representing whether or not
 * 		to use opacity. If set to true, the opacity of 0 is applied. If
 *		set to false, the opacity filter is not applied. Default: true.
 * @option String src This setting is provided so that one could change 
 *		the src of the iframe to whatever they need.
 *		Default: "javascript:false;"
 *
 * @name bgiframe
 * @type jQuery
 * @cat Plugins/bgiframe
 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
 */
jQuery.fn.bgIframe = jQuery.fn.bgiframe = function(s) {
	// This is only for IE6
	if ( jQuery.browser.msie && /6.0/.test(navigator.userAgent) ) {
		s = jQuery.extend({
			top     : 'auto', // auto == .currentStyle.borderTopWidth
			left    : 'auto', // auto == .currentStyle.borderLeftWidth
			width   : 'auto', // auto == offsetWidth
			height  : 'auto', // auto == offsetHeight
			opacity : true,
			src     : 'javascript:false;'
		}, s || {});
		var prop = function(n){return n&&n.constructor==Number?n+'px':n;},
		    html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
		               'style="display:block;position:absolute;z-index:-1;'+
			               (s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
					       'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+
					       'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+
					       'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+
					       'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+
					'"/>';
		return this.each(function() {
			if (jQuery('> iframe.bgiframe', this).length === 0) {
				this.insertBefore( document.createElement(html), this.firstChild );
			}
		});
	}
	return this;
};

})(jQuery);

/**
 * jCache - A client cache plugin for jQuery
 * Should come in handy when data needs to be cached in client to improve performance.
 * Author: 	Phan Van An 
 *			phoenixheart@gmail.com
 *			http://www.skidvn.com
 * License : Read jQuery's license

Usage:
    1. 	Include this plugin into your web document after jQuery:
    	<script type="text/javascript" src="js/jquery.jcache.js"></script>
    2.	[OPTIONAL] Set the max cached item number, for example 20
    	$.jCache.maxSize = 20; 
    3. 	Start playing around with it:
    	- Put an item into cache: $.jCache.setItem(theKey, the Value);
    	- Retrieve an item from cache: var theValue = $.jCache.getItem(theKey);
    	- ...
 */
(function (jQuery){
	this.version = '(beta)(0.0.1)';
	 jQuery.jCache = jQuery.jCache || {};
	
	/**
	 * The maximum items this cache should hold. 
	 * If the cache is going to be overload, oldest item will be deleted (FIFO).
	 * Since the cached object is retained inside browser's state, 
	 * a too big value on a too big web apps may affect system memory.
	 * Default is 10.
	 */
	this.maxSize = 10;
	
    /**
     * An array to keep track of the cache keys
     */
	this.keys = new Array();
	
	/**
	 * Number of currently cached items
	 */
	this.cache_length = 0;
	
	/**
	 * An associated array to contain the cached items
	 */
	this.items = new Array();
	
	/*
	 * @desc	Puts an item into the cache
	 *
	 * @param	string Key of the item
	 * @param 	string Value of the item
	 * @return	string Value of the item
	 */
	this.setItem = function(pKey, pValue)
	{
		if (typeof(pValue) != 'undefined') 
		{
			if (typeof(this.items[pKey]) == 'undefined') 
			{
				this.cache_length++;
			}

			this.keys.push(pKey);
			this.items[pKey] = pValue;
			
			if (this.cache_length > this.maxSize)
			{
				this.removeOldestItem();
			}
		}
	   
		return pValue;
	}
	
	/*
	 * @desc	Removes an item from the cache using its key
	 * @param 	string Key of the item
	 */
	this.removeItem = function(pKey)
	{
		var tmp;
		if (typeof(this.items[pKey]) != 'undefined') 
		{
			this.cache_length--;
			var tmp = this.items[pKey];
			delete this.items[pKey];
		}
	   
		return tmp;
	}

	/*
	 * @desc 	Retrieves an item from the cache by its key
	 *
	 * @param 	string Key of the item
	 * @return	string Value of the item
	 */
	this.getItem = function(pKey) 
	{
		return this.items[pKey];
	}

	/*
	 * @desc	Indicates if the cache has an item specified by its key
	 * @param 	string Key of the item
	 * @return 	boolean TRUE or FALSE
	 */
	this.hasItem = function(pKey)
	{
		return typeof(this.items[pKey]) != 'undefined';
	}
	
	/**
	 * @desc	Removes the oldest cached item from the cache
	 */
	this.removeOldestItem = function()
	{
		this.removeItem(this.keys.shift());
	}
	
	/**
	 * @desc	Clears the cache
	 * @return	Number of items cleared
	 */
	this.clear = function()
	{
		var tmp = this.cache_length;
		this.keys = new Array();
		this.cache_length = 0;
		this.items = new Array();
		return tmp;
	}
	
	jQuery.jCache = this;
	return jQuery;
})(jQuery);

