var _xsajax$transport_status = null;
//
// Copyright 2008 Competo Solutions, Inc. All rights reserved.
var Lun =  {
  Version: '0.4',
  Debug: false,
  Host: false ? 'http://localhost:3000' : 'http://lun.competo.com',
  Timer: null,
  Timeout: 50,
  Transport: null,
  Paginator: null,
  Head: null,
  Cmts: null,
  Topic: null,
  Msg: null,
  Err: null,
  Pager: null,
  Footer: null,
  NewCmtForm: null,
  PageSize: '',
  Closed: false,
  Url: null,
  Title: null,
  Desc: null,
  Keyword: null,
  ID: 0,

  getId: function() {this.ID += 1; return 'lcmtsrc-' + this.ID;},

  init: function() {
    var html = document.getElementsByTagName('html');
    var head = document.getElementsByTagName('head');
    if (head == null)
    {
      this.Head = document.createElement('head');
      html[0].appendChild(this.Head);
    }
    else
      this.Head = head[0];

    this.Url = document.location.href;
    this.Title = document.title;

    var metas = document.getElementsByTagName('meta');
    for(var i=0; i<metas.length; i++)
    {
      if (metas[i].name == 'description')
        this.Desc = metas[i].content;
      else if (metas[i].name == 'keywords')
        this.Keyword = metas[i].content;
    }

    this.Cmts = document.getElementById('lcmts');
    if (this.Cmts != null) {
      var p = this.Cmts.getAttribute('size');
      if (p != null && p != '')
        this.PageSize = p;

      p = this.Cmts.getAttribute('closed');
      this.Closed = p == 'true';

      if (this.Closed)
        this.Cmts.innerHTML = "<div class='lcmthead b'>Comments (Closed)</div>";
      else
        this.Cmts.innerHTML = "<div class='lcmthead b'>Comments</div>";

      this.Topic = this.appendDiv(this.Cmts, 'lcmttopic', null);
      this.Pager = this.appendDiv(this.Cmts, 'lcmtpager', null);
      this.Footer = this.appendDiv(this.Cmts, 'lcmtfooter', null);
    }
  },

  addScript: function(url) {
    var js = document.createElement('script');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', url);
    js.setAttribute('id', this.getId());
    this.Head.appendChild(js);
    return js;
  },
  
  addCss: function(file) {
    var lcss = document.createElement('link');
    lcss.setAttribute('rel', 'stylesheet');
    lcss.setAttribute('type', 'text/css');
    lcss.setAttribute('href', Lun.Host + '/stylesheets/' + file);
    this.Head.appendChild(lcss);
  },

  appendDiv: function (parentNode, css, text) {
    var d = document.createElement('DIV');
    d.setAttribute('class', css);
    if (text != null)
      d.innerHTML = text;
    parentNode.appendChild(d);
    return d;
  },

  insertDiv: function(parentNode, css, text) {
    var d = document.createElement('DIV');
    d.setAttribute('class', css);
    if (text != null)
      d.innerHTML = text;
    Element.insert(parentNode, {before:d});
    return d;
  },

  appendLabel: function(parentNode, css, text) {
    var l = document.createElement('LABEL');
    if (css != null)
      l.setAttribute('class', css);
    if (text != null)
      l.nodeValue = text;
    parentNode.appendChild(l);
    return l;
  },

  sanitize: function(s) {
    var html = s.stripScripts();
    return html;
  },

  trace: function(msg) {
    if (this.Debug && window.status) {
      window.status = msg;
    }
  },
  
  traceErr: function(msg) {
    if (this.Debug) {
      if (this.Msg != null)
        this.appendLabel(this.Msg, 'lcmterr', msg);
      else
        window.status = msg;
    }
  },
  
  traceErrs: function(errors) {
    if (this.Msg == null)
      return;

    var html = '';
    if (errors != null && errors.length > 0) {
      for(i=0; i<errors.length; i++) {
        html += "<label class='lcmterr'>" + errors[i][1] + "</label>";
      }
    }
    this.Msg.innerHTML = html;
  },

  Months: [["January","Jan"],["Febuary","Feb"],["March","Mar"],["April","Apr"],["May","May"],["June","Jun"],["July","Jul"],["August","Aug"],["September","Sep"],["October","Oct"],["November","Nov"],["December","Dec"]],
  Weekdays: [["Sunday","Sun"],["Monday","Mon"],["Tuesday","Tue"],["Wednesday","Wed"],["Thursday","Thu"],["Friday","Fri"],["Saturday","Sat"]],

  parseDate: function(isoDate) {
    var match = /(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2}:\d{2})([+|-]?\d{2}:\d{2})/.exec(isoDate);
    var date = new Date(match[1], match[2]-1, match[3]);
    date = this.Months[date.getMonth()][1] + " " + date.getDate() + " " + date.getFullYear() + " " + match[4] + " GMT " + match[5].replace(/:/, "");
    date = new Date(Date.parse(date));
    return date;
  },

  toDateString: function(date) {
    return this.Months[date.getMonth()][0] + " " + date.getDate() + ", " + date.getFullYear();
  },

  renderA: function(url, target, text) {
    if (target == null)
      return "<a href='" + url + "'>" + text + "</a>";
    else
      return "<a href='" + url + "' target='" + target + "' rel='nofollow'>" + text + "</a>";
  },

  renderInfo: function() {
    var html = "<input id='lcmttitle' name='t' value='" + this.Title + "' type='hidden'/>"
      + "<input id='lcmtdesc' name='d' value='" + this.Desc + "' type='hidden'/>"
      + "<input id='lcmtkey' name='k' value='" + this.Keyword + "' type='hidden'/>";
    Element.insert('lcmtnewcmtform', html);
  },

  renderNewCmtForm: function() {
    this.NewCmtForm = this.appendDiv(this.Footer, 'lcmtnew', null);
    this.Msg = this.insertDiv(this.NewCmtForm, 'lcmterr', null);
    
    var html;
    html = "<div id='lcmthead' class='lcmthead l'>Leave a comment</div>"
      + "<label class='lun b'>powered by <a title='Lun (alpha) is a free service offered by competo.com' href='" + Lun.Host + "'>lun</a></label>"
      + "<form id='lcmtnewcmtform'>"
      +   "<input id='lcmtp' name='id' value='' type='hidden'/>"
      +   "<input id='lcmturl' name='u' value='" + this.Url + "' type='hidden'/>"
      +   "<label>Name (required)</label>"
      +   "<input class='lcmtinput' id='cmt_name' name='c[n]' size='30' type='text'/>"
      +   "<label>Email (required but will not be published)</label>"
      +   "<input class='lcmtinput' id='cmt_email' name='c[e]' size='30' type='text'/>"
      +   "<label>Website</label>"
      +   "<input class='lcmtinput' id='cmt_ws' name='c[w]' size='30' type='text'/>"
      +   "<label>Comment (required)</label>"
      +   "<textarea class='lcmtinput' cols='60' id='cmt_cmt' name='c[c]' rows='8'></textarea>"
      +   "<a href='#' onclick=\"new Ajax.Request('" + this.Host + "/cmt/create.js?v=lr6&cb=Lun._newCmt',"
      +   "{method:'get', crossSite:true, parameters:Form.serialize('lcmtnewcmtform')}); return false;\">Add Comment</a>"
      + "</form>";
    this.NewCmtForm.innerHTML = html;
  },

  getParent: function(cmt) {
    var p = $('lcmt-'+cmt.parent_id);
    if (p == null)
      return this.Topic;
    else
      return p;
  },

  renderCmt: function(cmt) {
    var c;
    if (cmt.parent_id == null)
      c = this.appendDiv(this.Topic, 'lcmt', null);
    else
      c = this.appendDiv(this.getParent(cmt), 'lcmt', null);

    var s = "0px 0px 0px ";
    if (cmt.level == 0) 
      s += "0px";
    else
      s += "25px";

    c.setStyle({padding: s});
    c.id = 'lcmt-' + cmt.id;
    var u = this.appendDiv(c, 'lcmtuser', null);
    this.appendDiv(c, 'lcmtcmt', this.sanitize(cmt.content).gsub(/\n|\r/, '<br/>'));

    if (cmt.website == null || cmt.website.strip() == '')
      this.appendDiv(u, 'lcmtname', this.sanitize(cmt.created_by));
    else
      this.appendDiv(u, 'lcmtname', this.renderA(this.sanitize(cmt.website), '_blank', cmt.created_by));

    var date = this.parseDate(cmt.created_on);
    var at = this.appendDiv(u, 'lcmtat', date.toLocaleString());

    var html = "<a href='#' onclick=\"new Ajax.Request('"
      + this.Host + "/cmt/spam.js?v=lr5&cb=Lun._rm&u=" 
      + encodeURIComponent(this.Url) + "&id=" + cmt.id
      + "', {method:'get', crossSite:true}); return false;\">report as spam</a>";
    var spam = this.appendDiv(u, 'lcmtspam', html);
    spam.id = 'lcmts-' + cmt.id;

    if (!this.Closed) {
      html = "<a href='javascript:void(0);' onclick=\"Lun.moveNewCmtForm('" + cmt.id + "');\">Reply</a>";
      var r = this.appendDiv(c, 'lcmtreply', html);
      r.id = 'lcmtr-' + cmt.id;
    }

  },

  moveNewCmtForm: function(id) {
    if (id == null) {
      Element.insert(this.Footer, {bottom:this.NewCmtForm});
      $('lcmthead').innerHTML = 'Leave a comment';
      $('lcmtp').value = '';
    }
    else {
      Element.insert('lcmtr-'+id, {after:this.NewCmtForm});
      $('lcmthead').innerHTML = "<a href=\"javascript:Lun.moveNewCmtForm(null);\" title='click if you do not like to reply'>Cancel</a> Reply the above comment";
      $('lcmtp').value = id;
    }
  },

  _rm: function(r) {
    $('lcmts-' + r.id).innerHTML = '';
  },

  _newCmt: function(r) {
    this.trace('_newCmt');

    try {
      if (r == null) {
        this.trace('r is null');
        return;
      }

      this.traceErrs(r.errors);
      if (r.flash.error != null)
        this.Msg.innerHTML += r.flash.error;
      else {
        if (r.errors == null && r.created != null) {
          this.renderCmt(r.created.comment);
          this.moveNewCmtForm();
          Form.Element.clear('cmt_cmt');
        }
      }
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  callCmts: function() {
    try {
      if (this.Cmts == null)
        return;

      var p = this.Cmts.getAttribute('permalink');
      if (p != null && p != '') 
        this.Url = p.include('http') ? p : this.Url + '/' + p;

      this.setupPaginator();

      if (!this.Closed)
        this.renderNewCmtForm();

      this.callRpc(this.Host+'/cmt/show.js?v=lr1&s='+this.PageSize+'p=1&cb=Lun._cmts&u='+encodeURIComponent(this.Url));
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  _cmts: function(r) {
    try {
      this.trace('_cmts');
      if (r == null) {
        this.trace('r is null');
        return;
      }

      var cmts = r.comments;

      if (cmts != null && cmts.length > 0) {
        Element.select(this.Topic, 'div.lcmt').each(function(e) {e.remove();})
        for(i=0;i<cmts.length;i++) {
          this.renderCmt(cmts[i].comment);
        }

        if (r.pager.pages > 1)
          this.Pager.innerHTML = new this.Paginator(r.pager.page, 
              r.pager.size, r.pager.pages).toHTML();
      }
      else
        this.renderInfo();
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  callCount: function() {
    try {
      var cnts = $$('div.lcmtcnt');
      if (cnts == null || cnts.length == 0)
        return;

      var urls = '';
      for (var i=0; i<cnts.length; i++)
      {
        var p = cnts[i].getAttribute('permalink');
        if (p != null && p != '') {
          if (i < cnts.length - 1)
            urls += encodeURIComponent(p + ',');
          else
            urls += encodeURIComponent(p);
        }
      }

      var url = this.Host + '/cmt/count.js?v=lr2&cb=Lun._cnt&u=';

      if (urls == '')
        urls = this.Url;

      this.callRpc(url+urls);
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  _cnt: function(r) {
    try {
      this.trace('_cnt');

      if (r == null) {
        this.trace('r is null');
        return;
      }

      var cnts = $$('div.lcmtcnt');
      if (r.flash.error != null) 
        cnts[0].innerHTML = "<div class='lcmterr'>" + r.flash.error + "</div>";
      else {
        var n;
        cnts.each(function(e) {
          var url = e.getAttribute('permalink');
          if (url == null || url == '')
            url = document.location.href;
          n = r.urls[url];
          if (n == null || n == 0)
            e.innerHTML = 'comments (0)';
          else
            e.innerHTML = 'comments (' + n + ')';
        });
      }
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  callTop: function() {
    try {
      var tp = $('lcmttop');
      if (tp == null)
        return;

      var n = tp.getAttribute('n');
      var url = document.location.href;
      var p = tp.getAttribute('permalink');
      if (p != null && p != '') 
        url = p.include('http') ? p : url + '/' + p;

      this.callRpc(this.Host+'/cmt/top.js?v=lr3&cb=Lun._top&n='+n+'&u='+encodeURIComponent(url)); 
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  _top: function(r) {
    try {
      this.trace('_top');

      if (r == null) {
        this.trace('lr is null');
        return;
      }

      var ts = r.topics;
      if (ts != null && ts.length > 0) {
        var tp = $('lcmttop');
        var html = '';
        for(i=0;i<ts.length;i++) {
          var t = ts[i].topic;
          html += this.renderA(t.url, null, t.title + ' - Comments (' + t.comment_count + ')') + '<br/>';
        }
        tp.innerHTML = html;
      }
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  callLatest: function() {
    try {
      var latest = $('lcmtlatest');
      if (latest == null)
        return;

      var n = latest.getAttribute('n');
      var url = document.location.href;
      var p = latest.getAttribute('permalink');
      if (p != null && p != '') 
        url = p.include('http') ? p : url + '/' + p;

      this.callRpc(this.Host+'/cmt/latest.js?v=lr4&cb=Lun._latest&n='+n+'&u='+encodeURIComponent(url));
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  _latest: function(r) {
    this.trace('_latest');

    if (r == null) {
      this.trace('r is null');
      return;
    }

    try {
      var latest = $('lcmtlatest');
      var cmts = r.comments;
      if (cmts != null && cmts.length > 0) {
        var html = '';
        for(var i=0;i<cmts.length;i++) {
          var c = cmts[i].comment;
          html += c.created_by + ': ' + c.content.truncate(50, '...') + '<br/>';
        }
        latest.innerHTML = html;
      }
    }
    catch(ex) {
      this.traceErr(ex.message);
    }
  },

  load: function() {
    Element.remove(pid);

    if (this.Transport == null)
      this.setupRpc();

    this.callCmts();
    this.callTop();
    this.callLatest();
    this.callCount();
  },

  wait4Prototype: function() {  
    if (this.Timer != null) {  
      if (this.Timeout == 0) {  
        this.trace("timeout");
        this.load();
        return;  
      }  
      var to = typeof(Prototype);
      if (to == 'defined' || to == 'object' || to == 'function') {  
        this.trace("Prototype = " + to + ", timeout until " + this.Timeout);
        this.load();
        return;  
      }  
      this.Timeout -= 1;  
      this.trace("Prototype = " + to + ", timeout until " + this.Timeout);
    }  
    this.Timer = setTimeout("Lun.wait4Prototype()", 100);  
    return;  
  },

  callRpc: function(url, cb) {
    new Ajax.Request(url, {
      method: 'get',
      crossSite: true,
      onComplete: function(req) { 
        if (typeof(cb) == 'function')
          cb();
        Element.remove(req.transport.id);
      }
    });
  },

  setupPaginator: function() {
    this.Paginator = Class.create({
      initialize: function(page, size, pages) {
        this.current = page;
        this.size = size;
        this.pages = pages;
      },

      out_of_bounds: function() {
        return this.current > this.pages;
      },

      offset: function() {
        return (this.current - 1)*this.size;
      },

      prev: function() {
        return this.current > 1 ? (this.current-1) : null;
      },

      next: function() {
        return this.current < this.pages ? (this.current + 1) : null;
      },

      link_or_span: function(page, css, text) {
        if (text == null)
          text = page;

        var s;
        if (page != null && page != this.current)
          s = "<a href='#' class='" + css + "' onclick=\"new Ajax.Request('" 
            + Lun.Host + "/cmt/show.js?v=lr1&cb=Lun._cmts&p=" + page 
            + "&s=" + this.size + "&u=" + encodeURIComponent(Lun.Url) + "', "
            + "{method:'get', crossSite:true}); return false;\">" + text + "</a>";
        else
          s = "<span class='" + css + "'>" + text + "</span>";
        
        return s;
      },

      toHTML: function() {
        var links = [];
        var inner_window = 4, outer_window = 1;
        var window_from = this.current - inner_window;
        var window_to = this.current + inner_window;
        if (window_to > this.pages) {
          window_from -= window_to - this.pages;
          window_to = this.pages;
        }

        if (window_from < 1) {
          window_to += 1 - window_from;
          window_from = 1;
          window_to = this.pages;
          if (window_to > this.pages)
            window_to = this.pages;
        }

        var visibles = $R(1, this.pages).toArray();
        var left_gap = $R(2+outer_window, window_from);
        var right_gap = $R(window_to+1, this.pages-outer_window);
        if (left_gap.end - left_gap.start > 1) {
          var left = left_gap.toArray();
          for(var i=0;i<left.length;i++) 
            visibles = visibles.without(left[i]);
        }
        if (right_gap.end - right_gap.start > 1) {
          var right = right_gap.toArray();
          for(var i=0;i<right.length;i++) 
            visibles = visibles.without(right[i]);
        }
        
        var prev = null;
        for(var i=0;i<visibles.length;i++) {
          var n = visibles[i];
          if (prev != null && n > prev + 1)
            links.push("<span class='gap'>&hellip;</span>");
          links.push(this.link_or_span(n, 'current', null));
          prev = n;
        }

        links.unshift(this.link_or_span(this.prev(), 'disabled prev', '&laquo; Prev'));
        links.push(this.link_or_span(this.next(), 'disabled next', 'Next &raquo;'));
        return links.join(' ');
      }
    });
  },

  setupRpc: function() {
    /**
    * Written by Thierry Schellenbach
    * http:www.mellowmorning.com/2007/10/25/introducing-a-cross-site-ajax-plugin-for-prototype/
    * Version 0.8.0 for Prototype 1.5.0
    * Developed for www.commenthub.com
    * 
    * modeled after XmlHttpRequest http: * en.wikipedia.org/wiki/XMLHttpRequest
    * functions open, send (setRequestHeader) - variable readyState, status
    * 
    *   0 = uninitialized - open() has not yet been called.
    *   1 = open - send() has not yet been called.
    *   2 = sent - send() has been called, headers and status are available.
    *   3 = receiving - Downloading, responseText holds partial data.
    *   4 = loaded - Finished.
    * 
    * for which prototype does this:
    *   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']
    * unfortunately the onreadystatechange only works for the last 3, because 
    * prototype 1.5.0 assigns it too late, for our usage and prevents status 1
    * Prototype uses a timer, which in tests lead onSuccess to occur before onLoading
    * We use respondToReadyState to make a direct instruction and bypass the filter
    * 
    * TODO:
    * Removal of <script> nodes?
    */
    
    /**
    * Modified to support Prototype 1.6.0.2+
    * By John-David Dalton
    */
    this.Transport = Class.create({
        initialize: function() {
          this.readyState = 0;
          this.id = 0;
      },

      open: function(method, url, asynchronous) {
        if (method != 'GET')
          alert('Method should be set to GET when using cross site ajax');

        this.readyState = 1;
        /* little hack to get around the late assignment of onreadystatechange */
        this.respondToReadyState(1);
        this.onreadystatechange();
        this.url = url;
      },

      send: function() {
        this.readyState = 2;
        this.onreadystatechange();
        this.getScriptXS(this.url);
      },

      callback: function() {
        try{
          this.status = _xsajax$transport_status;
        } catch(e) {
          // to prevent people from writing code,
          // which is not cross browser compatible
          return;
        }
        this.readyState = 4;
        this.onreadystatechange();
        _xsajax$transport_status = null;
      },

      getScriptXS: function() {
        // determine arguments and generate <script> node
        var arg = { url: arguments[0] };
        (this.node = document.createElement('SCRIPT')).type = 'text/javascript';
        this.id = Lun.getId();
        this.node.id = this.id;
        this.node.src = arg.url;
        // FF and Opera properly support onload. MSIE has its own implementation.
        // Safari and Konqueror need some polling
        if (Prototype.Browser.IE) {
          // MSIE doesn't support the "onload" event on
          // <script> nodes, but it at least supports an
          // "onreadystatechange" event instead. But notice:
          // according to the MSDN documentation we would have
          // to look for the state "complete", but in practice
          // for <script> the state transitions from "loading"
          // to "loaded". So, we check for both here...
          var self = this;
          this.node.onreadystatechange =  function() {
            if (this.readyState === "complete" || this.readyState === "loaded")
              return self.callback.call(self);
          };
        }
        else if (/AppleWebKit\/|Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
          // Safari/WebKit and Konqueror/KHTML do not emit
          // _any_ events at all, so we need to use some primitive polling
          this.timepassed = 0;
          this.checkTimer = setInterval(function() {
            this.timepassed = this.timepassed + 100;
            if (_xsajax$transport_status != null) {
              this.callback();
              clearInterval(this.checkTimer);
            }
            if (this.timepassed > 10000)
              clearInterval(this.checkTimer);
          }.bind(this), 100);
        }
        else {
          // Firefox, Opera and other reasonable browsers can
          // use the regular "onload" event...
          this.node.onload = this.callback.bind(this);
        }

        // inject <script> node into <head> of document
        this.readyState = 3;
        this.onreadystatechange();
        var head = document.getElementsByTagName('HEAD')[0];
        head.appendChild(this.node);
      },

      setRequestHeader: function() {},
      onreadystatechange: function() {}
    });

    Ajax.Request.addMethods ({
      initialize: function($super, url, options) {
        $super(options);
        if (!this.options.crossSite)
          this.transport = Ajax.getTransport();
        else {
          this.transport = new Lun.Transport;
          this.options.asynchronous = false;
          this.transport.respondToReadyState = this.respondToReadyState.bind(this);
        }
        this.request(url);
      }
    });
  }
}

Lun.init();
Lun.addCss('l.css');
var pid = Lun.addScript('http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js');
Lun.wait4Prototype();

