text: /*jshint forin:true, noarg:true, noempty:true, eqeqeq:false, bitwise:true, strict:true, undef:true, curly:true, browser:true, indent:4, maxerr:50, onevar:false, nomen:false, regexp:false, plusplus:false, newcap:true */
  (function ($) {
  	"use strict";
  	//Thanks John - http://ejohn.org/blog/javascript-array-remove/
  	var arrayRemove = function (from, to) {
  		var rest = this.slice((to || from) + 1 || this.length);
  		this.length = from < 0 ? this.length + from : from;
  		return this.push.apply(this, rest);
  	};
  	$.fn.ajaxChosen = function (ajaxOptions, options, chosenOptions) {
  		var select = $(this),
  				chosen,
  				keyRight,
  				input,
  				inputBG,
  				callback,
  				throttle = false,
  				requestQueue = [],
  				typing = false,
  				loadingImg = '/img/loading.gif',
  				minLength = 1;
  
  		if ($('option', select).length === 0) {
  			//adding empty option so you don't have to, and chosen can perform search correctly
  			select.append('<option value=""></option>');
  		}
  		if (chosenOptions) {
  			select.chosen(chosenOptions);
  		} else {
  			select.chosen();
  		}
  		chosen = select.next();
  		input = $('input', chosen);
  		inputBG = input.get(0) ? input.get(0).style.background : '';
  		//copy out success callback
  		if ('success' in ajaxOptions && $.isFunction(ajaxOptions.success)) {
  			callback = ajaxOptions.success;
  		}
  		//replace with our success callback
  		ajaxOptions.success = function (data, textStatus, jqXHR) {
  			var items = data,
  					selected, valuesArray, valuesHash = {},
  					requestQueueLength = requestQueue.length,
  					old = false,
  					keep = false,
  					inputEmptied = false,
  					inputEmptiedValue = '';
  			if (typing) {
  				//server returned a response, but it's about to become an older response
  				//so discard it and wait until the user is done typing
  				requestQueue.shift();
  				return false;
  			}
  			if (requestQueueLength > 1) {
  				$.each(requestQueue, function (idx, elem) {
  					if (data.q === elem) {
  						if (idx !== (requestQueueLength - 1)) {
  							//found an older response, remove it from the queue and wait for newest response
  							old = true;
  							arrayRemove.call(requestQueue, idx);
  						} else {
  							//this handles the out of order request/response
  							//last request came in first, and we want to keep it
  							keep = true;
  							//remove all the other older requests
  							requestQueue.length = 0;
  						}
  						return false;
  					}
  				});
  				//if we found an old response or we found the newest response and want to keep processing
  				if (old || !keep) { return false; }
  			} else {
  				//only 1 request was made by the user remove it from queue and continue processing
  				if (typeof requestQueue.shift() === 'undefined') {
  					//If all the old responses have been discarded because we've received the new one already
  					return false;
  				}
  			}
  			//while the request was processing did the user "empty" the input box
  			inputEmptiedValue = $.trim(input.val());//if we have a minLength > 1 then we have to preserve the value (TODO::watch out for potential XSS/other breakages)
  			inputEmptied = inputEmptiedValue.length < minLength;
  
  			//if additional processing needs to occur on the returned json
  			if ('processItems' in options && $.isFunction(options.processItems)) {
  				items = options.processItems(data);
  			} else if ('results' in items) {
  				//default behavior if process items isn't defined
  				//expects there to be a results key in data returned that has the results of the search
  				items = items.results;
  			} else {
  				console.log('Expected results key in data, but was not found. Options could not be built');
  				return false;
  			}
  			//.chzn-choices is only present with multi-selects
  			selected = $('option:selected', select).not(':empty').clone().attr('selected', true);
  			//saving values for deduplication
  			if (!$.isArray(select.val())) {
  				valuesArray = [select.val()];
  			} else {
  				valuesArray = select.val();
  			}
  			$.each(valuesArray, function (i, value) {
  				valuesHash[value] = 1;
  			});
  			$('option', select).remove();
  
  			$('<option value=""/>').appendTo(select);
  			//appending this even on single select in the event the user changes their mind and input is blurred. Keeps selected option selected
  			selected.appendTo(select);
  
  			if (!inputEmptied) {
  				if ($.isArray(items)) {
  					//array of kv pairs [{id:'', text:''}...]
  					$.each(items, function (i, opt) {
  						if (typeof valuesHash[opt.id] === 'undefined') {
  							$('<option value="' + opt.id + '">' + opt.text + '</option>').appendTo(select);
  						}
  					});
  				} else {
  					//hash of kv pairs {'id':'text'...}
  					$.each(items, function (value, text) {
  						if (typeof valuesHash[value] === 'undefined') {
  							$('<option value="' + value + '">' + text + '</option>').appendTo(select);
  						}
  					});
  				}
  			}
  			//update chosen
  			select.trigger("chosen:updated");
  			//right key, for highlight options after ajax is performed
  			keyRight = $.Event('keyup');
  			keyRight.which = 39;
  			//highlight
  			input.val(!inputEmptied ? data.q : inputEmptiedValue).trigger(keyRight).get(0).style.background = inputBG;
  
  			if (items.length > 0) {
  				$('.no-results', chosen).hide();
  			} else {
  				$('.no-results', chosen).show();
  			}
  
  			//fire original success
  			if (callback) {
  				callback(data, textStatus, jqXHR);
  			}
  		};
  		//set loading image
  		options || (options = {});
  		if ('loadingImg' in options) {
  			loadingImg = options.loadingImg;
  		}
  		//set minimum length that will trigger autocomplete
  		if ('minLength' in options) {
  			minLength = options.minLength;
  		}
  
      $('.chosen-search > input, .chosen-choices .search-field input', chosen).
        bind('keyup', processValue).
        bind('paste', function (e) {
          var that = this;
          setTimeout(function() {
            processValue.call(that, e);
          }, 50);
        });
  
      function processValue(e) {
  			var field = $(this),
  					q = field.val();
                                         var eventId = $('#gal_event').val();
                                          
  			//don't fire ajax if...
  			if ((e.type === 'paste' && field.is(':not(:focus)')) ||
          (e.which && (
  				(e.which ===  9)  ||//Tab
  				(e.which === 13)  ||//Enter
  				(e.which === 16)  ||//Shift
  				(e.which === 17)  ||//Ctrl
  				(e.which === 18)  ||//Alt
  				(e.which === 19)  ||//Pause, Break
  				(e.which === 20)  ||//CapsLock
  				(e.which === 27)  ||//Esc
  				(e.which === 33)  ||//Page Up
  				(e.which === 34)  ||//Page Down
  				(e.which === 35)  ||//End
  				(e.which === 36)  ||//Home
  				(e.which === 37)  ||//Left arrow
  				(e.which === 38)  ||//Up arrow
  				(e.which === 39)  ||//Right arrow
  				(e.which === 40)  ||//Down arrow
  				(e.which === 44)  ||//PrntScrn
  				(e.which === 45)  ||//Insert
  				(e.which === 144) ||//NumLock
  				(e.which === 145) ||//ScrollLock
  				(e.which === 91)  ||//WIN Key (Start)
  				(e.which === 93)  ||//WIN Menu
  				(e.which === 224) ||//command key
  				(e.which >= 112 && e.which <= 123)//F1 to F12
  			))) { return false; }
  			//backout of ajax dynamically
  			if ('useAjax' in options && $.isFunction(options.useAjax)) {
  				if (!options.useAjax(e)) { return false; }
  			}
  			//hide no results
  			$('.no-results', chosen).hide();
  			//backout if nothing is in input box
  			if ($.trim(q).length < minLength) {
  				input.get(0).style.background = inputBG;
  				if (throttle) { clearTimeout(throttle); }
  				return false;
  			}
  
  			typing = true;
  
  			//add query to data
  			if ($.isArray(ajaxOptions.data)) {
  				//array
  				if (ajaxOptions.data[ajaxOptions.data.length - 1].name === 'q') {
  					ajaxOptions.data.pop();
  				}
  				ajaxOptions.data = ajaxOptions.data.concat({ name: 'q', value: q});
  			} else {
  				//hash
  				if (!('data' in ajaxOptions)) {
  					ajaxOptions.data = {};
  				}
  				$.extend(ajaxOptions.data, { data: {q: q,eventId:eventId} });
  			}
  			//dynamically generate url
  			if ('generateUrl' in options && $.isFunction(options.generateUrl)) {
  				ajaxOptions.url = options.generateUrl(q);
  			}
  
  			//show loading
  			input.get(0).style.background = 'transparent url("' + loadingImg + '") no-repeat right 3px';
  			//throttle that bitch, so we don't kill the server
  			if (throttle) { clearTimeout(throttle); }
  			throttle = setTimeout(function () {
  				requestQueue.push(q);
  				typing = false;
  				$.ajax(ajaxOptions);
  			}, 700);
  		};
  
  		return select;
  	};
  })(jQuery);
  