// ==UserScript==
// @name          Tability
// @namespace     http://jeffpalm.com/tability
// @description    Tabiliterize your site
// @include       http://*
// ==/UserScript==

/*
 * Copyright 2006 Jeffrey Palm.
 */


var VERSION = "0.2";
var DEBUG = false;
var BASE = 'http://jeffpalm.com/tability/';

function createButton(target) {

  function addOption(select,name) {
    var option = $n('option');
    select.appendChild(option);
    option.value = name;
    option.appendChild($t(name));
  }

  var button = $n('span');

  var select = $n('select');
  addStyle(select);
  button.appendChild(select);

  addOption(select, 'Line');
  addOption(select, 'Bar');

  var input = $n('input');
  addStyle(input);
  button.appendChild($t(" "));
  button.appendChild(input);
  input.type = 'submit';
  input.value = 'View graph';

  var onClick = function(event) {
    try {
      createEditable(select,target);
    } catch (e) {
      alert(e);
    }
    return false;
  };

  input.addEventListener('click',onClick,true);

  return button;
}

function createEditable(select,table) {

  // Get the type of graph
  var type = select[select.selectedIndex].value;

  // Grab all the rows
  var trs = table.getElementsByTagName("tr");
  if (!trs || trs.length==0) return;

  // - get the length of the first row
  var len = trs[0].getElementsByTagName("td").length;

  // - determine which columns to use based on the
  //   the first row's check boxes
  // - create an array of rows to use
  var firstTR = trs[0];
  var firstTDs = firstTR.getElementsByTagName("td");
  var colsToUse = new Array();
  for (var i=0; i<len; i++) {
    var useThisCol = firstTDs[i].firstChild.checked;
    if (useThisCol) colsToUse[i] = true;
  }
  //
  // Create the url and open up a new window
  //
  var url = createTable(table,colsToUse,type);
  var windowOptions = "width=510,height=510,resizable=1,scrollbars=1,location=" + (DEBUG ? "1" : "0");
  window.open(url, "tability", windowOptions);
}

// --------------------------------------------------
// API
// --------------------------------------------------

/**
 * DOM[table] (int -> boolean) String -> String[url]
 */
function createTable(table,colsToUse,type) {
  //
  // Ensure we have a type
  //
  if (!type) type = "Line";
  // 
  // Grab all the rows
  //
  var trs = table.getElementsByTagName("tr");
  var rows = new Array();
  if (!trs || trs.length==0) return;
  // Skip the first row, we've put that in
  // Skip the second row, because that should be titles
  for (var i=2; i<trs.length; i++) {
    var tr = trs[i];
    var tds = tr.getElementsByTagName("td");
    if (tds && tds.length>0) {
      var row = new Array();
      for (var j=0; j<tds.length; j++) {
        var val = tds[j].innerHTML;
        row.push(val);
      }
      rows.push(row);
    }
  }
  //
  // Get the length of the first row
  //
  if (rows.length==0) return;
  var row = rows[0];
  var len = row.length;
  //
  // Create the columns and the request skip the first col
  //
  var url = BASE + VERSION + "/graph.php?type=" + type;
  //
  // First check if we're using labels
  //
  if (colsToUse[0]) {
    url += "&labels=";
    for (var j=0; j<rows.length; j++) {
      var row = rows[j];
      var val = row[0];
      // Just replace ,s because we use those as meta-characters
      var newVal = val.replace(/,/g,'');
      url += newVal;
      if (j<rows.length-1) url += ",";
    }
  }
  //
  // Now fill with the data
  //
  url += "&data=";
  for (var i=1; i<len; i++) {
    //
    // check whether we want to use this column
    //
    var useThisCol = colsToUse[i];
    if (!useThisCol) continue;
    url += "|";
    for (var j=0; j<rows.length; j++) {
      var row = rows[j];
      var val = row[i];
      // remove non numeric things
      var newVal = val.replace(/[^\d]/g,'');
      url += newVal;
      if (j<rows.length-1) url += ",";
    }
  }
  return url;
}

// --------------------------------------------------
// Misc
// --------------------------------------------------

function addStyle(el) {
  el.style.border = "rgb(102,102,102) 1px solid";
  el.style.font = "11px/1.6em 'Lucida Grande', LucidaGrande, Lucida, Helvetica, Arial, sans-serif";
  el.style.color =  "#333";
}

function text(node) {
  return node && node.firstChild ? node.firstChild.nodeValue : '';
}

function getElementByTagName(doc,name) {
  var nodes = doc.getElementsByTagName(name);
  if (!nodes || nodes.length == 0) return 0;
  return nodes[0];
}

function $n(tag,on) {
  var e = document.createElement(tag);
  if (on) on.appendChild(e);
  return e;
}

function $t(text,on) {
  var e = document.createTextNode(text);
  if (on) on.appendChild(e);
  return e;
}

function insertBefore(newNode,target) {
  // http://lists.xml.org/archives/xml-dev/200201/msg00873.html
  var parent   = target.parentNode;
  var refChild = target; //target.nextSibling;  
  if(refChild) parent.insertBefore(newNode, refChild);
  else parent.appendChild(newNode);  
}

function insertAfter(newNode,target) {
  // http://lists.xml.org/archives/xml-dev/200201/msg00873.html
  var parent   = target.parentNode;
  var refChild = target.nextSibling;
  if(refChild) parent.insertBefore(newNode, refChild);
  else parent.appendChild(newNode);
}

/*
 * Insert a row of check boxes into the first row of the table
 */
function insertCheckBoxes(table) {
  //
  //  determine the number of rows based on the last row
  //
  var trs = table.getElementsByTagName("tr");
  if (!trs || trs.length==0) return;
  var tr = trs[trs.length-1];
  var tds = tr.getElementsByTagName("td");
  if (!tds || tds.length==0) return;
  var numCols = tds.length;
  //
  // Inser the new row
  //
  var newTR = $n("tr");
  var first = trs[0];
  insertBefore(newTR,first);
  for (var i=0; i<numCols; i++) {
    var td = $n("td");
    td.style.textAlign = "center";
    newTR.appendChild(td);
    var checkBox = $n("input");
    td.appendChild(checkBox);
    checkBox.type = "checkbox";
    checkBox.checked = true;
    addStyle(checkBox);
  }
}

function removeCheckBoxes(table) {
  var trs = table.getElementsByTagName("tr");
  if (!trs || trs.length==0) return;
  var tr = trs[0];
  tr.parentNode.removeChild(tr);
}

// We'll remember the links so we can hide them
// also need to remember to tables
var links = new Array();
var tables = new Array();
function hideLinks() {
  for (var i=0; i<links.length; i++) {
    var button = links[i];
    button.parentNode.removeChild(button);
  }
  for (var i=0; i<tables.length; i++) {
    var table = tables[i];
    removeCheckBoxes(table);
  }
  links = new Array();
  tables = new Array();
}
function showLinks() {
  var ts = document.getElementsByTagName('table');
  if (!ts || !ts.length) return;
  for (var i=0; i<ts.length; i++) {
    //
    // Create the button
    //
    var table = ts[i];
    var b = createButton(table);
    insertBefore(b,table);
    //
    // Insert the check boxes in the table
    //
    insertCheckBoxes(table);
    //
    // Now save these for later
    //
    links.push(b);
    tables.push(table);
  }
}

function main() {
  //
  // First check whether there are any tables, only then
  // will we put the tag at the top
  //
  try {
    var tables = document.getElementsByTagName("table");
    if (!tables || tables.length<=0) return;
  } catch (e) {return;}
  toggle();
}

var showing = false;
function toggle() {
  if (showing) {
    toggleOff();
    showing = false;
  } else {
    toggleOn();
    showing = true;
  }
}

var toggleNode;
function insertToggleNode(name,f) {
  //
  // Create the new node
  //
  var span = $n("span");
  
  span.appendChild($t("["));
  
  var a = $n("a");
  span.appendChild(a);
  a.href = "#";
  a.addEventListener('click',f,true);
  a.appendChild($t(name));
  
  span.appendChild($t("]"));
  //
  // If we haven't inserted the toggle node in, insert it
  // otherwise remove it's
  //
  if (!toggleNode) {
    toggleNode  = $n("span");
    var bodys = document.getElementsByTagName("body");
    var body = bodys[0];
    var firstChild = body.firstChild;
    body.insertBefore(toggleNode,firstChild);    
  } else {
    toggleNode.removeChild(toggleNode.firstChild);
  }
  //
  // Now, add the good stuff
  //
  toggleNode.appendChild(span);
}

function toggleOff() {
  insertToggleNode("Untabiliterize", function (e) {hideLinks(); toggleOn();});
}

function toggleOn() {
  insertToggleNode("Tabiliterize", function (e) {showLinks(); toggleOff();});
}

main();


