
/*
* Thease scripts based on "Search-as-you-type 1.2"
* http://code.google.com/p/search-as-you-type/
*/


/*
 * Copyright (C) 2006 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *      
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var ffSugestConfiguration = {
	styleDir: '/css/shared/suggest/',
	imageDir: '/img/shared/suggest/',
	categories: ['Products', 'Support', 'News', 'Sustainability', 'About Us'],
	ajaxResponderUrl: "/search/suggest.php",
	maxFullResults: 4,
	keystrokeDelay: 20,
	showResultsDelay: 200,
	bottomPageMargin: 10
};

function FFSugest(){

}

FFSugest.prototype.initialize = function(formFieldId, inputFieldId, focus) {
	var formEl = this.formEl = document.getElementById(formFieldId);
	var inputFieldEl = document.getElementById(inputFieldId);
	inputFieldEl.value = 'Search';

	this.initializeVariables_(inputFieldEl);

	this.detectBrowser_();
	this.attachStylesheets_();
	this.createDomElements_();
	this.waitingForSearchResultsEl.src = ffSugestConfiguration.imageDir + "loading.gif";

	this.restoreInputField_();
	this.addEventHandlers_();
	this.updateDimensionsAndShadow_(null);

	if (this.debugMode) {
		this.activateDebugConsole_();
	}

	if (focus) {
		this.focusInputField_();
	}

	this.initialized = true;
}

/**
 * Initialize all the variables needed for later.
 * @param {element} inputFieldEl An input field element Search-as-you-type
 *                               should attach itself to
 */
FFSugest.prototype.initializeVariables_ = function(inputFieldEl) {
  // Location (URL) of the parent page
  this.location = "" + window.location;

  // Protocol used by the parent page ("http" or "https").
  this.protocol = this.location.substr(0, this.location.indexOf("://") + 3);

  // <script> object for Ajax calls
  this.ajaxObject = null; 

  // Results from the last search
  this.results = {}; 

  // Search cache (containing previous responses)
  this.searchCache = []; 

  // Whether the whole as-you-type search engine has been initialized
  this.initialized = false; 
  
  // Whether we are waiting for Ajax response (shows a rotating progress 
  // icon if so)
  this.waitingForSearchResults = false; 

  // Whether search results window is hidden or visible 
  this.resultsWindowHidden = true; 
  
  // A handler to the input field
  this.inputFieldEl = inputFieldEl;

  // The query last typed by the user
  this.typedQuery = this.getInputFieldValue_(); 

  // A handler to the search results window element
  this.searchResultsEl = 0; 

  // A handler to the alternate search results window (we have two and switch
  // between them for better visuals)
  this.alternateSearchResultsEl = 0; 

  // Whether the input field currently has focus (can be 0, 0.5 or 1) 
  this.inputFieldHasFocus = 0;     

  // Whether any of the results is activated by navigating through it via
  // keyboard. -1 if no, 0 or more if yes (indicates the number of the 
  // active search result)
  this.activeResult = -1; 

  // Whether the search result window has been dismissed manually by clicking
  // somewhere else
  this.resultsWindowHiddenByClicking = false;

  // Whether the arrow key has been processed on keydown event, and can be
  // ignored on keypress (see handleBodyKeyPress for more information on why
  // this is necessary)
  this.arrowKeyProcessed = false;

  // The code of the last pressed key
  this.lastKeyPressed = 0;

  // Timer id of the JavaScript timer to show results
  this.showResultsTimeoutId = -1; 

  // The id of the JavaScript timer to fire a query after 
  // ffSugestConfiguration.keystrokeDelay ms have passed 
  // since the last keystroke
  this.keystrokeTimeoutId = -1; 

  // Current autocomplete value
  this.autocomplete = '';

  // Whether autocomplete has just been collapsed (i.e. turned into regular
  // regular input text by pressing Tab or right arrow)
  this.autocompleteJustCollapsed = false;

  // Contents of the tip appearing as the last search result for 5% of the
  // queries ('' if not available)
  this.tipText = '';

  // Whether we're in the debug mode (activated by adding 
  // ?debugSearchAsYouType to the URL)
  this.debugMode = this.location.indexOf("debugSearchAsYouType") > -1;
}


FFSugest.prototype.detectBrowser_ = function() {
	this.browserIE = false;
	this.browserFirefox = false;
	this.browserSafari = false;
	
	if ( navigator.userAgent.indexOf("MSIE") > -1 ) {
		this.browserIE = true;
	}
	else if ((navigator.userAgent.indexOf("Firefox/") > -1)) {
		this.browserFirefox = true;
		if ((navigator.userAgent.indexOf("Firefox/1.0.") > -1)) {
			this.browserFirefox10 = true;
		}
		else {
			this.browserFirefox10 = false;
		}
	}
	else if (navigator.userAgent.indexOf("Safari") > -1) {
		this.browserSafari = true;
		if (navigator.userAgent.indexOf("Version/") > -1) {
			this.browserSafari3OrHigher = true;
		}
	}
}


FFSugest.prototype.attachStylesheets_ = function() {
	this.attachStylesheet_('generic.css');
	this.attachStylesheet_('customized.css');
	if ( this.browserIE && jQuery.browser.version < 7 ) {
		this.attachStylesheet_('ie.css');
	}
	else if (this.browserSafari) {
		this.attachStylesheet_('safari.css');
	}
}

FFSugest.prototype.attachStylesheet_ = function(filename) {
	var el = document.createElement('link');
	el.href = ffSugestConfiguration.styleDir + filename;
	el.type = 'text/css';
	el.rel = 'stylesheet';
	document.getElementsByTagName('head').item(0).appendChild(el);
}


FFSugest.prototype.createDomElements_ = function() {

	var el = document.createElement("input");
	el.id = 'searchAsYouTypeBackupSearchField';
	el.style.display = 'none'; // in case CSS is not yet loaded
	document.body.appendChild(el);
	
	this.inputFieldEl.setAttribute('autocomplete', 'off')

	this.searchResultsEl = document.createElement("div");
	this.searchResultsEl.id = 'searchAsYouTypeResults1';
	this.searchResultsEl.className = 'searchResults';
	this.searchResultsEl.style.display = 'none';
	this.searchResultsEl.style.position = 'absolute'; 
	this.searchResultsEl.onclick = 'event.cancelBubble = true;';
	this.searchResultsEl.tabIndex = -1;

	this.alternateSearchResultsEl = document.createElement("div");
	this.alternateSearchResultsEl.id = 'searchAsYouTypeResults2';
	this.alternateSearchResultsEl.className = 'searchResults';
	this.alternateSearchResultsEl.style.display = 'none';
	this.alternateSearchResultsEl.style.position = 'absolute';
	this.alternateSearchResultsEl.onclick = 'event.cancelBubble = true;';
	this.alternateSearchResultsEl.tabIndex = -1;

	this.searchResultsShadowEl = document.createElement("div");
	this.searchResultsShadowEl.id = 'searchAsYouTypeResultsShadow';
	this.searchResultsShadowEl.style.visibility = 'hidden';
	this.searchResultsShadowEl.style.display = 'none';
	this.searchResultsShadowEl.style.left = 0;
	this.searchResultsShadowEl.style.top = 0;
	this.searchResultsShadowEl.style.width = 0;
	this.searchResultsShadowEl.style.height = 0;

	if ( this.browserIE && jQuery.browser.version < 7 ) {
		var el = document.createElement("div"); 
		el.id = 'searchAsYouTypeResultsShadowTOP';
		this.searchResultsShadowEl.appendChild(el);
	}

	var el = document.createElement("div");
	el.id = 'searchAsYouTypeResultsShadowL';
	this.searchResultsShadowEl.appendChild(el);
	var el = document.createElement("div");
	el.id = 'searchAsYouTypeResultsShadowR';
	this.searchResultsShadowEl.appendChild(el);
	var el = document.createElement("div"); 
	el.id = 'searchAsYouTypeResultsShadowTL';
	this.searchResultsShadowEl.appendChild(el);
	var el = document.createElement("div"); 
	el.id = 'searchAsYouTypeResultsShadowTR';
	this.searchResultsShadowEl.appendChild(el);
	
	this.allViewLinkEl = document.createElement("a");
	this.allViewLinkEl.id = 'searchAsYouTypeAllViewLink';
	this.allViewLinkEl.innerHTML = '<span id="searchAsYouTypeAllViewLinkInner">View all search results</span>';
	this.allViewLinkEl.onclick = searchAsYouTypeBind(this.formSubmit, this);
	this.allViewLinkEl.href = '';
	this.allViewLinkEl.style.visibility = 'hidden';
	this.allViewLinkEl.style.display = 'none'; 
	this.allViewLinkEl.style.left = 0;
	this.allViewLinkEl.style.top = 0;

	//Close Btn
	this.searchResultsCloseBtnImg = document.createElement("img");
	this.searchResultsCloseBtnImg.id = 'searchAsYouTypeCloseBtn';
	this.searchResultsCloseBtnImg.src = ffSugestConfiguration.imageDir + 'suggest_icn_close_01.gif';
	this.searchResultsCloseBtnImg.style.visibility = 'hidden'; 
	this.searchResultsCloseBtnImg.style.display = 'none';
	this.searchResultsCloseBtnImg.onmousedown = searchAsYouTypeBind(this.handleBodyClick, this);
	this.searchResultsCloseBtnImg.onmouseover = searchAsYouTypeBind(function(){
		this.src =ffSugestConfiguration.imageDir + 'suggest_icn_close_01_o.gif';
	}, this.searchResultsCloseBtnImg);
	this.searchResultsCloseBtnImg.onmouseout = searchAsYouTypeBind(function(){
		this.src =ffSugestConfiguration.imageDir + 'suggest_icn_close_01.gif';
	}, this.searchResultsCloseBtnImg);
	
	var el = document.createElement("searchAsYouType");
	el.id = 'searchAsYouType';

	el.appendChild(this.searchResultsEl);
	el.appendChild(this.alternateSearchResultsEl);
	el.appendChild(this.searchResultsShadowEl);
	el.appendChild(this.allViewLinkEl);
	el.appendChild(this.searchResultsCloseBtnImg);
	document.body.appendChild(el);

	this.waitingForSearchResultsEl = document.createElement("img");
	this.waitingForSearchResultsEl.style.visibility = 'hidden'; 
	this.waitingForSearchResultsEl.style.position = 'absolute'; 
	this.waitingForSearchResultsEl.src = ffSugestConfiguration.imageDir + "loading.gif";

	document.body.appendChild(this.waitingForSearchResultsEl);
}

FFSugest.prototype.getInputFieldValue_ = function() {
  return this.inputFieldEl.value.toLowerCase().replace(/^\s+/g, '').replace(/\s+$/g, '');
}

FFSugest.prototype.focusInputField_ = function() {
  this.inputFieldEl.focus();

  if (this.inputFieldEl.createTextRange && window.document.selection) {
    var sel = this.inputFieldEl.createTextRange();
    sel.collapse(true);
    sel.move("character", this.inputFieldEl.value.length);
    sel.select();
  }
}

FFSugest.prototype.clearInputField_ = function() {
  this.inputFieldEl.value = '';
}

FFSugest.prototype.saveInputField = function(e) {
  document.getElementById('searchAsYouTypeBackupSearchField').value = this.inputFieldEl.value;
  document.getElementById('searchAsYouTypeBackupSearchField').setAttribute("active", 1);
}

FFSugest.prototype.restoreInputField_ = function() {
  if (document.getElementById('searchAsYouTypeBackupSearchField').
        getAttribute("active")) {
    this.inputFieldEl.value = 
      document.getElementById('searchAsYouTypeBackupSearchField').value;
    this.typedQuery = this.getInputFieldValue_();
  }
}

FFSugest.prototype.addEventHandlers_ = function() {

	this.inputFieldEl.onkeyup = searchAsYouTypeBind(this.handleInputKeyUp, this);
	this.inputFieldEl.onkeypress = searchAsYouTypeBind(this.handleInputKeyPress, this);
	this.inputFieldEl.onkeydown = searchAsYouTypeBind(this.handleInputKeyDown, this);
	this.inputFieldEl.onfocus = searchAsYouTypeBind(this.handleInputFocus, this);
	this.inputFieldEl.onblur = searchAsYouTypeBind(this.handleInputBlur, this);
	this.inputFieldEl.onclick = searchAsYouTypeBind(this.handleInputClick, this);
	this.inputFieldEl.onmousedown = searchAsYouTypeBind(this.handleInputMouseDown, this);

	if (window.addEventListener) { // Mozilla, Netscape, Firefox
		document.body.addEventListener('click', searchAsYouTypeBind(this.handleBodyClick, this), false);
		document.addEventListener('keyup', searchAsYouTypeBind(this.handleBodyKeyUp, this), false);
		document.addEventListener('keydown', searchAsYouTypeBind(this.handleBodyKeyDown, this), false);
		document.addEventListener('keypress', searchAsYouTypeBind(this.handleBodyKeyPress, this), false);
		window.addEventListener('resize', searchAsYouTypeBind(this.handleBodyResize, this), false);
	} else { // IE
		document.body.attachEvent('onclick', searchAsYouTypeBind(this.handleBodyClick, this));
		document.body.attachEvent('onkeyup', searchAsYouTypeBind(this.handleBodyKeyUp, this));
		document.body.attachEvent('onkeydown', searchAsYouTypeBind(this.handleBodyKeyDown, this));
		document.onkeypress = searchAsYouTypeBind(this.handleBodyKeyPress, this);
		window.attachEvent('onresize', searchAsYouTypeBind(this.handleBodyResize, this));
	}

	// The below is for Firefox 1.5's fastback feature.
	// (...) CHANGE TO event listener
	try {
		window.onpageshow = function(event) { 
			if (event.persisted) {
				searchAsYouType.restoreInputField_(); 
			}
		};
	} catch(e) {
	}

	if ((this.browserFirefox) && (!this.browserFirefox10)) {
		window.onpagehide = searchAsYouTypeBind(this.saveInputField, this);
	} else {
		window.onunload = searchAsYouTypeBind(this.saveInputField, this);
	}
}

FFSugest.prototype.prepareRandomTip_ = function() {
	this.tipText = '';
}

FFSugest.prototype.updateDimensionsAndShadow_ = function(searchResultsEl) {
	// Figure out the absolute position of the input field element
	var el = this.inputFieldEl;
	var x = 0;
	var y = 0;
	var obj = el;
	do {
		x += obj.offsetLeft;
		y += obj.offsetTop;
		obj = obj.offsetParent;
	} while (obj);

	// Position the waiting animation, so it's inside the input field, flushed
	// right
	// (...) height too
	this.waitingForSearchResultsEl.style.left = (x + this.inputFieldEl.offsetWidth - 19) + 'px';
	this.waitingForSearchResultsEl.style.top = (y + 5) + 'px';

	// Position the search results canvas element
	if (searchResultsEl) {
		y += el.offsetHeight - 2;
		x -= 148;

		var w = 286;

		var setY = y + 22;
		searchResultsEl.style.left = x + "px";
		searchResultsEl.style.top = setY + "px";
		searchResultsEl.style.width = w + "px";

		var xPre = x;
		x = searchResultsEl.offsetLeft;
		y = searchResultsEl.offsetTop;
		w = searchResultsEl.offsetWidth;
		searchResultsEl.style.height = "auto";

		// set results pos
		var m = 0;
		if ( this.results.results && this.results.results.length ) m = this.results.results.length;
		for (var i = 0; i < m; i++) {
			var resObj = document.getElementById('searchResult'+i);
			if (resObj.getElementsByTagName('img').length) {
				var imgH = resObj.getElementsByTagName('img')[0].offsetHeight;
				var span = resObj.getElementsByTagName('span');
				var titleH = 0;
				var titleEl;
				var imgEl;
				for (var j = 0, jMax = span.length; j < jMax; j++) {
					if (span[j].className == 'suggestTitle') {
						titleH = span[j].offsetHeight;
						titleEl = span[j];
					}
					else if (span[j].className == 'suggestThumb') {
						imgEl = span[j];
					}
				}

				var detailH = 0;
				if (resObj.getElementsByTagName('p').length) {
					var detailH  = resObj.getElementsByTagName('p')[0].offsetHeight;
				}

				var dif = 0;
				if (imgH > titleH + 4 + detailH) {
					dif = (imgH - (titleH + 4 + detailH))/2;
					resObj.style.paddingTop = dif+'px';
					//imgEl.style.top = '-' + dif + 'px';
				}

				if (resObj.offsetHeight < imgH) {
					var setH = imgH - dif;
					resObj.style.height = setH + 'px';
				}
				resObj.style.display = 'block';
			}
		}

		var h = searchResultsEl.offsetHeight;
		if ( this.browserIE ) h = searchResultsEl.offsetHeight;

		// Resize shadows
		if ( this.browserIE ) x = xPre;
		this.resizeShadowEl_("", x, y-22, w, h);
		this.resizeShadowEl_("L", -25, 22, 25, h);
		this.resizeShadowEl_("TL", -25, 0, 25, 22);
		this.resizeShadowEl_("TR", w, 0, 25, 22);
		this.resizeShadowEl_("R", w, 22, 25, h);
		
		if ( this.browserIE && jQuery.browser.version < 7 ) {
			this.resizeShadowEl_("TOP", 0, 0, w, 22);
		}
		
		this.setAllViewLinkPos( x-25, y+h );
		
		this.setCloseBtnPos(x+277,y-12);
	}
}

FFSugest.prototype.setAllViewLinkPos = function( x, y ){
	this.allViewLinkEl.style.left = x + "px";
	this.allViewLinkEl.style.top = y + "px";
}


FFSugest.prototype.setCloseBtnPos = function ( x, y ) {
	this.searchResultsCloseBtnImg.style.left = x + "px";
	this.searchResultsCloseBtnImg.style.top = y + "px";
}

/**
 * Resize one of the shadow elements.
 * @param {string} id Id of the shadow element (cf. "BR")
 * @param {int} x Horizontal position (in pixels)
 * @param {int} y Vertical position (in pixels)
 * @param {int} w Width (in pixels)
 * @param {int} h Height (in pixels)
 */
FFSugest.prototype.resizeShadowEl_ = function(id, x, y, w, h) {
  var el = document.getElementById('searchAsYouTypeResultsShadow' + id);

  /* Wrapped around in try/catch because of an IE7 bug */
  try {
    el.style.left = x + "px";
    el.style.top = y + "px";
    el.style.width = w + "px";
    el.style.height = h + "px";
  } catch(e) {

  }
}

/**
 * Perform query search (an Ajax request) on whatever the user typed.
 * Skip if already in cache.
 */
FFSugest.prototype.search_ = function(dontDelayShowResults) {
  if (dontDelayShowResults === true) {
    this.delayShowResults = false;
  } else {
    this.delayShowResults = true;
  }

  // If a query is empty we don't do anything
  if (this.typedQuery.length == 0) {
    this.changeWaitingForSearchResults_(false);
    return;
  }

  URL = ffSugestConfiguration.ajaxResponderUrl;
  URL += "?query=" + encodeURIComponent(this.typedQuery);
  URL += "&jsonp=searchAsYouType.handleAjaxResponse";
  if (this.debugMode) {
    URL += "&debug=1";
  }

  if (this.waitingForSearchResults) {
    this.cancelCurrentSearch_();
  }

  if (this.debugMode) {
    this.addToDebugConsoleTimesNewLine_("<td>" + this.typedQuery + "</td>");

    var date = new Date();
    this.debugQueryStartTime = date.getTime();
  }

  this.changeWaitingForSearchResults_(true);

  // If already in cache, use cache
  if (this.searchCache["_" + this.typedQuery]) { 
    this.ajaxRequestStartTime = -1;
    this.processResults_(this.searchCache["_" + this.typedQuery].results, true);
  } else {
    var date = new Date();
    this.ajaxRequestStartTime = date.getTime();

    this.ajaxObject = document.createElement('script');
    this.ajaxObject.src = URL;
    this.ajaxObject.type = "text/javascript";
    this.ajaxObject.charset = "utf-8";
    document.getElementsByTagName('head').item(0).appendChild(this.ajaxObject);
  }
}

/**
 * Cancel the Ajax request we're currently waiting for.
 */
FFSugest.prototype.cancelCurrentSearch_ = function() {
  if (this.ajaxObject) {
    try {
      document.getElementsByTagName('head').item(0).
        removeChild(this.ajaxObject);
    } catch(e) {
    }
  }
}

/**
 * Show or hide the "results coming up" pie animation depending on 
 * whether it's needed. Abort the current Ajax request if necessary.
 * @param {bool} value Whether we're waiting or not for search results
 */
FFSugest.prototype.changeWaitingForSearchResults_ = function(value) {
  if (this.waitingForSearchResults != value) {
    if (value) {
      this.waitingForSearchResultsEl.style.visibility = 'visible';
    } else {
      this.waitingForSearchResultsEl.style.visibility = 'hidden';

      this.cancelCurrentSearch_();
    }
  }
  
  this.waitingForSearchResults = value;
}

FFSugest.prototype.handleAjaxResponse = function(results) {
	this.processResults_(results, false);
}

FFSugest.prototype.processResults_ = function(results, cached) {
	results.countNotCompact = 0;
	results.countExpanded = 0;
	for (var i in results.results) {
		if (results.results[i].style == 'expanded') {
			results.countExpanded++;
			results.countNotCompact++;
		} else if (results.results[i].style == 'normal') {
			results.countNotCompact++;
		}
	}

	// Copy the object for future reference
	this.results = searchAsYouTypeCloneObject(results);

	// Cache the results
	this.searchCache["_" + this.results.query] = {};
	this.searchCache["_" + this.results.query].results = searchAsYouTypeCloneObject(results);

	this.resultsWindowHiddenByClicking = false;

	// See if the results respond to the last typed query (Ajax requests might 
	// come out of order)
	if (results.query == this.typedQuery) {

		// If nothing has been returned, hide the results window
		if (!this.results.results.length) {
			this.hideResultsWindow_();
			this.changeWaitingForSearchResults_(false);
		} else {
			this.prepareResultsWindow_();
		}
	}
}

/**
 * Get an HTML snippet showing the current result type. This is used if
 * we show summarized results.
 * @param {string} type Search result type (e.g. "Conference rooms")
 * @return {string} Corresponding HTML snippet
 */
FFSugest.prototype.getResultTypeDescriptionHtml_ = function(type) {
  return '<h1>' + type + ": " + "</h1>";
}

/**
 * Get a CSS class name corresponding to a result type. What this does is
 * removes all of the spaces.
 * @param {string} type Search result type (e.g. "Conference rooms")
 * @return {string} Corresponding class name (e.g. "Conferencerooms")
 */
FFSugest.prototype.getResultTypeClassName_ = function(type) {
  return type.replace(/\ /g, "");
}

FFSugest.prototype.getResultsHtml_ = function(resultId) {
	var currentResultId = 0;
	var html = '';
	var categories = ffSugestConfiguration.categories;

	for ( var catNum = 0; catNum < categories.length; catNum++ ) {
		var catHit = false;
		for (var i = 0,iMax = this.results.results.length; i < iMax; i++) {
			if ( categories[catNum].toLowerCase() != this.results.results[i].type.toLowerCase() ) continue;
			
			if ( !catHit ) {
				html += '<span class="suggestCategoryTitle">'+categories[catNum]+'</span>';
				catHit = true;
			}
			
			
			if ((resultId == -1) || (resultId == currentResultId)) {
				var className = "searchResult " + this.getResultTypeClassName_(this.results.results[i].type);
				if (currentResultId == 0) {
					className += " first";
				}

				var linkUrl = this.results.results[i].moreDetailsUrl || '';
				html += '<div id="searchResult' + currentResultId + '" ' +
							'class="' + className + '" ' +
							'originalId="' + i + '" ' +
							'moreDetailsUrl="' + linkUrl + '" ' +
							 '>';
				var label = this.results.results[i].title || '';
				var analyseTxt = this.analyseFuncStr_( label, this.typedQuery, linkUrl );
				var anchorFunc = '';
				if ( !this.browserIE && ( linkUrl.indexOf('#') != -1 || location.hash != '' ) ) {
					var tmp = linkUrl.replace(/\/index\.html/,'/');
					tmp = tmp.replace(/#.*?$/,'');
					var tmp2 = location.pathname.replace(/\/index\.html/,'/');
					tmp2 = tmp2.replace(/#.*?$/,'');
					if ( tmp == tmp2 ) {
						anchorFunc = 'onclick="'+analyseTxt+'location.href=\''+ linkUrl +'\'; location.reload(); return false;"';
					}
				}

				if ( anchorFunc == '' && analyseTxt != '' ) {
					anchorFunc = 'onclick="'+ analyseTxt +'"';
				}

				html += '<a href="' + linkUrl + '" style="_zoom:1" ' + anchorFunc + '>';

				if ( this.results.results[i].thumbnail ) {
					var onClickFunc = '';
					if ( this.browserIE ) {
						onClickFunc = 'onclick="'+ analyseTxt + '"location.href=\''+ linkUrl +'\'"';
					}
					html += '<span class="suggestThumb"><img src="' + this.results.results[i].thumbnail + '" width="70" height="70" alt="" class="suggestThumbImg" ' + onClickFunc + ' /></span>';
				}
				html += '<span class="suggestTitle">' + label + '</span>';
				html += '</a>';
				if ( this.results.results[i].detail )
					html += '<p class="suggestDetail">' + this.results.results[i].detail + '</p>';
				html += '</div>';
			}

			currentResultId++;
		}
	
	}

	return html;
}

FFSugest.prototype.analyseFuncStr_ = function ( label, word, href ) {
	var res = '';
	if ( window._gaq ) {
		//console.log(word + '-' + href);
		res += "window._gaq.push(['_trackEvent' , 'suggest' , '"+ word.replace(/'/,'&#39;') + "-" + href + "']);";
	}
	return res;
}

FFSugest.prototype.prepareResultsWindow_ = function() {
	var showExpanded;

	this.activeResult = -1;

	this.resultsHtml = this.getResultsHtml_(-1);

	if (this.showResultsTimeoutId > -1) {
		clearTimeout(this.showResultsTimeoutId);
	}

	var time;

	if (this.delayShowResults) {
		if (this.ajaxRequestStartTime == -1) {
			time = 0;
		} else {
			var date = new Date();
			time = date.getTime() - this.ajaxRequestStartTime;
		}

		var time = ffSugestConfiguration.showResultsDelay - time;
		if (time <= 1) {
			time = 1;
		}
	} else {
		time = 1;
	} 

	this.showResultsTimeoutId =	setTimeout(searchAsYouTypeBind(this.showResultsWindow_, this), time);
}


FFSugest.prototype.showResultsWindow_ = function() {
	this.showResultsTimeoutId = -1;

	this.changeWaitingForSearchResults_(false);
	clearInterval(this.hideTimeout);

	this.resultsWindowHiddenByClicking = false;
	this.resultsWindowHidden = false;

	// cleaning ids for safari
	var i = 0;
	var el;
	while (el = document.getElementById('searchResult' + i)) {
		el.id = '';					 
		i++;
	}

	this.alternateSearchResultsEl.style.height = '1px';
	this.alternateSearchResultsEl.style.visibility = 'hidden';
	this.alternateSearchResultsEl.style.display = 'block';
	this.alternateSearchResultsEl.innerHTML = this.resultsHtml;
	this.alternateSearchResultsEl.style.opacity = 0.99;

	// We go through all of the links in the results, and remove tabindex
	// and make them override an iframe, if we're in one
	var els = this.alternateSearchResultsEl.getElementsByTagName('a');
	for (var i = 0, j = els.length; i < j; i++) {
		els.item(i).tabIndex = -1;
		els.item(i).target = "_top";
	}	

	// We go through all of the images, hide them, and assign the function
	// to show them when they're fully loaded. Since an image can resize
	// a search result window, we need to make sure that we recalculate the
	// dimensions (and shadows) on image load
	var els = this.alternateSearchResultsEl.getElementsByTagName('img');
	for (var i = 0, j = els.length; i < j; i++) {
		//els.item(i).style.display = 'none';
		els.item(i).onload = searchAsYouTypeBind(this.handleImageOnLoad, this, els.item(i));
	}

	this.updateDimensionsAndShadow_(this.alternateSearchResultsEl);

	this.searchResultsEl.style.visibility = 'hidden';
	this.searchResultsEl.style.display = 'none';

	this.searchResultsShadowEl.style.display = 'block';
	this.searchResultsShadowEl.style.visibility = 'visible';
	this.searchResultsShadowEl.style.opacity = 1;
	this.alternateSearchResultsEl.style.visibility = 'visible';

	this.allViewLinkEl.style.display = 'block';
	this.allViewLinkEl.style.visibility = 'visible';
	this.allViewLinkEl.style.opacity = 1;

	this.searchResultsCloseBtnImg.style.display = 'block';
	this.searchResultsCloseBtnImg.style.visibility = 'visible';
	this.searchResultsCloseBtnImg.style.opacity = 1;

	// Swap search result elements handlers
	var el = this.searchResultsEl;
	this.searchResultsEl = this.alternateSearchResultsEl;
	this.searchResultsEl.style.opacity = 1;
	this.alternateSearchResultsEl = el;
}


FFSugest.prototype.handleImageOnLoad = function(el) {
  if (el) {
    el.style.display = 'inline';

    this.updateDimensionsAndShadow_(this.searchResultsEl);
  }

  return false;
}

/**
 * Hide the search results window. This initializes the fadeout.
 */
FFSugest.prototype.hideResultsWindow_ = function() {
	if (this.resultsWindowHidden) {
		return;
	}

	this.clearAutocomplete_(true);

	this.hideOpacity = this.searchResultsEl.style.opacity;
	clearInterval(this.hideTimeout);
	this.fadeLastTime = new Date().getTime();
	this.hideTimeout = setInterval(searchAsYouTypeBind(this.fadeResultsWindow_, this), 20);

	this.resultsWindowHidden = true;
	this.activeResult = -1;
}


/**
 * Fade the search results window a little bit more. We're counting the 
 * time so it should always take the same amount of time, only perhaps be a 
 * little less smooth on less powerful machines.
 */
FFSugest.prototype.fadeResultsWindow_ = function() {
	var newTime = new Date().getTime();

	this.hideOpacity -= (newTime - this.fadeLastTime) * 0.005;
	this.fadeLastTime = newTime;

	if (this.hideOpacity <= 0) {
		clearInterval(this.hideTimeout);
		this.searchResultsEl.style.display = 'none';
		this.searchResultsShadowEl.style.visibility = 'hidden';
		this.allViewLinkEl.style.display = 'none';
		this.searchResultsCloseBtnImg.style.visibility = 'hidden';
	} else {
		this.searchResultsEl.style.opacity = this.hideOpacity;
		this.searchResultsShadowEl.style.opacity = this.hideOpacity;
		this.allViewLinkEl.style.opacity = this.hideOpacity;
		this.searchResultsCloseBtnImg.style.opacity = this.hideOpacity;
	}
}

/**
 * Activate (highlight) a result. Used for keyboard navigation
 * between search results.
 * @param {int} no The number of the result to activate
 */
FFSugest.prototype.highlightSearchResult_ = function(no) {
  document.getElementById('searchResult' + no).className += " highlighted";
}

/**
 * Deactivate (de-highlight) a result. Used for keyboard navigation
 * between search results.
 * @param {int} no The number of the result to deactivate
 */
FFSugest.prototype.unhighlightSearchResult_ = function(no) {
  document.getElementById('searchResult' + no).className =
    document.getElementById('searchResult' + no).className.
    replace(/ highlighted/, "");
}

/**
 * Activate (highlight) a next result, if possible.
 */
FFSugest.prototype.highlightNextSearchResult_ = function() {
  if (this.results.results.length) {
    if (this.activeResult == -1) {
      this.activeResult = 0;
      if (this.inputFieldHasFocus) {
        this.inputFieldEl.blur();
      }
      this.highlightSearchResult_(this.activeResult);
    } else if (this.activeResult < this.results.results.length - 1) {
      this.unhighlightSearchResult_(this.activeResult);
      this.activeResult++;
      this.highlightSearchResult_(this.activeResult);
    }
  }
}

/**
 * Deactivate (de-highlight) a next result, if possible.
 */
FFSugest.prototype.highlightPrevSearchResult_ = function() {
  if (this.results.results.length) {
    if (this.activeResult == 0) {
      // Going up from the first result will get us back in the input field
      this.unhighlightSearchResult_(this.activeResult);
      this.activeResult = -1;
      this.inputFieldEl.focus();
    } else if (this.activeResult > 0) {
      this.unhighlightSearchResult_(this.activeResult);
      this.activeResult--;
      this.highlightSearchResult_(this.activeResult);
    }
  }
}

/**
 * Expand a summarized result.
 * @param {event} e Browser event (or null if invoked from here)
 * @param {int} id Id of the result to be expanded
 */
FFSugest.prototype.expandSummaryResult_ = function(e, id) {
  e = e || window.event;

  if (e) {
    e.cancelBubble = true;
  }

  var dividerEl = document.createElement("divider");
  var el = document.getElementById('searchResult' + id);
  var result = this.results.results[el.getAttribute('originalId')];
  var elParent = el.parentNode;

  elParent.insertBefore(dividerEl, el);
  elParent.removeChild(el);
  elParent.parentNode.innerHTML = 
    elParent.parentNode.innerHTML.replace(/<divider>/, 
      "</div>" + this.getResultsHtml_(id) + 
      "<div class='searchResult summary " + 
      this.getResultTypeClassName_(result.type) + "'>");

  var el = document.getElementById('searchResult' + id);

  var newEl = document.createElement("span");
  newEl.innerHTML = '&nbsp;&middot; ';

  var elPrev = el.previousSibling;

  if (elPrev) {
    if (!elPrev.getElementsByTagName('a').length) {
      elPrev.parentNode.removeChild(elPrev);
    } else {
      elPrev.innerHTML = 
        elPrev.innerHTML.replace(new RegExp(newEl.innerHTML + "$"), "");
    }
  }

  var elNext = el.nextSibling;
  if (elNext) {
    if (!elNext.getElementsByTagName('a').length) {
      elNext.parentNode.removeChild(elNext);
    } else {
      elNext.innerHTML = 
        elNext.innerHTML.replace(new RegExp("^" + newEl.innerHTML), 
        this.getResultTypeDescriptionHtml_(result.type));
    }
  }

  this.updateDimensionsAndShadow_(this.searchResultsEl);

  return false;
}

FFSugest.prototype.addAutocompleteTextIfPossible_ = function() {
	return false;
}

FFSugest.prototype.collapseAutocomplete_ = function() {

}

FFSugest.prototype.clearAutocomplete_ = function(hideResultsWindow) {

}

FFSugest.prototype.handleBodyClick = function(e) {
	e = e || window.event;
	var targetElement = (e.target) ? e.target : e.srcElement;
	if ( targetElement.id != 'searchAsYouTypeCloseBtn' && (targetElement.className.match(/(searchAsYouType|searchResult|suggest)/) || targetElement.id.match(/(searchAsYouType|searchResult|suggest)/)) ) return;
	this.clearAutocomplete_();
	this.hideResultsWindow_();
	this.resultsWindowHiddenByClicking = true;
}

/**
 * Handle a key down event in the input field.
 * @param {event} e Browser event
 */      
FFSugest.prototype.handleInputKeyDown = function(e) {
  if (!this.initialized) {
    return;
  }

  e = e || window.event;
  var whichKey = (e.which) ? e.which : e.keyCode;

  if ((whichKey == 8) || (whichKey == 46)) {
    this.clearAutocomplete_(false);
  } 
}

/**
 * Handle a key up event in the input field. Fire a search query if
 * applicable.
 * @param {event} e Browser event
 */      
FFSugest.prototype.handleInputKeyUp = function(e) {
  if (!this.initialized) return;

  e = e || window.event;
  var whichKey = (e.which) ? e.which : e.keyCode;

  this.lastKeyPressed = whichKey;

  if (this.autocompleteJustCollapsed) {
    this.typedQuery = this.lastTypedQuery = this.getInputFieldValue_();
    this.autocompleteJustCollapsed = false;
    return;
  }

  // Changing the query to lowercase and stripping out the white
  // space surrounding it
  var query = this.getInputFieldValue_();

  if (query != this.typedQuery) {
    if (this.showResultsTimeoutId > -1) {
      clearTimeout(this.showResultsTimeoutId);
    }

    this.lastTypedQuery = this.typedQuery;

    // We don't auto-complete on Backspace
    if (whichKey != 8) {
      if (this.addAutocompleteTextIfPossible_()) {
        this.typedQuery = this.lastTypedQuery = this.getInputFieldValue_();

        this.search_();
      }
    }

    this.typedQuery = this.getInputFieldValue_();

    if (this.lastTypedQuery != this.typedQuery) {
      if (this.keystrokeTimeoutId != -1) {
        clearTimeout(this.keystrokeTimeoutId);
        this.keystrokeTimeoutId = -1;
      }
      if (!this.typedQuery) {
        this.hideResultsWindow_();
        this.clearInputField_();
      }

      if (whichKey == 8) {
        this.clearAutocomplete_(true);
      }

      this.keystrokeTimeoutId = setTimeout(
                         searchAsYouTypeBind(this.search_, this), 
                         ffSugestConfiguration.keystrokeDelay);
    }
  }

  return true;
}

FFSugest.prototype.handleInputKeyPress = function(e) {
  if (!this.initialized) { 
    return;
  }
  var valueToReturn = true;

  e = e || window.event;
  var whichKey = (e.which) ? e.which : e.keyCode;

  switch (whichKey) {
    case 9: // Tab
      if (this.autocompleteJustCollapsed) {
        valueToReturn = false;
      }
      break;
  }

  return valueToReturn;
}

/**
 * Handle a key down event in the document body.
 * @param {event} e Browser event
 */
FFSugest.prototype.handleBodyKeyDown = function(e) {
  var valueToReturn = true;

  if (!this.initialized) {
    return;
  }

  e = e || window.event;
  var whichKey = (e.which) ? e.which : e.keyCode;
  var targetElement = (e.target) ? e.target : e.srcElement;

  switch (whichKey) {
    case 13: // Enter
    case 32: // space
      if ((!this.resultsWindowHidden) && (this.activeResult >= 0)) {
        if (document.getElementById('searchResult' + this.activeResult).
              className.indexOf('summarized') == -1) {
          // Pressing Enter or space while a search result is active (navigated
          // to from the keyboard) will follow the "More info" link
          var el = document.getElementById('searchResult' + this.activeResult);

          if (el.href) {
            var url = el.href;
          } else if (el.getAttribute("moreDetailsUrl")) { 
            var url = el.getAttribute("moreDetailsUrl");
          }

          if (url) {
            this.hideResultsWindow_();
            this.goToUrl_(url);
          } 
        } else {
          // Otherwise zoom in on a given summary record.
          this.expandSummaryResult_(null, this.activeResult);
          this.highlightSearchResult_(this.activeResult);
        }
        valueToReturn = false;
      } 
      break;

    case 27: // Escape
      // Escape can do three things, in order of precedence:
      // 1. If the page with results is loading, Escape should
      //    be handled by the browser to cancel loading the page.
      // 2. If the pop-down with results is shown, Escape should
      //    remove it.
      // 3. Otherwise it should clear the field.

      if (this.inputFieldHasFocus) {
        // Safari sends Esc code twice, so we ignore the second time
        // it happens
        if (this.browserSafari && !this.browserSafari3OrHigher) {
          if (this.escapeKeyJustPressed) {
            this.escapeKeyJustPressed = false;
            break; 
          } else {
            this.escapeKeyJustPressed = true;
          }
        }

        if (!this.resultsWindowHidden) { 
          this.hideResultsWindow_();
          valueToReturn = false;
          this.inputFieldEl.focus();
          this.inputFieldHasFocus = 1;
        } else {
          this.clearInputField_();
          valueToReturn = false;
        }
      }
      break;

    case 35: // End
      if ((this.inputFieldHasFocus) && (this.autocomplete != '')) {
        this.collapseAutocomplete_();
        this.autocompleteJustCollapsed = true;
      }
      break;

    case 40: // down arrow
    case 63233: // down arrow
    case 39: // right arrow
      if (whichKey == 39) {
        if ((this.inputFieldHasFocus) && (this.autocomplete != '')) {
          this.collapseAutocomplete_();
          this.autocompleteJustCollapsed = true;
        }
      }

      // If we press down arrow in the input field, we can force the 
      // re-query 
      if ((this.resultsWindowHidden) && (this.inputFieldHasFocus) && 
          (whichKey != 39)) {
        this.search_(true);
        valueToReturn = false;
      } else if ((!this.resultsWindowHidden) && 
                 ((this.activeResult >= 0) || 
                  ((whichKey != 39) && (this.inputFieldHasFocus)))) {
      // If not, right or down arrow activate the next result
        this.highlightNextSearchResult_();
        valueToReturn = false;
        this.arrowKeyProcessed = true;
      }

      break;

    case 38: // up arrow
    case 63235: // up arrow
    case 37: // left arrow
      if (whichKey == 37) {
        this.clearAutocomplete_(true);
      }

      // If we press up arrow in the input field, we hide the pop-down
      if ((!this.resultsWindowHidden) && (this.inputFieldHasFocus) && 
          (whichKey != 37)) {
        this.hideResultsWindow_();
        valueToReturn = false;
        this.arrowKeyProcessed = true;
      } else if ((!this.resultsWindowHidden) && (this.activeResult >= 0)) {
        // If not, left or up arrow activate the previous result
        this.highlightPrevSearchResult_();
        valueToReturn = false;
        this.arrowKeyProcessed = true;
      }
      break;

    case 9: // Tab
      if (this.inputFieldHasFocus && (this.autocomplete != '')) {
        this.collapseAutocomplete_();
        this.autocompleteJustCollapsed = true;
        valueToReturn = false;
      }
      break;
  }

  if (!this.resultsWindowHidden && valueToReturn) {
    if (((!this.inputFieldHasFocus) && ((whichKey < 37) || (whichKey > 40))) ||
        ((whichKey == 9) && (!this.autocompleteJustCollapsed))) {
      this.hideResultsWindow_();
    }
  }

  if (!valueToReturn) {
    e.returnValue = false;
    if (e.preventDefault) {
      e.preventDefault();
    }
  }
}

/**
 * Handle a key press event in the document body.
 * @param {event} e Browser event
 */
FFSugest.prototype.handleBodyKeyPress = function(e) {
  var valueToReturn = true;

  if (this.initialized) {
    e = e || window.event;
    var whichKey = (e.which) ? e.which : e.keyCode;

    // Arrow keys have the same key codes here as some other characters
    // (for example, down arrow is the same as left parenthesis)
    // We have to detect whether the arrow key was pressed during key down,
    // and then ignore it here if that's the case (otherwise it'd scroll
    // the screen)
    if ((this.arrowKeyProcessed) && (whichKey >= 37) && (whichKey <= 40)) {
      this.arrowKeyProcessed = false;
      valueToReturn = false;
    }

    if (!valueToReturn) {
      e.returnValue = false;
      if (e.preventDefault) {
        e.preventDefault();
      }
    }
  }
}

/**
 * Handle a key up event in the document body.
 * @param {event} e Browser event
 */
FFSugest.prototype.handleBodyKeyUp = function(e) {
  var valueToReturn = true;
  
  e = e || window.event;
  var whichKey = (e.which) ? e.which : e.keyCode;
  var targetElement = (e.target) ? e.target : e.srcElement;

  this.arrowKeyProcessed = false;

  switch (whichKey) {
    case 32: // space
      if (this.inputFieldHasFocus && (this.autocomplete != '')) {
        this.clearAutocomplete_(true);
        valueToReturn = false;
      }
      break;
  }

  if (!valueToReturn) {
    e.returnValue = false;
    if (e.preventDefault) {
      e.preventDefault();
    }
  }
}    

/**
 * Handle a resize event in the document body (to recalculate the search
 * results window and its shadow).
 * @param {event} e Browser event
 */
FFSugest.prototype.handleBodyResize = function(e) {
  this.updateDimensionsAndShadow_(this.searchResultsEl);
}    

/**
 * Handle input field losing focus. Remember this in a variable.
 * @param {event} e Browser event
 */
FFSugest.prototype.handleInputBlur = function(e) {
  this.inputFieldHasFocus = 0;
}

FFSugest.prototype.handleInputFocus = function(e) {
	this.inputFieldHasFocus = 0.5;
}


/**
 * Handle mouse down on the input field. Collapses autocomplete if 
 * present.
 * @param {event} e Browser event
 */
FFSugest.prototype.handleInputMouseDown = function(e) {
  if (this.autocomplete) {
    this.collapseAutocomplete_();
  }
}

FFSugest.prototype.handleAutocompleteMouseDown = function(e) {

}

/**
 * Handle input field receiving a click.
 * @param {event} e Browser event
 */
FFSugest.prototype.handleInputClick = function(e) {
  e = e || window.event;
  e.cancelBubble = true;

  // Clicking on the input field again when it's already active
  // shows the pop-down again
  if (this.inputFieldHasFocus == 1) {
    if (this.resultsWindowHidden) {
      this.search_(true);
    }
  } else {
    this.inputFieldHasFocus = 1;
  }
}

/**
 * Handle a click on a search result. Goes to a "more details" URL if the
 * given search result has any.
 * @param {event} e Browser event
 */
FFSugest.prototype.handleSearchResultClick = function(e) {
  e = e || window.event;
  var el = (e.target) ? e.target : e.srcElement;

  while ((el.tagName != 'DIV') ||
         (el.className.indexOf('searchResult') == -1)) {
    el = el.parentNode;
  }

  if (el.getAttribute("moreDetailsUrl")) {
    this.goToUrl_(el.getAttribute("moreDetailsUrl"));
  }
}

/**
 * Handle a click in document body.
 * @param {event} e Browser event
 */
FFSugest.prototype.handleBodyClick = function(e) {
  e = e || window.event;
  var targetElement = (e.target) ? e.target : e.srcElement;

	if ( this.browserIE && targetElement.className == 'suggestThumbImg' ) return;

  this.clearAutocomplete_();
  this.hideResultsWindow_();
  this.resultsWindowHiddenByClicking = true;
}

/**
 * Go to a specific URL. If the current page is inside an iframe, it breaks
 * out of that iframe.
 * @param {string} url URL to go to
 */
FFSugest.prototype.goToUrl_ = function (url) {
  try {
    if (top.location != location) {
      top.location.href = url;
    } else {
      location.href = url;
    }
  } catch(e) {
    location.href = url;
  }
} 

/**
 * Activate the debug mode, create the debug console.
 */
FFSugest.prototype.activateDebugConsole_ = function() {
  document.write("<div onclick='event.cancelBubble = true;' " +
    "id='searchAsYouTypeDebugConsole' class='expanded'>" +
    "<div style='float: right'>" +
    "<button onclick='searchAsYouType.clearDebugConsoleTimes()'>Clear " +
    "console</button>" +
    "<button onclick='searchAsYouType.clearCache()'>Clear cache</button>" +
    "<button onclick='searchAsYouType.toggleDebugConsole(event)'>Show/hide" +
    "</button>" +
    "</div><h1>Search-as-you-type debug console</h1>" +
    "<br />" +
    "<table id='searchAsYouTypeDebugTimes'>" +
    "</table>" +
    "</div>");

 this.debugConsoleTimesHeader = 
    '<tr><th>Query</th>' +
    '<th>Auto-completed</th>' +
    '<th>No. of results</th>' +
    '<th>Delay before<br />displaying:<br />(fixed)</th>' +
    '<th title="JS: Time from launching a query to displaying it">' +
    'Total turn-around<br />client+server</th>' +
    '<th title="Ajax: Total time spent on the server">' +
    'Server:<br />Total time</th>' +
    '</tr>';

  this.clearDebugConsoleTimes();
}

/**
 * Show or hide the debug console.     
 * @param {event} e Browser event
 */
FFSugest.prototype.toggleDebugConsole = function(e) {
  var debugConsoleEl = document.getElementById('searchAsYouTypeDebugConsole');

  if (debugConsoleEl.className.indexOf('expanded') != -1) {  
    debugConsoleEl.className = 
      debugConsoleEl.className.replace(/expanded/, 'contracted');
  } else {
    debugConsoleEl.className = 
      debugConsoleEl.className.replace(/contracted/, 'expanded');
  }

  e = e || window.event;
  e.cancelBubble = true;

  this.inputFieldEl.focus();
}

/**
 * Add a new line to a debug console times table.
 * @param {text} line A new line to be added
 */
FFSugest.prototype.addToDebugConsoleTimesNewLine_ = function(line) {
  this.debugConsoleTimesContents = 
    this.debugConsoleTimesCurrentLine + this.debugConsoleTimesContents;

  this.debugConsoleTimesCurrentLine = "<tr>" + line;

  document.getElementById("searchAsYouTypeDebugTimes").innerHTML = 
    this.debugConsoleTimesHeader + this.debugConsoleTimesCurrentLine + 
    this.debugConsoleTimesContents;
}

/**
 * Append to the most recent line to a debug console times table.
 * @param {text} line A text to be appended
 */
FFSugest.prototype.addToDebugConsoleTimesCurrentLine_ = function(line) {
  this.debugConsoleTimesCurrentLine += line;

  document.getElementById("searchAsYouTypeDebugTimes").innerHTML = 
    this.debugConsoleTimesHeader + this.debugConsoleTimesCurrentLine + 
    this.debugConsoleTimesContents;
}

/**
 * Clear the debug console.
 */
FFSugest.prototype.clearDebugConsoleTimes = function() {
  this.debugConsoleTimesContents = '';
  this.debugConsoleTimesCurrentLine = '';
  document.getElementById("searchAsYouTypeDebugTimes").innerHTML = 
    this.debugConsoleTimesHeader;

  this.inputFieldEl.focus();
}

/**
 * Clear the search cache. Used only for debugging.
 */
FFSugest.prototype.clearCache = function() {
  this.searchCache = [];

  this.inputFieldEl.focus();
}

FFSugest.prototype.formSubmit = function(){
	this.formEl.submit();
	return false;
}


function searchAsYouTypeBind(fn, self, var_args) {
  var boundargs = fn.boundArgs_ || [];
  boundargs = boundargs.concat(Array.prototype.slice.call(arguments, 2));

  if (typeof fn.boundSelf_ != "undefined") {
    self = fn.boundSelf_;
  }

  if (typeof fn.foundFn_ != "undefined") {
    fn = fn.boundFn_;
  }

  var newfn = function() {
    // Combine the static args and the new args into one big array
    var args = boundargs.concat(Array.prototype.slice.call(arguments));
    return fn.apply(self, args);
  }

  newfn.boundArgs_ = boundargs;
  newfn.boundSelf_ = self;
  newfn.boundFn_ = fn;

  return newfn;
}

/** 
 * A helper function cloning an object. It should support well arrays and
 * objects inside the object being cloned.
 * @param {object} obj An object to be cloned
 * @return {object} A cloned object
 */
function searchAsYouTypeCloneObject(obj) {
  if (obj instanceof Array) {
    var newObj = [];
  } else {
    var newObj = {};
  }

  for (var i in obj) {
    if (obj[i].constructor.toString().indexOf("Array") != -1) {
      newObj[i] = searchAsYouTypeCloneObject(obj[i]);
    } else if (typeof obj[i] == 'object') {
      newObj[i] = searchAsYouTypeCloneObject(obj[i]);
    } else {
      newObj[i] = obj[i];
    }
  }

  return newObj;
}

var ffSuggest = new FFSugest();

jQuery(function($){
	if ( !ffSuggest.initialized ) {
		if ( $('#searchKeyword').length && $('#siteSearch').length ) {
			ffSuggest.initialize('siteSearch', 'searchKeyword', false);
		}
	}
});

