/**
 * @fileoverview jQuery Autocomplete
 * Version 1.2 (13/03/2010)
 * Written by Yehuda Katz (wycats@gmail.com) and Rein Henrichs (reinh@reinh.com)
 * Additional contributions from Emmanuel Gomez, Austin King, 
 * Nikos Dimitrakopoulos, Javier Gonel
 * @requires jQuery v1.2, jQuery dimensions plugin
 *
 * Copyright 2007-2010 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
 *
 * Simple autocomplete with basic JSON data source
 * <code>$('input#user-name').autocomplete({list: ["quentin", "adam", "admin"]});</code>
 *
 * Simple autocomplete with Ajax loaded JSON data source
 * <code>$('input#user-name').autocomplete({ajax: "/usernames.js"});</code>
 *
 */
(function($) {
	/** @namespace */
	$.ui = $.ui || {};
  
	/** @namespace */
	$.ui.autocomplete = $.ui.autocomplete || {};
  
	var active = {};

	var KEY = {
		ESC: 27,
	    RETURN: 13,
	    TAB: 9,
	    BS: 8,
	    DEL: 46,
	    UP: 38,
	    DOWN: 40
	};
	
	$.fn.autocomplete = function(opt) {

		/* Default options */
		opt = $.extend({}, {
			/**
			 * Milliseconds after the last keystroke to (re-)filter the list
			 */
			timeout: 500,
			/**
			 * How many options are too many?
			 * if there are more matches than threshold, the list will not be displayed
			 */
			threshold: 100,
			/**
			 * By default the with of the input box is used for the autocomplete
			 * suggestions. If you want to change that, specify here a width in pixels.
			 */
			adjustWidth: true,
			/**
			 * Maximum number of items to show. By default show all.
			 */
			maxResults: undefined,
			/**
			 * Minumum number of characters needed before starting the autocomplete
			 * If set to 'undefined', the empty string will be passed to the
			 * 'filterList' function. This can be used to display
			 * a full list when no characters are introduced.
			 */
			minCharacters: 0,
			/**
			 * Get the complete list of items
			 * override this to control how to get the list of potential matches
			 * default is to use the 'list' option passed during initialization
			 *  and trigger "updateList" event with list as data
			 *
			 * @param input the text input being autocompleted
			 */
			getList: function(input) {
				input.triggerHandler("updateList", [opt.list]);
			},
			/**
			 * Called to determine if a given data item matches the user's input
			 *
			 * @param {string} item data item being tested for match
			 * @param {RegExp} matcher regex to test the item with
			 * @return {boolean} true if this data item matches user input
			 */
			match: function(item, matcher) {
				// Do not use !==
				
				return (item.match)?(item.match(matcher) != undefined):true;
				
			},
			/**
			 * Called to build the matcher
			 *
			 * @param typed the text entered by user in the text input
			 * @return regex used to filter the complete list
			 */
			matcher: function(typed) {
				return new RegExp(typed);
			},
			/**
			 * Update the list of matching items
			 * override this to control how the filtered list is generated from the complete list
			 *
			 * @param list complete list to be filtered
			 * @param val text entered in the text input
			 * @return filtered list with items that match _val_ (however matching is defined)
			 */
			filterList: function(list, val) {
				var matcher = opt.matcher(val),
					grepCallback = function(text, i) {
						return opt.match(text, matcher);
					},
					index = 0;
			
				if (this.maxResults) {
					grepCallback = function(text, i) {
						if (index>this.maxResults) {
							return false;
						}
						index++;
						return opt.match(text, matcher);
					};
				}

				return $.grep(list, grepCallback);
			},
      /**
       * Update the list of matching items
       * override this to control how markup is built from the list of matches
       *
       * @param unfilteredList unfiltered list of potential matches
       * @param val the text in the input field
       *
       * @return container (which should be positioned and visible)
       */
      updateList: function(unfilteredList, val) {
        if (opt.minCharacters && val.length <= opt.minCharacters) {
          return false;
        }

        var list = opt.filterList(unfilteredList, val);
        if(list.length === 0 || list.length > opt.threshold) {return false;}
        return opt.buildList(list);
      },
      /**
       * Build the list of matches
       * override this to control how markup is built from the list of matches
       * make sure the elements in the list have the matching object
       * in their $.fn.data as "originalObject"
       *
       * @param list list of items matching user input
       * @return container
       */
      buildList: function(list) {
        var listItems = $(list).map(function() {
          var node = $(opt.template(this))[0];
          $.data(node, "originalObject", this);
          return node;
        });
        // IE seems to wrap the wrapper in a random div wrapper so
        // drill down to the node in opt.wrapper.
        var container = $(opt.wrapper).append(listItems);// .parents(":last").children();
        // TODO: need to verify that parents(":last").children really does the below
        var wrapTag = $(opt.wrapper)[0].tagName;
        while (container[0].tagName !== wrapTag) {
          container = container.children(':first');
        }
        return container;
      },
      wrapper: "<ul class='jq-ui-autocomplete'></ul>",
      /**
       * Display the list of matches
       * override this to control the container position or size
       * or to skip appending the list to the html body
       *
       * @param input the text input being autocompleted
       * @param container the container of the list of matches (typically an ol/ul)
       *
       * @return container (which should be positioned and visible)
       */
      displayList: function(input, container) {
        var offset = input.offset();
        container
          .css({
            top: offset.top + input.outerHeight(),
            left: offset.left,
            width: this.adjustWidth?input.width():undefined
          })
          .appendTo("body");
        return container;
      },
      /**
       * Dismiss the list of matches
       * override this to control how the list container is dismissed
       * default is to remove the element
       *
       * @param container the container of the list of matches (typically an ol/ul)
       */
      dismissList: function(container) {
        container.remove();
      },
      template: function(str) { return "<li>" + opt.insertText(str) + "</li>"; },
      /**
       * Provide a value for the text input from the active object,
       * also used to fill the li element in the default _template_ implementation
       *
       * @param item active item in the list
       */
      insertText: function(item) { return item; },
      displayText: function(item) { return item; }
    }, opt);

		/* 
		 * Additional options from autocomplete.ext (for example 'ajax', and 'templateText') 
		 * if these options where passed in the opt object and the $.ui.autocomplete.ext is present.
		 */
		if($.ui.autocomplete.ext) {
			for(var ext in $.ui.autocomplete.ext) {
				if(opt[ext]) {
					opt = $.extend(opt, $.ui.autocomplete.ext[ext](opt));
					delete opt[ext];
				}
			} 
		}

		function preventTabInAutocompleteMode(e) {
			var k = e.which || e.keycode;
			if ($.data(document.body, "autocompleteMode") && k == KEY.TAB) {
				e.preventDefault();
			}
		}

    function startTypingTimeout(e, element) {
      $.data(element, "typingTimeout", window.setTimeout(function() {
        $(e.target || e.srcElement).triggerHandler("autocomplete");
      }, opt.timeout));
    }

    function  handleKeyDownUp(e) {
      var k = e.which || e.keycode;
      if ((k == KEY.UP || k == KEY.DOWN) && !$.data(this, "typingTimeout")) {
        startTypingTimeout(e, this);
      } else if (k == KEY.BS || k == KEY.DEL) {
        var typingTimeout = $.data(this, "typingTimeout");
        if (typingTimeout) {
          window.clearInterval(typingTimeout);
        }
        startTypingTimeout(e, this);
      } else {
        preventTabInAutocompleteMode(e);
      }
    }
    
		return this.each(function() {
			$(this)
				//.attr("autocomplete", "off")
				.keydown(handleKeyDownUp)
				.keyup(handleKeyDownUp)
				.keypress(function(e) {
					var typingTimeout = $.data(this, "typingTimeout");
					var k = e.keyCode || e.which; // keyCode == 0 in Gecko/FF on keypress
					if(typingTimeout) {
						window.clearInterval(typingTimeout);
					}

					if($.data(document.body, "suppressKey")) {
						return $.data(document.body, "suppressKey", false);
					} else if($.data(document.body, "autocompleteMode") && k < 32 && k != KEY.BS && k != KEY.DEL) {
						return false;
					} else if (k == KEY.BS || k == KEY.DEL || k > 32) { // more than ESC and RETURN and the like
						startTypingTimeout(e, this);
					}
				})
				.bind("autocomplete", function() {
					var self = $(this);

					self.one("updateList", function(e, completeList, matchVal) {
				        opt.searchstring = matchVal || self.val();
						var container = opt.updateList(completeList, matchVal || self.val());
						// turn off autcomplete mode even if the list is empty (container === false)
						$("body").triggerHandler("off.autocomplete");
						if (container === false) { return false; }

						opt.container = opt.displayList(self, container);
						$("body").autocompleteMode(opt.container, self, container.find("li").length, opt);
					});

					opt.getList(self);
				});

				if (typeof opt.init == "function") { opt.init(self); }
		});
	};

	$.fn.autocompleteMode = function(container, input, size, opt) {
		var original = input.val(),
			selected = -1,
			mouseDown = false;

		$.data(document.body, "autocompleteMode", true);

		$("body").one("cancel.autocomplete", function() {
			input.triggerHandler("cancelled.autocomplete");
			$("body").triggerHandler("off.autocomplete");
			input.val(original);
		});

		$("body").bind("activate.autocomplete", function(e) {
			//console.log('activate');
			// Try hitting return to activate autocomplete and then hitting it again on blank input
			// to close it.  w/o checking the active object first this input.triggerHandler() will barf.
			if (active.length) {
				// if activate and selected -> clicked!
				input.triggerHandler("activated.autocomplete", [$.data(active[0], "originalObject"), active]);
			}
			// if nothing active when activate event and we had a mousedown event before
			// then the user is dragging something in the list. Ignore action.
			else if(mouseDown) {
				mouseDown = false;
				if(document.activeElement && document.activeElement != input)
					input.trigger("focus");
				return false;
			}
			$("body").triggerHandler("off.autocomplete");
		});

	$("body").one("off.autocomplete", function(e, reset) {
		opt.dismissList(container);
		$.data(document.body, "autocompleteMode", false);
		input.unbind("keydown.autocomplete");
		$("body").add(window)
			.unbind("click.autocomplete")
			.unbind("cancel.autocomplete")
			.unbind("activate.autocomplete");
    });

	// If a click bubbles all the way up to the window, close the autocomplete
	$(window).bind("click.autocomplete", function() {
		//alert('test');
		$("body").triggerHandler("cancel.autocomplete");
	});

    var select = function() {
      active = container.find("li")
        .removeClass("active")
        .filter(":visible")
        .slice(selected, selected + 1)
        .addClass("active");
      if (active.length) {
        input.triggerHandler("itemSelected.autocomplete", [$.data(active[0], "originalObject"), active]);
        input.val(opt.displayText($.data(active[0], "originalObject")));
      } else {
        input.triggerHandler("noneSelected.autocomplete");
        input.val(original);
      }
    };

	container
		.mouseover(function(e) {
	        // If you hover over the container, but not its children, return
	        if(e.target == container[0]) { return; }
	        var selectedItem = $(e.target).is('li') ? $(e.target)[0] : $(e.target).parents('li')[0];
	        // Set the selected item to the item hovered over and make it active
	        selected = container.find("li").index(selectedItem);
	        select();
		})
		// IE8 was triggering spurious activate events when clicking outside
		// the container when a list item was active. I couldn't figure out where
		// the activate events were coming from, so I'm deactivating the list
		.mouseout(function(e) {
			selected = -1;
			select();
		})
		.mousedown(function() { mouseDown = true; 
		  //console.log('mousedown');
		})
		.bind("click.autocomplete", function(e) {
        $("body").triggerHandler("activate.autocomplete");
        $.data(document.body, "suppressKey", false);
      });

    input
      .bind("keydown.autocomplete", function(e) {
        var k = e.which || e.keyCode; // in IE e.which is undefined

        switch(k) {
          case KEY.ESC:
            $("body").triggerHandler("cancel.autocomplete");
            break;
			// TAB & Return behave in the same way.
          case KEY.TAB:
		  case KEY.RETURN:
            if (selected == -1) {
				// If nothing is selected, select the first
				selected = selected >= size - 1 ? 0 : selected + 1;
				select();
            } 
            $("body").triggerHandler("activate.autocomplete");
            break;
          case KEY.DOWN:
            selected = selected >= size - 1 ? 0 : selected + 1;
            select();
            break;
          case KEY.UP:
            selected = selected <= 0 ? size - 1 : selected - 1;
            select();
            break;
          default:
            return true;
        }

        $.data(document.body, "suppressKey", true);
      });
  };

})(jQuery);


/*
 * jQuery Autocomplete Extensions
 * Version 1.2 (07/03/2010)
 * Written by Yehuda Katz (wycats@gmail.com) and Rein Henrichs (reinh@reinh.com)
 * Additional contributions from Emmanuel Gomez, Austin King
 * Nikos Dimitrakopoulos, Javier Gonel
 * @requires: jQuery v1.2 or later
 * 
 * Copyright 2007-2009 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
 *
 */

(function($) {
	/** @namespace */
  $.ui = $.ui || {};
	/** @namespace */
  $.ui.autocomplete = $.ui.autocomplete || {};
	/** @namespace */
  $.ui.autocomplete.ext = $.ui.autocomplete.ext || {};
  
  /*
   * @description Overrides the default 'getList' option with remote call replacement.
   *
   * @param {Object} opt Should contain a .ajax property with the url of the remote service to call.
   * @returns A function which calls the remote service, fetches the result and triggers an "updateList" event on the input element.
   */
  $.ui.autocomplete.ext.ajax = function(opt) {
    var ajax = opt.ajax;
    return { getList: function(input) {
      if (input.val().match(/^\s*$/)) return false;
      $.getJSON(ajax, { s: input.val(), atr:input.attr('atr') }, function(json) { input.trigger("updateList", [json]); });
    } };
  };

  /*
   * @description Overrides the default 'template' option.
   *
   * @param {Object} opt Should contain a .templateText string with the template to parse and return.
   *    if .templateBegin or .templateEnd are present,
   *    they will determine begin/end tags in template compilation
   * @returns A function that executes the given template with the obj passed to it.
   */
  $.ui.autocomplete.ext.templateText = function(opt) {
    var begin = opt.templateBegin || "<%", end = opt.templateEnd || "%>";
    var template = $.makeTemplate(opt.templateText, begin, end);
    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);


