// Copyright © 2007 NextSmallThings, Inc. All Rights Reserved.


if (!window.console) {console = {log:function(){}};}

function VirtualFrameEngine(src,callback,waitTime) {
  this._frame = Util.appendNewElement(document.body,'iframe',null, {width:0, height:0, frameBorder:0});
  this._scriptSrc = src;
  this._callback = callback;
  setTimeout(this.executeQuery.bind(this),waitTime);
}

VirtualFrameEngine.prototype.pollIfLoaded = function() {
  if (this.resultWindow().results) {
    this.processResults();
  }
  else {
    setTimeout(this.pollIfLoaded.bind(this),10);
  }
};
  
VirtualFrameEngine.prototype.executeQuery = function() {
  var wind = this._frame.contentWindow;
  if (!wind) {console.log('no wind!');return setTimeout(this.executeQuery.bind(this),5);} //Safari may take a while to load
  this._w = wind;
  var doc = wind.document;
  if (!doc) {console.log ('no doc!');}
  var headElt = doc.getElementsByTagName('head')[0] || doc.body; //Safari has no head for this iframe
  this.yScript1 = Util.appendNewElement(headElt,'script',null,null,'window.'+this._callback+' = function (r) {window.results=r;}');
  this.yScript2 = Util.appendNewElement(headElt,'script',null,{src:this._scriptSrc});
  if (Util.isSafari()) {
    this.pollIfLoaded();
  }
  else {
    this.yScript2.onload = this.processResults.bind(this);
    var thisHdl = this;
    this.yScript2.onreadystatechange = function () {if (this.readyState=='loaded') {thisHdl.processResults();}};
  }
};

VirtualFrameEngine.prototype.resultWindow = function() {
  return this._frame.contentWindow; //document.parentWindow ? this._frame.document.parentWindow : top.frames[this._name];
};

VirtualFrameEngine.prototype.getResults = function() {
  return this.resultWindow().results;
};

function SpellChecker(query) {
  var speller = new VirtualFrameEngine('http://search.yahooapis.com/WebSearchService/V1/spellingSuggestion?appid=YahooDemo&output=json&query='+encodeURIComponent(query)+'&callback=spell','spell',50);
  speller.processResults = function() {
    var results = this.getResults();
    try {
      var spellDiv = $('spelling');
      if (results.ResultSet.Result && spellDiv) {
        Util.appendNewTextNode(spellDiv, 'Did you mean ');
        Util.appendNewElement(spellDiv,'a',null,{href:'/search/meta?query='+encodeURIComponent(results.ResultSet.Result)},results.ResultSet.Result);
        Util.appendNewTextNode(spellDiv,'?');
      }
    }
    catch (e) {
      //do nothing
    }
  };
  return speller;
}

function YahooEngine(meta, query, weight) {
  var yEngine = new VirtualFrameEngine('http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=clipclip&results=10&output=json&query='+encodeURIComponent(query)+'&callback=urlz','urlz',5);
  yEngine._metaEngine = meta;
  yEngine._query = query;
  yEngine._weight = weight;
  yEngine._name = 'yhoo';
  yEngine.processResults = function() {
    var results = this.getResults();
    if (results.Error) {
      console.log('Yahoo Err' + results.Error);
    }
    else {
      var yResults = this._yResults = results.ResultSet.Result;
      for (var i = 0; i < yResults.length; i++) {
        var yUrl = this.decodeHTMLEntities(yResults[i].Url);
        var yTitle = this.decodeHTMLEntities(yResults[i].Title);
        var ySummary = this.decodeHTMLEntities(yResults[i].Summary);
        this._metaEngine.addResult({url:yUrl, rank:i, title:yTitle, summary:ySummary,
          score:(1.0/(i+1)*this._weight), engine:this._name});
      }
    }
    this._metaEngine.completed(this._name);
  };
  yEngine.decodeHTMLEntities = function(str) {
    if (!str) {return '';}
    if (!this._dummyElt) {
      this._dummyElt = document.createElement('span');
    }
    this._dummyElt.innerHTML = str;
    return this._dummyElt.childNodes[0].data;
  };
  yEngine.name = function() {
    return this._name;
  };
  return yEngine;
}


function AskEngine(meta, query, weight, results) {
  this._mStart = Util.now();
  this._metaEngine = meta;
  this._query = query;
  this._weight = weight;
  this._name = 'ask';
  if (results) {
    this.addResults(results);
  }
  else {
    this._ajax = new Ajax.Request('/search/ask_json?query='+encodeURIComponent(query),
      {method:'get',onSuccess:this.processResults.bind(this),
      onFailure:errFunc,onException: function(t,e) {console.log('err: /search/ask_json'+e);}});
  }
}
var debugask1;
var debugask2;
AskEngine.prototype.processResults = function(transport) {
  var results = eval(eval(transport.responseText));
  debugask1=transport.responseText;
  debugask2=results;
  this.addRelated(results.r1);
  this.addResults(results.te);
};

AskEngine.prototype.addResults = function(results) {
  for (var i = 0; i < results.length; i++) {
    if (results[i]) {this._metaEngine.addResult(results[i]);};
  }
  this._metaEngine.completed(this._name);
};  

AskEngine.prototype.addRelated = function(results) {
  var relatedDiv = $('related');
  if (!relatedDiv) {return;}
  if (results.length > 0) {
    Util.appendNewElement(relatedDiv,'br',null,{});
    Util.appendNewTextNode(relatedDiv,'Ask.com Related searches: ');
  }
  for (var i = 0; i < results.length; i++) {
    suggestion = results[i].title;
    Util.appendNewElement(relatedDiv, 'a', null, {href:'/q/'+encodeURIComponent(suggestion)}, suggestion);
    if (i != results.length-1) {Util.appendNewTextNode(relatedDiv,', ');}
  }
  this._metaEngine.completed(this._name);
};  

function CuilEngine(meta, query, weight, results) {
  this._mStart = Util.now();
  this._metaEngine = meta;
  this._query = query;
  this._weight = weight;
  this._name = 'cuil';
  if (results) {
    this.addResults(results);
  }
  else {
    this._ajax = new Ajax.Request('/search/cuil_json?query='+encodeURIComponent(query),
      {method:'get',onSuccess:this.processResults.bind(this),
      onFailure:errFunc,onException: function(t,e) {console.log('err: /search/cuil_json'+e);}});
  }
}

CuilEngine.prototype.processResults = function(transport) {
  var results = eval(transport.responseText);
  this.addResults(results);
};

CuilEngine.prototype.addResults = function(results) {
  for (var i = 0; i < results.length; i++) {
    if (results[i]) {this._metaEngine.addResult(results[i]);};
  }
  this._metaEngine.completed(this._name);
};  

function appendScript(url) {
  var head = document.getElementsByTagName("head")[0];
  var script = document.createElement("script");
  script.charset = "utf-8";
  script.src = url;
  script.type = 'text/javascript';
  head.appendChild(script);
}


MSNEngine.prototype.processResults = function(transport) {
  var results = eval(transport.responseText);
  this.addResults(results);
};

MSNEngine.prototype.addResults = function(results) {
  for (var i = 0; i < results.length; i++) {
    if (results[i]) {this._metaEngine.addResult(results[i]);};
  }
  this._metaEngine.completed(this._name);
};  

MSNEngine.updateFuncIndex = 0;
MSNEngine.getStaticUpdateFunc = function(updateFunc) {
  var updateFuncStr = 'updateFunc' + (++MSNEngine.updateFuncIndex);
  MSNEngine[updateFuncStr] = updateFunc;
  return 'MSNEngine.' + updateFuncStr;
};

function MSNEngine(meta, query, weight, results) {
  this._mStart = Util.now();
  this._metaEngine = meta;
  this._query = query;
  this._weight = weight;
  this._name = 'msn';
  var updateFuncStr = MSNEngine.getStaticUpdateFunc(this.processResults.bind(this));
  var queryUrl = "http://api.search.live.net/json.aspx?AppId=F49E8B6ADE036032B7D27B226B4EFE2B34E4B409&Market=en-US&" +
    "Version=2.0&Sources=Web&JsonType=callback&JsonCallback=" + updateFuncStr + "&Query=" + encodeURIComponent(query);
  appendScript.defer(queryUrl);
}

MSNEngine.prototype.processResults = function(reply) {
  var results = reply.SearchResponse.Web.Results
  if (results) {
    for (var i=0; i<results.length; ++i) {
      this._metaEngine.addResult({url:results[i].Url, rank:i, title:results[i].Title, summary:results[i].Description,
        score:(1.0/(i+1)*this._weight), engine: this._name});
    }
  }
  this._metaEngine.completed(this._name);
};

MSNEngine.prototype.addResults = function(results) {
  for (var i = 0; i < results.length; i++) {
    if (results[i]) {this._metaEngine.addResult(results[i]);};
  }
  this._metaEngine.completed(this._name);
};  

function GoogleEngine(meta, query, weight) {
  this._metaEngine = meta;
  this._query = query;
  this._name = 'google';
  this._weight = weight;
  var g = this._gwebSearch = new GwebSearch();
  g.setResultSetSize(GSearch.LARGE_RESULTSET);
  g.setRestriction(GSearch.RESTRICT_SAFESEARCH, GSearch.SAFESEARCH_MODERATE);
  g.setSearchCompleteCallback(this,this.processResults);
  gStart = (new Date()).getTime();
  g.execute(query);
}

GoogleEngine.prototype.processResults = function() {
   var g = this._gwebSearch;
  var gResults = g.results;
  for (var i = 0; i < gResults.length; i++) {
    this._metaEngine.addResult({url:gResults[i].unescapedUrl, rank:i, title:gResults[i].title, summary:gResults[i].content,
      score:(1.0/(i+1)*this._weight), engine:this._name});
  }
  this._metaEngine.completed(this._name);
};

CuilEngine.prototype.name = AskEngine.prototype.name = GoogleEngine.prototype.name = MSNEngine.prototype.name = function() {
  return this._name;
};

function MetaEngine(query, engines, g, y, m, qid, msnResults, doSuggestions,a) {
  this._query = query;
  this._results = $H({});
  this._completed = [];
  this._engines = engines;
  this._number_of_engines = engines.split(/ /).length;
  this._y = new YahooEngine(this, query, y);
  this._g = new GoogleEngine(this, query, g);
  this._m = new MSNEngine(this, query, m, msnResults);
  if (this._engines.match(/ask/)) {
      this._a = new AskEngine(this, query, a);
  };
  if (this._engines.match(/cuil/)) {
      this._c = new CuilEngine(this, query, a);
  };
  this._q = qid;
  this._doSuggestions = doSuggestions;
  if (doSuggestions) {this.fetchSuggestions();}
  this._speller = new SpellChecker(query);
  this._start = Util.now();
  this._related_terms = [];
}

var errFunc = function(t) {
    console.log('Error ' + t.status + ' -- ' + t.statusText);
};

MetaEngine.normalizeURL = function(url) {
  var match = url.match(/^https?:\/\/(www\.)?(.*?)(\/?|\/(index|default)(\.\w*))$/i);
  return (match && match[2] ? match[2] : url).toLowerCase();
};

MetaEngine.boldKeywords = function(keywords, str) {
  if (!str) {return '';}
  var keywordArray = keywords.split(/\s/);
  keywordArray = keywordArray.collect(function(keyword) {
    var strippedKeyword = keyword.gsub(/^[\+'"~]|['"]$/,"");
    return strippedKeyword.length < 3 ? null : (new RegExp(strippedKeyword)).toString().slice(1,-1);
  }).compact();
  if (keywordArray.length == 0) {return str;}
  var keywordRegex = new RegExp(keywordArray.join('|'),"i");
  return str.gsub(keywordRegex, function(match) { return '<b>'+match[0]+'</b>';});
};

MetaEngine.prototype.fetchSuggestions = function() {
  var head = document.getElementsByTagName("head")[0];
  var script = document.createElement("script");
  script.charset = "utf-8";
  script.src = 
'http://search.yahooapis.com/WebSearchService/V1/relatedSuggestion?appid=YahooDemo&output=json&callback=m.processSuggestions&query=' + encodeURIComponent(this._query);
  head.appendChild(script);
};

MetaEngine.prototype.normalizeSearchTerm = function(term) {
  return term.gsub(/\s+|\+/,' ').strip().toLowerCase();
};

MetaEngine.prototype.referrerSearchTerm = function() {
  try {
    var match = document.referrer.match(/\.google\.[^?]*\?(.*)/);
    if (match[1]) {
      var params = match[1].split('&');
      for (var i=0; i < params.length; ++i) {
        var param = params[i].split('=');
        if (param[0] == 'q') {return this.normalizeSearchTerm(decodeURIComponent(param[1]));}
      };
    }
  }
  catch (e) { //do nothing
  }
  return false;
};

MetaEngine.prototype.oldRelatedTerm = function(term) {
  try {
    var relatedDiv = $('related');
    var aNodes = relatedDiv.getElementsByTagName('a');
    for (var i=0; i < aNodes.length; ++i) {
      if (this.normalizeSearchTerm(aNodes[i].innerHTML) == term) {return true;}
    }
  }
  catch (e) { //do nothing
  }
  return this.normalizeSearchTerm(this._query) == term;
};

MetaEngine.prototype.processSuggestions = function(returnObj) {
  try {
    suggestions = returnObj.ResultSet.Result || [];
    if (suggestions && suggestions.length > 0) {
      var relatedDiv = $('related');
      if (!relatedDiv) {return setTimeout(this.processSuggestions.bind(this, returnObj),50);}
      var self = this;
      suggestions = suggestions.collect(function(term) {return self.normalizeSearchTerm(term);});
      Util.appendNewTextNode(relatedDiv,'Related searches: ');
      suggestions.each(function(suggestion, index) {
        Util.appendNewElement(relatedDiv, 'a', null, {href:'/q/'+encodeURIComponent(suggestion)}, suggestion);
        if (index != suggestions.length-1) {Util.appendNewTextNode(relatedDiv,', ');}
      });
      this._related_terms = suggestions;
    }
  }
  catch (e) {
    //do nothing
  }
};

MetaEngine.prototype.getBoldResult = function(result,attr) {
  return (result.engine == this._g.name()) ? result[attr] :MetaEngine.boldKeywords(this._query, result[attr]);
};

MetaEngine.prototype.getResultHash = function(result,ranks) {
  var titleStr = Util.unbold(result.title);
  
  var paramsHash = $H(ranks).merge({url:result.url, title:titleStr});
  if (this._q) {paramsHash.set('q', this._q);}
  else {paramsHash.set('query', this._query);}
  return paramsHash; 
};

MetaEngine.gotoResult = function() {
  var paramsHash = this.clickHash;
  var paramsStr = paramsHash.collect(function(elt) {return elt[0] + '=' + encodeURIComponent(elt[1]);}).join('&');
  this.href = '/search/result?' + paramsStr;
  return true;
};

MetaEngine.prototype.addResult = function(result) {
  var normalURL = MetaEngine.normalizeURL(result.url);
  if (!normalURL.match(/url\.com\/(search|q)/)) {
    var resultsArray = this._results.get(normalURL) || [];
    resultsArray.push(result);
    this._results.set(normalURL, resultsArray);
  }
};

MetaEngine.prototype.loadUserVotes = function() {
  var params = this._results.keys().collect(function(elt) {return encodeURIComponent(elt);}).join('&');
  params += '&query=' + encodeURIComponent(this._query);
  //var refTerm = this.referrerSearchTerm();
  //if (refTerm && !this.oldRelatedTerm(refTerm)) {this._related_terms[this._related_terms.length] = refTerm;}
  if (this._related_terms.length > 0) {params += '&related='+ encodeURIComponent(this._related_terms);}
  this._userVotesAjax = new Ajax.Request('/search/user_votes_json',{method:'get', parameters:params,onSuccess:this.processUserVotes.bind(this),
    onFailure:errFunc,onException: function(t,e) {throw e;}});
};

MetaEngine.prototype.gotoTopResult = function() {
  location.href = this._sortedResults[0][1][0].url;
};

MetaEngine.prototype.completed = function(name) {
  this._completed.push(name);
  this._sortedResults = null;
  if (this._completed.length >= this._number_of_engines) {
    console.log('got all results. last was: '+name);
    var resultSize = this._results.keys().length;
    if (resultSize == 0) {
      this.renderNullResults();
    }
    else {
      try {
        this.loadUserVotes();
      }
      catch (e) {
        console.log('loadUserVotes err: ' + e);
        //firefox has a bug that it can't do an ajax request from an iframe
        setTimeout(this.loadUserVotes.bind(this),10);
      }
      this.sortResults();
      if (this.isBullseye()) {
        if (urlGlobals.bullseye) {
          return this.gotoTopResult();
        }
        else {
          this.renderBullseye();
        }
      }
      this.setSearchUrls();
    }
  }
  this.sortResults();
  this.renderResults();
};

MetaEngine.prototype.setSearchUrls = function() {
  try {
    for (var i=0;i < $('serp').tHead.rows[0].cells.length-1; ++i) {
      var link = $('h'+i);
      link.href += encodeURIComponent(this._query);
    }
  }
  catch (e) {
    // do nothing
  }
};
  
MetaEngine.prototype.getURLScore = function(normalURL) {
  return this._votesHash ? this._votesHash[normalURL] : null;
};

MetaEngine.prototype.sortBy = function(result) {
  var score = result[1].inject(0, function(sum, search) {return sum - search.score;});
  urlScore = this.getURLScore(result[0]);
  if (urlScore) {score += (urlScore.no - urlScore.yes);}
  result[1].score = score;
  return score;
};

MetaEngine.prototype.isBullseye = function() {
  if (this._sortedResults) {
    var topResult = this._sortedResults[0][1];
    var topCount = 0;
    topResult.each(function(engineResult) {if (engineResult.rank==0) {++topCount;}});
    return topCount == this._number_of_engines;
  }
  return false;
};
    
MetaEngine.prototype.sortResults = function() {
  if (!this._sortedResults) {
    resultsHash = this._results;
    this._sortedResults = resultsHash.sortBy(this.sortBy.bind(this));
  }
};

MetaEngine.prototype.h2Class = function(normalURL) {
  if (this._votesHash) {
    var urlScore = this._votesHash[normalURL];
    return urlScore ? ((urlScore.yes >= urlScore.no) ? 'has_yes_votes' : 'has_no_votes') : '';
  }
  else {
    return '';
  }
};

MetaEngine.prototype.userLink = function(user, parentElt) {
  if (!user) {console.log('userLink: user is null');}
  return Util.appendNewElement(parentElt,'a',null,{href:'/user/'+user},user);
};

MetaEngine.prototype.avatarLink = function(user, parentElt) {
  if (this._userHash) {
    if (!user) {console.log('avatarLink: user is null');}
    var path = this._userHash[user];
    if (!path) {path = '/images/default_avatar.gif';}
    var userLinkElt = Util.appendNewElement(parentElt,'a',null,{href:'/user/'+user});
    Util.appendNewElement(userLinkElt,'img',null,{src:path,width:16,height:16,title:user,border:0});
  }
};

MetaEngine.prototype.renderBullseye = function() {
  var bullseyeDiv = $('bullseye');
  if (bullseyeDiv) {
    Util.appendNewElement(bullseyeDiv,'img',null,{src:'/images/bullseye_s.gif', alt:"bull's eye!"});
    Util.appendNewElement(bullseyeDiv,'h2',null,null,'All '+this._number_of_engines+' search engines agreed on the best result!');
    if (urlGlobals.user) {
      var pElt = Util.appendNewElement(bullseyeDiv,'p',null,null,'When this happens the ');
      Util.appendNewElement(pElt,'a',null,
        {href:'#',onclick:onclick="new Ajax.Request('/account/smart_not_lucky', {asynchronous:true, evalScripts:true}); return false;"},
        'next time, just take me to the top result directly');
    }
    else {
      var pElt = Util.appendNewElement(bullseyeDiv,'p');
      Util.appendNewElement(pElt,'a',null,{href:'/account/signup'},'Sign up');
      Util.appendNewTextNode(pElt, ' to go directly to the top result, when this happens the next time.');
    }
		Element.show(bullseyeDiv);
  }
  else {
    setTimeout(this.renderBullseye.bind(this),200);
  }
};

MetaEngine.prototype.renderUserVotes = function(urlScore, parentElt) {
  var avatarDiv = Util.appendNewElement(parentElt,'div','avatar');
  for (var i=0; i < urlScore.voters.length; ++i) {
    var user = urlScore.voters[i];
    if (!user) {console.log('ruv: user is null');}
    this.avatarLink(user,avatarDiv);
  }
  
  var moreDiv = Util.appendNewElement(parentElt,'div','more_yes');
  var txt = '';
  if (urlScore.voters.length == 1) {
    this.userLink(urlScore.voters[0],moreDiv);
    txt = urlScore.yes > 0 ? ' liked' : ' disliked';
  }
  else {
    txt = urlScore.yes + ' out of ' + (urlScore.yes+urlScore.no) + ' liked';
  }
  txt += ' this link';
  Util.appendNewTextNode(moreDiv,txt);
  
  if (urlScore.comments.length > 0) {this.renderComments(urlScore, parentElt);}
};

MetaEngine.prototype.renderComments = function(urlScore, parentElt) {
  var commentsLinkDiv = Util.appendNewElement(parentElt,'div','comments');
  var commentsLink = Util.appendNewElement(commentsLinkDiv,'a',null,
    {href:'javascript:void(0)'},Util.pluralize(urlScore.comments.length,'comment'));
  var moreCommentsDiv = Util.appendNewElement(parentElt,'div','clearfix add_comment',null,null,{display:'none'});
  var statsDiv = Util.appendNewElement(moreCommentsDiv,'div','statistics');
  var closeLink = Util.appendNewElement(statsDiv,'a',null,{href:'javascript:void(0)'}, 'Close');
  commentsLink.onclick = closeLink.onclick = function() {Element.toggle(moreCommentsDiv, commentsLink);};
  var ul = Util.appendNewElement(moreCommentsDiv,'ul','feedback');
  for (var i=0; i < urlScore.comments.length; ++i) {
    var commentsHash = urlScore.comments[i];
    var li = Util.appendNewElement(ul,'li');
    var byDiv = Util.appendNewElement(li,'div','by');
    this.avatarLink(commentsHash.user, byDiv);
    Util.appendNewTextNode(byDiv, ' ');
    this.userLink(commentsHash.user, byDiv);
    Util.appendNewTextNode(byDiv, ' while searching for ');
    Util.appendNewElement(byDiv,'a',null,{href:'/search/meta?query='+encodeURIComponent(commentsHash.keywords)},commentsHash.keywords);
    Util.appendNewTextNode(byDiv, ' ' + Util.relativeTime(new Date(commentsHash.created_at*1000)));
    var actualCommentDiv = Util.appendNewElement(li,'div','comment');
    var pClass = 't_' + (commentsHash.vote == 'yes' ? 'up' : 'down');
    Util.appendNewElement(actualCommentDiv,'p',pClass,null,commentsHash.comment);
  }
  li = Util.appendNewElement(ul,'li','form');
  if (this._login) {
    var formDiv = Util.appendNewElement(li,'div',null,{id:'new_comment_'+urlScore.id});
    var formElt = Util.appendNewElement(formDiv,'form',null,{id:'f',method:'post'});
    formElt.onsubmit = function() {new Ajax.Request('/search/vote', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;};
    Util.appendNewElement(formElt,'input',null,{id:'user_vote_id', name:'user_vote_id', value:urlScore.last_vote, type:'hidden'});
    var voteField = Util.appendNewElement(formElt,'input',null,{id:'vote', name:'vote', type:'hidden',value:''});
    Util.appendNewElement(formElt,'label',null,null,'Add comments:');
    Util.appendNewElement(formElt,'input','text',{id:'comments',name:'comments',size:50,value:'',type:'text'});
    var yesBtn = Util.appendNewElement(formElt,'input',null,{src:'/images/btn_yes.gif',type:'image'});
    var noBtn = Util.appendNewElement(formElt,'input',null,{src:'/images/btn_spam.gif',type:'image'});
    yesBtn.onclick = function() {if (this.alreadyClicked) {return false;}voteField.value = 'yes';this.alreadyClicked = true;reportTrackingEvent('search/vote/yes');return true;};
    noBtn.onclick = function() {if (this.alreadyClicked) {return false;}voteField.value = 'spam';this.alreadyClicked = true;reportTrackingEvent('search/vote/spam');return true;};
  }
  else {
    Util.appendNewElement(li,'a',null,{href:'/account/signup'},'Sign in to comment');
  }
};

MetaEngine.prototype.renderNullResults = function() {
  var searchDiv = $('search_page');
  Element.hide('result');
  Element.hide('sidecol');
  Element.hide('explanation');
  var mesgDiv = Util.appendNewElement(searchDiv,'div',null,{id:'message'});
  Util.appendNewElement(mesgDiv,'h1',null,null,"We couldn't find any results for: "+this._query);
  Util.appendNewElement(mesgDiv,'h2',null,null,'Suggestions:');
  var ul = Util.appendNewElement(mesgDiv,'ul',null,{id:'no_result'});
  Util.appendNewElement(ul,'li',null,null,'Check your spelling');
  Util.appendNewElement(ul,'li',null,null,'Try more general words');
  Util.appendNewElement(ul,'li',null,null,'Try different words that mean the same thing');
  Util.appendNewElement(ul,'li',null,null,'Ask the community for help!');
};


var IE = document.all?true:false;

function getX(e) { if (IE) { x = event.clientX + document.body.scrollLeft-1; } else { x = e.pageX; }; return x;}
function getY(e) { if (IE) { y = event.clientY + document.body.scrollTop -1; } else { y = e.pageY; }; return y;}

MetaEngine.prototype.voteFromResultList = function(event,url,updown) {

    var popupbox = $('vote_popup');

    popupbox.style.left  = (getX(event) - 320) +"px";
    popupbox.style.top   = (getY(event) -  20)+"px";

    this._ajax = new Ajax.Request('/search/vote_from_list?comments=&vote='+updown+'&url=http://'+escape(url)+'&q='+this._q ,
      {method:'get',
      onFailure:errFunc,onException: function(t,e) {console.log('err: voteFromResultList '+e);}});

    /* 
      /search/vote?title=Title&comments=&vote=spam&thoo=1&url=http://www.dishnetwork.com&action=vote&q=34&controller=search&google=1&msn=1&yhoo=1
   */
}


MetaEngine.prototype.thanksForVoting = function(vote,url,keywords) {
    this.loadUserVotes();
    Element.show('vote_popup');
};
MetaEngine.prototype.pleaseLoginPopup = function(vote,url,keywords) {
    var response='Please Login (at the top of the page) to Vote.'
    Element.show('vote_popup');
    Element.show('login');
    Element.hide('welcome');
    $('username').focus();

    $('vote_popup_body').innerHTML=response;
}

function maxOf(arry, iterator) {
  var result, resultMax = Number.NEGATIVE_INFINITY;
  arry.each(function(value, index) {
    var aMax = (iterator || Prototype.K)(value, index);
    if (result == undefined || aMax > currentMax) {
      result = value;
      currentMax = aMax;
    }
  });
  return result;
}

MetaEngine.prototype.renderResults = function() {
  function addRankCell(engineName) {
    position = ranks[engineName] || '–';
    Util.appendNewElement(row,'td','rankings',null,position);
  }
  
  try {
    var table = $('serp');
    Element.hide(table);
    var tableBody = table.tBodies[0];
    while (tableBody.rows.length > 0) { 
    tableBody.deleteRow(0); 
    } 
    for (var i=0;i < this._sortedResults.length; ++i) { 

      var row = Util.appendNewElement(tableBody, 'tr'); 
      var result = this._sortedResults[i]; 
      var resultHash = maxOf(result[1], function(eResult) {return eResult.score;}); 
      var ranks = {};
      result[1].each(function(engineResult) {ranks[engineResult.engine] = engineResult.rank+1;}); 
      addRankCell(this._g.name()); 
      addRankCell(this._y.name()); 
      addRankCell(this._m.name()); 
      if (this._engines.match(/ask/)) addRankCell(this._a.name()); 
      if (this._engines.match(/cuil/)) addRankCell(this._c.name()); 
      var mainCell = Util.appendNewElement(row,'td','text'); 
      var h2 = Util.appendNewElement(mainCell,'h2', this.h2Class(result[0]));
      var resultLink = Util.appendNewElement(h2,'a',null,{href:resultHash.url, id:'a_'+i});
      resultLink.clickHash = this.getResultHash(resultHash, ranks);
      resultLink.onclick = MetaEngine.gotoResult;
      resultLink.innerHTML = this.getBoldResult(resultHash,'title').truncate(60); 
      Util.appendNewElement(mainCell,'div','desc').innerHTML = this.getBoldResult(resultHash,'summary'); 
      Util.appendNewElement(mainCell,'div','url').innerHTML = MetaEngine.boldKeywords(this._query, result[0].truncate(50));

      var formTD    = Util.appendNewElement(row,'td','votes')
      var voteForm  = Util.appendNewElement(formTD,'form',null,{id:'f',method:'post'});

      var urlScore = this.getURLScore(result[0]);

      var user_voted_up = false;
      var user_voted_down = false;
      if (urlScore && urlScore.comments) {
          for (var c=0; c < urlScore.comments.length; ++c) {
              var commentsHash = urlScore.comments[c];
              if (this._login == commentsHash.user) {
                  if (commentsHash.vote == 'yes') user_voted_up = true;
                  else  user_voted_down = true;
              }
          }
      }
      var img_up  = user_voted_up   ? 't_up_blue.gif'   : 't_up.gif';
      var img_down = user_voted_down ? 't_down_blue.gif' : 't_down.gif';

      voteForm.innerHTML="<a href='#' onclick=\"m.voteFromResultList(event,'"+result[0]+"','yes'); return false;\"><img src='/images/"+img_up+"' title='vote up'></a><br>"+
                         "<a href='#' onclick=\"m.voteFromResultList(event,'"+result[0]+"','spam'); return false;\"><img src='/images/"+img_down+"' title='vote down'></a>";

      if (urlScore) {
        this.renderUserVotes(urlScore,mainCell);
      }
    } 
  
    Element.show(table);
  }
  catch (e) {
    setTimeout(this.renderResults.bind(this),50);
  }
};
  
MetaEngine.prototype.processUserVotes = function(transport) {
  this._r = transport.responseText;
  this._userVotes = eval(transport.responseText);
  this._votesHash = this._userVotes[0];
  delete this._votesHash['']; // json has empty string key as last hash hack
  this._userHash = this._userVotes[1];
  this._login = this._userVotes[2];
  this._sortedResults = null;
  this.sortResults();
  this.renderResults();
};

function ToolBarInit(tool) { 
  toolbar=tool; 
} 

function ask_ad(ask_ad_data) {
    var ad = "<div class='ask_ad'>";
    ad += "<p class='header'><a href='"+ask_ad_data.rediruri+"'>"+ask_ad_data.title+"</a></p>";
    ad += "<p class='body'><a href='"+ask_ad_data.rediruri+"'>"+ask_ad_data.summary+"</a></p>";
    ad += "<p class='link'><a href='"+ask_ad_data.rediruri+"'>"+ask_ad_data.baseuri+"</a></p>";
    ad += "</div>";
    return ad;
}
function process_ask_ads(query) {
    // alert("processing ask ads for "+query);
    this._ajax = new Ajax.Request('/search/ask_ads_json?query='+encodeURIComponent(query),
      {method:'get',
        onSuccess: function(transport){
            var result = transport.responseText;
            result_array = eval( result );
            result_top_string = "<div>";
            result_col_string = "<div>";
            if (result_array.length > 0) {
                result_top_string += '<fieldset class="ask_ads"><legend>Sponsored results:</legend>';
                result_top_string += '<img src="'+result_array[0].impressionuri+'">';
            }
            if (result_array.length > 3) {
                result_col_string += '<fieldset class="ask_ads"><legend>Sponsored results:</legend>';
            }
            for (var j=0;j<3 && j<result_array.length;++j) {
                var row = result_array[j];
                result_top_string += "<div style='float:left'>"
                result_top_string += ask_ad(row);
                result_top_string += "</div>";
            }
            for (var j=4;j<result_array.length;++j) {
                var row = result_array[j];
                result_col_string += "<span>";
                result_col_string += ask_ad(row);
                result_col_string += "</span><br>";
            }
            if (result_array.length > 0) {
                result_top_string += '</fieldset>';
            }
            if (result_array.length > 3) {
                result_col_string += '</fieldset>';
            }

            result_top_string += "</div>";
            result_col_string += "</div>";
            $('askads_topcol').innerHTML=result_top_string;
            $('askads_rightcol').innerHTML=result_col_string;
        },
      onFailure:errFunc,
      onException: function(t,e) {console.log('err: /search/ask_ads_json'+e);}});

}
 
