agentOfChaos / inflatable switchblade

// ==UserScript==
// @name        inflatable switchblade
// @namespace   agentOfChaos
// @description Adds a button linking directly to the current strip's reactions page on sluggy.net
// @include     http://www.sluggy.com/*
// @version     1.1
// @grant       GM_xmlhttpRequest
// @author agentOfChaos
// ==/UserScript==
/*
 * Author: agentOfChaos
 * All the code is GPLv3
 * enjoy!
 */
var $ = unsafeWindow.jQuery;
var basePage;
var updatepage="http://feeds.feedburner.com/sluggy_freelance?format=xml";
var homepage="http://www.sluggy.com/";
var update_timeout = 8000;
var etag,lastModified;
var starting_hour;
var base="http://www.sluggy.net/forum/";
var months=["January","February","March","April","May","June","July","August","September","October","November","December"];
var baseURL="http://www.sluggy.net/forum/viewforum.php?f=4&start=";
var interruptSearch=false;
var target="Daily";
var numreq;

//// begin of reaction link locator code
function deltaG( date2, date1 )
{
  //Get 1 day in milliseconds
  var one_day=1000*60*60*24;
  // Convert both dates to milliseconds
  var date1_ms = date1.getTime();
  var date2_ms = date2.getTime();
  // Calculate the difference in milliseconds
  var difference_ms = date2_ms - date1_ms;
  // Convert back to days and return
  return Math.round(difference_ms/one_day); 
}
function arrot(val, notch)
{
  var divis=Math.floor(val/notch);
  return divis*notch;
}
function dateFromSlugURL(url)
{
  var sect=url.split("/");
  var len=sect.length;
  var offset=0;
  var datestr=sect[len-1];
  var ys,ms,ds,year,day,month;
  var date;
  if (len<=6) return new Date(); // homepage: set date as "today"
  if (datestr.length>6)offset=2; // year is 4-digit: read only last 2
  ys=datestr.substr(0+offset,2);
  ms=datestr.substr(2+offset,2);
  ds=datestr.substr(4+offset,2);
  if (ys>=97) // sluggy won't run ~100 years, or will it?
  {
    year="19" + ys;
  }
  else year="20" + ys;
  month=ms-1;
  day=ds;
  date=new Date(year,month,day);
  return date;
}
function getBBpageId(today, stripdate)
{
    var delta=deltaG(new Date(2014,8,31),new Date(2004,12,15));
    var adjust = 2940 / delta;
    var dist = deltaG(today,stripdate);
    console.log("delta: "+delta + " ; approx. location: "+arrot(dist * adjust,20));
  return arrot(dist * adjust,20);
}
function spiral(num) // maps number set N to Z
{
    if (num%2===0) return num/2;
    else return -((num+1)/2);
}
function find_string(mbody,tgt)
{
  if (mbody.match(tgt)) return true;
  else return false;
}
function run_search(cont)
{
  var num=spiral(cont);
  var loc=basePage+(num*20);
  var url=baseURL + loc;
  // request page via XMLHTTP
  if (loc < 0) return;
  console.log("Beginning request "+url);
  GM_xmlhttpRequest({
  method: "GET",
  url: url,
  headers: {
    "User-Agent": "Mozilla/5.0",    // If not specified, navigator.userAgent will be used.
    "Accept": "text/html"            // If not specified, browser defaults will be used.
    },
  onload: function(response) {
    var code=response.responseText;
      if (find_string(code,target))
      {
	        interruptSearch=true;
          console.log("found: " + code.substr(0,100) + " ...");
          extract_link(code);
      }
    }
  });
  numreq++;
}
function extract_link(code)
{
    console.log("extracting link...");
    var box=document.createElement("div"); // create a dummy html element for processing
    var items;
    var dest;
    box.innerHTML=code;
    items=box.getElementsByTagName("a");
    console.log("elements: "+items.length);
    for (var cont=0; cont<items.length; cont++) // scan every <a> element for target
    {
        if (find_string(items[cont].innerHTML,target))
        {
            console.log("final url: "+items[cont].href);
            dest=items[cont].href.split("/");
            UI_buttonGo(base+dest[dest.length-1]);
            break;
        }
    }
    console.log("done!");
    
}
function loop_search(radius)
{
    var stop=(2*radius)+1;
    var pos;
    numreq=0;
    for (var cont=0; cont<stop && !interruptSearch; cont++)
    {
       console.log("searching ..." + cont);
       run_search(cont);
    }
}
//// end of reaction link locator code
//// begin of ref link crawler code
function fetch_reflink(popup)
{
    var ret = new Array(500);
    if (popup.length > 0)
        {
        var links = popup.find("a");
        var cont = 0;
        for (var c=0; c<links.length; c++)
            {
            urlo = $(links[c]).attr("href");
            if (urlo.charAt(0) == "/")
                {
                ret[cont] = links[c];
                cont++;
                }
            }
        }
    else
        {
        console.log("no refs to fetch");
        }
    return ret;
}
function visitLink(link)
{
    var url = $(link).attr("href");
    GM_xmlhttpRequest({
     method: "GET",
     url: url,
     headers: {
       "User-Agent": "Mozilla/5.0",    // If not specified, navigator.userAgent will be used.
       "Accept": "text/html"            // If not specified, browser defaults will be used.
       },
     onload: function(data){
        updateLink($(link),data.responseText);
     }
    });
}
function updateLink(link, code)
{
    var deposit = document.createElement("div"); // create a dummy html element for processing
    deposit.innerHTML = code;
    var imgs_code = $($(deposit).find(".comic_content")[0]).html();
    var img_url = $($($(deposit).find(".comic_content")[0]).find("img")[0]).attr("src");
    $(link).removeAttr("href");
    $(link).attr("id",img_url.split("/").pop().split(".")[0]);
    $(link).css("text-decoration","underline");
    $(link).css("color","blue");
    document.getElementById(img_url.split("/").pop().split(".")[0]).addEventListener('click',
       function(){showRef(imgs_code, img_url, $(link));},
       true);
    $(link).attr("target","self");
}
function refPreloader(popup)
{
    links = fetch_reflink(popup);
    for (var cont=0; cont<links.length; cont++)
        {
        console.log("exploring ref " + $(links[cont]).attr("href"));
        visitLink(links[cont]);
        }
}
//// end of ref link crawler
//// update checker
function engage_updater()
{
    GM_xmlhttpRequest({
     method: "HEAD",
     url: updatepage,
     onload: function(data){
         headers = data.responseHeaders.split("\n");
         storeHeadersNgo(headers);
     }
    });
}
function storeHeadersNgo(headers)
{
    var s_et = "Etag: ";
    var s_lm = "Last-Modified: ";
    var s_tm = "Date: ";
    var timestr,serverdate,localdate,s_hour,s_minute,deltam;
    for (var n in headers)
         {
             h = headers[n];
             if (h.indexOf(s_et) === 0) {etag = h.substr(s_et.length);}
             else if (h.indexOf(s_lm) === 0) {lastModified = h.substr(s_lm.length);}
             else if (h.indexOf(s_tm) === 0) {timestr = h.substr(s_tm.length);}
         }
    serverdate = new Date(timestr);
    localdate = new Date();
    s_hour = serverdate.getUTCHours();
    s_minute = serverdate.getUTCMinutes();
    console.log("server reports date: " + timestr);
    deltam = ((24-s_hour)*60) + (60 - s_minute) - 1;
    if (s_hour <= 2)
       {
       console.log("starting update check now!");
       window.setTimeout(function(){
           starting_hour = localdate.getHour();
           check_updates();
       },update_timeout);
       }
    else
        {
        console.log("starting update check in " + deltam + " minutes");
        window.setTimeout(function(){
            starting_hour = localdate.getHour();
            check_updates();
        },deltam*60000);
        }
}
function compareHeaders(headers)
{
    var s_et = "Etag: ";
    var s_lm = "Last-Modified: ";
    var t_etag;
    var t_lastModified;
    for (var n in headers)
         {
             h = headers[n];
             if (h.indexOf(s_et) === 0) {t_etag = h.substr(s_et.length);}
             else if (h.indexOf(s_lm) === 0) {t_lastModified = h.substr(s_lm.length);}
         }
    if ((lastModified == t_lastModified) && (etag==t_etag)) return true;
    return false;
}
function check_updates()
{
    var localdate = new Date();
    GM_xmlhttpRequest({
     method: "HEAD",
     url: updatepage,
     onload: function(data){
         code = data.status;
         cmp = compareHeaders(data.responseHeaders.split("\n"));
         if (cmp === false) location.reload();
         else
              {
                  if (localdate.getHour() - starting_hour < 3)
                     window.setTimeout(function(){check_updates()},update_timeout);
              }
         }
    });
}
//// end of update checker
//// estetic functions
function UI_buttonWait()  // create dummy button while fetching the url
{
    $(".buttons.fg-buttonset").prepend("<a id=\"locutus\" class=\"fg-button fg-button-icon-left ui-corner-all ui-state-default\" href=\"#\"><span class=\"ui-icon ui-icon-help\"></span>Reactions</a>");
}
function UI_buttonGo(link)  // create real button now
{
    $("#locutus").remove();
    $(".buttons.fg-buttonset").prepend("<a target=\"_blank\" id=\"locutus\" class=\"fg-button fg-button-icon-left ui-corner-all ui-state-default\" href=\""+link+"\"><span class=\"ui-icon ui-icon-comment\"></span>Reactions</a>");
}
function UI_buttonVote()  // add vote button
{
    $(".buttons.fg-buttonset").prepend("<a target=\"_blank\" class=\"fg-button fg-button-icon-left ui-corner-all ui-state-default\" href=\"http://topwebcomics.com/vote/14089/default.aspx\"><span class=\"ui-icon ui-icon-help\"></span>Vote!</a>");
}
function UI_navarea_grow() // expand the navbar to accomodate new buttons
{
    $("#comic_navigation").css("width","860px");
    $(".fg-buttonset").css("margin-left","50px");
}
//// end of estetic functions
function showRef(imgs_code, urlz, link) // manages opening / closing of "quick refs"
{
    var attr = $(link).attr("opened");
    var url = urlz.replace(/\./g,"").replace(/\//g,"")
    if (typeof attr !== 'undefined' && attr !== false)
    {
        var ou = $(link).attr("opened_url")
        if (ou == url)
            {
            $(link).removeAttr("opened")
            $(link).removeAttr("opened_url")
            $(link).css("color","blue")
            //$(".comic_popup").css("max-width",oldmwidth)
            $("#quickrefimage_" + url).remove()
            }
    }
    else 
    {
        $(link).attr("opened","true")
        $(link).attr("opened_url",url)
        $(link).css("color","red")
        //oldmwidth = $(".comic_popup").css("max-width")
        $(".comic_popup").css("max-width","1000px")
        $(link).parent().append("<div id=\"quickrefimage_" + url + "\">" + imgs_code + "</div>")
        refPreloader($($("#quickrefimage_" + url).find(".comic_popup")[0]))
    }
}
function main()
{
    UI_buttonWait();
    UI_buttonVote();
    UI_navarea_grow()
    var today=new Date();
    var url=location.href;
    var comicday=dateFromSlugURL(url);
    var pageID=getBBpageId(today,comicday);
    basePage=pageID;
    target=months[comicday.getMonth()] + " [^,]*\\b" + comicday.getDate() + "\\b[^,]*, " + comicday.getFullYear();
    console.log("target acquired: " + target);
    loop_search(10);
    refPreloader($(".comic_popup"));
    if (location.href == homepage) engage_updater();
}
main();