const filters = [
  {
    name: "Plain text link",
    regexp: /https?\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!]/g,
    href: function(match) { return match[0]; }
  },
  {
    name: "Email address",
    regexp: /[a-z0-9_\-+=.]+@[a-z0-9\-]+(\.[a-z0-9-]+)+/ig,
    href: function(match) { return "mailto:" + match[0]; }
  },
  {
    name: "Bug number with comment number",
    regexp: /bug \#?(\d+) comment \#?(\d+)/ig,
    href: function(match) { return "https://bugzilla.mozilla.org/show_bug.cgi?id=" + match[1] + "#c" + match[2]; }
  },
  {
    name: "Bug number",
    regexp: /bug \#?(\d+)/ig,
    href: function(match) { return "https://bugzilla.mozilla.org/show_bug.cgi?id=" + match[1]; }
  },
  {
    name: "ISBN --> Amazon",
    regexp: /ISBN( number)?:? \#?((\d(-)?){9}[\dx])/ig,
    href: function(match) { return "http://www.amazon.com/exec/obidos/ASIN/" + alphanumerics(match[2]); }
  },
  {
    name: "US phone number --> Skype",
    regexp: /((\(\d{3}\)[\s-]?)|(\d{3}(\s|-)))\d{3}[\s-]\d{4}/g,
    href: function(match) { return "callto:+1 " + digits(match[0]); }
  },
  {
    name: "Twitter link",
	regexp: /\@([a-z0-9\_]+)(?![-a-z0-9\_])/ig,
    href: function(match) { return "http://www.twitter.com/" + match[1]; }
  }


];

function digits(s)
{
  return s.replace(/[^0-9]/g, "");
}

function alphanumerics(s)
{
  return s.replace(/[^0-9a-z]/ig, "");
}

function styleLink(a, filter)
{
  a.style.color = "#a6cad3";
}

function fixFilters()
{
  var i, r;
  for (i = 0; r = filters[i]; ++i) {
    r.classNamePart = r.name.toLowerCase().replace(/[^0-9a-z]+/ig, "-");
    if(!r.regexp.global)
      alert("AutoLink filter " + r.name + " is not global! This will break stuff!");
  }
}
fixFilters();

var moddingDOM = false;

window.addEventListener("load", init, false);
function init()
{
  document.addEventListener("DOMNodeInserted", nodeInserted, false);
  setTimeout(go, 50, document.body);
}

function nodeInserted(e)
{

  if (!moddingDOM)
    go(e.target);
}

const skippedElements = { 
  a:        true,
  noscript: true,
  head:     true,
  script:   true,
  style:    true,
  textarea: true,
  label:    true,
  select:   true,
  button:   true
}

const gmail = (location.host == "gmail.google.com");

function skipChildren(node)
{
  if (node.tagName)
  {
    if (skippedElements[node.tagName.toLowerCase()]) {
      return true;
    }
    
    if (gmail) {
      if (node.className == "ac")
        return true;
      if (node.className == "ilc sxs")
        return true;
    }
  }

  return false;
}


function go(traversalRoot)
{
  var m;

  for (m = traversalRoot; m != undefined; m = m.parentNode) {
    if (skipChildren(m)) {
      return;
    }
  }

  traversalRoot.normalize();

  function cont(n, didChildren)
  {
    var k = 0;
    var q;
    
    while (n && k < 100)
    {
      ++k;
	  
      if (!didChildren && n.nodeType == 3) {
        if((q = runFiltersOnTextNode(n))) {
          n = q[0];
         
          if (q[1]) 
            continue;
        }
      }
  

      if (!n.firstChild)
        didChildren = true;
  
      if (didChildren && n == traversalRoot)
        break;
      else if (!didChildren && n.firstChild && !skipChildren(n)) {
        n = n.firstChild;
      }
      else {
        if (n.nextSibling) {
          n = n.nextSibling;
          didChildren = false;
        }
        else {
          n = n.parentNode;
          didChildren = true;
        }
      }
    }
  
    if (!n) {
    }
    else if (n == traversalRoot) {

    }
    else {

      setTimeout(cont, 10, n, didChildren);
    }
    
  }
  
  cont(traversalRoot, false);
}

function runFiltersOnTextNode(node)
{
  var source, j, regexp, match, lastLastIndex, k, filter, href, anyChanges;
  var used, unused, firstUnused, lastUnused, a, parent, nextSibling;
  
  source = node.data;
  
  anyChanges = false;

  k=0;
  
  for (j = 0; filter = filters[j]; ++j) {
    regexp = filter.regexp;
    
    if (regexp.test(source)) {

      parent = node.parentNode;
      nextSibling = node.nextSibling;

      
      regexp.lastIndex = 0;
      firstUnused = null;

      for (match = null, lastLastIndex = 0; k < 40 && (match = regexp.exec(source)); ) {
      
        href = genLink(filter, match); 
        
        if (href != null && href != location.href) { 
          ++k;

          unused = document.createTextNode(source.substring(lastLastIndex, match.index));
          if (!anyChanges) {
            anyChanges = true;
            parent.removeChild(node);
            firstUnused = unused;
            moddingDOM = true;
          }
          parent.insertBefore(unused, nextSibling);

          used = document.createTextNode(match[0])
  
          a = document.createElement("a");
          a.href = href;
          a.target = "_blank";
          a.className = "autolink autolink-" + filter.classNamePart;
  
          styleLink(a, filter);
  
          a.appendChild(used);
          parent.insertBefore(a, nextSibling);
          
          lastLastIndex = regexp.lastIndex;
        }

      }

      if (anyChanges) {
        lastUnused = document.createTextNode(source.substring(lastLastIndex));
        parent.insertBefore(lastUnused, nextSibling);
        moddingDOM = false;
        return [firstUnused, true]
      }
      
      return [node, false];
    }
  }
  return null;
}

function genLink(filter, match)
{
  try {
    return filter.href(match); 
  }
  catch(er) {
    return "data:text/plain,Error running AutoLink function for filter: " + encodeURIComponent(filter.name) + "%0A%0A" + encodeURIComponent(er);
  }
}
