String.prototype.lTrim = function() { return this.replace(/^\s*/, ""); };
String.prototype.rTrim = function() { return this.replace(/\s*$/, ""); };
String.prototype.trim = function() { return this.lTrim().rTrim(); };

RegExp.escape = function(text) {
  if (!arguments.callee.sRE) {
    var specials = [
      '/', '.', '*', '+', '?', '|',
      '(', ')', '[', ']', '{', '}', '\\'
    ];
    arguments.callee.sRE = new RegExp(
      '(\\' + specials.join('|\\') + ')', 'g'
    );
  }
  return text.replace(arguments.callee.sRE, '\\$1');
};

function getViewport() {
  var x, y;
  if (self.innerHeight) {
    y = self.innerHeight;
    x = self.innerWidth;
  }
  else if (document.documentElement && document.documentElement.clientHeight) {
    y = document.documentElement.clientHeight;
    x = document.documentElement.clientWidth;
  }
  else if (document.body) {
    y = document.body.clientHeight;
    x = document.body.clientWidth;
  }
  
  return [x,y];
}

if(typeof Lijit=="undefined"){
  var Lijit={staticBase: '', releaseTag : ''};
}

Lijit.init = function(e) {
  Lijit.LoginBox.init();
};

Lijit.register = function() {
  Event.observe(window, 'load', Lijit.init);
};

Lijit.res = function(url, rev, base) {
  if (base == null) {
    base = Lijit.staticBase;
  }
  if (base == null) {
    base = "";
  }
  if (rev == null) {
    rev = Lijit.releaseTag;
  }
  if (base.indexOf('http:') == 0 && document.location.protocol == 'https:') {
    // Adjust link to be SSL.
    base = 'https' + base.substring(4);
  }

  if (rev != null) {
    if (url.indexOf('/res/') == 0) {
      url = '/___' + rev + url;
    }
    else if (url.indexOf('/files/informers/') == 0) {
      url = '/files/informers/___' + rev + url.substring(17);
    }
  }

  return base + url;
};

Lijit.register();

/**
 * Get an the "real position" of an element.
 * Based on http://www.quirksmode.org/js/findpos.html
 * @arguments (dom)element
 */
Lijit.getElementPosition = function(el) {
	var curleft = curtop = 0;
	if (el.offsetParent) {	
		do {			
			curleft += el.offsetLeft;			
			curtop += el.offsetTop;	
		} while (el = el.offsetParent);
	}
	return [curleft,curtop];
}

Lijit.UserAutoComplete = {
  init:function() {
    var input = $('lijit_quick_user_search');
    if(input) {
      input.value = "";
      var userAutocompleter = 
        new Ajax.Autocompleter('lijit_quick_user_search', 
          'lijit_quick_user_search_auto_complete', 
          '/api/informers', 
          { minChars: 1, 
            updateElement: Lijit.UserAutoComplete.userSelected,
            indicator: 'lijit_quick_user_search_indicator'
          }
        );

      Event.observe('lijit_quick_search_form', 'submit', Lijit.UserAutoComplete.submit);
      Event.observe('find_a_user_link', 'click', Lijit.UserAutoComplete.toggle);
    }
  },
  submit:function(e) {
    // Don't allow submits, do the auto complete thing
    Event.stop(e);
  },
  firstFocus: true,
  focus:function(e) {
    // Make the searchbox bigger
    var input = $('lijit_quick_user_search');
    if(Lijit.UserAutoComplete.firstFocus) {
      new Effect.Scale(input, 200, {scaleContent: false, scaleY: false});
      Lijit.UserAutoComplete.firstFocus = false;
    }
    
  },
  userSelected:function (li){
    document.location.href = li.id;   
  },
  toggle:function(e) {
    var find_div = $('find_a_user_div');
    if(find_div) {
      if(find_div.visible())
          Effect.Fade(find_div, {duration: .5});
      else
        new Effect.Appear(find_div, {duration: .5, afterFinish: function(e){$('lijit_quick_user_search').focus();}});
    }
    Event.stop(e);
  }
};

/*
 * This script attatches a click event to the sign in button
 */
Lijit.LoginBox = {
  cookies: -1,
  init:function(e) {
    var signin_link = $('signin_link');
    if(signin_link) {
      Event.observe(signin_link, 'click', Lijit.LoginBox.toggle);
    }
  },
  toggle:function(e) {
    var login_box = $('login');
    if(login_box) {
      // If the login box exists, stop the event, otherwise it will continue to the login page
      Event.stop(e);
      
      if(!login_box.visible()) {
        Effect.Appear(login_box, {duration: .5, afterFinish: function(e){if($('username'))$('username').focus();}});
        $('signin_link').addClassName('selected');
        $('signin_link').blur();
      }
      else {
        Effect.Fade(login_box, {duration: .5});
        $('signin_link').removeClassName('selected');
      }
    }
  }
};

Lijit.ImageLoad = Class.create();

Lijit.ImageLoad.prototype = {
  image: null,
  initialize:function(img, target) {
    this.options = Object.extend({
      defaultImage: Lijit.res('/res/images/default_informer_image.png'),
      width: 16,
      height: 16,
      completeCheck: false
    }, arguments[2] || {});
    this.setup(img, target);    
  },
  setup:function(url, target) {
    this.target = $(target);
    if(!this.target || this.target.tagName != 'IMG')
      return;    // First set it to the default
    
    if(!this.target.src || this.target.src == '')
      this.reset();

    if (url && url != '') {
      this.image = new Image(this.options.width, this.options.height);
      if(navigator.appVersion.match(/Konqueror|Safari|KHTML/)) {
        this.image.onload = this.onLoad.bindAsEventListener(this);
        this.image.onerror = this.onError.bindAsEventListener(this);
      }
      else {
        Event.observe(this.image, 'load', this.onLoad.bindAsEventListener(this));
        Event.observe(this.image, 'error', this.onError.bindAsEventListener(this));
      }
      this.image.src = url;
    }
  },
  reset:function() {
    this.target.src = this.options.defaultImage;
  },
  onLoad:function(e) {
    // on a successful load, swap the image
    this.target.src = this.image.src;
  },
  onError:function(e) {
    // do nothing here
    this.favicon = null;
  }
};

Lijit.Favicon = Class.create();

Object.extend(Object.extend(Lijit.Favicon.prototype, Lijit.ImageLoad.prototype), {
  initialize:function(url, target) {
    this.options = Object.extend({
      defaultImage: Lijit.res('/res/images/default_favicon.png'),
      width: 16,
      height: 16,
      completeCheck: true
    }, arguments[2] || {});

    if (url && url != ''){
      if(url.startsWith('http://www.lijit.com/users/')) {
        this.setup('/informer_image.php?informer_uri=' + escape(url), target);
      }
      else {
        var parsedUrl = new Poly9.URLParser(url);
        this.setup('http://' + parsedUrl.getHost() + '/favicon.ico', target);
      }
    }
    else {
      this.setup('', target);
    }
  }
});

Lijit.Utils = {
   pause: function(ms) {
      var now=new Date();
      var exitTime=now.getTime()+ms;
      while(true){
        now=new Date();
        if (now.getTime() > exitTime) return;
      }
    },
    forceImgCallback: function(url) {
      var img = document.createElement('IMG');
      img.src = url;
      Lijit.Utils.pause(800);
    },
    linkTargetForEvent: function(e) {
      var target = null;
      if (e) {
          target = e.target;
      }
      else {
          target = window.event.srcElement;
      }
      target = Lijit.Utils.resolveTarget(target);
      if (target && target.nodeName.toUpperCase() == 'A') {
        var href = target.href;
        var linktext = null;
        if (target.innerHTML) {
          linktext = target.innerHTML;
        }
        else if (target.innerText) {
          linktext = target.innerText;
        }
        else if (target.text) {
          linktext = target.text;
        }
        if (target && linktext) {
          return { node: target, url: href, text: linktext };
        }
    }
    return null;
  },
  resolveTarget: function(obj) {
    for (var j=0; j<=5; j++) {
      if (obj && obj.nodeName.toUpperCase() == 'A') {
        return obj;
      }
      if (!obj.parentNode) {
        return null;
      }
      obj=obj.parentNode;
    }
    return null;
  },
  /**
   * Bolds Query result
   * @param {String} str Value to return, performs search on this string. Probably 'description' text.
   * @param {Regex} reg Regular expression for search.
   * @returns str properly bolded.
   */
  boldQuery:function(q, str) {
    if (!q || q.length == 0) {
      // No bolding to be done.
      return str;
    }
    // Delete boolean control characters (like plus and minus).
    var clean_q = q.replace(/[\+\-]/g, '');

    // Turn the query into an RE alternation; ensure matching on word boundary.
    var parts = $w(RegExp.escape(clean_q)).map(function (e) { return '\\b' + e +'\\b'; });
    var re = new RegExp("(" + parts.join("|") + ")", "gi");

    return str.replace(re,"<b>$1</b>"); 
  }
};

Lijit.Search = Class.create();

Lijit.Search = {
  BLOG: "blog",
  CONTENT: "content",
  NETWORK: "network",
  PICKS: "picks",
  recordResults:function(id, length) {
    // record results count
    if (length >= 0 && id && typeof(id) != 'undefined') {
      var params = 'id=' + id + '&count=' + length;
      var markReq = new Ajax.Request('/search_mark_count.html', { method: 'get', parameters: params });
    }
  }
};

Lijit.Search.Nutch = Class.create();
Lijit.Search.Nutch.prototype = {
  initialize:function(query, type, target) {
    this.options = Object.extend({
      hitsPerSite: 0,
      lang: 'en',
      hitsPerPage: 10,
      overlay: null,
      overlayOptions: {},
      delayedShow: true,
      noResults: null,
      postProcess: null
    }, arguments[3] || {});
    this.results = [];
    this.target = $(target);
    this.query = query;
    this.type = type;
    this.options.overlay = $(this.options.overlay);
    this.tries = 2;
    this.loaded = false;
    this.switchPageListeners = [];
    this.targetHTML = "";
    
    if(!this.options.delayedShow)
      this.request(0);
  },
  recordResults:function(id, length) {
    // record results count
    if (length >= 0 && id && typeof(id) != 'undefined') {
      var params = 'id=' + id + '&count=' + length;
      var markReq = new Ajax.Request('/search_mark_count.html', { method: 'get', parameters: params });
    }
  },
  addPageSwitchListener:function(callback) {
    this.switchPageListeners.push(callback);
  },
  request:function(start) {
    this.loaded = true;
    if (!this.query || this.query.length == 0) {
      return this.noResults();
    }
    $(this.target).addClassName('busy');
    if(this.options.overlay)
      Element.hide(this.options.overlay);
    this.target.style.width = 'auto';
    this.targetHTML = this.target.innerHTML;
    this.target.innerHTML += '<div class="busy_message"><img src="' + Lijit.res('/res/images/page_busy.gif') + '" style="margin-bottom:4px;margin-left:-5px;"/><div style="position: absolute; text-align: center; top: 60px; left: 0; font-weight: bold; width: 194px;">searching</div></div>';
    if(!start)
      start = 0;
    var params = {start: start};
    var selector = (gInformerID != '0' ? gInformerID : gUser);
    var searchURL = "/api/json/search/" + selector + "/" + this.type + "?q=" + encodeURIComponent(this.query);
    if (typeof(gSearchPlatform) !== 'undefined') {
      searchURL += "&p=" + gSearchPlatform;
    }
    new Ajax.Request(searchURL, {method: 'get', onSuccess: this.processResults.bind(this), parameters: params});
  },
  show:function() {
    if(!this.loaded) {
      this.request(0);
    }
  },
  noResults:function() {
    if (this.options.noResults) {
      this.target.innerHTML = $(this.options.noResults).innerHTML;
      this.target.addClassName('noResults');
      this.target.removeClassName('busy');
    }
    return;
  },
  processResults:function(response, skipEval) {
    try{
      var json = response;
      if (!skipEval) {
        json = json.responseText.evalJSON();
      }
      this.target.innerHTML = "";

      this.engine = json.engine;

      var google = document.getElementById('powered_google_' + this.type);
      if ( google != null && this.engine == 3) {                         // hybrid, means we need to show/hide the google based on tab
         if (this.type != "blog") {        
             Element.show(google);  
         } else {
             Element.hide(google);  
         }
      }
      
      if (!json.success || !json.search.totalResults) {
        return this.noResults();
      }
      this.search = json.search;
      
      // Create header
      this.header = $(document.createElement('div'));
      this.header.addClassName('searchHeader');
      this.header.innerHTML = "Displaying " + (this.search.startIndex + 1) + " - " + (this.search.startIndex + this.search.pageLength) + " of about " + (this.search.totalResults) + " results";
      //this.header.innerHTML += "<a href='" + this.search.nutchURL + "'>opensearch<a/>";
      this.target.appendChild(this.header);
      
      // create objects and display
      this.results = [];
      this.overlays = [];
      
      if(this.options.overlay)
        this.options.overlay.innerHTML = "";
      
      this.search.results.each(function(item) {
        var searchResult = new Lijit.Search.Result(item, this);
        this.results.push(searchResult);
      }.bind(this));
      
      // Create footer
      this.footer = $(document.createElement('div'));
      this.footer.addClassName('searchFooter');
      this.target.appendChild(this.footer);
      this.buildPager(this.footer);
      this.target.removeClassName('busy');

      if(this.options.overlay)
        new Effect.Appear(this.options.overlay);

      this.recordResults(gSearchID, this.results.length);
       
      if (this.options.postProcess) {
        this.options.postProcess(this);
      }
    }
    catch(e) {
      if (typeof(console) != 'undefined') {
        console.dir(e);
      }
    }
  },
  buildPager:function(footer) {
    if(this.search.totalResults > this.search.itemsPerPage) {
      var newCurrentPage = (this.search.startIndex / this.search.itemsPerPage) + 1;
      var totalPages = Math.ceil(this.search.totalResults / this.search.itemsPerPage);
      var ul = $(document.createElement('ul'));
      var pageArray = [];
      var page = 1;
      for(var x = 0; x < this.search.totalResults && page < 100; x += this.search.itemsPerPage) {
        var li = document.createElement('li');
        ul.appendChild(li);

        var a = document.createElement('a');
        a.href = "#";
        a.onclick = "return false;";
        Event.observe(a, 'click', this.switchPage.bindAsEventListener(this, x));            
        li.appendChild(a);
        a.innerHTML = page;
        pageArray[page] = a;
        page++;
      }
      this.pagerList = ul;

      // previous arrow
      if(newCurrentPage > 1) {
        var previous = $(document.createElement('a'));
      	previous.addClassName('searchArrow');
        previous.href = "#";
        previous.onclick = "return false;";
        Event.observe(previous, 'click', this.switchPage.bindAsEventListener(this, (newCurrentPage - 2) * this.search.itemsPerPage));
        previous.innerHTML = "&laquo;";
        footer.appendChild(previous);
      }

      // next arrow
      if(newCurrentPage < totalPages) {
        var next = $(document.createElement('a'));
        next.addClassName('searchArrow');
        next.href = "#";
        next.onclick = "return false;";
        Event.observe(next, 'click', this.switchPage.bindAsEventListener(this, (newCurrentPage) * this.search.itemsPerPage));
        next.innerHTML = "&raquo;";
        footer.appendChild(next);
      }
      footer.appendChild(this.pagerList);
      
      // scroll current into view
      if(this.currentPage) {
        $(pageArray[this.currentPage]).removeClassName('current');
      }
      
      this.currentPage = newCurrentPage;
      
      var newPager = $(pageArray[this.currentPage]);
      newPager.addClassName('current');
      var newPagePos = newPager.positionedOffset();
      var center = Math.max(0, newPagePos[0] - (this.pagerList.getWidth() / 2) + (newPager.getWidth() /2));
      this.pagerList.scrollLeft = center;
      
    }
  },
  switchPage:function(e, start) {
    Event.stop(e);
    window.scrollTo(0,0);
    this.request(start);
    this.switchPageListeners.each(function(listener) {
        listener(this);  
    });
  }
};

// A subclass with the special rendering behavior for the "picks" style display.
Lijit.Search.NutchPicks = Class.create(Lijit.Search.Nutch, {
  processResults:function(response, skipEval) {
    try{
      var json = response;
      if (!skipEval) {
        json = json.responseText.evalJSON();
        this.target.innerHTML = "";
      }
      this.search = json.search;

      if (!json.success || 
        (!json.search.blog.totalResults && !json.search.content.totalResults && !json.search.network.totalResults)) {
        return this.noResults();
      }

      this.target.hide();

      if (!skipEval) {
        this.target.innerHTML = this.targetHTML;
      }

      this.results = [];
      
      if(this.options.overlay)
        this.options.overlay.innerHTML = "";
      
      $A(['blog', 'content', 'network']).each(function (search_type) {
          var search = this.search[search_type];
          var par = $(search_type + '_summary');
          var target = $(search_type + '_summary_results');
          target.innerHTML = "";

          if (search.results && search.results.length > 0) {
            search.results.each(function(item) {
              var searchResult = new Lijit.Search.Result(item, { target: target });
              this.results.push(searchResult);
              }.bind(this));

            par.show();
          }
        }.bind(this));
      
      // Create footer
      this.footer = $(document.createElement('div'));
      this.footer.addClassName('searchFooter');
      this.target.appendChild(this.footer);
      // this.buildPager(this.footer);
      this.target.show();
      this.target.removeClassName('busy');

      if(this.options.overlay)
        new Effect.Appear(this.options.overlay);

      this.recordResults(gSearchID, this.results.length);
        
      if (this.options.postProcess) {
        this.options.postProcess(this);
      }
    }
    catch(e) {
      if (typeof(console) != 'undefined') {
        console.dir(e);
      }
    }
  }
});

Lijit.Search.ImpressionView = Class.create();
Lijit.Search.ImpressionView.prototype = {
  initialize:function(baseURL, search_id) {
    // &r= just to bust cache.
    var url = baseURL + '/impression.html?id=' + search_id + '&r=' + Math.random();
    new Ajax.Request(url, { method: 'get' });
  }
};

Lijit.Search.Result = Class.create();
Lijit.Search.Result.prototype = {
  initialize:function(json, parent) {
    this.parent = parent;
    this.title = json.title;
    this.imageuri = json.imageuri;
    this.link = json.link;
    this.report = json.report;
    this.query = json.query.strip();
    this.description = json.description;
    this.render();
  },
  trackClick: function() {
    if(this.report) {
      Lijit.SearchClickTracker.gReports.set(this.link, this.report);
      this.overlay = new Lijit.Search.Overlay(this);
    }
  },
  render:function() {
    this.searchNode = $(document.createElement('div'));
    this.searchNode.addClassName('floatcontainer');
    this.searchNode.addClassName('serpWrap');
    
    this.parentNode = $(this.parent.target);  
  
    // Pass the rendering off to a url specific renderer
    if (Lijit.Search.Result.RenderManager.find(this.link).render(this) == true) {
      return;
    }

    this.parentNode.appendChild(this.searchNode);
    
    // This is a gross fix that allows prototype to assign an event to the link element, I'll have to look deeper into why it isn't working
    this.linkNode = $('linkFix');
    this.linkNode.id = null;
    Event.observe(this.linkNode, 'click', Lijit.SearchClickTracker.handleClick);

    this.trackClick();
  }
}

Lijit.Search.Result.RenderManager = {
  renderers: [],
  find:function(url) {
    var renderer = Lijit.Search.Result.RenderManager.renderers.find(function(r) {
      return r.match(url);
    });
    
    if(typeof renderer != "undefined")
      return renderer;
    else
      return Lijit.Search.Result.DefaultRenderer;
  },
  register:function(renderer) {
    Lijit.Search.Result.RenderManager.renderers.push(renderer);
  }
};

Lijit.Search.Result.DefaultRenderer = {
  unique_image_id:0,
  dom_loaded:false,
  render:function(result) {


    if (typeof(result.imageuri) !== 'undefined' && 
        result.imageuri != null && 
        result.imageuri.length > 0)
    {

      var thumbnail;
      try {
        thumbnail = result.imageuri.evalJSON();
        thumbnail.isJSONEncoded = true;
      }
      catch (e) {
        thumbnail = new Object();
        thumbnail.url = result.imageuri;
        thumbnail.isJSONEncoded = false;
      }

      var img = new Image();    
      var div = document.createElement('div');
      div.className = "resultsThumbnail";
      if (!thumbnail.isJSONEncoded) {
        div.id = "div_" + (Lijit.Search.Result.DefaultRenderer.unique_image_id++) + "_img";
        img.onload = function() {
            Lijit.Search.Result.DefaultRenderer.onImageLoad(result, div, img, false);
        };
        
        img.src = thumbnail.url; 
      }
      else {
        img.src = thumbnail.url; 
        img.height = thumbnail.height;
        img.width = thumbnail.width;
        Lijit.Search.Result.DefaultRenderer.onImageLoad(result, div, img, true);
      }

      result.searchNode.addClassName('media_result');
      result.searchNode.appendChild(div);
      
    }
    
    var h2 = document.createElement('h2');
    h2.className = 'title';
    
    result.linkNode = document.createElement('a');
    result.linkNode.innerHTML = Lijit.Utils.boldQuery(result.query, result.title).truncate(75);
    result.linkNode.href = result.link;
    result.linkNode.className = 'link';
    result.linkNode.target = "_top";
    result.linkNode.id = "linkFix";
    
    h2.appendChild(result.linkNode);
    
    var descText = "<div class='description'>";
    // descText += result.description.replace(result.queryMatch, "<b>$1</b>");
    descText += Lijit.Utils.boldQuery(result.query, result.description);
    descText += "<br /><span class='link'>" + result.link.replace("http://", "").truncate(75) + "</span>";
    descText += "</div>";
    
    result.searchNode.appendChild(h2);
    result.searchNode.innerHTML += descText;
  },
  
  onImageLoad:function(result, div, img, isJSON) {
    if (Lijit.Search.Result.DefaultRenderer.dom_loaded || isJSON) {
        Lijit.Search.Result.DefaultRenderer.renderImage(result, div, img, isJSON);
    }
    else {
        document.observe("dom:loaded", function() {
            Lijit.Search.Result.DefaultRenderer.renderImage(result, div, img, isJSON);
        });
    }
    
  },
  
  renderImage:function(result, div, img, isJSON) {
    var width = 75;
    var height = 75;
    if (img.width != 0 && img.height != 0) {
        var ratio = img.width / img.height;
        if (img.width > img.height) {            
            height = width / ratio;
        }
        else if (img.width < img.height) {
            width = height * ratio;
        }
    }            

    // EPA: This is here to solve a race condition. If the page isn't loaded then we can set 
    // the innerHTML on the object directly without using the document. If the page is loaded 
    // then we have to use the document.getElementById to retrieve the element so we can set 
    // the innerHTML.
    var element = div;
    if (!isJSON) {
        element = document.getElementById(div.id);
        if (!element) {
            element = div;
        }
    }

    // EPA: We are depending on the browser to cache the image. We have to load the image first to
    // get the width and height to calculate ratio. I will submit a request to the search team to supply a height and
    // width when they store the image so we don't have to request it twice (lucky for us the cache is used).
    element.innerHTML = '<a href="' + result.link 
        + '" target="_top"><img align="center" width="' + width 
        + '" height="' + height + '"' 
        + ' src="' + img.src + '"></a>';    
  }
    
};


if (!Lijit.Search.Result.DefaultRenderer.dom_loaded) {
    document.observe("dom:loaded", function() {
        Lijit.Search.Result.DefaultRenderer.dom_loaded = true;
    });
}

/*
 * This class wraps all overlay functionality, rendering and positioning
 */
Lijit.Search.Overlay = Class.create();
Lijit.Search.Overlay.prototype = {
  commentsMouseOutTimeout: null,
  initialize:function(searchObj) {
    this.search = searchObj;
              
    if(searchObj.report) {
      this.report = searchObj.report;
      this.searchNode = $(searchObj.searchNode);
      this.render();
    }
  },
  render:function() {
    // Visible overlay
    this.div = $(document.createElement('div'));
    this.searchNode.appendChild(this.div);
    this.div.addClassName('overlay');

//    var span = $(document.createElement('span'));
//    span.innerHTML = "What's the Connection?";
//    this.div.appendChild(span);
//
//    this.connectionSpan = span;
//    
//    Event.observe(this.connectionSpan, 'mouseover', this.onMouseOver.bindAsEventListener(this));
//    Event.observe(this.div, 'mouseout', this.onMouseOut.bindAsEventListener(this));      

    // Chains (hidden "drawer")
    this.chains = $(document.createElement('span'));
    this.chains.addClassName('chains');
    this.chains.style.display = 'none';
    this.div.appendChild(this.chains);

    // Let the search refer back to us.
    this.search.overlay = this;
    
    Event.observe(this.chains, 'mouseover', this.onMouseOver.bindAsEventListener(this));  

    // Find comments.
    if (typeof(Lijit.CommentManager) != 'undefined') {
      Lijit.CommentManager.fetchComments(this.search.link, this.renderComments.bind(this));
    }
  },
  renderComments: function(comments) {
    if (comments && comments.count > 0) {

      //"Fix" JS Kit. Needs to look like this: "JS-Kit"
      if (comments.provider_name.toLowerCase() == 'js kit') {
        comments.provider_name = "JS-Kit"; 
      } 

      var span = $(document.createElement('span'));
      span.addClassName('comments');
      span.innerHTML = comments.count + (comments.count == 1 ? ' Comment' : ' Comments') + ' on ' + comments.provider_name;
      this.div.insertBefore(span, this.chains);

      // Will hold properties related to the comments showing and hiding.
      this.comments = {
        handle: span,
        content: $(document.createElement('div')),
        json: comments
      };

      this.comments.content.style.display = 'none';
      this.comments.handle.parentNode.parentNode.appendChild(this.comments.content);

      Event.observe(this.comments.handle, 'click', this.toggleCommentVisibility.bindAsEventListener(this));
    }
  },
  toggleCommentVisibility: function(el) {
    //Get referrence to the node that called the event.
    var element = Event.element(el);
    //Get it's sibling. This is .commentsWrap
    var el = (element.ancestors()[0].next());
    
    if (typeof el != 'undefined' && el.visible()) {
      this.hideComments();
    } 
    else {
      this.displayComments();
    }
  },
  displayComments: function(e) {
    
    var content = this.comments.content;

    //Build post suffix for linking to the post source.
    //Also build link to service.
    var postCommentURLSuffix = null;
    var serviceLinkURL = null;
    var provider = this.comments.json.provider_name.toLowerCase(); 
    switch (provider) {
      case 'disqus':
        postCommentURLSuffix = '#disqus_thread';
        serviceLinkURL = 'http://disqus.com/';
        break;
      case 'intense debate':
        postCommentURLSuffix = '#IDCommentsHeadLogin';
        serviceLinkURL = 'http://www.intensedebate.com/';
        break;
      case 'wordpress':
        serviceLinkURL = 'http://wordpress.org/';
        postCommentURLSuffix = '';
        break;
      case 'typepad':
        serviceLinkURL = 'http://typepad.com/';
        postCommentURLSuffix = '';
        break;
      default:
        serviceLinkURL = 'http://js-kit.com/';
        postCommentURLSuffix = '';
        break;
    }

    if (content.className != 'commentsWrap') {
      // We're rendering this sucka for the first time.
      content.addClassName('commentsWrap');

      content.innerHTML = '<a target="_blank" href="' + serviceLinkURL + '" title="' + this.comments.json.provider_name + '"><img class="commentLogo" src="' + this.comments.json.provider_logo +'" alt="Comments Powered by ' + this.comments.json.provider_name + '" /></a>';

      var list = $(document.createElement('ul'));

      // this.comments.json was the response from /api/json/comments on lijit.
      // Iterate through each comment; draw it.
      this.comments.json.comments.each(function(comment) {
        var li = $(document.createElement('li'));
        var dt = new Date(comment.time * 1000);

        var html = dt.toString();
        if (typeof(comment.name) != 'undefined' && comment.name.length > 0) {
          html = "On " + html + " " + comment.name + " said,";
        }
        html = '<p class="commentDate">' + html + '</p>';
        if (typeof(comment.avatar) != 'undefined' && comment.avatar.length > 0) {
          html += '<img class="avatar" src="' + comment.avatar + '" alt="' + comment.name + '" />';
        }
        html += '<p>' + comment.text + '</p>';
        li.innerHTML = html;
        list.appendChild(li);
      });

      content.appendChild(list);

      var linkComment = 'View more comments';
      var commentCount = this.comments.json.comments.length;
      if (commentCount == 1) {
        linkComment = 'View comment';
      }
      else if (commentCount == 2) {
        linkComment = 'View comments';
      }

      var contextLinkPara = document.createElement('p');
      contextLinkPara.className = 'viewMore';
      contextLinkPara.innerHTML = "<a target=\"_blank\" href=\"" + this.comments.json.post_url + postCommentURLSuffix + "\" title=\"" + linkComment +"\">" + linkComment + "...</a>";

      content.appendChild(contextLinkPara);
    }

    // At this point we're rendered and mouseOut event registered. Just pop the div.
    this.comments.handle.addClassName('on');
    content.show();
  },
  hideComments: function(e) {
    this.comments.content.hide(); 
    this.comments.handle.removeClassName('on');
  },
  renderChains:function() {
    if(this.report.chains) {
      var chainHTML = "";
      this.report.chains.each(function(chain, i) {
        if(i > 0) {
          var arrow = Math.min(i, 3);
          chainHTML += "<img src='" + Lijit.res('/res/images/tiny_arrow_' + arrow + '.png') + "' style='padding: 0pt 1em' />";  
        }

        chainHTML += "<a target='_top' href='/informers?uri=" + encodeURI(chain.uri) + "'>" + chain.name.truncate(50) +  "</a>";
      }.bind(this));

      this.chains.innerHTML = chainHTML;
    }
  },
  openTray:function(e) {
   this.chains.show();
   this.div.style.height = "auto";
	//adding 'on' class to what's the connection link on mouseover
	//1/8/09 MB and RP forever
	this.div.childElements()[0].addClassName('on');
  },
  closeTray:function(e) {
    this.chains.hide();
	//removing 'on' class to what's the connection link on mouseout
	//1/8/09 MB and RP forever
	 this.div.childElements()[0].removeClassName('on');	

   // Add "comments" class when chains tray is closed.
   if (typeof(this.comments) != 'undefined') {
     this.comments.handle.addClassName('comments');
   }
  },
  onMouseOut:function(e) {
    clearTimeout(this.mouseInTimeout);
    this.mouseOutTimeout = setTimeout(this.closeTray.bind(this), 200);
  },
  onMouseOver:function(e) {
    clearTimeout(this.mouseOutTimeout);
    this.mouseInTimeout = setTimeout(this.openTray.bind(this), 200);
    this.fetchFullReport();
  },
  fetchFullReport:function() {
    if (!this.report) {
      return this.noChains();
    }
    // If the chains div is empty, then it the loading hasn't started yet
    if(this.chains.innerHTML == '') {
      this.chains.innerHTML = "Loading <img src='" + Lijit.res('/res/images/busy_10.gif') + "' />";

      var url = '/api/json/overlay';
      
      var params = {
        chains_id: gInformerID,
        report_json: Object.toJSON(this.report)
      };
      
      this.reportRequest = new Ajax.Request(url, {method: 'post', onSuccess: this.processChainCallback.bindAsEventListener(this), parameters: params});
      this.requestTimeout = setTimeout(this.noReport.bind(this), 5000);
    }
  },
  noReport:function(e) {
    this.reportRequest.transport.abort();
    this.noChains();
  },
  processChainCallback:function(response) {
    clearTimeout(this.requestTimeout);
    var json = response.responseText.evalJSON();
    if(typeof json != "undefined" && json.success && json.report && json.report.chains) {
      this.report = json.report;
      // We should have enough information to render chains now
      this.renderChains();
    }
    else {
      this.noChains();
    }
  },
  noChains:function() {
    this.chains.innerHTML = 'No connection information available';
  }
};

/** 
* @projectDescription   Poly9's polyvalent URLParser class
*
* @author Denis Laprise - denis@poly9.com - http://poly9.com
* @version  0.1 
* @namespace  Poly9
*
* Usage: var p = new Poly9.URLParser('http://user:password@poly9.com/pathname?arguments=1#fragment');
* p.getHost() == 'poly9.com';
* p.getProtocol() == 'http';
* p.getPathname() == '/pathname';
* p.getQuerystring() == 'arguments=1';
* p.getFragment() == 'fragment';
* p.getUsername() == 'user';
* p.getPassword() == 'password';
*
* See the unit test file for more examples.
* URLParser is freely distributable under the terms of an MIT-style license.
*/

if (typeof Poly9 == 'undefined') {
 var Poly9 = {};
}

/**
 * Creates an URLParser instance
 *
 * @classDescription  Creates an URLParser instance
 * @return {Object} return an URLParser object
 * @param {String} url  The url to parse
 * @constructor
 * @exception {String}  Throws an exception if the specified url is invalid
 */
Poly9.URLParser = function(url) {
 this._fields = {'Username' : 4, 'Password' : 5, 'Port' : 7, 'Protocol' : 2, 'Host' : 6, 'Pathname' : 8, 'URL' : 0, 'Querystring' : 9, 'Fragment' : 10};
 this._values = {};
 this._regex = null;
 this.version = 0.1;
 this._regex = /^((\w+):\/\/)?((\w+):?(\w+)?@)?([^\/\?:]+):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(\w*)/;
 for(var f in this._fields)
  this['get' + f] = this._makeGetter(f);
 if (typeof url != 'undefined')
  this._parse(url);
};
 
/**
 * @method 
 * @param {String} url  The url to parse
 * @exception {String}  Throws an exception if the specified url is invalid
 */
Poly9.URLParser.prototype.setURL = function(url) {
  this._parse(url);
};

Poly9.URLParser.prototype._initValues = function() {
   for(var f in this._fields)
   this._values[f] = '';
};

Poly9.URLParser.prototype._parse = function(url) {
  this._initValues();
  var r = this._regex.exec(url);
  if (!r) throw "DPURLParser::_parse -> Invalid URL"
  for(var f in this._fields) if (typeof r[this._fields[f]] != 'undefined')
   this._values[f] = r[this._fields[f]];
};

Poly9.URLParser.prototype._makeGetter = function(field) {
 return function() {
  return this._values[field];
 }
};

Lijit.Tabs = Class.create();

Lijit.Tabs.prototype = {
  initialize:function(ul) {
    try{
      this.options = Object.extend({
        sortable: false,
        lockable: false,
        prefix: 'tab_',
        onSortComplete: function(){},
        onLockClick: function(){},
        onActivate: function(){},
        beforeActivate: function(){return true;}
      }, arguments[1] || {});
    
      this.ul = $(ul);
      
      if(this.options.sortable) {
        if(this.options.lockable) {
          this.lock = document.createElement('img');
          this.locked = false;
          this.lock.src = Lijit.res("/res/images/lock_unlocked.png");
          this.ul.appendChild(this.lock);
          this.lock.align = "right";
          Event.observe(this.lock, 'click', this.lockClick.bindAsEventListener(this));
          this.lockClick(null);
        }
        else {
          this.sortOn();
        }
      }
      
      var firstTab = location.hash;
      if(typeof firstTab == "undefined" || firstTab == "" || !$('stats_' + firstTab.substring(2))) {
        firstTab = this.ul.childElements().detect(function(li) {return (li.tagName.toUpperCase() == 'LI');}).id;
      }
      else {
        firstTab = firstTab.substring(2);
      }
      
      this.ul.childElements().each(function(li) {
        if(li.tagName.toUpperCase() == 'LI' && !li.hasClassName('ignore')) {
          Event.observe(li, 'click', this.tabClicked.bindAsEventListener(this));
        }
      }.bind(this));
      
      this.activateTab(firstTab);
    }
    catch(e) {
      //alert(e.message);
    }
  },
  tabClicked:function(e) {
    var target = $(Event.element(e));
    if (!this.options.beforeActivate(target)) {
      if (e) {
        Event.stop(e);
      }
      return false;
    }
    target.blur();
    while(!target.tagName || target.tagName.toUpperCase() != 'LI')
      target = $(target.parentNode);
    this.activateTab(target, e);
  },
  activateTab:function(target, e) {
    this.ul.childElements().each(function(li) {
      if(li.tagName.toUpperCase() == 'LI') {
        $(li).addClassName('inactive_button');
        $(li).removeClassName('active_button');
      }
    });
    
    $(target).addClassName('active_button');
    $(target).removeClassName('inactive_button');
    
    this.currentTab = $(target);
    this.options.onActivate($(target));
  },
  addTab:function(title) {
    var name = title.toLowerCase().replace(/ /gi, "_"); 
    var tab = $(document.createElement('li'));
    tab.id = "tab_" + name;
    tab.addClassName("inactive_button");
    
    var a = $(document.createElement('a'));
    a.id = tab.id + "_link";
    a.href = "#h" + tab.id;
    a.innerHTML = title;
    
    tab.appendChild(a);
    
    this.ul.appendChild(tab);
    Event.observe(tab, 'click', this.tabClicked.bindAsEventListener(this));
    if(this.options.sortable)
      this.sortOn();
    return tab;
  },
  removeTab:function(id) {
    var tab = $(id);
    if(tab)
      Element.remove(tab);
    if(this.options.sortable)
      this.sortOn();
    
    this.activateTab(this.getFirstTab());
  },
  getFirstTab:function() {
    return this.ul.childElements().detect(function(li) {return (li.tagName.toUpperCase() == 'LI');});
  },
  onSortComplete:function(ul) {
    this.options.onSortComplete();
  },
  lockClick:function(e) {
    this.locked = !this.locked;
    if(this.locked) {
      this.sortOff();
      this.lock.src = "/res/images/lock_locked.png";
      this.lock.title = "Make the tabs sortable";
    }
    else {
      this.sortOn();
      this.lock.src = "/res/images/lock_unlocked.png";
      this.lock.title = "Make the tabs un-sortable";
    }
    
    this.options.onLockClick(this.locked);
  },
  sortOn:function() {
    Sortable.create(this.ul, {tag: 'li', ghosting: false, constraint: 'horizontal', overlap: 'horizontal', onUpdate: this.onSortComplete.bindAsEventListener(this)});
  },
  sortOff:function() {
    Sortable.destroy(this.ul);
  }
};

Lijit.SwitchableDivs = Class.create();

Lijit.SwitchableDivs.prototype = {
  initialize:function() {
    this.options = Object.extend({
      sortable: false,
      onSortComplete: {},
      beforeActivate: function(){return true;}
    }, arguments[0] || {});  
    
    this.divs = [];
  },
  addDiv:function(clickable, target, onActivate) {
    this.divs.push({clickable: $(clickable), target: $(target), onActivate: onActivate});
    Event.observe($(clickable), 'click', (function(e) { this.activateDiv($(clickable), e) }).bindAsEventListener(this));
  },
  removeDiv:function(clickable, target) {
    this.divs = this.divs.select(function(div) {
      return !(div.clickable == clickable && div.target == target);
    });
  },
  activateDiv:function(clickable, e) {
    clickable = $(clickable);
    if (!this.options.beforeActivate(clickable)) {
      if (e) {
        Event.stop(e);
      }
      return false;
    }
    // turn off all divs
    this.divs.each(function(div) {
      $(div.target).hide();
      if(div.clickable == clickable)
        $(div.target).show();
        if (div.onActivate) {
          div.onActivate();
        }
    });
  }
};

/**
* Show a standard lijit message as feedback to the user
* @param msg the actual text message
* @param isError if true, then this is an error message (red background). If not set, or
* false then this is just a standard message (green background)
* @param timeout is set, then the message fades after the specified timeout (ms)
*/
Lijit.showMessage = function(msg, isError, timeout) {
  var content = $('content');
  if (content) {
    var messageDiv = $(document.createElement('div'));
    var image = $(document.createElement('img'));
    image.src = '/res/images/close_button.png';
    image.style.cssFloat = 'right';
    image.style.styleFloat = 'right';
    image.style.cursor = 'pointer';
    
    messageDiv.addClassName('messages');
    if (isError) {
      messageDiv.addClassName('error');
    }
    else {
      messageDiv.addClassName('status');
    }

    var text = $(document.createElement('span'));
    text.innerHTML = msg;

    messageDiv.appendChild(image);
    messageDiv.appendChild(text);

    if (content.firstChild) {
      content.insertBefore(messageDiv, content.firstChild);
    }
    else {
      content.appendChild(messageDiv);
    }
    
    if (timeout){
    	setTimeout("Lijit.hideMessages()", timeout);
    }
    //new Effect.Pulsate(messageDiv, { pulses: 3, duration: 3.0 });
    Event.observe(image, 'click', function(e) { messageDiv.hide(); });
  }
    
};


/**
 * Manages what account is "active" - imulates a dropdown. It blocks the onclick event.
 * @args ul element
 */
Lijit.AccountSwitchManager = function(ulArg) {
  //SF Fix for ie.
  if (Prototype.Browser.IE) {
    var sfEls = document.getElementById("subNav").getElementsByTagName("LI");
    for (var i=0; i<sfEls.length; i++) {
      sfEls[i].onmouseover=function() {
        this.className+=" sfhover";
      }
      sfEls[i].onmouseout=function() {
        this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
      }
    }
  }
};

/**
* Clears any messages currently being displayed
*/
Lijit.hideMessages = function() {
	
	var msgList = $$('.messages');
	
	if (msgList){
		for (var i=0; i<msgList.length; i++){
			msgList[i].fade();
			//msgList[i].innerHTML = "";
		}
	}
	
};

