/* alter jquery to do selection in batch namer */
$.fn.selection = function(start, end) {

	if (start !== undefined) {
		return this.each(function() {
			if (this.createTextRange) {
				var selRange = this.createTextRange();
				if (end === undefined || start == end) {
					selRange.move("character", start);
					selRange.select();
				} else {
					selRange.collapse(true);
					selRange.moveEnd("character", end);
					selRange.moveStart("character", start);
					selRange.select();
				}
			} else if (this.setSelectionRange) {
				this.setSelectionRange(start, end);
			} else if (this.selectionStart) {
				this.selectionStart = start;
				this.selectionEnd = end;
			}
			GeneralUtils.autocompleteSemaphoreOpened = true;
		});
	}
	var field = this[0];
	if (field.createTextRange) {
		var range = document.selection.createRange(), orig = field.value, teststring = "<->", textLength = range.text.length;
		range.text = teststring;
		var caretAt = field.value.indexOf(teststring);
		field.value = orig;
		this.selection(caretAt, caretAt + textLength);
		return {
			start : caretAt,
			end : caretAt + textLength
		}
	} else if (field.selectionStart !== undefined) {
		return {
			start : field.selectionStart,
			end : field.selectionEnd
		}
	}

};

jQuery(document)
		.ready(

				function($) {
					/*
					 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
					 * 
					 * AutoComplete
					 * 
					 * inputElementSelector: string jquery selector for the
					 * input element autocompleteElementSelector: string jquery
					 * selector for the autocomplete element the contact list
					 * generated by backend selectionCallback: function a
					 * function to be called each time the user selects a name
					 * by click or enter/right/tab key maxSize: integer the
					 * maximum height of the autocomplete element (number of
					 * entries) more entries will trigger the scrollbar
					 * showOnFocus: boolean defines if autocomplete list should
					 * be shown on focus or only after a character has been
					 * pressed enterPressedCallback: function(inputElement) ->
					 * boolean if present then this function will be triggered
					 * when <enter> has been pressed in the input field (or on
					 * the autocomplete list) as a parameter this function gets
					 * the input element returned boolean value indicates
					 * whether default enter behaviour for input field should be
					 * suppressed or not (false = event suppressed) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
					 */

					window.AutoCompleteCache = {
						// constructor
						initialize : function() {
							return this;
						},

						cache : {}, // cache data map (letter -> contact
						// elements)

						put : function(key, value) {
							this.cache[key] = value;
						},

						get : function(key) {
							if (this.cache[key])
								return this.cache[key];
							return null;
						},

						remove : function(key) {
							this.cache[key] = null;
						}

					}.initialize();

					window.AutoComplete = function(inputElementSelector, autocompleteElementSelector, selectionCallback, maxSize, enterPressedCallback, formSelector, editDetailsMode) {
						this.initialize(inputElementSelector, autocompleteElementSelector, selectionCallback, maxSize, enterPressedCallback, formSelector, editDetailsMode);
						return this;
					};

					window.AutoComplete.prototype = {
						// constructor
						initialize : function(inputElementSelector, autocompleteElementSelector, selectionCallback, maxSize, enterPressedCallback, formSelector, editDetailsMode) {

							this.self = this;
							this.selectionCallback = selectionCallback;
							this.enterPressedCallback = enterPressedCallback;
							this.editDetailsMode = editDetailsMode;
							// init and get input element and autocomplete
							// element
							this.inputElement = $(inputElementSelector).eq(0);
							this.autocompleteElement = $(autocompleteElementSelector).eq(0);
							// sanity check
							if (!this.inputElement.length || !this.autocompleteElement.length) {
								return false;
							}

							this.size = maxSize || 3;

							this.formSelector = formSelector;

							this.bindGeneralEvents();
							this.bindInputEvents();
							this.resizeAutocompleter();

						},

						// properties
						self : null,
						inputElement : null,
						autocompleteElement : null,
						size : 0,
						currentinputtext : "",
						previousinputtext : "",
						showOnFocus : null,
						selectionCallback : null,
						enterPressedCallback : null,
						shown : false,
						lastEscTstamp : 0, // last moment that esc was pressed
						// on this
						// object
						prefixedContactsLengthLimit : 300, // how many contacts
						// do we
						// read
						// for given one-letter prefix
						loadedDataPrefix : "",
						initializingData : false, // very simple mutex
						manualCheckMode : false,
						newNameMode : false,
						inputText : '',
						autocompletedText : '',
						interval : null,
						editDetailsMode : false,
						// methods
						addEmailTemplate : '<li id="add_email" class="add_email"><input type="text" name="email_input" value="Add an email address for this person (optional)" tabindex="2"/></li>',
						addEmailEditDetailsTemplate : '<li id="add_email" class="add_email"><input type="text" name="email_input" value="Edit an email address for this person (optional)" tabindex="2"/></li>',

						enterNameMessage : 'Enter a name in the field above &ndash; then click "Save."',

						templateEntry : '<li class="match template" name="_NAME_" title="Click to select this person�" style="display: list-item;">' + '<div class="mugshot-wrapper">'
								+ '<img class="mugshot left" alt="avatar" src="/images/transparent.gif" style=""/>' + '</div>' + '<div class="name" title="_NAME_">'
								+ '<strong><span class="highlight">_NAME_</span></strong>' + '<br/>' + '<em class="account_type">Facebook friend</em>' + '</div>'
								+ '<input class="hidden" type="checkbox" value="match" name="match"/>' + '<label class="checkbox">Selected</label>'
								+ '<input class="contactUserId" type="hidden" value=""/>' + '</li>',

						newNameTemplateEntry : '<li class="match new" name="fi" title="Click to add a new person with this name..." style="display: list-item;">' + '<div class="mugshot-wrapper">'
								+ '<img class="mugshot email" height="25" width="25" alt="Mugshot" src="/images/transparent.gif" style="background-image: url(/images/transparent.gif);"/>' + '</div>'
								+ '<div class="name">' + '<strong>' + '<span class="highlight"></span>' + '</strong>' + '<br/>' + '<em>Add a new person with this name</em>' + '</div>'
								+ '<input class="hidden" type="checkbox" value="match" name="match"/>' + '<label class="checkbox">Selected</label>' + '</li>',

						formSelector : '',
						lastViewportHeight : 0,
						updateContactListWithNewName : function(newName) {
							var firstInputLetter = newName.substring(0, 1).toLowerCase();
							AutoCompleteCache.remove(firstInputLetter);
						},
						attachAddEmailhandlers : function(self) {
							$('#autocompleter #add_email input').blur(function() {
								var add_email = $('#autocompleter #add_email input').attr('value');
								if (add_email === null || add_email == "") {
									$('#autocompleter #add_email input').attr('value', 'Add an email address for this person (optional)');
								} else {
									$(this).css('text-align', 'center');
								}
							});

							$('#autocompleter #add_email input').focus(function() {
								self.removeEmailAlert();
								var add_email = $('#autocompleter #add_email input').attr('value');
								if (add_email === null || add_email == "Add an email address for this person (optional)") {
									$(this).attr('value', '').css('text-align', 'left');
								} else {
									$(this).css('text-align', 'left');
								}
							});

						},

						updateContactListWithNewData : function(newContact) {
							
							
							if (newContact && newContact.name) {
								// we're getting data from cache that matches
								// new
								// contact's
								// prefix
								var firstInputLetter = newContact.name.substring(0, 1).toLowerCase();
								var contactList = AutoCompleteCache.get(firstInputLetter);
								if (contactList) {
									contactList.push(newContact);
									contactList.sort(function(contact1, contact2) {
										if (contact1 && contact2 && contact1.name && contact2.name) {
											return (contact1.name.toLowerCase() > contact2.name.toLowerCase());
										}
										return 0;
									});
								}
							}
						},

						initializeDataForInputText : function(inputtext) {
							/* get contact list */
							var self = this;

							if (self.initializingData)
								return; // very simple mutex simulation - will
							// at least
							// reduce race conditions freq
							self.initializingData = true;

							var firstInputLetter = inputtext.substring(0, 1).toLowerCase();
							// first trying to read data from cache
							var contactList = AutoCompleteCache.get(firstInputLetter);
							if (contactList == null) {
								// not found in cache - making relevant ajax
								// call
								// (synchronous)
								$.ajax( {
									type : 'GET',
									url : "/contact-list",
									data : {
										limit : self.prefixedContactsLengthLimit,
										regExp : firstInputLetter
									},
									dataType : 'json',
									async : false,
									timeout : 5000,
									success : function(result) {
										contactList = result.contactList;
										AutoCompleteCache.put(firstInputLetter, contactList);
									}
								});
							}
							this.loadedDataPrefix = firstInputLetter;

							// populate internal contact list
							var contacts = new Array();
							for ( var i = 0, len = contactList.length; i < len; i++) {
								contacts.push( {
									"avatarUrl" : contactList[i].avatarUrl,
									"claimed" : contactList[i].claimed,
									"defaultIdentityId" : contactList[i].defaultIdentityId,
									"facebookUser" : contactList[i].facebookUser,
									"name" : contactList[i].name,
									"nameLowerCased" : contactList[i].name.toLowerCase(),
									"userId" : contactList[i].userId
								});
							}
							// removing previous and creating current
							// autocomplete
							// entries
							this.autocompleteElement.find("#scrollable_area ul").empty();

							// creating template table row

							this.autocompleteElement.find("#scrollable_area ul").append(this.templateEntry);
							var trTemplate = this.autocompleteElement.find("li.template");

							// inserting contacts from list...
							for (i = 0, len = contacts.length; i < len; i++) {
								var newElem = trTemplate.clone(true).removeClass("template");

								// account type
								var accountTypeEm = newElem.find(".account_type");
								if (contacts[i].facebookUser) {
									if (contacts[i].claimed) {
										accountTypeEm.addClass("pr");
										accountTypeEm.html('Polar Rose user');
									} else {
										accountTypeEm.addClass("fb");
										accountTypeEm.html('Facebook friend');
										newElem.addClass('facebook')
									}
								} else {
									accountTypeEm.html('No contact details');
								}
								// mugshot
								var mugshotImg = newElem.find(".mugshot");
								if (contacts[i].avatarUrl) {
									mugshotImg.attr("alt", "Mugshot");
									// mugshotImg.attr("src",
									// contacts[i].avatarUrl);
									mugshotImg.attr("title", contacts[i].name);
									mugshotImg.css('background-image', 'url(' + contacts[i].avatarUrl + ')');
								} else {
									mugshotImg.attr("src", '/images/no_photo.gif');

								}

								// other stuff
								newElem.attr("name", contacts[i].nameLowerCased);
								newElem.attr("id", contacts[i].defaultIdentityId);
								newElem.find(".name strong").html(contacts[i].name);
								newElem.find(".name").attr('title', contacts[i].name);
								newElem.find(".contactUserId").val(contacts[i].userId);

								// and inserting

								$("#scrollable_area ul").append(newElem);
							}
							trTemplate.remove(); // removing unneeded
							// template entry
							this.autocompleteElement.find("#scrollable_area ul li:last").addClass("last");
							this.autocompleteElement.find("#scrollable_area ul li:first").addClass("first");
							$('#autocompleter').show();
							$("#scrollable_area ul").append(this.newNameTemplateEntry);
							var faceboxUrl = $(self.formSelector + ' .edit_face_container .photo img').attr('src');
							faceboxUrl = faceboxUrl.replace('face100x100', 'face50x50');
							$('#autocompleter li img.mugshot.email').css('background-image', 'url(' + faceboxUrl + ')');
							this.autocompleteElement.find('li.match').bind("click", function() {
								var match = this;
								if (!$(match).hasClass('new')) {
									if (self.manualCheckMode == true || $(match).find('.checked').length > 0) {
										self.newNameMode = true;
										self.resetSelectionCallback();
										self.manualCheckMode = false;
										self.update();
										$('#name-field').focus();

										return false;
									}
									if (!self.manualCheckMode) {
										/* uncheck */
										var id = $(match).attr('id');
										self.manualCheckMode = true;
										self.get(id);
										self.update(id);
									}
								} else {
									self.resetSelectionCallback();
									self.newNameMode = true;
									self.update();
									$('#name-field').focus();
								}
								return false;
							});
							self.initializingData = false;
						},

						bindGeneralEvents : function() {
							var self = this;
							$(window).resize(function() {
								self.resizeAutocompleter();
							});

						},
						resizeAutocompleter : function() {
							var self = this;
							var viewportHeight = $(window).height();
							if (viewportHeight <= 460) {
								if (!$("#autocompleter #scrollable_area").hasClass('autocompleter-small')) {
									$("#autocompleter #scrollable_area").removeClass('autocompleter-large');
									$("#autocompleter #scrollable_area").removeClass('autocompleter-medium');
									$("#autocompleter #scrollable_area").addClass('autocompleter-small');

									this.size = 2;
								}
							} else if (viewportHeight >= 640) {
								if (!$("#autocompleter #scrollable_area").hasClass('autocompleter-large')) {
									$("#autocompleter #scrollable_area").removeClass('autocompleter-medium');
									$("#autocompleter #scrollable_area").removeClass('autocompleter-small');
									$("#autocompleter #scrollable_area").addClass('autocompleter-large');
									this.size = 5;
								}
							} else {
								if (!$("#autocompleter #scrollable_area").hasClass('autocompleter-medium')) {
									$("#autocompleter #scrollable_area").removeClass('autocompleter-large');
									$("#autocompleter #scrollable_area").removeClass('autocompleter-small');
									$("#autocompleter #scrollable_area").addClass('autocompleter-medium');
									this.size = 3;
								}
							}
						},
						bindInputEvents : function() {
							var self = this;
							// bind events for the input element
						this.inputElement.attr("autocomplete", "off").bind("keydown", function(e) {
							switch (e.keyCode) {
							case 17:
							case 18:
								break;
							case 27: /* escape */
								if (self.autocompleteElement.is(":visible")) {
									self.lastEscTstamp = new Date().getMilliseconds();
									self.hide();
									/* unselect potential selection */
								}
								break;
							case 38: /* up arrow */
								self.hover("prev");
								break;
							case 39: /* right arrow */
								self.chooseSelectedRow();
								break;
							case 40: /* down arrow */
								self.hover("next");
								break;
							}
						}).bind("keyup", function(e) {
							switch (e.keyCode) {
							case 8:
								self.update();
								self.show();
								break;
							case 9: /* tab */
							case 13: /* enter */
							case 16:
							case 17:
							case 27: /* escape */
							case 37: /* left arrow */
							case 38: /* up arrow */
							case 39: /* right arrow */
							case 40: /* down arrow */
								break;
							default:
								self.setSelectionInterval();
								self.update();
								self.show();
								break;
							}
							return true;
						}).bind("click", function() {
							return false;
						}).bind("dblclick", function() {
							return false;
						});
					},

						chooseSelectedValue : function() {
							var self = this;
							if (self.shown) {
								self.get();
								self.update();
								self.hide();
							}
						},
						highlight : function(value, term) {

							return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "i"),
									"<span class=\"highlight\">$1</span>");
						},
						setSelectionInterval : function() {
							if (GeneralUtils.autocomplete.interval != null) {
								window.clearInterval(GeneralUtils.autocomplete.interval);
							}
							GeneralUtils.autocomplete.interval = window.setInterval('GeneralUtils.autocomplete.addNameEnding()', 10);
						},

						addNameEnding : function() {
							var inputtext = GeneralUtils.autocomplete.inputElement.val().substring(0, GeneralUtils.autocomplete.inputElement.selection().start);
							if ($('li .match:visible').length == 1) {
								var text = $('#autocompleter li.match:visible').find('strong').text();
								$(GeneralUtils.autocomplete.formSelector + ' input.name-field').val(text).selection(inputtext.length, text.length);
							}
							if (GeneralUtils.autocomplete.interval != null) {
								window.clearInterval(GeneralUtils.autocomplete.interval);
							}
						},
						update : function(id) {
							/* get input... to lower case, trim */
							var self = this;
							var inputtext = this.inputElement.val().toLowerCase().replace(/^[\s\n\r\u2028\u2029]*/, "").replace(/[\n\r\u2028\u2029]*$/, "");
							if (self.previousinputtext.length > inputtext.length) {
								$('#wrapper').removeClass('facebook_friend_wrapper');
								self.manualCheckMode = false;
								self.newNameMode = false;
							}
							inputtext = this.escape(inputtext);
							this.currentinputtext = inputtext;

							/* if inputtext is empty - simply hiding everything */
							if ($.trim(inputtext).length == 0) {
								this.previousinputtext = "";
								this.hide();
								return;
							}

							/*
							 * if inputtext nonempty then loading data matching
							 * inputtext prefix if needed
							 */
							if (!this.prefixesMatch(inputtext, this.loadedDataPrefix, 1)) {
								this.initializeDataForInputText(inputtext);
							}

							/* filtering data and showing only matching entries */
							if (self.newNameMode) {
								self.prepareNewNameMode(self, this.autocompleteElement);
							} else {
								if (inputtext !== this.previousinputtext) {
									var regExp = new RegExp("^" + inputtext);
									/*
									 * iterating manually through the rows and
									 * showing/hiding entries (jquery selectors
									 * are too slow for this)
									 */
									var self = this;
									if (this.autocompleteElement.is(":hidden")) {
										this.show(); /*
														 * has to be done this
														 * way - IE7
														 */
										/* problems */
									}
									var visibleEntriesCount = 0;
									var visibleEntry = null;
									if (self.manualCheckMode) {
										this.autocompleteElement.find("div ul li").hide();
										visibleEntriesCount = 1;
										visibleEntry = $('#' + id);
										visibleEntry.show();
									} else {
										this.autocompleteElement.find("div ul li").each(function() {
											if (!$(this).hasClass('new') && !$(this).hasClass('add_email')) {
												var name = $(this).attr("name");
												if (name.match(regExp)) {
													visibleEntry = $(this);
													$(this).show();
													visibleEntriesCount++;
													visibleEntry.find('label.checkbox').removeClass('checked');
												} else {
													$(this).hide();
												}
											}
										});
									}
									self.filterEntries(this.autocompleteElement, visibleEntriesCount, visibleEntry, self);

									var currentText = this.inputElement.val();

									self.highlightNames(self, inputtext);
								}
							}
							this.previousinputtext = inputtext;
						},

						get : function(id) {

							/* get name from hovered/selected entry */
							var name = $('#' + id).attr('name');
							var callbackResult = null;
							if (name) {
								/*
								 * name is not empty - filling input element
								 * with name and preparing the result to be
								 * returned
								 */
								var defaultIdentityId = id;
								var userId = null;
								callbackResult = {
									"name" : name,
									"userId" : userId,
									"defaultIdentityId" : defaultIdentityId
								};
								/*
								 * set focus, populate name and update
								 * autocomplete contents
								 */
								this.inputElement.focus();
								this.inputElement.val($('#' + id + ' .name').attr('title'));

							}
							this.selectionCallback(callbackResult);
						},

						setSelectionCallback : function(id) {
							var name = $('#' + id).attr('name');
							var callbackResult = null;
							if (name) {
								var defaultIdentityId = id;
								var userId = null;
								callbackResult = {
									"name" : name,
									"userId" : userId,
									"defaultIdentityId" : defaultIdentityId
								};
							}
							this.selectionCallback(callbackResult);
						},
						resetSelectionCallback : function() {
							this.selectionCallback(null);
							$(this.formSelector).find("[name=nameeIdentityId]").attr('value', '');
							$(this.formSelector).find("[name=nameeUserId]").attr('value', '');
						},
						getEmail : function() {
							if ($('#add_email input').attr('value') == 'Add an email address for this person (optional)') {
								$('#add_email input').attr('value', '');
								$(this.formSelector).find("[name=email]").attr('value', '');
							} else {
								$(this.formSelector).find("[name=email]").attr('value', $('#add_email input').attr('value'));
							}
							return $(this.formSelector).find("[name=email]").attr('value');
						},

						getName : function() {
							if ($('#name-field').attr('value') == 'Unknown Person') {
								$('#name-field').attr('value', '');
							}
							return $('#name-field').attr('value');
						},

						prepareNewNameMode : function(self, autocompleteElement) {
							$('.match').hide();
							if (autocompleteElement.find('div ul li.add_email').length == 0) {
								autocompleteElement.find('div ul').append(self.addEmailTemplate);
							}

							if (autocompleteElement.find('div ul li.add_email:visible').length == 0) {
								autocompleteElement.find('div ul li.new').hide();
								self.attachAddEmailhandlers(self);
								autocompleteElement.find('div ul li.add_email').show();
								$('#wrapper').removeClass('facebook_friend_wrapper');

							}
							$('#autocompleter_footer span').html("To share your photos with this person add their email address ↑");

						},
						highlightNames : function(self, inputtext) {
							$('#autocompleter li.match:visible .name strong').each(function() {
								$(this).html(self.highlight($(this).text(), inputtext));
							});
							$('#autocompleter li.match.new .name strong').each(function() {
								$(this).html(self.highlight(inputtext, inputtext));
							});

						},
						filterEntries : function(autocompleteElement, visibleEntriesCount, visibleEntry, self) {
							if (visibleEntriesCount == 1) {
								self.setSelectionCallback(visibleEntry.attr('id'));
								visibleEntry.find('label.checkbox').addClass('checked');
								if (visibleEntry.hasClass('facebook')) {
									$('#wrapper').addClass('facebook_friend_wrapper');
								}
								autocompleteElement.find('div ul li.new').hide();
							} else {
								$('#wrapper').removeClass('facebook_friend_wrapper');
							}

							if (visibleEntriesCount > 1) {
								autocompleteElement.find('div ul li.new').show();
							}

							if (visibleEntriesCount == 0) {
								self.resetSelectionCallback();
								
								if (autocompleteElement.find('div ul li.add_email').length == 0) {

									autocompleteElement.find('div ul').append(self.addEmailTemplate);
								}
								if (autocompleteElement.find('div ul li.add_email:visible').length == 0) {
									autocompleteElement.find('div ul li.new').hide();
									autocompleteElement.find('div ul li.add_email').show();
									self.attachAddEmailhandlers(self);
									$('#autocompleter_footer span').html("To share your photos with this person add their email address ↑");
								}

								if (self.editDetailsMode && EditDetails.email && EditDetails.email != '') {
									$('#add_email input').attr('value', EditDetails.email);
								}
							} else {
								autocompleteElement.find('div ul li.add_email').hide();
								$('#autocompleter_footer span').html("To select or deselect a person's name click on the checkbox ↑");

							}
							autocompleteElement.find('div ul li').removeClass('first');
							autocompleteElement.find('div ul li:visible:first').addClass('first');

						},
						/** ******* VALIDATION METHODS ********** */
						invalidEmail : function() {
							$(this.formSelector + ' #add_email input').addClass('alert');
							$(this.formSelector + ' #autocompleter_footer').addClass('alert');
							$(this.formSelector + ' #autocompleter_footer span').html('Please enter a valid email address or leave this field empty ↑');

						},

						invalidName : function() {
							$(this.formSelector + ' .name-input input.name-field').removeClass('focused');
							$(this.formSelector + ' .name-input .reset').hide();
							$(this.formSelector + ' #autocompleter').hide();
							$(this.formSelector + ' p.message').addClass('alert');
							$(this.formSelector + ' input.name-field').addClass('alert');
							$(this.formSelector + ' p.message').removeClass('not_visible');
							$(this.formSelector + ' p.message').html('Names cannot contain these characters: ^#%?*&=+\\/<>');
						},
						emptyName : function() {
							$(this.formSelector + ' .name-input input.name-field').removeClass('focused').val('');
							$(this.formSelector + ' .name-input .reset').hide();
							$(this.formSelector + ' p.message').addClass('alert');
							$(this.formSelector + ' .control.secondary .infolink').addClass('alert')
							$(this.formSelector + ' input.name-field').addClass('alert');
							$(this.formSelector + ' p.message').html('You must enter a name above or choose an option below.');
						},

						removeEmailAlert : function() {
							$(this.formSelector + ' #add_email input').removeClass('alert');
							$(this.formSelector + ' #autocompleter_footer').removeClass('alert');
						},
						removeNameAlert : function() {
							if ($(this.formSelector + ' input.name-field').hasClass('alert')) {
								$(this.formSelector + ' .name-input input.name-field').addClass('focused');
								$(this.formSelector + ' .name-input .reset').show();
								$(this.formSelector + ' p.message').removeClass('alert');
								$(this.formSelector + ' input.name-field').removeClass('alert');
								$(this.formSelector + ' p.message').removeClass('not_visible');
								$(this.formSelector + ' p.message').html('Enter a name in the field above &ndash; then click "Save."');
								$(this.formSelector + ' .control.secondary .infolink').removeClass('alert')
							}
						},

						/**
						 * *************SHOW/HIDE
						 * AUTOCOMPLETER*********************
						 */
						show : function(dontResetScrollTop) {
							if (this.currentinputtext == "") {
								return;
							}
							$('#autocompleter').show();
							$(this.formSelector + ' .message').addClass('not_visible');
						},

						hide : function() {
							this.shown = false;
							this.autocompleteElement.hide();
							$(this.formSelector + ' .message').removeClass('not_visible');
						},

						isShown : function() {
							var escPressedDelay = 100; /* in millis */
							if (this.shown || (new Date().getMilliseconds() - this.lastEscTstamp < escPressedDelay))
								return true;
							return false;
						},
						hover : function(what) {
							if (!what) {/* no hover/selected */
								/* clear hover */
								this.autocompleteElement.find("li.match.hover").removeClass("hover");
								/* scroll to top if no hover/select? */
								this.scrollTop = 0;
								return;
							} else if (what === "next" || what === "prev") { /* up/down */
								/* arrow */
								/* step */
								if (!this.autocompleteElement.find("li.match.hover").removeClass("hover").eq(0)[what](":visible").addClass("hover").length) {

									this.autocompleteElement.find("li.match:visible:first").addClass("hover");
								}
							} else { /* select entry based on element */
								/* clear and set this element as hover */
								$(what).addClass("hover").siblings(".hover").removeClass("hover");
							}
							this.show(true);
							/* scroll into view */
							var sTop = $('#scrollable_area').scrollTop() || 0;
							var eTop = this.autocompleteElement.find("li.match.hover:first").length ? this.autocompleteElement.find("li.match.hover:first")[0].offsetTop : 0;
							var ach = (this.autocompleteElement.find("li.match:visible:first").height() || 43) * (this.size - 1);
							if (eTop < sTop) {
								$('#scrollable_area').scrollTop(eTop);
							} else if (eTop > (sTop + ach)) {
								$('#scrollable_area').scrollTop(eTop - ach);
							} else {
								$('#scrollable_area').scrollTop(0);
							}
						},
						chooseSelectedRow : function() {
							if (!$('li.match.hover label.checkbox').hasClass('checked')) {
								$('li.match.hover').click();
							}
						},
						escape : function(text) {
							/* escaping input text */
							if (!arguments.callee.sRE) {
								var specials = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\' ];
								arguments.callee.sRE = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
							}
							return text.replace(arguments.callee.sRE, '\\$1');
						},

						prefixesMatch : function(text1, text2, prefixLength) {
							if (text1.length < prefixLength || text2.length < prefixLength)
								return false;
							return (text1.substring(0, prefixLength) == text2.substring(0, prefixLength));
						}
					};

				});
