/*
*  Ajax Autocomplete for jQuery, version 1.0.7
*  (c) 2009 Tomas Kirda
*
*  Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
*  For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/
*
*  Last Review: 07/27/2009
*/
(function ($) {
	$.fn.autocomplete = function(options) {
	return this.each(function() {
	  return new Autocomplete(this, options);
	});
	};
	
	var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g');
	
	var fnFormatResult = function(value, data, currentValue) {
	var pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')';
	return value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
	};
	
	var Autocomplete = function(el, options) {
	this.el = $(el);
	this.el.attr('autocomplete', 'off');
	this.suggestions = [];
	this.data = [];
	this.photos = [];
	this.titres = [];
	this.regions = [];
	this.countries = [];
	this.badQueries = [];
	this.selectedIndex = -1;
	this.currentValue = this.el.val();
	this.intervalId = 0;
	this.cachedResponse = [];
	this.onChangeInterval = null;
	this.ignoreValueChange = false;
	this.url = options.url;
	this.isLocal = false;
	this.options = {
	  autoSubmit: false,
	  minChars: 1,
	  maxHeight: 200,
	  deferRequestBy: 0,
	  width: 0,
	  margin: 0,
	  border: '#383F49',
	  highlight: true,
	  params: {},
	  fnFormatResult: fnFormatResult,
	  delimiter: null
	};
	if (options) { $.extend(this.options, options); }
	if(this.options.lookup){
	  this.isLocal = true;
	  if($.isArray(this.options.lookup)){ this.options.lookup = { suggestions:this.options.lookup, data:[] }; }
	}
	this.initialize();
	};
	
	Autocomplete.prototype = {
	
	ajax: null,
	killerFn: null,
	
	initialize: function() {
	
	  var me, zindex;
	  me = this;
	
	  zindex = 80000;
	
	  this.killerFn = function(e) {
		if ($(e.target).parents('.autocomplete').size() === 0) {
		  me.killSuggestions();
		  me.disableKillerFn();
		}
	  };
	
	  var uid = new Date().getTime();
	  var autocompleteElId = 'Autocomplete_' + uid;
	
	  if (!this.options.width) { this.options.width = this.el.width(); }
	  this.mainContainerId = 'AutocompleteContainter_' + uid;
	
	  $('<div id="' + this.mainContainerId + '" style="position:absolute;z-index:' + zindex + '"><div class="autocomplete" id="' + autocompleteElId + '" style="display:none; width:' + this.options.width + 'px;"></div></div>').appendTo('body');
	
	  this.container = $('#' + autocompleteElId);
	  this.fixPosition();
	  if (window.opera) {
		this.el.keypress(function(e) { me.onKeyPress(e); });
	  } else {
		this.el.keydown(function(e) { me.onKeyPress(e); });
	  }
	  this.el.keyup(function(e) { me.onKeyUp(e); });
	  this.el.blur(function() { me.enableKillerFn(); });
	  this.el.focus(function() { me.fixPosition(); });
	  this.el.click(function() { me.onValueChange(); });
	
	  this.container.css({ borderColor: ''+this.options.border+'', maxHeight: this.options.maxHeight + 'px' });
	},
	
	fixPosition: function() {
	  var offset = this.el.offset();
	  $('#' + this.mainContainerId).css({ top: (offset.top + this.el.innerHeight()) + 'px', left: (offset.left-this.options.margin) + 'px' });
	},
	
	enableKillerFn: function() {
	  var me = this;
	  $(document).bind('click', me.killerFn);
	},
	
	disableKillerFn: function() {
	  var me = this;
	  $(document).unbind('click', me.killerFn);
	},
	
	killSuggestions: function() {
	  var me = this;
	  this.stopKillSuggestions();
	  this.intervalId = window.setInterval(function() { me.hide(); me.stopKillSuggestions(); }, 300);
	},
	
	stopKillSuggestions: function() {
	  window.clearInterval(this.intervalId);
	},
	
	onKeyPress: function(e) {
	
	  if (!this.enabled) { return; }
	  // return will exit the function
	  // and event will not fire
	  switch (e.keyCode) {
		case 27: //Event.KEY_ESC:
		  this.el.val(this.currentValue);
		  this.hide();
		  break;
		case 9: //Event.KEY_TAB:
		case 13: //Event.KEY_RETURN:
		  if (this.selectedIndex === -1) {
			this.hide();
			return;
		  }
		  this.select(this.selectedIndex);
		  if (e.keyCode === 9/* Event.KEY_TAB */) { return; }
		  break;
		case 38: //Event.KEY_UP:
		  this.moveUp();
		  break;
		case 40: //Event.KEY_DOWN:
		  this.moveDown();
		  break;
		default:
		  return;
	  }
	  e.stopImmediatePropagation();
	  e.preventDefault();
	},
	
	onKeyUp: function(e) {
	  switch (e.keyCode) {
		case 38: //Event.KEY_UP:
		case 40: //Event.KEY_DOWN:
		  return;
	  }
	  clearInterval(this.onChangeInterval);
	  if (this.currentValue !== this.el.val()) {
		if (this.options.deferRequestBy > 0) {
		  // Defer lookup in case when value changes very quickly:
		  var me = this;
		  this.onChangeInterval = setInterval(function() { me.onValueChange(); }, this.options.deferRequestBy);
		} else {
		  this.onValueChange();
		}
	  }
	},
	
	onValueChange: function() {
	  clearInterval(this.onChangeInterval);
	  this.currentValue = this.el.val();
	  var q = this.getQuery(this.currentValue);
	  this.selectedIndex = -1;
	  if (this.ignoreValueChange) {
		this.ignoreValueChange = false;
		return;
	  }
	  if (q === '' || q.length < this.options.minChars) {
		this.hide();
	  } else {
		this.getSuggestions(q);
	  }
	},
	
	getQuery: function(val) {
	  var d, arr;
	  d = this.options.delimiter;
	  if (!d) { return $.trim(val); }
	  arr = val.split(d);
	  return $.trim(arr[arr.length - 1]);
	},
	
	getSuggestionsLocal: function(q) {
	  var ret, arr, len, val;
	  arr = this.options.lookup;
	  len = arr.suggestions.length;
	  ret = { suggestions:[], data:[] };
	  for(var i=0; i< len; i++){
		val = arr.suggestions[i];
		if(val.toLowerCase().indexOf(q.toLowerCase()) === 0){
		  ret.suggestions.push(val);
		  ret.data.push(arr.data[i]);
		  if(this.options.params.type == 'city') {
			ret.regions.push(arr.regions[i]);
			ret.countries.push(arr.countries[i]);
		  } else {
			ret.photos.push(arr.photos[i]);
			ret.titres.push(arr.titres[i]);
		  }
		}
	  }
	  return ret;
	},
	
	getSuggestions: function(q) {
	  var cr, me, ls;
	  cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q];
	  if (cr && $.isArray(cr.suggestions)) {
		this.suggestions = cr.suggestions;
		this.data = cr.data;
		if(this.options.params.type == 'city') {
			this.regions = cr.regions;
			this.countries = cr.countries;
		} else {
			this.photos = cr.photos;
			this.titres = cr.titres;
		}
		this.suggest();
	  } else if (!this.isBadQuery(q)) {
		me = this;
		me.options.params.query = q;
		$.post(this.url, me.options.params, function(txt) { me.processResponse(txt); }, 'text');
	  }
	},
	
	isBadQuery: function(q) {
	  var i = this.badQueries.length;
	  while (i--) {
		if (q.indexOf(this.badQueries[i]) === 0) { return true; }
	  }
	  return false;
	},
	
	hide: function() {
	  this.enabled = false;
	  this.selectedIndex = -1;
	  this.container.hide();
	},
	
	suggest: function() {
	  if (this.suggestions.length === 0) {
		this.hide();
		return;
	  }
	
	  var me, len, div, f;
	  me = this;
	  len = this.suggestions.length;
	  f = this.options.fnFormatResult;
	  v = this.getQuery(this.currentValue);
	  this.container.hide().empty();
	  for (var i = 0; i < len; i++) {
		if(this.options.params.type == 'city') {
			div = $('<a class="listpanel fbox"><div class="white">'+this.suggestions[i]+'</div><div class="blue pixel10">'+this.regions[i]+', '+this.countries[i]+'</div></a>');
		} else if(this.options.params.type == 'search') {
			div = $('<a class="listpanel fbox"><div class="fleft"><img src="http://www.sofamous.com/photos/'+this.photos[i]+'-c50.jpg" width="50" height="50"></div><div class="fleft normal grey mmargleft width200"><div class="l2padtop a title">'+this.suggestions[i]+'</div><div class="l2padtop">'+this.titres[i]+'</div></div><div class="clear"></div></a>');
		} else if(this.options.params.type == 'user' || this.options.params.type == 'jump' || this.options.params.type == 'friends') {
			div = $('<a class="listpanelmini fbox"><div class="fleft"><img src="http://www.sofamous.com/photos/'+this.photos[i]+'-c25.jpg" width="25" height="25"></div><div class="fleft normal grey smargleft width100"><div class="white">'+this.suggestions[i]+'</div><div class="blue pixel10">'+this.titres[i]+'</div></div><div class="clear"></div></a>');
		} else {
			if (i < (len-1)) div = $((me.selectedIndex === i ? '<div class="selected"' : '<div') + '><div class="li borderbottomlight"><div class="fleft l1padtop l1padbottom width50 center"><img src="'+this.photos[i]+'" style="max-width:40px;max-height:40px;"></div><div class="fleft"><div class="mpadtop">' + f(this.suggestions[i], this.data[i], v) + '</div></div><div class="clear"></div></div></div>');
			else div = $((me.selectedIndex === i ? '<div class="selected"' : '<div') + '><div class="li"><div class="fleft l1padtop l1padbottom width50 center"><img src="'+this.photos[i]+'" style="max-width:40px;max-height:40px;"></div><div class="fleft"><div class="mpadtop">' + f(this.suggestions[i], this.data[i], v) + '</div></div><div class="clear"></div></div></div>');
		}
		div.mouseover((function(xi) { return function() { me.activate(xi); }; })(i));
		div.click((function(xi) { return function() { me.select(xi); }; })(i));
		this.container.append(div);
	  }
	  this.enabled = true;
	  this.container.show();
	},
	
	processResponse: function(text) {
	  var response;
	  try {
		response = eval('(' + text + ')');
	  } catch (err) { return; }
	  if(this.options.params.type == 'pick' && response.suggestions.length === 0) {Pick.newpick();}
	  if (!$.isArray(response.data)) { response.data = []; }
	  this.cachedResponse[response.query] = response;
	  if (response.suggestions.length === 0) { this.badQueries.push(response.query); }
	  if (response.query === this.getQuery(this.currentValue)) {
		this.suggestions = response.suggestions;
		this.data = response.data;
		if(this.options.params.type == 'city') {
		  this.regions = response.regions;
		  this.countries = response.countries;
		} else {
		  this.photos = response.photos;
		  this.titres = response.titres;
		}
		this.suggest(); 
	  }
	},
	
	activate: function(index) {
	  var divs = this.container.children();
	  var activeItem;
	  // Clear previous selection:
	  if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) {
		$(divs.get(this.selectedIndex)).removeClass('selected');
	  }
	  this.selectedIndex = index;
	  if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) {
		activeItem = divs.get(this.selectedIndex);
		$(activeItem).addClass('selected');
	  }
	  return activeItem;
	},
	
	deactivate: function(div, index) {
	  div.className = '';
	  if (this.selectedIndex === index) { this.selectedIndex = -1; }
	},
	
	select: function(i) {
	  var selectedValue = this.suggestions[i];
	  if (selectedValue) {
		this.el.val(selectedValue);
		if (this.options.autoSubmit) {
		  var f = this.el.parents('form');
		  if (f.length > 0) { f.get(0).submit(); }
		}
		this.ignoreValueChange = true;
		this.hide();
		this.onSelect(i);
	  }
	},
	
	moveUp: function() {
	  if (this.selectedIndex === -1) { return; }
	  if (this.selectedIndex === 0) {
		this.container.children().first().removeClass('selected');
		this.selectedIndex = -1;
		return;
	  }
	  this.adjustScroll(this.selectedIndex - 1);
	},
	
	moveDown: function() {
	  if (this.selectedIndex === (this.suggestions.length - 1)) { return; }
	  this.adjustScroll(this.selectedIndex + 1);
	},
	
	adjustScroll: function(i) {
	  var activeItem, offsetTop, upperBound, lowerBound;
	  activeItem = this.activate(i);
	  offsetTop = activeItem.offsetTop;
	  upperBound = this.container.scrollTop();
	  lowerBound = upperBound + this.options.maxHeight - 25;
	  if (offsetTop < upperBound) {
		this.container.scrollTop(offsetTop);
	  } else if (offsetTop > lowerBound) {
		this.container.scrollTop(offsetTop - this.options.maxHeight + 25);
	  }
	  //this.el.val(this.suggestions[i]);
	},
	
	onSelect: function(i) {
	  var me, onSelect, getValue, s, d;
	  me = this;
	  onSelect = me.options.onSelect;
	  getValue = function(value) {
		var del, currVal;
		del = me.options.delimiter;
		currVal = me.currentValue;
		if (!del) { return value; }
		var arr = currVal.split(del);
		if (arr.length === 1) { return value; }
		return currVal.substr(0, currVal.length - arr[arr.length - 1].length) + value;
	  };
	  s = me.suggestions[i];
	  d = me.data[i];
	  me.el.val(getValue(s));
	  if ($.isFunction(onSelect)) {
		  if(this.options.params.type == 'city') {
			  c = me.countries[i];
			  onSelect(s, d, c);
		  } else if(this.options.params.type == 'search') {
			  onSelect(s, d);
		  } else {
			  c = me.photos[i];
			  onSelect(d, s, c);
		  }
	  }
	}
	
	};

	var types = ['DOMMouseScroll', 'mousewheel'];
	
	if ($.event.fixHooks) {
		for ( var i=types.length; i; ) {
			$.event.fixHooks[ types[--i] ] = $.event.mouseHooks;
		}
	}
	
	$.event.special.mousewheel = {
		setup: function() {
			if ( this.addEventListener ) {
				for ( var i=types.length; i; ) {
					this.addEventListener( types[--i], handler, false );
				}
			} else {
				this.onmousewheel = handler;
			}
		},
		
		teardown: function() {
			if ( this.removeEventListener ) {
				for ( var i=types.length; i; ) {
					this.removeEventListener( types[--i], handler, false );
				}
			} else {
				this.onmousewheel = null;
			}
		}
	};
	
	$.fn.extend({
		mousewheel: function(fn) {
			return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
		},
		
		unmousewheel: function(fn) {
			return this.unbind("mousewheel", fn);
		}
	});
	
	
	function handler(event) {
		var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
		event = $.event.fix(orgEvent);
		event.type = "mousewheel";
		
		// Old school scrollwheel delta
		if ( event.wheelDelta ) { delta = event.wheelDelta/120; }
		if ( event.detail     ) { delta = -event.detail/3; }
		
		// New school multidimensional scroll (touchpads) deltas
		deltaY = delta;
		
		// Gecko
		if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
			deltaY = 0;
			deltaX = -1*delta;
		}
		
		// Webkit
		if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
		if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
		
		// Add event and delta to the front of the arguments
		args.unshift(event, delta, deltaX, deltaY);
		
		return ($.event.dispatch || $.event.handle).apply(this, args);
	}

})(jQuery);

