var rnews = {
  status      : null,
  feeds       : [], // [feedid, refreshReq, refreshResp, timedOut]
  refreshing  : 0,  // num actively refreshing
  maxParallel : 2,
  refTimeout  : 2000, // ms
  yelHiMap    : [ '#ffffff', '#fdffee', '#fbffdd', '#f9ffcc', '#f7ffbb', '#f5ffaa', '#f3ff99', '#f1ff88', '#efff77' ],  // W -> Y, fadergb.pl
  grnHiMap    : [ '#ffffff', '#f7fbf0', '#f0f7e1', '#e8f4d3', '#e1f0c5', '#daecb8', '#d3e9ab', '#cce59e', '#c6e292' ],
  widthMap    : [ '49%', '61%', '71%', '79%', '85%', '90%', '93%', '95.5%', '97%', '98%', '98.5%', '98.75%', '99%' ],
  doneFunc    : null
};

function rnewsInit (msg, async, max, timeout) {
  rnews.status = document.getElementById('status');
  rnews.doneFunc = function() { fade(rnews.status,msg,2); }
  if (!async) {
    rnews.doneFunc();
    rnews.doneFunc = null;
  } else {
    if (typeof(max) != 'undefined')
      rnews.maxParallel = max;
    if (typeof(timeout) != 'undefined')
      rnews.refTimeout = timeout;
    if (rnews.maxParallel <= 0)
      rnews.maxParallel = 1;
    refreshFeeds();
    if (rnews.maxParallel > 1)
      setTimeout(refreshFeeds, rnews.refTimeout / 4);
  }
}

function initFeed(fid) {
  rnews.feeds.push({ id:fid, req:false, resp:false });
}

// find feed, remove all dts & msgs, add loading dt, empty more dt, issue refresh
function update(id) {
  var feed = document.getElementById('feed'+id);
  var dl = feed.getElementsByTagName('dl')[0];
  var ee = dl.getElementsByTagName('dd');
  for (var i = ee.length-1; i >= 0; i--)  //live array
    dl.removeChild(ee[i]);
  ee = dl.getElementsByTagName('dt');
  for (var i = ee.length-1; i >= 0; i--)
    dl.removeChild(ee[i]);
  ee = getElementsByClass ('error',feed.parentNode,'P');
  for (var i = ee.length-1; i >= 0; i--)
    ee[i].parentNode.removeChild(ee[i]);
  ee = getElementsByClass ('warn',feed.parentNode,'P');
  for (var i = ee.length-1; i >= 0; i--)
    ee[i].parentNode.removeChild(ee[i]);

  var e = document.createElement('dt');
  e.setAttribute ('id', 'none'+id);
  setClass(e,'loading');
  e.innerHTML = '&mdash; Loading articles...';
  dl.appendChild(e);

  e = document.createElement('dt');
  e.setAttribute ('id', 'more'+id);
  setClass(e,'more');
  e.innerHTML = '';
  dl.appendChild(e);

  fade (rnews.status, 'Updating feed', 2);
  sendRequest('ajax.php?op=update&id='+id, handleResponse, 0);
}

function markfeed(id) {
  sendRequest('ajax.php?op=markfeed&id='+id, handleResponse, 0);
  var feed = document.getElementById('feed'+id);
  /* change class of items from new to seen */
  var dts = feed.getElementsByTagName("dt");
  for (var j = 0; j < dts.length; j++) {
    var links = dts[j].getElementsByTagName("a");
    for (var i = 0; i < links.length; i++) {
      if ((links[i].getAttribute("class") == "new") ||
         (links[i].getAttribute("className") == "new")) {
        setClass(links[i], 'seen');
      }
    }
    if ((dts[j].getAttribute("class") == "feedlink") ||
        (dts[j].getAttribute("className") == "feedlink")) {
      collapse(dts[j].getAttribute("id"));
    }
  }
  highlight (feed.getElementsByTagName('dl')[0], rnews.grnHiMap);
}

/* Mark the DT of the given id as read. */
function marklink(id) {
  var theDT = document.getElementById(id);
  for (var i = 0; i < theDT.childNodes.length; i++)
    setClass(theDT.childNodes[i], 'visited');
  return true;
}

function expand(fid, id) {
  var theAnchor = document.getElementById(id).firstChild;
  theAnchor.firstChild.nodeValue = "* ";
  sendRequest('ajax.php?op=expand&fid='+fid+'&id='+id, handleResponse, 0);
}

/* Remove the first sibling of the DT with the given id.  Change the DT's
 * first anchor text and link to expand.
 */
function collapse(id) {
  var theDT = document.getElementById(id);
  var theSibling = theDT.nextSibling;
  while (theSibling.nodeType == "3")
    theSibling = theSibling.nextSibling;
  if (theSibling.nodeName == 'DD')
    theDT.parentNode.removeChild(theSibling);  /* Gone! */
  var theAnchor = theDT.firstChild;
  /*theAnchor.setAttribute ('onclick', 'expand('+id+');return false;'); */
  theAnchor.onclick = function() { expand('-1',id); return false; } /* IE doesnt play nice */
  theAnchor.title = "expand";
  theAnchor.firstChild.nodeValue = "+ ";
}

function gowide(id) {
  var e = document.getElementById('feed'+id);
  var fp = e.parentNode.parentNode;
  var buts = getElementsByClass('wide',fp,'div');
  for (var i=buts.length-1; i>=0; i--)
    buts[i].parentNode.removeChild(buts[i]);  // kill gowide links

  var feeds = getElementsByClass('feed',fp,'div');
  for (i=0; i<feeds.length; i++) {
    var cf = document.createElement("div");
    setClass(cf,'clearfix');
    cf.appendChild(feeds[i]);
    stretch (feeds[i], 0, 'feedwide', rnews.widthMap);
    fp.parentNode.insertBefore(cf, fp);
  }

  fp.parentNode.removeChild(fp);
}

// call on page load, after all feeds have been queued
function refreshFeeds() {
  if (rnews.refreshing >= rnews.maxParallel) return;
  var i;
  for (i=0; i<rnews.feeds.length; i++) {
    var f = rnews.feeds[i];
    if (f.req == false) {
      f.req = true;
      rnews.refreshing++;
      return refresh(f);
    }
  }
  if (rnews.refreshing == 0 && rnews.doneFunc) {
    rnews.doneFunc();
    rnews.doneFunc = null;
  }
}

function refresh(feed) {
//  var d = new Date();
//  log(d.getTime()+' requesting '+feed.id+' ('+rnews.refreshing+')');
  sendRequest('ajax.php?op=refresh&id='+feed.id, handleResponse, 0);
  setTimeout (function() {
      if (feed.resp == false){ //ignore timeout if already refreshed
//log('timeout feed '+feed.id);
        refreshFeeds(); }
    }, rnews.refTimeout);
}

/* For op='expand', data='id|description HTML ...'.
 * Find the DT with given id, create a DD sibling containing the description.
 * Change the DT's first anchor text and link to collapse.
 */
function handleResponseExpand(response) {
  var i = response.indexOf('|',0);
  if (i != -1) {
    var id = response.substring(0,i);
    var theDD = document.createElement("dd");
    theDD.innerHTML = response.substring(i+1);
    /* Add the new node after the DT node */
    var theDT = document.getElementById(id);
    var theDD = theDT.parentNode.insertBefore(theDD, theDT.nextSibling);
    /* Change the DT's first anchor to '-' and change its link to collapse */
    var theAnchor = theDT.firstChild;
    theAnchor.onclick = function() { collapse(id); return false; }
    theAnchor.title = "hide";
    theAnchor.firstChild.nodeValue = "-- ";
  }
}

/* For op='more', data='feedid|morelink|linkid|title HTML|linkid|title HTML| ...'.
 * Append a new DT with each new link received.
 */
function handleResponseMore(response) {
  var i = response.indexOf('|',0);
  if (i == -1) return;

  var feedid = response.substring(0,i);
  var theDT = document.getElementById('none'+feedid);
  if (theDT) { theDT.parentNode.removeChild(theDT); }
  var moreDT = document.getElementById('more'+feedid);
  var theDL = moreDT.parentNode;
  theDL.removeChild(moreDT);

  var oldi = i;
  i = response.indexOf('|',i+1);
  if (i == -1) return;
  var morelink = response.substring(oldi+1,i);

  var wasNone = false;
  oldi = i;
  while ((i = response.indexOf('|',i+1)) != -1) {
    var id = response.substring(oldi+1,i);

    oldi = i;
    i = response.indexOf('|',i+1);
    if (i == -1)   //last link
      var link = response.substring(oldi+1);
    else
      var link = response.substring(oldi+1,i);

    var newDT = document.createElement("dt");
    newDT.setAttribute ('id', id);
    setClass(newDT,'feedlink');
    newDT.innerHTML = link;
    theDL.appendChild(newDT);

    if (id.substring(0,4) == 'none') wasNone = true;

    if (i == -1) break;
    oldi = i;
  }

  /* add the more link back at the end, if it exists */
  if (morelink) {
    moreDT.innerHTML = morelink;
    theDL.appendChild(moreDT);
  }

  return !wasNone;
}

function more(id, n) {
  sendRequest('ajax.php?op=more&id='+id+'&n='+n, handleResponse, 0);
}

function showFeedMsg (feedid, cls, html) {
  var fc = document.getElementById('feed'+feedid);
  var p = document.createElement('p');
  setClass(p,cls);
  p.innerHTML = html;
  fc.parentNode.insertBefore(p,fc);
}

// Data is 'feedid|err|infolink|<as for more>'.  We refresh the next feed and
// highlight the added articles (if any).
//
function handleResponseRefresh(response) {
  var i = response.indexOf('|',0); // end of id
  if (i == -1) return;
  var feedid = response.substring(0,i);

//log('got response for '+feedid);
  var j;
  for (j=0; j<rnews.feeds.length; j++) {
    var f = rnews.feeds[j];
    if (f.id == feedid) {
      f.resp = true;
      break;
    }
  }
  rnews.refreshing--;
  refreshFeeds();

  var oldi = i;
  var i = response.indexOf('|',i+1); // end of err
  if (i == -1) return;
  var errstr = response.substring(oldi+1,i);
  oldi = i;
  var i = response.indexOf('|',i+1); // end of warn
  if (i == -1) return;
  var warnstr = response.substring(oldi+1,i);
  oldi = i;
  i = response.indexOf('|',i+1); // end of infolink
  if (i == -1) return;
  var infolink = response.substring(oldi+1,i);

  if (errstr) showFeedMsg (feedid, 'error', errstr);
  if (warnstr) showFeedMsg (feedid, 'warn', warnstr);

  var info = document.getElementById('info'+feedid);
  if (info) info.innerHTML = infolink;

  if (handleResponseMore(response.substring(i+1))) {
    var f = document.getElementById('feed'+feedid);
    highlight (f.getElementsByTagName('dl')[0], rnews.yelHiMap, 500);
  }
}

/* Response is 'op|data'.  We switch on op and pass data to handlers.
 */
function handleResponse(req) {
  var response = req.responseText;
  var o = response.indexOf('|');
  if (o != -1) {
    var op = response.substring(0,o);
    var rest = response.substring(o+1);
    if (op == 'expand') {
      handleResponseExpand(rest);
    } else if (op == 'more') {
      handleResponseMore(rest);
    } else if (op == 'refresh') {
      handleResponseRefresh(rest);
    } else if (op == 'ack') {
      fade (rnews.status, rest, 1);
      return;
    }
  }
}

//--------------------------------------------------------------------------
// From quirksmode
//
function sendRequest(url,callback,postData) {
  var req = createXMLHTTPObject();
  if (!req) return;
  var method = (postData) ? "POST" : "GET";
  req.open(method,url,true);
  req.setRequestHeader('User-Agent','XMLHTTP/1.0');
  if (postData)
    req.setRequestHeader('Content-type','application/x-www-form-urlencoded');
  req.onreadystatechange = function () {
    if (req.readyState != 4) return;
    if (req.status != 200 && req.status != 304) {
//    alert('HTTP error ' + req.status);
      return;
    }
    callback(req);
  }
  if (req.readyState == 4) return;
  req.send(postData);
}

var XMLHttpFactories = [
  function () {return new ActiveXObject("Msxml2.XMLHTTP")},  //ie6,7
  function () {return new XMLHttpRequest()}, //ff
  function () {return new ActiveXObject("Msxml3.XMLHTTP")},
  function () {return new ActiveXObject("Microsoft.XMLHTTP")}
];

function createXMLHTTPObject() {
  var xmlhttp = false;
  for (var i=0;i<XMLHttpFactories.length;i++) {
    try { xmlhttp = XMLHttpFactories[i](); }
    catch (e) { continue; }
    break;
  }
  return xmlhttp;
}

