// ==UserScript==
// @name          Jureeka
// @namespace     http://www.jureeka.org
// @description   Turns legal citations in webpages into hyperlinks that direct you to online legal source material.
// ==/UserScript==

/*
  Warnings:

    * This triggers a memory leak bug in Firefox.
    * This triggers a dataloss bug in Firefox when editing long Wikipedia pages.

  Original author of AutoLink: Jesse Ruderman - http://www.squarefree.com/ - wrote the original script of Autolink.  

  Original author of Jureeka: Michael Poulshock.

  License: MPL, GPL, LGPL.

*/


const timeBefore = new Date();

/***********************************
 *             Filters             *
 ***********************************/

const theDomain = "www.jureeka.net/Jureeka";


/***********************************
 *             Filters             *
 ***********************************/

//href (function) - returns the URL to be used for a link, or |null| to cancel link creation.
//If multiple filters match a string, the first filter will win.


const filters = [
  {
    name: "U.S. and State Constitutions",
    regexp: /([A-Za-z\.]+) Const.,? (((a|A)rt\.|(a|A)mend\.|(p|P)mbl\.|(p|P)reamble) ([XVI]+))?(, ?(§|s|S|§|&#167) ?([0-9]+))?(, ?cl\. ?([0-9]+))?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=Constitutions&juris=" + match[1] + "&part=" + match[3] + "&art=" + match[8] + 

"&sec=" + match[11] + "&cl=" + match[13]; }
  },
  {
    name: "U.S. Supreme Court",
    //regexp: /([0-9]+) (US|U\.S\.|U\. S\.) ([0-9]+)(, ?([0-9]+)(\.|;|,|-))?/ig,
    regexp: /([0-9]|[1-9][0-9]|[1-5][0-9][0-9]) (US|U\.S\.|U\. S\.) ([0-9]+)(, ?([0-9]+)(\.|;|,|-))?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=U.S.&vol=" + match[1] + "&page=" + match[3] + "&pinpoint=" + match[5]; }
  },
  {
    name: "U.S. Code",
    regexp: /([0-9]+) U\.? ?S\.? ?C\.?( ?S\.?|A\.?)? (§|s|S|§|&#167|Sect?\.?|sect?\.?)* ?([0-9]+)(([a-z]+)(-([0-9])([a-z])?)?)?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=U.S.C.&vol=" + match[1] + "&sec=" + match[4] + "&sec2=" + match[6] + "&sec3=" 

+ match[8] + "&sec4=" + match[9] ; }
  },
  {
    name: "Code of Federal Regulations",
    regexp: /([0-9]+) (C\.?F\.?R\.?|Code of Federal Regulations) (§|s|S|Parts?)* ?([0-9aA]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=CFR&title=" + match[1] + "&part=" + match[4]; }
  },
  {
    name: "Treasury Regulations",
    regexp: /Treas\.? ?Reg\.? ?(§|s|S|§|&#167|Sec\.?|sec\.?|Parts?)* ?([0-9aA]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=CFR&title=26&part=" + match[2]; }
  },
  {
    name: "Fed. R. Civ. P.",
    regexp: /(F\.?R\.?C\.?P\.?|Fed\.? ?R\.? ?Civ\.? ?Pr?o?c?\.?|Federal Rules? of Civil Procedure) ?(Rule)? ?([0-9]+)?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=FRCP&rule=" + match[3]; }
  },
  {
    name: "Federal Register",
    regexp: /((6|7)[0-9]) (F\.?R\.?|Fed\. ?Reg\.) ([0-9,]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=FedRegister&vol=" + match[1] + "&page=" + match[4]; }
  },
  {
    name: "Federal Reporter, Second Series",
    //regexp: /(4[5-9][0-9]|[5-9][0-9][0-9]) ?F\.? ?2d\.? ?([0-9]+)/g,
    regexp: /(17[8-9]|1[8-9][0-9]|[2-9][0-9][0-9]) ?F\.? ?2d\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=F2d&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Federal Reporter, Third Series",
    //regexp: / ([1-9]|[1-7][0-9]|8[0-5]) ?F\.? ?3d\.? ?([0-9]+)/g,
    regexp: /([1-9]|[1-9][0-9]|[1-9][0-9][0-9]) F\.? ?3d\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=F3d&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Federal Reporter, First Series",
    regexp: /([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-7][0-9]|28[0-1]) F\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=F1d&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Fed. R. Evid.",
    regexp: /(FRE | fre |Fed\.? ?R\.? ?Evid\.?|Federal Rules? of Evidence)( ?[0-9]+)?/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=FRE&rule=" + match[2]; }
  },
  {
    name: "Fed. R. Crim. P.",
    regexp: /(FRCrP|Fed\.? ?R\.? ?Crim\.? ?Pr?o?c?\.?|Federal Rules? of Criminal Procedure) (([0-9]+)\.?([0-9])?)?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=FRCrimP&rule=" + match[3] + "&ruleDec=" + match[4]; }
  },
  {
    name: "Fed. R. App. P.",
    regexp: /(FRAP|Fed\.? ?R\.? ?App\.? ?Pr?o?c?\.?|Federal Rules? of Appellate Procedure) ([0-9]+(\.1)?)?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=FRAP&rule=" + match[2]; }
  },
  {
    name: "Uniform Commercial Code",
    regexp: /(UCC|U\.C\.C\.|Uniform Commercial Code) ?(§|§|§)* ?(([1-9A]+)-([0-9]+))/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=UCC&part=" + match[4] + "&prov=" + match[5]; }
  },
  {
    name: "Regional State Reporters (U.S. - 3d ser.)",
    //regexp: /([1-9]|[1-9][0-9]|[1-9][0-9][0-9]) (So\.( ?2d\.?)?|P\.?( ?2d\.?| ?3d\.?)?|A\.?( ?2d\.?)?|(N|S)\.?(W|E)\.?( ?3d\.?)?) ([0-9]+)/ig,
    regexp: /([1-9]|[1-9][0-9]|[1-9][0-9][0-9]) ((So\.?|P\.?|S\.?W\.?|S\.?E\.?|N\.?W\.?|N\.?E\.?|A\.?) ?3d\.?) ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&vol=" + match[1] + "&rptr=" + match[2] + "&page=" + match[4]; }
  },
  {
    name: "Regional State Reporters (U.S. - P)",
    regexp: /([0-9]|[1-9][0-9]|1[0-9][0-9]|20[0-8]) P\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=p&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - P2d)",
    regexp: /(9[3-9][0-9]) P\.? ?2d\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=p2d&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - SW)",
    regexp: /(9[2-9]|1[0-9][0-9]|2[0-3][0-9]|24[0-2]) S\.? ?W\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=sw&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - SW2d)",
    regexp: /(9[3-9][0-9]) S\.? ?W\.? ?2d ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=sw2d&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - NW)",
    regexp: /(3[2-9]|[4-9][0-9]|1[0-8][0-9]) N\.? ?W\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=nw&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - NW2d)",
    regexp: /(559|5[6-9][0-9]|[6-9][0-9][0-9]) N\.? ?W\.? ?2d ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=nw2d&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - So.)",
    regexp: /(3[8-9]|[4-8][0-9]|9[0-2]) So\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=so&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - So2d)",
    regexp: /(6[8-9][0-9]|7[0-9][0-9]) So\.? ?2d\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=p&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - SE)",
    regexp: /(2[1-9]|[3-7][0-9]|8[0-8]) S\.? ?E\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=se&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - SE2d)",
    regexp: /(48[1-9]|49[0-9]|[5-9][0-9][0-9]) S\.? ?E\.? ?2d ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=se2d&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - NE)",
    regexp: /(3[1-9]|[4-9][0-9]|1[0-2][0-9]|13[0-5]) N\.? ?E\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=ne&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Regional State Reporters (U.S. - NE2d)",
    regexp: /(67[5-9]|6[8-9][0-9]|[7-9][0-9][0-9]) N\.? ?E\.? ?2d ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=ne2d&vol=" + match[1] + "&page=" + match[2]; }
  },
  { 
    name: "Regional State Reporters (U.S. - A)",
    regexp: /(3[1-9]|[4-9][0-9]|10[0-9]|11[0-6]) A\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=a&vol=" + match[1] + "&page=" + match[2]; }
  },
  { 
    name: "Regional State Reporters (U.S. - A2d)",
    regexp: /(68[6-9]|69[0-9]|[7-9][0-9][0-9]) A\.? ?2d ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RegionalRptrs&rptr=a2d&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Code of Alabama",
    regexp: /(Alabama |Ala\.? ?)Code/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=AlabamaCode"; }
  },
  {
    name: "Alabama Appellate Courts",
    regexp: /[0-9]+ Ala\.?( ?Civ\.? ?App\.? ?)? [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=AlabamaCases"; }	
  },
  {
    name: "Alaska Statutes",
    regexp: /Alaska Stat(\.?|utes?)/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=AlaskaCode"; }
  },
  {
    name: "Alaska Appellate Courts",
    regexp: /([0-9]+) P\.?(2|3)d ([0-9]+)(, ?[-0-9 n\.]+)? \(Alaska ((Ct\.)? ?App\.?)? ?[0-9]+\)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=AlaskaCases&vol=" + match[1] + "&reporter=" + match[2] + "&page=" + match[3]; 
 }
  },
  {
    name: "Arizona Statutes",
    regexp: /Ariz(\.|ona) (Rev\.)? ?Stat(\.?|utes?)( Ann\.?)? (§|s|S|§|&#167|(s|S)ection)* ?([0-9]+)-([-0-9A\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=ArizonaStatutes&title=" + match[7] + "&sec=" + match[8]; ; }
  },
  {
    name: "Arizona Supreme Court",
    regexp: /[0-9]+ Ariz\.? [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=ArizonaCases"; }
  },
  {
    name: "Arkansas Code",
    regexp: /Ark(\.?|ansas) Code( Ann\.?)?/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=ArkansasCode"; }
  },
  {
    name: "Arkansas cases",
    regexp: /[0-9]+ (Ark\.?|Ark\.? ?App.) [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=ArkansasCases&ct=" + match[1]; }
  },
  {
    name: "California cases",
    regexp: /[0-9]+ Cal\.? ?(App\.?)? ?(2d|3d|4th)?\.? ?(Supp\.?)? [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=CAcases"; }
  },
  {
    name: "Colorado Statutes",
    regexp: /Colo(\.?|rado) (Rev\.?)? ?Stat(\.?|utes?)( Ann\.?)? (§|s|S|§|&#167|(s|S)ection)* ?([-0-9\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=ColoradoStatutes&prov=" + match[7]; }
  },
  {
    name: "Connecticut Statutes",
    regexp: /Conn(\.?|ecticut) ?Gen(\.?|eral) ?Stat(\.?|utes?)( Ann\.?)? (§|s|S|§|&#167|(s|S)ection)* ?([0-9]+(A|a)?)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=ConnStatutes&title=" + match[7]; }
  },
  {
    name: "Connecticut cases",
    regexp: /[0-9]+ Conn\.? (App\.?)? [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=ConnCases"; }
  },
  {
    name: "Delaware Code",
    regexp: /Del(\.?|aware) ?Code ?( Ann\.?)? (t|T)it\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=DelawareCode&title=" + match[4]; }
  },
  {
    name: "Delaware cases",
    regexp: /Del\.? (Ch\.?)? 2[0-9]+/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=DelawareCases"; }
  },
  {
    name: "D.C. Code",
    regexp: /(D\.?C\.?|District of Columbia) Code (Ann.?)?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=DCCode"; }
  },
  {
    name: "D.C. Court of Appeals",
    regexp: /D\.? ?C\.? ?App\.? (1999|2[0-9]+)/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=DCCases"; }
  },
  {
    name: "Florida Statutes",
    regexp: /Fla\.? Stat\.? ( Ann\.?)? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9]+)\.([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=FloridaStatutes&ch=" + match[4] + "&sec=" + match[5]; }
  },
  {
    name: "Florida cases",
    regexp: /Fla\.? (Dist\.?)? ?(App\.?)? ?[1-5]? ?(Dist\.)? ?(199|200)[0-9]/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=FloridaCases"; }
  },
  {
    name: "Georgia Code",
    regexp: /Ga\.? Code ( Ann\.?)? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9]+)-([-0-9A\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=GeorgiaCode&title=" + match[4] + "&sec=" + match[5]; }
  },
  {
    name: "Georgia Supreme Court",
    regexp: /[0-9]+ Ga\.? [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=GeorgiaCases"; }
  },
  {
    name: "Hawai'i Statutes",
    regexp: /Haw(\.?|ai'?i) Rev(\.?|ised) Stat(\.?|utes)?( Ann\.?)?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=HawaiiStatutes"; }
  },
  {
    name: "Hawai'i Appellate Courts",
    regexp: /(8[7-9]|9[0-9]|1[0-9]+) Haw(\.?|ai'?i) [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=HawaiiCases"; }
  },
  {
    name: "Idaho Code",
    regexp: /Idaho Code/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=IdahoCode"; }
  },
  {
    name: "Idaho Supreme Court",
    regexp: /[0-9]+ Idaho [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=IdahoCases"; }
  },
  {
    name: "Illinois Statutes",
    regexp: /([0-9]+) (ILCS|Ill\.? Comp\.? Stat\.?( Ann\.)?)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=IllinoisStatutes&ch=" + match[1]; }
  },
  {
    name: "Illinois Cases",
    regexp: /[0-9]+ Ill\.? ?(2d|App\.? ?3d) ?[0-9][0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=IllinoisCases"; }
  },
  {
    name: "Indiana Code",
    regexp: /(I\.?C\.?|Ind(\.?|iana) ?Code ?(Ann(\.?|otated))?) ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9\.]+)-([0-9\.]+)-([0-9\.]+)-([0-9\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=IndianaCode&title=" + match[7] + "&art=" + match[8] + "&sec=" + match[9]; }
  },
  {
    name: "Iowa Code",
    regexp: /Iowa ?Code ?(Ann(\.?|otated))? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9A-Z\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=IowaCode&sec=" + match[5]; }
  },
  {
    name: "Iowa Cases",
    regexp: /\(Iowa (App\.?)? ?(199(8|9)|200[0-9])\)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=IowaCases"; }
  },
  {
    name: "Kansas Statutes",
    regexp: /(K\.?S\.?A\.?|Kan(\.?|sas) ?Stat\.? ?( Ann\.?)?) ?(§|s|S|§|&#167|(s|S)ection)? ?([0-9a]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=KansasStatutes&ch=" + match[6]; }
  },
  {
    name: "Kansas Cases",
    regexp: /\((Kan\.? ?(App\.?)?) ?(199(7|8|9)|200[0-9])\)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=KansasCases&ct=" + match[1]; }
  },
  {
    name: "Kentucky Statutes",
    //regexp: /K(y\.|entucky) ?Rev(\.|ised) ?Stat(\.|utes) ?( Ann(\.|otated))? ?(§|s|S|§|&#167)* ?(([0-9]+)([A-G])\.(([0-9]+)-)?([0-9a]+)/g,
    regexp: /K(y\.|entucky) ?Rev(\.|ised) ?Stat(\.|utes) ?(Ann(\.|otated))?/ig,
   // href: function(match) { return "http://" + theDomain + "/US.aspx?doc=KentuckyStatutes&ch=" + match[7] + "&chltr=" + match[9] + "&subch=" + match  

 //[9] + "&sec=" + match[10]; 
   href: function(match) { return "http://" + theDomain + "/US.aspx?doc=KentuckyStatutes";
}
  },
  {
    name: "Kentucky Cases",
    regexp: /Ky\.? (App\.?)? ?(199(6|7|8|9)|200[0-9])/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=KentuckyCases"; }
  },
  {
    name: "Maine Statutes",
    regexp: /Me\.? ?Rev\.? ?Stat\.? ?(Ann\.?)?,? ?(T|t)it(\.?|le) ?([-0-9A]+),? (§|s|S|§|&#167|(s|S)ection)* ?([-0-9A]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MaineStatutes&title=" + match[4] + "&sec=" + match[7]; }
  },
  {
    name: "Maine Supreme Court",
    regexp: /(199[7-9]|200[0-9]) ME ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MaineSupCt&year=" + match[1] + "&no=" + match[2]; }
  },
  {
    name: "Maryland Court of Appeals",
    regexp: /(33[7-9]|3[4-9][0-9]|4[0-9][0-9]) Md\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MDCourtApp&vol=" + match[1]; }
  },
  {
    name: "Maryland Court of Special Appeals",
    regexp: /(10[4-9]|1[1-9][0-9]) Md\.? ?App\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MDCourtSpApp&vol=" + match[1]; }
  },
  {
    name: "Massachusetts General Laws",
    regexp: /Mass\.? ?Gen\.? ?Laws ?ch\.? ?([0-9A-Z]+),? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MassGenLaws&ch=" + match[1] + "&sec=" + match[4]; }
  },
  {
    name: "Massachusetts SJC Cases",
    regexp: /([3-4][0-9][0-9]) Mass\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MassSJCCases&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Massachusetts Ct. App. Cases",
    regexp: /([1-9]|[1-9][0-9]) Mass\.? App\.? Ct\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MassCtAppCases&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Michigan Compiled Laws",
    regexp: /Mich\.? ?Comp\.? ?Laws ?(Ann\.?)? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MichStatutes&sec=" + match[4]; }
  },
  {
    name: "Michigan Supreme Court",
    regexp: /([0-9]+) Mich\.? ?[0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MichSupremeCt&vol=" + match[1]; }
  },
  {
    name: "Michigan Court of Appeals",
    regexp: /([0-9]+) Mich\.? ?(Ct\.?)? ?App\.? ?[0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MichCtApp&vol=" + match[1]; }
  },
  {
    name: "Minnesota Statutes",
    regexp: /Minn\.? ?Stat\.? ?(Ann\.?)? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9][0-9A-Z\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MinnStatutes&sec=" + match[4]; }
  },
  {
    name: "Minnesota Cases",
    regexp: /\(Minn\.? ?(Ct\.? ?App\.?)? ?(199[6-9]|200[0-9])\)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MinnCases"; }
  },
  {
    name: "Mississippi Code",
    regexp: /Miss\.? ?Code ?Ann\.?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MississippiCode"; }
  },
  {
    name: "Mississippi Cases",
    regexp: /Miss\.? ?(Ct\.? ?App\.?)? ?(199[6-9]|200[0-9])/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=MississippiCases"; }
  },
  {
    name: "New Jersey Statutes",
    regexp: /(N\.?J\.?S\.?A\.?|N\.?J\.? Stat\.? Ann\.?)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NJSA"; }
  },
  {
    name: "New Jersey Administrative Code",
    regexp: /(N\.?J\.?A\.?C\.?|N\.?J\.? Administrative Code)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NJAC"; }
  },
  {
    name: "New Jersey Appellate Cases",
    regexp: /[0-9]+ N\.? ?J\.?( Super\.?)? [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NJAppCases&cite=" + match[0]; }
  },
  {
    name: "New Mexico Statutes",
    regexp: /(NMSA|N\.?M\.? ?Stat\.? ?(Ann\.)?)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NMStatutes"; }
  },
  {
    name: "New Mexico Cases",
    regexp: /(199[8-9]|200[0-9]) ?-?(NMCA|NMSC) ?-?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NMCases&year=" + match[1] + "&ct=" + match[2] + "&no=" + match[3]; }
  },
  {
    name: "New York Court of Appeals",
    regexp: /(79|[8-9][0-9]|[1-4][0-9][0-9]) N\.?Y\.?2d\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NYCtApp&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "North Dakota Code",
    regexp: /N\.?D\.? ?Cent\.? ?Code (§|s|S|§|&#167|(s|S)ection)* ?([0-9\.]+)-([0-9\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NDCode&title=" + match[3] + "&ch=" + match[4]; }
  },
  {
    name: "North Dakota Supreme Court",
    regexp: /(199[7-9]|200[0-9]) ?ND ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NDSupremeCases&year=" + match[1] + "&no=" + match[2]; }
  },
  {
    name: "North Dakota Ct. of Appeals",
    regexp: /(199[8-9]|200[0-9]) ?ND App\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NDAppealsCases"; }
  },
  {
    name: "North Dakota Cases",
    regexp: /([0-9]+) N\.?W\.?2d\.? ([0-9]+)(, [0-9]+)? ?\(N\.D\. ?(Ct. ?App.)? ?[0-9]+\)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NDNW2dCases&vol=" + match[1]; }
  },
  {
    name: "N.Y. C.P.L.R.",
    regexp: /(New York|N\.?Y\.? ?)?(C\.?P\.?L\.?R\.?|Civil Practice Law and Rules)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NYCPLR"; }
  },
  {
    name: "Ohio Code",
    regexp: /Ohio ?Rev\.? ?Code ?(Ann\.?)? ?(§|s|S|§|&#167|(s|S)ection)* ?([-0-9\.A]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=OhioCode&sec=" + match[4]; }
  },
  {
    name: "Ohio Administrative Code",
    regexp: /Ohio ?Admin\.? ?Code ?(§|s|S|§|&#167|(s|S)ection)* ?([-0-9\.:]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=OhioAdminCode&sec=" + match[3]; }
  },
  {
    name: "Ohio Supreme Court",
    regexp: /(199[2-9]|200[0-9])-Ohio-([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=OhioSupCt&year=" + match[1] + "&no=" + match[2]; }
  },
  {
    name: "Oregon Statutes",
    regexp: /Ore?(\.?|egon) ?Rev\.? ?Stat\.? ?(§|s|S|§|&#167|(s|S)ection)* ?([-0-9a-d]+)(.([-0-9a-d]+))?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=OregonStatutes&ch=" + match[4] + "&sec=" + match[5]; }
  },
  {
    name: "Pennsylvania Statutes",
    regexp: /([0-9]+) Pa\. ?C(ons\.)? ?S(tat\.)? ?(Ann\.)?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=PAStatutes&title=" + match[1]; }
  },
  {
    name: "Pennsylvania Code of Regulations",
    regexp: /([0-9]+) Pa\.? ?Code ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9]+)(\.([0-9]+))?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=PACode&title=" + match[1] + "&ch=" + match[4] + "&sec=" + match[6]; }
  },
  {
    name: "Pennsylvania Supreme Court",
    regexp: /\(Pa\.? ?(199[7-9]|200[0-9])\)/g,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=PASupremeCt"; }
  },
  {
    name: "Laws of Puerto Rico",
    regexp: /P\.?R\.? ?Laws ?Ann\.?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=PRLaws"; }
  },
  {
    name: "General Laws of Rhode Island",
    regexp: /R\.?I\.? ?Gen\.? ?Laws ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9\.A]+)-([-0-9\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RIGenLaws&title=" + match[3] + "&sec=" + match[4]; }
  },
  {
    name: "South Carolina Codes",
    regexp: /S\.?C\.? ?Code (Ann\.?)? ?(Regs\.?)? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9\.A]+)-([0-9\.]+)(-([0-9\.]+))?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=SCCodes&type=" + match[2] + "&title=" + match[5] + "&ch=" + match[6] + "&sec=" 

+ match[8]; }
  },
  {
    name: "Tennessee Code",
    regexp: /Tenn(\.?|essee) ?Code ?(Ann(\.?|otated))?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=TNCode"; }
  },
  {
    name: "UtahCode",
    regexp: /Utah Code ?Ann\.? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9A-F]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=UTCode&title=" + match[3]; }
  },
  {
    name: "Vermont Supreme Court",
    regexp: /(15[4-9]|16[0-9]|17[0-8]) Vt\. [0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=VTSupremeCt&vol=" + match[1]; }
  },
  {
    name: "Vermont Code",
    regexp: /Vt\.? ?Stat\.? ?Ann\.? ?tit\.? ?([0-9A]+), ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9]+)([a-z]+)?/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=VTCode&title=" + match[1] + "&sec=" + match[4] + "&sec2=" + match[5]; }
  },
  {
    name: "Virginia Code",
    regexp: /Va\.? ?Code ?Ann\.? ?(§|s|S|§|&#167|(s|S)ection)* ?([-0-9A\.:]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=VACode&sec=" + match[3]; }
  },
  {
    name: "Virginia Cases",
    regexp: /[0-9]+ Va\.? ?(App\.?)? ?[0-9]+/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=VACases"; }
  },
  {
    name: "Revised Code of Washington",
    regexp: /Wash\.? ?Rev\.? ?Code? ?(Ann\.?)? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9A-Z\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=RevCodeWash&sec=" + match[4]; }
  },
  {
    name: "Wisconsin Statutes",
    regexp: /Wis\.? ?Stat\.? ?(Ann\.?)? ?(§|s|S|§|&#167|(s|S)ection)* ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=WIStatutes&ch=" + match[4]; }
  },
  {
    name: "U.S. Public Laws",
    regexp: /Pub(\.?|lic) ?L(\.?|aw) ?(No\.?)? ?(10[4-9]|11[0-9])-([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=USPubLaws&cong=" + match[4] + "&no=" + match[5]; }
  },
  {
    name: "Congressional Resolutions",
    regexp: /(S\.? ?((Con\.?|J\.?)? ?Res\.?)?|H\.? ?R\.? ?((Con\.?|J\.?)? ?Res\.?)?) ?([0-9]+),? ?(10[3-9]|11[0-9])(th|rd) Cong\./ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=CongRes&type=" + match[1] + "&no=" + match[6] + "&cong=" + match[7]; }
  },
//  {
//    name: "Congressional Reports",
//    regexp: /(H\.? ?(R\.)?|S\.?) ?Rep\.? ?Nos?\.? (109|110)-([0-9]+)/ig,
//    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=CongReps&type=" + match[1] + "&cong=" + match[3] + "&no=" + match[4]; }
//  },
  {
    name: "NLRB Cases",
    regexp: /([0-9]+) N\.?L\.?R\.?B\.? ?(No\.?)? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=NLRB&vol=" + match[1] + "&page=" + match[3]; }
  },
  {
    name: "BIA Cases",
    regexp: /([0-9]+) I\.? ?& ?N\.? ?Dec\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=BIA&vol=" + match[1] + "&page=" + match[2]; }
  },
  {
    name: "Decisions of the Comptroller General",
    regexp: /([0-9]+) (Comp|COMP)\.? ?(Gen|GEN)\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=GOA&vol=" + match[1] + "&page=" + match[4]; }
  },
  {
    name: "U.S. Patents",
    regexp: /U\.? ?S\.? ?Patent\.? ?Nos?\.? ([0-9,]+)/ig,
    href: function(match) { return "http://" + theDomain + "/US.aspx?doc=USPatents&no=" + match[1]; }
  },

			// INTERNATIONAL LAW
  {
    name: "International Court of Justice",
    regexp: /I\.?C\.?J\.? Reports ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=ICJ&year=" + match[1]; }
  },
  {
    name: "International Court of Justice",
    regexp: /\[?\(?([0-9]+)\)?\]? I\.?C\.?J\.?/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=ICJ&year=" + match[1]; }
  },
  {
    name: "Permanent Court of International Justice",
    regexp: /P\.?C\.?I\.?J\.?,? ?\(?(S|s)er(\.?|ies) ([ABC/])\)?,? ?No\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=PCIJ&ser=" + match[3] + "&no=" + match[4]; }
  },
  {
    name: "European Court Reports",
    regexp: /\[?\(?(1969|19[7-9][0-9]|200[0-9])\)?\]? E\.?C\.?R\.? (([I]+)-)?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=ECR&year=" + match[1] + "&page=" + match[4] + "&part=" + match[3]; }
  },
  {
    name: "European Court of Human RIghts",
    regexp: /(E\.?Ct\.?H\.?R\.?|Eur\.? ?Ct\.? ?H\.?R\.?)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=ECtHR"; }
  },
  {
    //European citation form
    name: "Official Journal of the European Union",
    regexp: /O\.?J\.? ?(No\.? ?)?(L|C)?\.? ?([0-9]+),? ?[0-9]+\. ?[0-9]+\. ?([0-9]+)((,|\/)? ?(p\.)? ?([0-9]+))?/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=OJEU&ser=" + match[2] + "&issue=" + match[3] + "&year=" + match[4] + 

"&page=" + match[8]; }
  },
  {
    //U.S. citation form
    name: "Official Journal of the European Union",
    regexp: /\[?\(?(19[0-9][0-9]|20[0-9][0-9])\)?\]? O\.?J\.? \(?(L|C)?\.? ?([0-9]+)\)?((,|\/)? ?(p\.?)? ?([0-9]+))?/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=OJEU&year=" + match[1] + "&ser=" + match[2] + "&issue=" + match[3] + 

"&page=" + match[7]; }
  },
  {
    name: "Inter-American Court of Human Rights",
    regexp: /Inter-Am(\.|erican) ?Ct?\.? ?H\.?R\.?,? ?\(?(S|s)er(\.?|ies) ([AC])\)?,? ?No\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=IACtHR&ser=" + match[4] + "&no=" + match[5]; }
  },
  {
    name: "United Nations Documents",
    regexp: /U\.?N\.? Doc\.?( No\.?)? ([A-Z0-9ubdmenorvay\/\.]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=UNDoc&docsymbol=" + match[2]; }
  },
  {
    name: "UN Committee Against Torture",
    regexp: /(CAT\/[A-Z0-9ubdmenorvay\/\.]+)/g,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=UNDoc&docsymbol=" + match[1]; }
  },
  {
    name: "World Trade Organization documents",
    regexp: /(WT\/[A-Za-z0-9\/\.]+)/g,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=WTODoc&docsymbol=" + match[1]; }
  },
  {
    name: "International Labor Organization Conventions",
    regexp: /I\.?L\.?O\.? Conv(\.?|ention) ?\(?(No\.? )? ?([0-9]+)\)?/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=ILO&conv=" + match[3]; }
  },
  {
    name: "UN Reports of International Arbitral Awards (RIAA)",
    regexp: /([0-9]+) R\.?I\.?A\.?A\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=RIAA&vol=" + match[1] + "&page=" + match[2]; }
  },

			//CANADA

  {
    name: "Canada - Federal Courts",
    regexp: /([\[0-9+\] [0-9]+) (S\.?C\.?R\.?|F\.?C\.?) ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=CanadaFedCases&vol=" + match[1] + "&rptr=" + match[2] + "&page=" + match[3]; }
  },
  {
    name: "Supreme Court of Canada",
    regexp: /([0-9]+) (S\.?C\.?C\.?) ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=CanadaFedCases&vol=" + match[1] + "&rptr=" + match[2] + "&page=" + match[3]; }
  },
  {
    name: "Constitution Act (Canada)",
    regexp: /Constitution Act, (1867|1982)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=CanadaConst&year=" + match[1]; }
  },
  {
    name: "Consolidated Statutes of Canada",
    regexp: /(((R\.?)?S\.?C\.?|C\.?R\.?C\.?) [0-9]+, c\. [-0-9A-Z]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=CanadaStatutes&cite=" + match[1]; }
  },
  {
    name: "Consolidated Regulations of Canada",
    regexp: /((S\.?I\.?|S\.?O\.?R\.?)\/[-0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=CanadaRegs&cite=" + match[1]; }
  },


			// UNITED KINGDOM
  {
    name: "UK Appeals Cases",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ([0-9]+)? ?A\.?C\.? ([0-9]+)/g,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=UKAppealsCases&cite=" + match[0]; }
  },
  {
    name: "UK House of Lords",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ([0-9]+)? ?U\.?K\.?H\.?L\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=UKHL&cite=" + match[0]; }
  },
  {
    name: "UK Privy Council",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ([0-9]+)? ?U\.?K\.?P\.?C\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=UKPC&cite=" + match[0]; }
  },
  {
    name: "UK Queen's Bench",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ([0-9]+)? ?Q\.?B\.? ([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=UKQB&cite=" + match[0]; }
  },
  {
    name: "England and Wales Court of Appeal",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ([0-9]+)? ?E\.?W\.?C\.?A\.? (Civ\.?|Crim\.?|Admin\.?)? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=EWCA&cite=" + match[0]; }
  },
  {
    name: "England and Wales High Court",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ([0-9]+)? ?E\.?W\.?H\.?C\.? (Civ\.?|Crim\.?|Admin\.?|Ch\.?)? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=EWCA&cite=" + match[0]; }
  },
  {
    name: "UK Weekly Law Reports",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ? ?([0-9]+)? ?W\.?L\.?R\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=WLR&cite=" + match[0]; }
  },
  {
    name: "UK All England Reports",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ([0-9]+)? ?All E\.?R\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=AllER&cite=" + match[0]; }
  },
  {
    name: "Supreme Court of Ireland",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ? ?([0-9]+)? ?I\.?E\.?S\.?C\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=IESC&cite=" + match[0]; }
  },
  {
    name: "Court of Appeal in Northern Ireland",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ? ?([0-9]+)? ?N\.?I\.?C\.?A\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=NICA&cite=" + match[0]; }
  },

			// AUSTRALIA

  {
    name: "Australia Commonwealth Law Reports",
    regexp: /\[?\(?(19[0-9][0-9]|200[0-9])\)?\]? ? ?([0-9]+)? ?C\.?L\.?R\.? ?([0-9]+)/ig,
    href: function(match) { return "http://" + theDomain + "/Intl.aspx?doc=AustraliaCLR&cite=" + match[0]; }
  }

];


/***********************************
 *  Helper functions for filters   *
 ***********************************/

function digits(s)
{
  return s.replace(/[^0-9]/g, "");
}

function alphanumerics(s)
{
  return s.replace(/[^0-9a-z]/ig, "");
}


/***********************************
 *           Link styling          *
 ***********************************/
    
/*

  You can make links generated by AutoLink look different from normal links
  by editing styleLink below and/or by setting up user style sheet rules.
  
  Example: on squarefree.com, make autolinked plain text links green. (Firefox trunk only.)
  
    @-moz-document domain(squarefree.com) { 
      .autolink-plain-text-link { color: green ! important; }
    }
      
*/

function styleLink(a, filter)
{
  a.style.borderBottom = "1px solid green";
}


/***********************************
 *           Fix filters           *
 ***********************************/

function fixFilters()
{
  var i, r;
  for (i = 0; r = filters[i]; ++i) {
    // lowercase, and replace each run of non-alphanumerics with a single hyphen
    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();


/***********************************
 *      When and where to run      *
 ***********************************/

var moddingDOM = false;

window.addEventListener("load", init, false);

function init()
{
   document.addEventListener("DOMNodeInserted", nodeInserted, false);
   setTimeout(go, 50, document.body);
}

// This makes it work at Gmail.
// 20% performance penalty on a plain text file with a link on almost every line.
// Tiny performance penalty on pages with few automatically added links.
function nodeInserted(e)
{
  // our own modifications should not trigger this.
  // (we don't want our regular expression objects getting confused)
  // (we want better control over when we recurse)
  
  //GM_log("Inserted: " + e.target);
  
  if (!moddingDOM)
    go(e.target);
}



/***********************************
 *          DOM traversal          *
 ***********************************/


/*

  This script uses manual DOM traversal, in an iterative way without a stack!

  Advantages of snapshot XPath:
    * Much less code
    * 20-40% faster
    * May be possible to get another speed boost by including the regexp in the XPath expression - http://www.developer.com/xml/article.php/10929_3344421_3
    * All the cool people are using it
  
  Advantages of manual DOM traversal:
    * Lets us stop+continue (snapshot xpath doesn't let us)
    * Lets us modify DOM in strange ways without worrying.
    * Easier to control which elements we recurse into.

*/


// Ignore all children of these elements.
const skippedElements = { 
  a:        true, // keeps us from screwing with existing links. keeps us from recursing to death :)
  noscript: true, // noscript has uninterpreted, unshown text children; don't waste time+sanity there.
  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") // gmail autocomplete (fake dropdown)
        return true;
      if (node.className == "ilc sxs") // invite foo to gmail (fake link/button)
        return true;
    }
  }

  return false;
}

// Don't insert hyperlinks on certain legal research sites
function go(traversalRoot)
{
  var currentURL = location.host;
  if(currentURL.indexOf('lexis') == -1 && currentURL.indexOf('westlaw') == -1 && currentURL.indexOf('loislaw') == -1 && currentURL.indexOf('fastcase') == -1)
  {
    go2(traversalRoot);
  }
}


function go2(traversalRoot)
{
  var m;
  
  // Ensure we're not already in a forbidden element.
  for (m = traversalRoot; m != undefined; m = m.parentNode) {
    if (skipChildren(m)) {
      return;
    }
  }

  // work around bug, or in case previous user scripts did crazy stuff
  traversalRoot.normalize();

  function cont(n, didChildren)
  {
    var k = 0; // split work into chunks so Firefox doesn't freeze
    var q;
    
    while (n && k < 100)
    {
      ++k;
    
      // Do stuff at this node
      if (!didChildren && n.nodeType == 3) {
        if((q = runFiltersOnTextNode(n))) {
          n = q[0];

          // if there were changes, run filters again on the new text node that's here          
          if (q[1]) 
            continue;
        }
      }
  
      // Traverse to the "next" node in depth-first order

      if (!n.firstChild)
        didChildren = true;
  
      if (didChildren && n == traversalRoot)
        break;
      else if (!didChildren && n.firstChild && !skipChildren(n)) {
        n = n.firstChild;
        // didChildren is already false and should stay false
      }
      else {
        if (n.nextSibling) {
          n = n.nextSibling;
          didChildren = false;
        }
        else {
          n = n.parentNode;
          didChildren = true;
        }
      }
    } // end while
  
    if (!n) {
      //GM_log("Odd. traversalRoot was " + traversalRoot);
    }
    else if (n == traversalRoot) {
      //GM_log("Done");
      //alert("AutoLink time: " + (new Date() - timeBefore))
    }
    else {
      // Continue after 10ms.
      //GM_log("will have to continue");
      setTimeout(cont, 10, n, didChildren);
    }
    
  } // end function cont
  
  cont(traversalRoot, false);
}


/***********************************
 *         Running filters         *
 ***********************************/

// runFiltersOnTextNode
// Return: node at which to continue traversal, or |null| to mean no changes were made.

function runFiltersOnTextNode(node)
{
  // Too many variables.  Good hint that I need to split this function up :P
  var source, j, regexp, match, lastLastIndex, k, filter, href, anyChanges; // things
  var used, unused, firstUnused, lastUnused, a, parent, nextSibling; // nodes
  
  source = node.data;
  
  anyChanges = false;

  // runFiltersOnTextNode has its own do-too-much-at-once avoider thingie.
  // assumption: if there is one text node with a lot of matches,
  // it's more important to finish quickly than be transparent.
  // (e.g. plain text file FULL of links)
  // assumption: 40 * 100 = 140.
  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;
      
      // Optimization from the linkify that came with Greasemonkey(?):
      // instead of splitting a text node multiple times, take advantage
      // of global regexps and substring.

      for (match = null, lastLastIndex = 0; k < 40 && (match = regexp.exec(source)); ) {
      
        // this should happen first, so RegExp.foo is still good :)
        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.title = "Link to " + filter.name + " added by Cornell LII";
          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);
  }
}