text: /*
   *
   * Copyright (c) 2006-2010 Sam Collett (http://www.texotela.co.uk)
   * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
   * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
   *
   * Version 2.2.5
   * Demo: http://www.texotela.co.uk/code/jquery/select/
   *
   *
   */
  
  ;(function($) {
  
  /**
   * Adds (single/multiple) options to a select box (or series of select boxes)
   *
   * @name     addOption
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     jQuery
   * @example  $("#myselect").addOption("Value", "Text"); // add single value (will be selected)
   * @example  $("#myselect").addOption("Value 2", "Text 2", false); // add single value (won't be selected)
   * @example  $("#myselect").addOption({"foo":"bar","bar":"baz"}, false); // add multiple values, but don't select
   *
   */
  $.fn.addOption = function()
  {
  	var add = function(el, v, t, sO, index)
  	{
  		var option = document.createElement("option");
  		option.value = v, option.text = t;
  		// get options
  		var o = el.options;
  		// get number of options
  		var oL = o.length;
  		if(!el.cache)
  		{
  			el.cache = {};
  			// loop through existing options, adding to cache
  			for(var i = 0; i < oL; i++)
  			{
  				el.cache[o[i].value] = i;
  			}
  		}
  		if (index || index == 0)
  		{
   			// we're going to insert these starting  at a specific index...
  			// this has the side effect of el.cache[v] being the
  			// correct value for the typeof check below
  			var ti = option;
  			for(var ii =index; ii <= oL; ii++)
  			{
  				var tmp = el.options[ii];
  				el.options[ii] = ti;
  				o[ii] = ti;
  				el.cache[o[ii].value] = ii;
  				ti = tmp;
  			}
  		}
  
  		// add to cache if it isn't already
  		if(typeof el.cache[v] == "undefined") el.cache[v] = oL;
  		el.options[el.cache[v]] = option;
  		if(sO)
  		{
  			option.selected = true;
  		}
  	};
  
  	var a = arguments;
  	if(a.length == 0) return this;
  	// select option when added? default is true
  	var sO = true;
  	// multiple items
  	var m = false;
  	// other variables
  	var items, v, t;
  	if(typeof(a[0]) == "object")
  	{
  		m = true;
  		items = a[0];
  	}
  	if(a.length >= 2)
  	{
  		if(typeof(a[1]) == "boolean")
  		{
  			sO = a[1];
  			startindex = a[2];
  		}
  		else if(typeof(a[2]) == "boolean")
  		{
  			sO = a[2];
  			startindex = a[1];
  		}
  		else
  		{
  			startindex = a[1];
  		}
  		if(!m)
  		{
  			v = a[0];
  			t = a[1];
  		}
  	}
  	this.each(
  		function()
  		{
  			if(this.nodeName.toLowerCase() != "select") return;
  			if(m)
  			{
  				for(var item in items)
  				{
  					add(this, item, items[item], sO, startindex);
  					startindex += 1;
  				}
  			}
  			else
  			{
  				add(this, v, t, sO, startindex);
  			}
  		}
  	);
  	return this;
  };
  
  /**
   * Add options via ajax
   *
   * @name     ajaxAddOption
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     jQuery
   * @param    String url      Page to get options from (must be valid JSON)
   * @param    Object params   (optional) Any parameters to send with the request
   * @param    Boolean select  (optional) Select the added options, default true
   * @param    Function fn     (optional) Call this function with the select object as param after completion
   * @param    Array args      (optional) Array with params to pass to the function afterwards
   * @example  $("#myselect").ajaxAddOption("myoptions.php");
   * @example  $("#myselect").ajaxAddOption("myoptions.php", {"code" : "007"});
   * @example  $("#myselect").ajaxAddOption("myoptions.php", {"code" : "007"}, false, sortoptions, [{"dir": "desc"}]);
   *
   */
  $.fn.ajaxAddOption = function(url, params, select, fn, args)
  {
  	if(typeof(url) != "string") return this;
  	if(typeof(params) != "object") params = {};
  	if(typeof(select) != "boolean") select = true;
  	this.each(
  		function()
  		{
  			var el = this;
  			$.getJSON(url,
  				params,
  				function(r)
  				{
  					$(el).addOption(r, select);
  					if(typeof fn == "function")
  					{
  						if(typeof args == "object")
  						{
  							fn.apply(el, args);
  						}
  						else
  						{
  							fn.call(el);
  						}
  					}
  				}
  			);
  		}
  	);
  	return this;
  };
  
  /**
   * Removes an option (by value or index) from a select box (or series of select boxes)
   *
   * @name     removeOption
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     jQuery
   * @param    String|RegExp|Number what  Option to remove
   * @param    Boolean selectedOnly       (optional) Remove only if it has been selected (default false)
   * @example  $("#myselect").removeOption("Value"); // remove by value
   * @example  $("#myselect").removeOption(/^val/i); // remove options with a value starting with 'val'
   * @example  $("#myselect").removeOption(/./); // remove all options
   * @example  $("#myselect").removeOption(/./, true); // remove all options that have been selected
   * @example  $("#myselect").removeOption(0); // remove by index
   * @example  $("#myselect").removeOption(["myselect_1","myselect_2"]); // values contained in passed array
   *
   */
  $.fn.removeOption = function()
  {
  	var a = arguments;
  	if(a.length == 0) return this;
  	var ta = typeof(a[0]);
  	var v, index;
  	// has to be a string or regular expression (object in IE, function in Firefox)
  	if(ta == "string" || ta == "object" || ta == "function" )
  	{
  		v = a[0];
  		// if an array, remove items
  		if(v.constructor == Array)
  		{
  			var l = v.length;
  			for(var i = 0; i<l; i++)
  			{
  				this.removeOption(v[i], a[1]);
  			}
  			return this;
  		}
  	}
  	else if(ta == "number") index = a[0];
  	else return this;
  	this.each(
  		function()
  		{
  			if(this.nodeName.toLowerCase() != "select") return;
  			// clear cache
  			if(this.cache) this.cache = null;
  			// does the option need to be removed?
  			var remove = false;
  			// get options
  			var o = this.options;
  			if(!!v)
  			{
  				// get number of options
  				var oL = o.length;
  				for(var i=oL-1; i>=0; i--)
  				{
  					if(v.constructor == RegExp)
  					{
  						if(o[i].value.match(v))
  						{
  							remove = true;
  						}
  					}
  					else if(o[i].value == v)
  					{
  						remove = true;
  					}
  					// if the option is only to be removed if selected
  					if(remove && a[1] === true) remove = o[i].selected;
  					if(remove)
  					{
  						o[i] = null;
  					}
  					remove = false;
  				}
  			}
  			else
  			{
  				// only remove if selected?
  				if(a[1] === true)
  				{
  					remove = o[index].selected;
  				}
  				else
  				{
  					remove = true;
  				}
  				if(remove)
  				{
  					this.remove(index);
  				}
  			}
  		}
  	);
  	return this;
  };
  
  /**
   * Sort options (ascending or descending) in a select box (or series of select boxes)
   *
   * @name     sortOptions
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     jQuery
   * @param    Boolean ascending   (optional) Sort ascending (true/undefined), or descending (false)
   * @example  // ascending
   * $("#myselect").sortOptions(); // or $("#myselect").sortOptions(true);
   * @example  // descending
   * $("#myselect").sortOptions(false);
   *
   */
  $.fn.sortOptions = function(ascending)
  {
  	// get selected values first
  	var sel = $(this).selectedValues();
  	var a = typeof(ascending) == "undefined" ? true : !!ascending;
  	this.each(
  		function()
  		{
  			if(this.nodeName.toLowerCase() != "select") return;
  			// get options
  			var o = this.options;
  			// get number of options
  			var oL = o.length;
  			// create an array for sorting
  			var sA = [];
  			// loop through options, adding to sort array
  			for(var i = 0; i<oL; i++)
  			{
  				sA[i] = {
  					v: o[i].value,
  					t: o[i].text
  				}
  			}
  			// sort items in array
  			sA.sort(
  				function(o1, o2)
  				{
  					// option text is made lowercase for case insensitive sorting
  					o1t = o1.t.toLowerCase(), o2t = o2.t.toLowerCase();
  					// if options are the same, no sorting is needed
  					if(o1t == o2t) return 0;
  					if(a)
  					{
  						return o1t < o2t ? -1 : 1;
  					}
  					else
  					{
  						return o1t > o2t ? -1 : 1;
  					}
  				}
  			);
  			// change the options to match the sort array
  			for(var i = 0; i<oL; i++)
  			{
  				o[i].text = sA[i].t;
  				o[i].value = sA[i].v;
  			}
  		}
  	).selectOptions(sel, true); // select values, clearing existing ones
  	return this;
  };
  /**
   * Selects an option by value
   *
   * @name     selectOptions
   * @author   Mathias Bank (http://www.mathias-bank.de), original function
   * @author   Sam Collett (http://www.texotela.co.uk), addition of regular expression matching
   * @type     jQuery
   * @param    String|RegExp|Array value  Which options should be selected
   * can be a string or regular expression, or an array of strings / regular expressions
   * @param    Boolean clear  Clear existing selected options, default false
   * @example  $("#myselect").selectOptions("val1"); // with the value 'val1'
   * @example  $("#myselect").selectOptions(["val1","val2","val3"]); // with the values 'val1' 'val2' 'val3'
   * @example  $("#myselect").selectOptions(/^val/i); // with the value starting with 'val', case insensitive
   *
   */
  $.fn.selectOptions = function(value, clear)
  {
  	var v = value;
  	var vT = typeof(value);
  	// handle arrays
  	if(vT == "object" && v.constructor == Array)
  	{
  		var $this = this;
  		$.each(v, function()
  			{
        				$this.selectOptions(this, clear);
      			}
  		);
  	};
  	var c = clear || false;
  	// has to be a string or regular expression (object in IE, function in Firefox)
  	if(vT != "string" && vT != "function" && vT != "object") return this;
  	this.each(
  		function()
  		{
  			if(this.nodeName.toLowerCase() != "select") return this;
  			// get options
  			var o = this.options;
  			// get number of options
  			var oL = o.length;
  			for(var i = 0; i<oL; i++)
  			{
  				if(v.constructor == RegExp)
  				{
  					if(o[i].value.match(v))
  					{
  						o[i].selected = true;
  					}
  					else if(c)
  					{
  						o[i].selected = false;
  					}
  				}
  				else
  				{
  					if(o[i].value == v)
  					{
  						o[i].selected = true;
  					}
  					else if(c)
  					{
  						o[i].selected = false;
  					}
  				}
  			}
  		}
  	);
  	return this;
  };
  
  /**
   * Copy options to another select
   *
   * @name     copyOptions
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     jQuery
   * @param    String to  Element to copy to
   * @param    String which  (optional) Specifies which options should be copied - 'all' or 'selected'. Default is 'selected'
   * @example  $("#myselect").copyOptions("#myselect2"); // copy selected options from 'myselect' to 'myselect2'
   * @example  $("#myselect").copyOptions("#myselect2","selected"); // same as above
   * @example  $("#myselect").copyOptions("#myselect2","all"); // copy all options from 'myselect' to 'myselect2'
   *
   */
  $.fn.copyOptions = function(to, which)
  {
  	var w = which || "selected";
  	if($(to).size() == 0) return this;
  	this.each(
  		function()
  		{
  			if(this.nodeName.toLowerCase() != "select") return this;
  			// get options
  			var o = this.options;
  			// get number of options
  			var oL = o.length;
  			for(var i = 0; i<oL; i++)
  			{
  				if(w == "all" || (w == "selected" && o[i].selected))
  				{
  					$(to).addOption(o[i].value, o[i].text);
  				}
  			}
  		}
  	);
  	return this;
  };
  
  /**
   * Checks if a select box has an option with the supplied value
   *
   * @name     containsOption
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     Boolean|jQuery
   * @param    String|RegExp value  Which value to check for. Can be a string or regular expression
   * @param    Function fn          (optional) Function to apply if an option with the given value is found.
   * Use this if you don't want to break the chaining
   * @example  if($("#myselect").containsOption("val1")) alert("Has an option with the value 'val1'");
   * @example  if($("#myselect").containsOption(/^val/i)) alert("Has an option with the value starting with 'val'");
   * @example  $("#myselect").containsOption("val1", copyoption).doSomethingElseWithSelect(); // calls copyoption (user defined function) for any options found, chain is continued
   *
   */
  $.fn.containsOption = function(value, fn)
  {
  	var found = false;
  	var v = value;
  	var vT = typeof(v);
  	var fT = typeof(fn);
  	// has to be a string or regular expression (object in IE, function in Firefox)
  	if(vT != "string" && vT != "function" && vT != "object") return fT == "function" ? this: found;
  	this.each(
  		function()
  		{
  			if(this.nodeName.toLowerCase() != "select") return this;
  			// option already found
  			if(found && fT != "function") return false;
  			// get options
  			var o = this.options;
  			// get number of options
  			var oL = o.length;
  			for(var i = 0; i<oL; i++)
  			{
  				if(v.constructor == RegExp)
  				{
  					if (o[i].value.match(v))
  					{
  						found = true;
  						if(fT == "function") fn.call(o[i], i);
  					}
  				}
  				else
  				{
  					if (o[i].value == v)
  					{
  						found = true;
  						if(fT == "function") fn.call(o[i], i);
  					}
  				}
  			}
  		}
  	);
  	return fT == "function" ? this : found;
  };
  
  /**
   * Returns values which have been selected
   *
   * @name     selectedValues
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     Array
   * @example  $("#myselect").selectedValues();
   *
   */
  $.fn.selectedValues = function()
  {
  	var v = [];
  	this.selectedOptions().each(
  		function()
  		{
  			v[v.length] = this.value;
  		}
  	);
  	return v;
  };
  
  /**
   * Returns text which has been selected
   *
   * @name     selectedTexts
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     Array
   * @example  $("#myselect").selectedTexts();
   *
   */
  $.fn.selectedTexts = function()
  {
  	var t = [];
  	this.selectedOptions().each(
  		function()
  		{
  			t[t.length] = this.text;
  		}
  	);
  	return t;
  };
  
  /**
   * Returns options which have been selected
   *
   * @name     selectedOptions
   * @author   Sam Collett (http://www.texotela.co.uk)
   * @type     jQuery
   * @example  $("#myselect").selectedOptions();
   *
   */
  $.fn.selectedOptions = function()
  {
  	return this.find("option:selected");
  };
  
  })(jQuery);