// ==UserScript==
// @name          BNet Squelcher
// @namespace     http://bnsq.lordaeron.org
// @description   Adds useful features to Blizzard Entertainment forums
// @include       http://forums.battle.net/*
// @include       https://forums.battle.net/*
// ==/UserScript==

function BNSQ_getVersion()
{
    return "GM1.3";
}

function BNSQ_backendAPIname()
{
    return "greasemonkey";
}

function GM_setValue(prefName,value)
{
	var key = "BNSQ_GM_" + prefName + ".value";
    var typeKey = "BNSQ_GM_" + prefName + ".type";
    var prefType=typeof(value);
    switch (prefType) {
        case "string": localStorage.setItem(key, value); localStorage.setItem(typeKey, prefType); break;
        case "boolean": localStorage.setItem(key, value); localStorage.setItem(typeKey, prefType); break;
        case "number": 
            if (value % 1 != 0) {
                throw new Error("Cannot set preference to non integral number");
            }
            localStorage.setItem(key, value);
			localStorage.setItem(typeKey, prefType); break;
        default:
            throw new Error("Cannot set preference with datatype: " + prefType);
    }
}
function GM_getValue(prefName, defaultValue)
{
    var key = "BNSQ_GM_" + prefName + ".value";
    var typeKey = "BNSQ_GM_" + prefName + ".type";
	if (localStorage.getItem(key) == null)
	    return defaultValue;
    var prefType = localStorage.getItem(typeKey);
	var stringedValue = localStorage.getItem(key);
	if (prefType == null && defaultValue != null)
	    prefType = typeof(defaultValue);
    switch (prefType) {
        case "string": return stringedValue;
        case "boolean": 
			if (stringedValue == "true" || "1") {return true;}
			else if (stringedValue == "false" || "0") {return false;} //if it's not "true", "1", "false", or "0", just let it return the defaultValue in the last line of this function.
        case "number": return parseInt(stringedValue);
    }
	return defaultValue; //if the type pref is lost or is of an unhandled type and we can't guess the type from the defaultValue, we'll pretend that the pref just isn't there.
}

function GM_openInTab(url)
{
	//var port = chrome.extension.connect({name: "GM_openInTab"});
    //port.postMessage({url: url});
	window.open(url);
}

/*function GM_xmlhttpRequest(options)
{
    var onload = options["onload"];
	var onerror = options["onerror"];
	//alert(typeof onerror);
    chrome.extension.sendRequest({name: "GM_xmlhttpRequest", method: options["method"], url: options["url"], headers: options["headers"]}, function(resp) {
        switch(resp.callbackType)
	    {
	    case "onload":
		  if(typeof onload == "function")
		    onload(resp);
		  break;
		case "onerror":
		  if(typeof onerror == "function")
		    onerror(resp);
		  break;
		} 
    });
};*/
var chromexmlhttp;
function GM_xmlhttpRequest(options)
{
    var onload = options["onload"];
    chromexmlhttp = new XMLHttpRequest();
    chromexmlhttp.open(options["method"], options["url"], true);
	//alert(typeof onload);
    chromexmlhttp.onload = function(resp) {
    var resptext = chromexmlhttp.responseText;
    var responseDetails = { responseText : resptext }
    onload(responseDetails);
    };
chromexmlhttp.send(null);
}
/**********************************************************************************************
BNet Squelcher 2.1 By Maged
Contributers: Ylleks
Edited by: (Your Name Here)
Script made available under the Creative Commons Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/
Attribution requirements:
*Keep the line "BNet Squelcher 2.1 By Maged" above intact.
*If you release this script in extension form, "Maged" must at least be listed as a contributer in the About dialog.
 ***********************************************************************************************/
 
 /*********************
 Best viewed in Notepad++
 **********************/
 


currentVersion = 2160;
currentVersionString = "";//reserved for Greasemonkey releases
try {
 if (BNSQ_getVersion()) {currentVersionString = BNSQ_getVersion();} //determines BNSQ's version string automatically in the extension version via the GM api backend
} catch(err) {}
var backendAPI;
try
{
 switch(BNSQ_backendAPIname())
 {
 case "firefox":
  backendAPI = "firefox";
  break;
 case "chrome":
  backendAPI = "chrome";
  break;
 default:
  backendAPI = "greasemonkey";
 }
}
catch(err)
{
 backendAPI = "greasemonkey";
}
bnsqUserAgent = navigator.userAgent + " BNet Squelcher/" + currentVersionString + " (rv:" + currentVersion + "; +http://bnsq.lordaeron.org/)";
migrationCheck(currentVersion);//mirgration system to solve version incompatibilities and gather update statistics. Number is the current verion of the script's version number. May not always look like the release version string used in the extension.

exBML = new Array();
exBMLpost = new Array();
exBML["url"] = "exURL(inputP,inputC);";
exBMLpost["url"] = "exURLpost(inputP,inputC);";
//exBML["test"] = "'<b><!content></b>'";
exBML["img"] = "exIMG(inputP,inputC);"
exBMLpost["img"] = "exIMGpost(inputP,inputC);";
exBML["color"] = "'<span style=\"color: ' + customColorDecode(params) + ';\"><content></span>'";
exBML["colour"] = "'<span style=\"color: ' + customColorDecode(params) + ';\"><content></span>'";
exBML["s"] = "'<strike><!content></strike>'";
exBML["strike"] = "'<strike><!content></strike>'";
exBML["quote"] = "exQuote(params,content);";
exBMLpost["quote"] = "content=content.replace(/^\\n*/,'').replace(/\\n*$/,'');'[<noparse>quote][<noparse>i]Originally posted by [<noparse>b]<!params>[<noparse>/b][<noparse>/i]\\n\\n<!content>[<noparse>/quote]'";
exBML["blizzquote"] = "'<blockquote><small><hr NOSHADE color = \"#9E9E9E\" size = \"1\"><small class = \"white\">Q u o t e:</small><br><i>Originally posted by <b><span style=\"color: #00C0FF\"><params=\"Blizzard\"></span></b></i> <img height=\"12\" src=\"/images/icons/blizz.gif\"/>\\n<br>\\n<br><span class=\"blue\"><!content></span><hr NOSHADE color = \"#9E9E9E\" size = \"1\"></small></blockquote>'";
exBMLpost["blizzquote"] = "content=content.replace(/^\\n*/,'').replace(/\\n*$/,'');'[<noparse>quote][<noparse>i]Originally posted by [<noparse>b]<!params>[<noparse>/b][<noparse>/i] [<noparse>Blizzard Poster]\\n\\n<!content>[<noparse>/quote]'";
exBML["blizzardquote"] = exBML["blizzquote"];
exBMLpost["blizzardquote"] = exBMLpost["blizzquote"];
exBML["bluequote"] = exBML["blizzquote"];
exBMLpost["bluequote"] = exBMLpost["blizzquote"];
exBML["bquote"] = exBML["blizzquote"];
exBMLpost["bquote"] = exBMLpost["blizzquote"];
exBML["bnsqversion"] = "if(params) { if(params==currentVersionString || params==currentVersion) {'<b><span style=\"color: lime;\">' + currentVersionString + '</span></b>';} else {'<b><span style=\"color: red;\">' + currentVersionString + '</span></b>';}} else {currentVersionString;}";
exBML["noparse"] = "if (content) {content.replace(/\\[/g, '[<noparse>');} else {'<error>'}";
exBML["blink"] = "'<div style=\"text-decoration: blink;\"><!content></div>'";
exBML["donatemaged"] = "'<form action=\"https://www.paypal.com/cgi-bin/webscr\" method=\"post\"><input name=\"cmd\" value=\"_s-xclick\" type=\"hidden\"><input name=\"encrypted\" value=\"-----BEGIN PKCS7-----MIIHNwYJKoZIhvcNAQcEoIIHKDCCByQCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYCf1KQ1o69rKiibl//vzCT436qs42pBksN5bKECXwGocnZT/FQipe+wk3F6GGeDyR5dM7MgwAwxsJ09GTvDcdqn32HyJV3X05SxM9CH7/rdsPCl6Y70PeFKspuOEI5zQ6hHWlAvlNfssjzPKT6zCW86IQSME2BHszn7URsvqdvdNjELMAkGBSsOAwIaBQAwgbQGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQI+YLzsJ6T5juAgZC/rENr+9Jw+WfjFH5tqKwya7ao3AGULtCqj3zyL8Rci/XEAlF4T3Pwd9T14xB/KNPOl2ka1qCHUSX8JPQaQkHfBZZzIfLfPTraiIkvOeNo8BJGsxTV1AR2j6sO1IUXb4RUL/mBNQr2J3lZamFdreg7G5iGurzmzyG5ZMT2q7S3RWc/pSCMyCi4g03gxGAKKT+gggOHMIIDgzCCAuygAwIBAgIBADANBgkqhkiG9w0BAQUFADCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20wHhcNMDQwMjEzMTAxMzE1WhcNMzUwMjEzMTAxMzE1WjCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMFHTt38RMxLXJyO2SmS+Ndl72T7oKJ4u4uw+6awntALWh03PewmIJuzbALScsTS4sZoS1fKciBGoh11gIfHzylvkdNe/hJl66/RGqrj5rFb08sAABNTzDTiqqNpJeBsYs/c2aiGozptX2RlnBktH+SUNpAajW724Nv2Wvhif6sFAgMBAAGjge4wgeswHQYDVR0OBBYEFJaffLvGbxe9WT9S1wob7BDWZJRrMIG7BgNVHSMEgbMwgbCAFJaffLvGbxe9WT9S1wob7BDWZJRroYGUpIGRMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAIFfOlaagFrl71+jq6OKidbWFSE+Q4FqROvdgIONth+8kSK//Y/4ihuE4Ymvzn5ceE3S/iBSQQMjyvb+s2TWbQYDwcp129OPIbD9epdr4tJOUNiSojw7BHwYRiPh58S1xGlFgHFXwrEBb3dgNbMUa+u4qectsMAXpVHnD9wIyfmHMYIBmjCCAZYCAQEwgZQwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tAgEAMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0wODEwMjMyMzU5MTdaMCMGCSqGSIb3DQEJBDEWBBTYPK2f1DIaZht+SQt2fHrJbCMhojANBgkqhkiG9w0BAQEFAASBgKkON4PEB2MipZZKfTc7IP3LirJk7UDJLuYQEYqLZNtkvPsI4P5b45JBMjyL09nUOJNCTvmaObNQsjlgF0M7EBw0UhaEuhwd5jLVoJUOHpCKwmAAux6mkc4P1V7FGlu0n72cW6mjoOC5l9D/12zJId+T9DP5obkCNwbWUzaPM18e-----END PKCS7-----\" type=\"hidden\"><input src=\"https://www.paypal.com/en_US/i/btn/x-click-but21.gif\" name=\"submit\" alt=\"\" border=\"0\" type=\"image\"><img alt=\"\" src=\"https://www.paypal.com/en_US/i/scr/pixel.gif\" border=\"0\" height=\"1\" width=\"1\"></form>'";
exBML["noembed"] = "'<content>'";

if (/^http(s)?:\/\/(web.archive.org\/web\/[0-9]*\/http(s)?:\/\/)?forums\.battle\.net\/thread\.html/.test(document.location.href) && orderedXpath("//div[@class='resultbox']/../../script[2]").snapshotItem(0)/*&& unsafeWindow.postIdArray*/) //If we are in a thread
{
 //quote overflow protection
 if (GM_getValue("extendedBML", true)) 
 {
  document.body.innerHTML = document.body.innerHTML.replace( /<blockquote>(?:<\/?[^><]*>)*Q u o t e:(?:<br>)?(?: )?(?:<\/small>)?(?:<br>)?(?: )?(?:<\/small>)*/g,"[quote]")
                            .replace(/(?:<hr[^>]*>)+(?:<\/?small[^>]*>)*(<\/blockquote>)*(?!<\/ins>)/g, "[/quote]").replace(/<\/small>((?:\W)?<\/?li>(?:\W)?)<small>/gi, "$1");
 }
 //Parse out some data for later
 topicId = /topicId=([0-9]*)/.exec(xpath("/html/body/div/table/tbody/tr/td/table/tbody/tr/td/div/div/div/ul/li/span/b/a").snapshotItem(0).href)[1];
 topicSubject = xpath("/html/body/div/table/tbody/tr/td/table/tbody/tr/td/div/div/div/ul/li/span/b/a").snapshotItem(0).textContent;
 sid = document.getElementsByName('sid')[0].value;
 forumId = document.getElementsByName('forumId')[0].value;
 stationId = document.getElementsByName('stationId')[0].value;
 if (/pageNo=([0-9]*)/.test(document.location.href))
  pageNo = /pageNo=([0-9]*)/.exec(document.location.href)[1];
 else
  pageNo = 1;
 //Define the arrays
 forumsPostIdArray = new Array(); // array grabbed from the forums
 postIdArray = new Array();
 characterIdArray = new Array();
 avatarNodeList = new Array();
 postBodyNodeList = new Array();
 postInfoNodeList = orderedXpath("//tr[@id]");
 //alert(postInfoNodeList.snapshotLength);
 avatarNodeSnapshot = orderedXpath("//div[@id='avatar11']/div[@class='shell']/table/tbody/tr/td | //div[@id='avatar21']/div[@class='shell']/table/tbody/tr/td");
 postBodyNodeSnapshot = orderedXpath("//div[@class='message-format']/span");
 for (var i = 0; i < postInfoNodeList.snapshotLength; i++)
 {
  forumsPostIdArray[i] = /body(.*)/.exec(postInfoNodeList.snapshotItem(i).id)[1];
  //Parse out the varibles that Blizzard so nicely added. postId[i]_characterId[i]_switch(1 or 2)
  postIdArray[i] = forumsPostIdArray[i].split("_")[0];
  characterIdArray[i] = forumsPostIdArray[i].split("_")[1];
  //alert(postIdArray[i] + "\n" + characterIdArray[i]);
  //Grab the nodes where the avatar is stored
  avatarNodeList[i] = avatarNodeSnapshot.snapshotItem(i);
  //alert(avatarNodeList[i].style.cssText);
  postBodyNodeList[i] = postBodyNodeSnapshot.snapshotItem(i);
 }
 if (document.getElementById("logoutbuttons")) //if we are logged in
 {
  currentCharacterId = /characterId=([0-9]*)/.exec(orderedXpath("//div[@class='account-loggedin']//a[starts-with(@href,'/search.html')]").snapshotItem(0).href)[1];
  loggedInAvatarNode = orderedXpath("//div[@class='account-loggedin']//td[@height='64'][@width='64'][@style]").snapshotItem(0);
  //loggedInAvatarNode.style.background = "transparent url(http://avatars.lordaeron.org/AvatarBorders3.png) no-repeat scroll -2pt -1pt";
 }
 //Finally, let's do stuff
  if (GM_getValue("reordertitle", true))
 {
  document.title = document.title.replace(/^([^>]+) -> (.*)$/,"$2 <- $1");
 }
 //Hardcodes Maged's avatar, just because I can
 switch(backendAPI)
 {
 case "firefox":
  setAvatar("109710997335","chrome://bnetsquelcher/content/BNSQnew.png");
  break;
 case "chrome":
  setAvatar("109710997335",chrome.extension.getURL("BNSQ64.png"));
  break;
 default:
  setAvatar("109710997335","http://avatars.lordaeron.org/109710997335");
 }
 //setAvatar("109711299228","http://avatars.lordaeron.org/captcha.jpg");
 if (GM_getValue("avatar", true))
 {
  getAndSetAvatars(characterIdArray,avatarNodeList);
  try
  {
   if (currentCharacterId && loggedInAvatarNode)
   {
    setLoggedInAvatar(currentCharacterId,loggedInAvatarNode);
   }
  } catch(err){}
 }
 //if (GM_getValue("submittosc2pod", ture))
 //{
 // var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
 // var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
 // var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
 // findAndSubmitToSc2pod();
 //}
 if (GM_getValue("stripsigs", false)) 
 {
  stripSigs();
 }
 if (GM_getValue("extendedBML", true)) 
 {
 addPageFunctions();
  for (var i = 0; i < postBodyNodeList.length; i++)
  {
   postBodyHTML = postBodyNodeList[i].innerHTML //check out HTML so that the DOM parser doesn't screw stuff up
   postBodyHTML = postBodyHTML.replace(/<wbr>/gi,""); //the <wbr>'s get in the way
   postBodyHTML = exBMLpreParse(postBodyHTML);
   postBodyHTML = autoLinkEmbed(postBodyHTML); //auto-links and auto-embeds links/images - prefs are in function 
   //alert(postBodyHTML);
   postBodyNodeList[i].innerHTML = parseBML(postBodyHTML,"get");
  }
 }
}

if (/^http(s)?:\/\/(web.archive.org\/web\/[0-9]*\/http(s)?:\/\/)?forums\.battle\.net\/board\.html/.test(document.location.href)) //If we are on the board
{
 if (document.getElementById("logoutbuttons")) //if we are logged in
 {
  currentCharacterId = /characterId=([0-9]*)/.exec(orderedXpath("//div[@class='account-loggedin']//a[starts-with(@href,'/search.html')]").snapshotItem(0).href)[1];
  loggedInAvatarNode = orderedXpath("//div[@class='account-loggedin']//td[@height='64'][@width='64'][@style]").snapshotItem(0);
  //loggedInAvatarNode.style.background = "transparent url(http://avatars.lordaeron.org/AvatarBorders3.png) no-repeat scroll -2pt -1pt";
  if (GM_getValue("avatar", true))
  {
   setLoggedInAvatar(currentCharacterId,loggedInAvatarNode);
  }
 }
}

 //Parse the in response to/preview box on the posting/preview page
if(/^http(s)?:\/\/forums\.battle\.net\/post\.html/.test(document.location.href) && !/&debug=1/.test(document.location.href))
 {
  try
  {
  postBodyView = xpath("//div[@class='message-format']/span").snapshotItem(0);
  } catch(err){}
  if (postBodyView)
  {
   if (GM_getValue("extendedBML", true)) 
   {
    postBodyView.innerHTML = postBodyView.innerHTML.replace( /<blockquote>(?:<\/?[^><]*>)*Q u o t e:(?:<br>)?(?: )?(?:<\/small>)?(?:<br>)?(?: )?(?:<\/small>)*/g,"[quote]")
                             .replace(/(?:<hr[^>]*>)+(?:<\/?small[^>]*>)*(<\/blockquote>)*(?!<\/ins>)/g, "[/quote]").replace(/<\/small>((?:\W)?<\/?li>(?:\W)?)<small>/gi, "$1");
   }
   infoString = postBodyView.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.id;
   postId = infoString.split("_")[0].replace(/^body/i,"");
   characterId = infoString.split("_")[1];
   //alert(postId + "\n" + characterId);
   avatarNode = orderedXpath("//div[@class='shell']/table/tbody/tr/td[@style]").snapshotItem(1);
   //alert(replyToAvatarNode.parentNode.innerHTML);
   if (document.getElementById("logoutbuttons")) //if we are logged in
   {
    currentCharacterId = /characterId=([0-9]*)/.exec(orderedXpath("//div[@class='account-loggedin']//a[starts-with(@href,'/search.html')]").snapshotItem(0).href)[1];
    loggedInAvatarNode = orderedXpath("//div[@class='account-loggedin']//td[@height='64'][@width='64'][@style]").snapshotItem(0);
    //loggedInAvatarNode.style.background = "transparent url(http://avatars.lordaeron.org/AvatarBorders3.png) no-repeat scroll -2pt -1pt";
   }
   if (GM_getValue("avatar", true))
   {
    characterIdArray = new Array();
    avatarNodeList = new Array();
    characterIdArray[0] = characterId;
    avatarNodeList[0] = avatarNode;
    getAndSetAvatars(characterIdArray,avatarNodeList);
    try
    {
     if (currentCharacterId && loggedInAvatarNode)
     {
      setLoggedInAvatar(currentCharacterId,loggedInAvatarNode);
     }
    } catch(err){}
   }
   if (GM_getValue("extendedBML", true)) 
   {
    addPageFunctions();
	postBodyHTML = postBodyView.innerHTML
    postBodyHTML = postBodyHTML.replace(/<wbr>/gi,"");
    postBodyHTML = exBMLpreParse(postBodyHTML);
	postBodyHTML = autoLinkEmbed(postBodyHTML);
    postBodyView.innerHTML = parseBML(postBodyHTML,"get");
   }
  }
 }
 
 
if (/^http(s)?:\/\/forums\.battle\.net\/post\.html/.test(document.location.href) && document.getElementById("postAlt")) //If we are in the posting page
{
 originalMessageNode = document.getElementById("message");
 if(xpath("//div[@class='chardata']/span/b").snapshotItem(0))
 {inResponseTo = xpath("//div[@class='chardata']/span/b").snapshotItem(0).textContent/*.replace(/\]/g,"\xAD\xAD]")*/;}
 else
 {inResponseTo = null;}
 if (/\]/.test(inResponseTo))
  {autoQuoteDelimiter = '"';}
 else
  {autoQuoteDelimiter = '';}
 inResponseToBlue = false;
 try
 {
  postBodyView = xpath("//div[@class='message-format']/span").snapshotItem(0);
  if (/^\n\t\t\t\t<span class="blue">/.test(postBodyView.parentNode.innerHTML))
  {
   inResponseToBlue = true;
  }
 } catch(err){}
 if (GM_getValue("exBMLdegrade", true))
 {
  originalMessageNode.value = exBMLpostParse(originalMessageNode.value,inResponseTo,inResponseToBlue);
 }
 else
 {
  if (GM_getValue("quoteusernames", true))
  {
   if (inResponseToBlue)
   {originalMessageNode.value = originalMessageNode.value.replace(/^\[quote\](?!\[i\]Originally posted by \[b\])((?:.|\n)*)\[\/quote\]$/gi,"[quote][i]Originally posted by [b]"+inResponseTo+"[/b][/i] [Blizzard Poster]\n\n$1[/quote]");}
   else
   {originalMessageNode.value = originalMessageNode.value.replace(/^\[quote\](?!\[i\]Originally posted by \[b\])((?:.|\n)*)\[\/quote\]$/gi,"[quote][i]Originally posted by [b]"+inResponseTo+"[/b][/i]\n\n$1[/quote]");}
  }
 }
 postSubmitNodes = document.getElementById("postAlt").getElementsByTagName("input");
 postPreview = postSubmitNodes[0];
 postSubmit = postSubmitNodes[1];
 if (GM_getValue("debug", false) || /&debug=1/.test(document.location.href))
  {postPreview.addEventListener("mouseover"/*"click"*/, function(event) { exBMLdegrade(); }, false);}
 //else
  //{postPreview.addEventListener("click", function(event) { exBMLdegrade(); }, false);}
 //postSubmit.addEventListener("click", function(event) { exBMLdegrade(); }, false);
 postForm = document.getElementById("postForm")
 postForm.addEventListener("submit", function(event) { exBMLdegrade(event); }, false);
 postForm.removeAttribute("onsubmit");
 postOptionsDiv = document.getElementById("postAlt").parentNode.childNodes[1];
 postOptionsDiv.style.marginTop="8px";
 postDegradeExBMLparent = document.createElement("ul");
 postDegradeExBMLparent.innerHTML = '<li class="check-box"><input type="checkbox" class="button" id="exbmldegrade" value="true" checked="true"/></li>\n<li class="sig-desc">Gracefully degrade exBML</li>';
 postOptionsDiv.appendChild(postDegradeExBMLparent);
 postDegradeExBML = document.getElementById("exbmldegrade");
 postDegradeExBML.checked = GM_getValue("exBMLdegrade", true);
 
 //turn off autocomplete for the CAPTCHA input box, if it's there...
 try
 {
  document.getElementsByName('securityAnswer')[0].setAttribute('autocomplete', 'off');
 } catch(err){}
}


////////////////////
//FUNCTION LIST//
///////////////////

//Feature functions

function setAvatar(characterID,URL)
{
 for (var i = 0; i < characterIdArray.length; i++)
 {
  //alert(characterIdArray[i]+"\n"+characterID);
  if (characterIdArray[i] == characterID)
  {
   //alert("setAvatarForPost("+i+","+URL+");");
   setAvatarForPost(i,URL);
  }
 }
}
function setAvatarForPost(postIndex,URL)
{
 //if (! /[()]/.test(URL))
  avatarNodeList[postIndex].style.background = "transparent url(" + URL + ") no-repeat scroll 0pt 0pt";
}
function getAndSetAvatars(characterIdArray,avatarNodeList)
{
 //alert(characterIdArray);
 GM_xmlhttpRequest(
 {
  method:"GET",
  url:"http://avatars.lordaeron.org/users.txt",
  headers:{"User-Agent":bnsqUserAgent},
  onload:function(responseDetails)
  {
   avatarUserArray = new Array();
   avatarUserArray = responseDetails.responseText.split("\n");
   //alert(avatarUserArray);
   Quicksort(avatarUserArray, 0, avatarUserArray.length);//Heh. Using Blizzard's own functions is fun
   //alert(avatarUserArray);
   for (var i = 0; i < characterIdArray.length; i++) 
   {
    if (Quickfind(characterIdArray[i])) 
	{
     setAvatarForPost(i,"http://avatars.lordaeron.org/" + characterIdArray[i]);//<3 how Firefox doesn't need to know the image type
    }
   }
  }
 })
}

function setLoggedInAvatar(characterId,avatarNode)
{
 GM_xmlhttpRequest(
 {
  method:"GET",
  url:"http://avatars.lordaeron.org/users.txt",
  headers:{"User-Agent":bnsqUserAgent},
  onload:function(responseDetails)
  {
   avatarUserArray = new Array();
   avatarUserArray = responseDetails.responseText.split("\n");
   Quicksort(avatarUserArray, 0, avatarUserArray.length);//Heh. Using Blizzard's own functions is fun
   if (Quickfind(characterId)) 
   {
    avatarNode.style.background = "transparent url(http://avatars.lordaeron.org/" + characterId + ") no-repeat scroll -2pt -1pt";
   }
  }
 })
}

function stripSigs()
{
 for (var i = 0; i < postBodyNodeList.length; i++)
 {
  postBodyNodeList[i].innerHTML = stripSig(postBodyNodeList[i].innerHTML);
 }
}

function stripSig(postBodyString)
{
 //postBodyString = postBodyString.split("\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t")[0];
 postBodyString = postBodyString.split("\t\t\t\t\t\t\t\n\t\t\t\t\t")[0];
 return postBodyString;
}

function findAndSubmitToSc2pod()
{
 sc2podParams = "sid=" + sid + "&forumId=" + forumId + "&topicId=" + topicId + "&pageNo=" + pageNo + "&ver=" + currentVersion;
 sc2podIndex = 0;
 for (var i = 0; i < postBodyNodeList.length; i++)
 {
  if (postBodyNodeList[i].className == "blue")
  {
   postId = postIdArray[i];
   postContent = sc2podParse(postBodyNodeList[i].innerHTML);
   md5_hash = MD5(postContent);
   md5_hash2 = hex_md5(postContent);
   if (GM_getValue("debug") || /&debug=1/.test(document.location.href))
   {
    GM_openInTab("data:text/plain;charset=utf-8," + encodeURIComponent("sid: " + sid + "\nforumId: " + forumId + "\ntopicId: " + topicId + "\npageNo: " + pageNo + "\npostId: " + postId + "\nHash: " + md5_hash +"\nHash2: " + md5_hash2 + "\nVersion: " + currentVersion + '\n\nContent: "' + postContent + '"\n\nVerbose: "' + verboseEscape(postContent) + '"' ));
   }
   sc2podParams += "&postId[" + sc2podIndex + "]=" + postId + "&hash[" + sc2podIndex++ + "]=" + md5_hash;
   thereIsDataToSubmitToSc2pod = true;
  }
 }
 if (GM_getValue("debug") || /&debug=1/.test(document.location.href))
 {
  //GM_openInTab("data:text/plain;charset=utf-8," + encodeURIComponent(sc2podParams));
 }
}

function sc2podParse(content)
{
 content = stripSig(content);
 content = content
 .replace(/(^\s+|\s+$)/g, "")
 .replace( /<blockquote>(<small>)*(<\/small>)*<hr (size="1" )?color="#9e9e9e" (size="1" )?noshade="noshade"( size="1")?>(<small>)*(<small class="white">)?Q u o t e:(<br>)?( )?<\/small>(<br>)?( )?(<\/small>)*/g, '< blockquote>' )
 .replace(/(<small>)*(<\/small>)*<hr (size="1" )?color="#9e9e9e" (size="1" )?noshade="noshade"( size="1")?>(<small>)*(<\/small>)*<\/blockquote>/g, '< /blockquote>' )
 .replace(/\n<br>/g, "\x0D")
 .replace(/\n/g, "\x0D\x0A")
 .replace(/<b>/g, "< b>")
 .replace(/<\/b>/g, "< /b>")
 .replace(/<\S[^><]*>/g, "")
 .replace(/< b>/g, "<b>")
 .replace(/< \/b>/g, "</b>")
 .replace(/< blockquote>/g, "<blockquote>")
 .replace(/< \/blockquote>/g, "</blockquote>")
 .replace(/"/g, '&quot;')
 .replace(/\u2018/g, '&lsquo;')
 .replace(/\u2019/g, '&rsquo;')
 .replace(/\u2013/g, '&ndash;')
 .replace(/(^\s+|\s+$)/g, "");
 return content;
}

function parseBML(postHTML,parserMode)
{
 BMLopenRegexpSingle = /\[(\w+)(?:=(?:[^\]]+)?)?\]/i;
 BMLopenRegexp = /\[(\w+)(?:=(?:[^\]]+)?)?\]/gi; //used to determine how many tags there are
 BMLregexp = /\[(\w+)(?:=((?:"[^"]+")|(?:'[^']+')|(?:[^\]]+)?))?\](((?:.|\n)+)?\[\/\1\])?/im;
 if (BMLopenRegexpSingle.test(postHTML)) //no need to parse if there is no exBML
 {
  tagCount = postHTML.match(BMLopenRegexp).length;
  //alert(tagCount);
  if (tagCount > 50)
   {return postHTML;}
  for (var e = 0; e < 50; e++)
  {
   BMLstring = BMLregexp.exec(postHTML)[0]; //the original BML - stored so that it can be replaced with the exBML output later
   tagType = BMLregexp.exec(postHTML)[1]; //the type of tag; i.e. [url] = url
   params = BMLregexp.exec(postHTML)[2]; //the tag's parameters (if they exist, else undifined); i.e. [url=http://example.com] = http://example.com
   content = BMLregexp.exec(postHTML)[4]; //the content between the opening and closing tags (if it exists, else undifined); i.e. [url]http://example.com[/url] = http://example.com
   contentClose = BMLregexp.exec(postHTML)[3]; //content + closing tag (if exists); used internally to fix a problem that arises when two of the same tag types exist
   a=0 //initializes/resets overflow counter
   if (content) //if there's no closing tag, why would I remove any?
   {
    closeTag = "[/" + tagType + "]"; //don't really feel like parsing it out =/
    content = content.split(closeTag)[0]; //makes sure only the first closing tag is used to define the content, not another tag's closing tag
    contentClose = content.split(closeTag)[0] + closeTag;
	BMLstring = BMLstring.split(closeTag)[0] + closeTag; //don't want to remove another tag's closer...
   }
   while (BMLregexp.test(contentClose) && !/noparse/i.test(tagType)) //digs deeper into and parses exBML inside exBML
   {
    BMLstring = BMLregexp.exec(contentClose)[0];
	tagType = BMLregexp.exec(contentClose)[1];
	params = BMLregexp.exec(contentClose)[2];
	content = BMLregexp.exec(contentClose)[4];
	contentClose = BMLregexp.exec(contentClose)[3];
    if (content) //if there's no closing tag, why would I remove any?
    {
     closeTag = "[/" + tagType + "]"; //don't really feel like parsing it out =/
     content = content.split(closeTag)[0]; //makes sure only the first closing tag is used to define the content, not another tag's closing tag
	 contentClose = content.split(closeTag)[0] + closeTag;
     BMLstring = BMLstring.split(closeTag)[0] + closeTag; //don't want to remove another tag's closer...
    }
	a++
	//if (a>7) //why 7? don't ask me why, it doesn't really matter... =/ (see below)
	 //break; //prevents possible browser stalls; no technical reason for this other than as a preventitive measure: the parser could in theory dig deeper than the amount of characters that are allowed in a post could be tags
	//alert("1\nBML String: " + BMLstring + "\nType: " + tagType + "\nParams: " + params + "\nContent: " + content); //used to debug as exBML is being dug into
   }
   //alert("2\nBML String: " + BMLstring + "\nType: " + tagType + "\nParams: " + params + "\nContent: " + content); //used to debug as exBML is being dug into
   if (content) //if there's no closing tag, why would I remove any?
   {
    closeTag = "[/" + tagType + "]"; //don't really feel like parsing it out =/
    content = content.split(closeTag)[0]; //makes sure only the first closing tag is used to define the content, not another tag's closing tag
    BMLstring = (BMLstring.split(closeTag)[0] + closeTag); //don't want to remove another tag's closer...
   }
   //alert("3\nBML String: " + BMLstring + "\nType: " + tagType + "\nParams: " + params + "\nContent: " + content); //debugs the final info
   postHTML = postHTML.replace(BMLstring,executeBML(BMLstring,tagType,params,content,parserMode).replace(/\$/g,"$$$$"),"m");
   //alert(postHTML);
   //alert(tagCount+","+e);
   //postHTML = postHTML.replace(BMLstring,e);
   //postBodyNodeList[i].innerHTML = postBodyNodeList[i].innerHTML + "<br>" + postHTML;
   if (BMLregexp.exec(postHTML) == null)
   {
    break;
   }
  }
  //postBodyNodeList[i].innerHTML = /*postBodyNodeList[i].innerHTML + "<br>" + */postHTML;
 }
 return postHTML.replace(/<noparse>/g,"");
}

function executeBML(BMLstring,tagType,params,content,parserMode)
{
 if (typeof exBML[tagType.toLowerCase()] == "function")//see: [some] tags being read as a function.
 {
  return BMLstring.replace(/\[/g, "[<noparse>");
 }
 if ((exBML[tagType.toLowerCase()] && parserMode == "get") || (exBMLpost[tagType.toLowerCase()] && parserMode == "post"))
 {
  var rawparams, delimiter;
  if (params) 
  {
   rawparams = params.replace(/"(?![^<]*>)/g,'&quot;').replace(/'(?![^<]*>)/g,"&#39;");
   delimiterTestArray = /(?:^"([^"]+)"$)|(?:^'([^']+)'$)|(?:^`([^`]+)`$)|(?:([^\]]+))/.exec(params);
   if (delimiterTestArray[1])
   {
	delimiter = '"';
	params = delimiterTestArray[1];
   }
   else if (delimiterTestArray[2])
   {
	delimiter = "'";
	params = delimiterTestArray[2];
   }
   else if (delimiterTestArray[3])
   {
	delimiter = "`";
	params = delimiterTestArray[3];
   }
   else
   {delimiter = "";}
  }
  if (parserMode == "post" && (/<(!)?(params|content|input(P|C)?)(?:=\"([^"]*)\")?>/gi.test(BMLstring))) {return BMLstring.replace(/\[/g, "[<noparse>");}
  if (params && parserMode == "get") {params = params.replace(/"(?![^<]*>)/g,'&quot;').replace(/'(?![^<]*>)/g,"&#39;");}//sanitize user inputs; don't touch pre/blizzard-sanitized html
  if (content && parserMode == "get") {content = content.replace(/"(?![^<]*>)/g,'&quot;').replace(/'(?![^<]*>)/g,"&#39;");}
  input = undefined; inputP = undefined; inputC = undefined;
  if (!params && content){input = content; inputP = content; inputC = content;}
  if (params && !content){input = params; inputP = params; inputC = params;}
  if (params && content){inputP = params; inputC = content;}
  if (params && content && params==content){input = params;}
  //alert("\nBMLstring: " + BMLstring + "\ntagType: " + tagType + "\nrawparams: " + rawparams + "\ndelimiter: " + delimiter + "\nparams: " + params + "\ncontent: " + content + "\ninput: " + input + "\ninputP: " + inputP + "\ninputC: " + inputC);
  //alert(eval(exBML[tagType.toLowerCase()]));
  if (parserMode == "get") {exBMLoutput = eval(exBML[tagType.toLowerCase()]);}
  if (parserMode == "post") {exBMLoutput = eval(exBMLpost[tagType.toLowerCase()]);}
  if (/<error>/i.test(exBMLoutput) || (/<!params>/i.test(exBMLoutput) && !params) || (/<!content>/i.test(exBMLoutput) && !content) || (/<!input(P|C)>/i.test(exBMLoutput) && (!inputP || !inputC)) || (/<!input>/i.test(exBMLoutput) && !input))
  {
   return BMLstring.replace(/\[/g, "[<noparse>");
  }
  try
  {
   if (params){exBMLoutput = exBMLoutput.replace(/<(?:!)?params(?:=\"(?:[^"]*)\")?>/gi,params.replace(/\$/g,"$$$$"));}
   else {exBMLoutput = exBMLoutput.replace(/<params(?:=\"([^"]*)\")?>/gi,"$1");}
   if (content){exBMLoutput = exBMLoutput.replace(/<(?:!)?content(?:=\"(?:[^"]*)\")?>/gi,content.replace(/\$/g,"$$$$"));}
   else {exBMLoutput = exBMLoutput.replace(/<content(?:=\"([^"]*)\")?>/gi,"$1");}
   if (input){exBMLoutput = exBMLoutput.replace(/<(?:!)?input(?:=\"(?:[^"]*)\")?>/gi,input.replace(/\$/g,"$$$$"));}
   else {exBMLoutput = exBMLoutput.replace(/<input(?:=\"([^"]*)\")?>/gi,"$1");}
   if (inputP && inputC) {exBMLoutput = exBMLoutput.replace(/<(?:!)?inputP(?:=\"(?:[^"]*)\")?>/gi,inputP.replace(/\$/g,"$$$$")).replace(/<(?:!)?inputC(?:=\"(?:[^"]*)\")?>/gi,inputC.replace(/\$/g,"$$$$"));}
   else {exBMLoutput = exBMLoutput.replace(/<inputP(?:=\"([^"]*)\")?>/gi,"$1").replace(/<inputC(?:=\"([^"]*)\")?>/gi,"$1");}
  } catch(err) {
   //alert(exBMLoutput);
   if (parserMode == "get")
   {
    return exBMLoutput + "<br><span style=\"color: red;\">exBML Parser Error: " + err.message + "</span>";
   }
   else
   {
    return exBMLoutput;
   }
  }
  return exBMLoutput;
 }
 return BMLstring.replace(/\[/g, "[<noparse>");
}

//exBML functions

function exURL(URL,text)
{
 if (URL && text)
 {
  URL = URL.replace(/<\/?a[^><]*>/g,"");//filter out any links already present
  text = text.replace(/<\/?a[^><]*>/g,"");
  URL = URL.replace(/<\/?img src=\"([^"]*)[^><]*>/gi,"$1");
  if (/^\W*http:\/\//i.test(text) && URL != text) //don't allow links to go somewhere other than the user intends
  {
   return '<a href="' + text.replace(/^\W*http:\/\//i,"http://") + '" target="_new" onclick="return warn(this)">' + text + '</a>';
  }
  if (/^\W*javascript:/i.test(URL))
  {
   return '<a href="' + URL + '" onclick="return jsWarn(this)">' + URL + '</a>';
  }
  if (/^\W*mailto:/i.test(URL))
  {
   return '<a href="' + URL + '" target="_new">' + text + '</a>';
  }
  if (/^(www\.)?([-\w]+)\.(com|org|net|us|info|edu|gov)/i.test(URL)) //not an exaustive list of TLD's. However, it doesn't really matter since this is to fix a user error.
  {
   URL = "http://" + URL;
  }
  return '<a href="' + URL + '" target="_new" onclick="return warn(this)">' + text + '</a>';
 }
 return '<error>'
}

function exURLpost(URL,text)
{
 if (URL && text)
 {
  if ((URL != text) && !(/\)/i.test(URL)) && !(/\)/i.test(text)))
  {
   return '\xAD\xAD' + text + '\xAD\xAD \xAD\xAD\xAD(' + URL + ')\xAD\xAD\xAD'
  }
  if (URL == text)
  {
   if (/^https?:\/\/[^!)\[\]~<>}'"\s]*[^.!);,\[\]-_~<>}'"\s]$/i.test(URL))
   {
    return '[<noparse>u]' + URL + '[<noparse>/u]';
   }
   /*else
   {
    return '[<noparse>b][<noparse>u][<noparse>/b]' + URL + '[<noparse>b][<noparse>/u][<noparse>/b]';
   }*/
  }
 }
 return '<error>';
}

function exIMG(URL,text)
{
 if (URL && text)
 {
  URL = URL.replace(/<\/?a[^><]*>/g,"").replace(/<\/?img src=\"([^"]*)[^><]*>/gi,"$1");//filter out any links/images present
  text = text.replace(/<\/?a[^><]*>/g,"").replace(/<\/?img src=\"([^"]*)[^><]*>/gi,"$1");

  if (/^\W*http:\/\/forums\.battle\.net\/captcha\.jpg/i.test(URL))
   {
    if (URL == text)
     text = text.replace(/^(\W*http:\/\/)forums\.battle\.net(\/captcha\.jpg)/i,"$1"+"12.129.242.36"+"$2");
	URL = URL.replace(/^(\W*http:\/\/)forums\.battle\.net(\/captcha\.jpg)/i,"$1"+"12.129.242.36"+"$2");
   }
  if (URL == text)
  {
   return '<img src="' + URL + '" alt="' + URL + '" onload="resizeImg(this)">';
  }
  else
  {
   return '<img src="' + URL + '" alt="' + URL + '" title="' + text + '" onload="resizeImg(this)">';
  }
 }
 return '<error>';
}

function exIMGpost(URL,text)
{
 return '<error>';
}

function exQuote(username,content)
{
 if (!username && (/^<i>Originally posted by <b>(.*)<\/b><\/i> \[Blizzard Poster\][\n]<br>[\n]<br>/i.test(content)))
 {
  return '[blizzquote="' + /^<i>Originally posted by <b>(.*)<\/b><\/i> \[Blizzard Poster\][\n]<br>[\n]<br>/i.exec(content)[1] + '"]' + content.replace(/^<i>Originally posted by <b>(.*)<\/b><\/i> \[Blizzard Poster\][\n]<br>[\n]<br>/i, "") + "[/blizzquote]";
 }
 if (username)
 {
  quotePrefix='<i>Originally posted by <b><!params></b></i>\n<br>\n<br>';
 }
 else 
 {
  quotePrefix='';
 }
 return '<blockquote><small><hr NOSHADE color = \"#9E9E9E\" size = \"1\"><small class = \"white\">Q u o t e:</small><br>' + quotePrefix + '<!content><hr NOSHADE color = \"#9E9E9E\" size = \"1\"></small></blockquote>'//"[quote]<!content>[/quote]"
}

function customColorDecode(color)
{
 if (color)
 {
  color = /([^;]*);?/.exec(color)[1];//sanitize color input
  if (/^blizzblue$/i.test(color))
   return "#00C0FF";
  if (/^mvpblue$/i.test(color))
   return "#A7BFD8";
  if (/^mvpgreen$/i.test(color))
   return "#5df644";
  return color;
 }
 return '<error>'
}

function addPageFunctions()
{
var jsWarn = "function jsWarn(jsURI)\n{\n if (confirm('WARNING! You are attempting to execute potentially hazardous thrid-party JavaScript.\\nKnowing this, do you want to continue anyway?'))\n {\n  location.href=jsURI.href\;\n }\n return false;\n}"
document.body.appendChild(document.createElement("script")).innerHTML=jsWarn;
var resizeImg = /*"function resizeImg(img)\n{alert(img.parentNode.parentNode.scrollWidth)}";//*/"function resizeImg(img)\n{\n if(parseInt(document.styleSheets[2][\"cssRules\"][281].style[\"width\"]) < img.width)\n {\n  //alert(parseInt(document.styleSheets[2][\"cssRules\"][281].style[\"width\"])+\"\\n\"+img.width);\n  img.setAttribute(\"width\", \"100%\");\n }\n}"
document.body.appendChild(document.createElement("script")).innerHTML=resizeImg;
//var toggleImgSize = "function toggleImgSize(img)\n{\n if(img.width = 100%)\n {\n  img.removeAttribute(\"width\");\n } else {\n  resizeImg(img);\n }\n}"
//document.body.appendChild(document.createElement("script")).innerHTML=toggleImgSize;
if (GM_getValue("disablelinkinterceptor", false))
{
 var linkWarn = "function warn(url)\n{\n return true;\n}"
 document.body.appendChild(document.createElement("script")).innerHTML=linkWarn;
}
}

function exBMLpreParse(string)
{
 string = string.replace(/\xAD\xAD([^)]+)\xAD\xAD \xAD\xAD\xAD\(([^)]+)\)\xAD\xAD\xAD/g,"[url=`$2`]$1[/url]");
 //string = string.replace(/\[quote\]<br><i>Originally posted by <b>(.*)\<\/b>\<\/i> \[Blizzard Poster\][\n]<br>[\n]<br>/gi,"[blizzquote=$1]");
 string = string.replace(/\[quote\]<br><i>Originally posted by <b>(.*)\<\/b>\<\/i>[\n]<br>[\n]<br>/gi,'[quote="$1"]');
 string = string.replace(/<u>(https?:\/\/[^!)\[\]~<>}'"\s]*[^.!);,\[\]-_~<>}'"\s])<\/u>/gi,'[url]$1[/url]');
 //alert(string);
 return string;
}

function exBMLpostParse(string,inResponseTo,inResponseToBlue)
{
 string = string.replace(/\[quote\]\[i\]Originally posted by \[b\]([^\]]*)\[\/b\]\[\/\i\][\n][\n]/gi,'[quote=$1]').replace(/\[quote\]\[i\]Originally posted by \[b\](.*)\[\/b\]\[\/\i\][\n][\n]/gi,'[quote="$1"]');
 //string = string.replace(/\[quote\]\[i\]Originally posted by \[b\](.*)\[\/b\]\[\/\i\] \[Blizzard Poster\][\n][\n]/gi,"[blizzquote=$1]");
 if (GM_getValue("quoteusernames", true) && /&op=3/.test(document.location.href) && inResponseTo)
 {
  if (inResponseToBlue)
  {string = string.replace(/^\[quote\]((?:.|\n)*)\[\/quote\]$/gi,"[blizzquote="+autoQuoteDelimiter+inResponseTo+autoQuoteDelimiter+"]$1[/blizzquote]");}
  else
  {string = string.replace(/^\[quote\]((?:.|\n)*)\[\/quote\]$/gi,"[quote="+autoQuoteDelimiter+inResponseTo+autoQuoteDelimiter+"]$1[/quote]");}
 }
 string = string.replace(/\xAD\xAD([^)]+)\xAD\xAD \xAD\xAD\xAD\(([^)]+)\)\xAD\xAD\xAD/g,'[url="$2"]$1[/url]');
 string = string.replace(/\[u\](https?:\/\/[^!);,\[\]-_~<>}'"\s]*[^.!);,\[\]-_~<>}'"\s])\[\/u\]/gi,'[url]$1[/url]');
 return string;
}

function exBMLdegrade(event)
{
 if (postDegradeExBML.checked)
 {
  originalMessageNode = document.getElementById("message");
  newMessageNode = originalMessageNode.cloneNode(true);
  originalMessageNode.removeAttribute("name");
  if (document.getElementById("newmessage") == null)
  {
   newMessageNode.id = "newmessage";
   hiddenDiv = document.createElement("div");
   if (!GM_getValue("debug", false) && !/&debug=1/.test(document.location.href))
    hiddenDiv.style.display = "none";
   originalMessageNode.parentNode.appendChild(hiddenDiv);
   hiddenDiv.appendChild(newMessageNode);
  }
  else
  {
   newMessageNode.setAttribute("name","message");
   oldHiddenMessage = document.getElementById("newmessage");
   parentDiv = oldHiddenMessage.parentNode;
   newMessageNode.id = "newmessage";
   parentDiv.replaceChild(newMessageNode, oldHiddenMessage);
  }
  newMessageNode.style.height = "120px";
  messageNode = document.getElementById("newmessage");
  messageNode.value = originalMessageNode.value
  //alert(messageNode.value);
  messageNode.value = parseBML(messageNode.value,"post");
 }
 if (!checkPostForm())
 {
  if (event)
  {
   try 
    {
      event.stopPropagation();
      event.preventDefault();
    } catch (ex) {}
  }
  return false; // for IE-compat - the same reason I have try/catch
 }
}

function checkPostForm() 
{
 try
 {
  if(document.getElementsByName("subject")[0].value == "" || document.getElementsByName("subject")[0].value == null)
  {
   alert("Subject is a mandatory field");
   return false;
  }

  if(document.getElementsByName("message")[0].value == "" || document.getElementsByName("message")[0].value == null)
  {
   alert("Message is a mandatory field");
   return false;
  }

  return true;
 } catch(err) {return true;} //if something is messed up, just let the server handle it
}

function resizeImgs(bodyNode)
{
 imgs = bodyNode.getElementsByTagName("img");
 for (var i = 0; i < imgs.length; i++)
 {
  //alert(imgs[i].parentNode.parentNode.scrollWidth + "\n" + imgs[i].width);
  if(imgs[i].parentNode.parentNode.scrollWidth < imgs[i].width)
   imgs[i].setAttribute("width", "100%");
 }
}

function autoLinkEmbed( inner )
{
 var autolink = GM_getValue("autolink", true) //prevent race conditions
 var autoembed = GM_getValue("autoembed", true)
 var result = "";
 if (autolink || autoembed)
 {
  inner = inner.replace(/<\/?a[^><]*>/g,""); //remove all forum-added links 
 } else {return inner;}
 
 while ( true )
 {
  var i = inner.indexOf( "http" );
  if ( i == -1 )
  {
   result += inner;
   break;
  }
	
  result += inner.slice(0,i);
  var p2 = inner.slice(i);
  	
  var linkEnd = p2.search( /(\.|\?)?((("|'|`)?\])|("|'|\[\/)|\s)/ );
  if ( linkEnd == -1 )
  {
   linkEnd = p2.length;
  }

  var link = p2.slice(0,linkEnd);
  	
  if ( link.search( /(.*jpg$)|(.*gif$)|(.*png$)/i ) != -1 && !/\[noembed\]/i.test(inner) && autoembed)
  {
   link = exIMG(link,link)//"<img src='" + link + "'>";
  }
  else if (autolink)
  {
   link = exURL(link,link)//"<a href='" + link + "'>" + link + "</a>";
  }

  result += link;
  inner = p2.slice( linkEnd );
 }
 //alert(result);
 return result;
}

//General functions

function migrationCheck(currentVersion)
{
 lastVersion=GM_getValue("lastversion",0);
 if (lastVersion != currentVersion)
 {
  statsURL = 'http://bnsq.lordaeron.org/bnsqstats.php?lastver=' + lastVersion + "&currentver=" + currentVersion;
  GM_xmlhttpRequest({
		method:"GET",
		url:statsURL,
		headers:{"User-Agent":bnsqUserAgent},
		}) //Submit the version info for statistical reasons.
  GM_setValue("lastversion",currentVersion);//Set extensions.bnetsquelcher.lastversion to the current version to reset the info for the next update.
  GM_openInTab("http://forum.lordaeron.org/viewtopic.php?p=2241#p2241");
 }
}

function xpath(query) {
    return document.evaluate(query, document, null,
        XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}

function orderedXpath(query) {
    return document.evaluate(query, document, null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}

function verboseEscape(string) {
	string = string.replace(/\n/g, "\\n")
	.replace(/\r/g, "\\r")
	.replace(/\t/g, "\\t");
	return string;
}

function Quickfind(entry, resetTemp, loBound, hiBound)
 //returns 0 if it doesn't find anything
 //Use: Quickfind(theNumberYouWanttofind, 1)
 /**************************************************************
  This function adapted from the algorithm given in:
   Data Abstractions & Structures Using C++, by
   Mark Headington and David Riley, pg. 586.
 
  Quicksort is the fastest array sorting routine for
  unordered arrays.  Its big O is n log n.
  **************************************************************/
 {

 vec = avatarUserArray;
 
  if (!resetTemp) {
   temp = 0;
   loBound = 0;
   hiBound = avatarUserArray.length - 1;
  }
  
 
  if (hiBound - loBound <= 3) {
  
   if (vec[loBound] == entry || vec[hiBound] == entry || vec[hiBound-1] == entry || vec[loBound+1] == entry) {
    temp++;
   }
  }
 
  // Three or more items to sort
  pivot = vec[parseInt((loBound + hiBound) / 2)];
  loSwap = loBound + 1;
  hiSwap = hiBound;
 
 
  
  do {

   // Find the right loSwap
   while (loSwap <= hiSwap && vec[loSwap] <= pivot)
    loSwap++;
 
   // Find the right hiSwap
   while (vec[hiSwap] > pivot)
    hiSwap--;
 
   // Swap values if loSwap is less than hiSwap
   if (loSwap < hiSwap)
   {
    temp = vec[loSwap];
   }
  } while (loSwap < hiSwap);
 
  // 2 or more items in first section  
  if (loBound < hiSwap - 1)
   temp = Quickfind(entry, 1, loBound, hiSwap - 1);
 

  // 2 or more items in second section
  if (hiSwap + 1 < hiBound)
   temp = Quickfind(entry, 1, hiSwap + 1, hiBound);
  
  return temp;
 }
 
 
 function Quicksort(vec, loBound, hiBound)
 /**************************************************************
  This function adapted from the algorithm given in:
   Data Abstractions & Structures Using C++, by
   Mark Headington and David Riley, pg. 586.
 
  Quicksort is the fastest array sorting routine for
  unordered arrays.  Its big O is n log n.
  **************************************************************/
 {
 
  var pivot, loSwap, hiSwap, temp;

  // Two items to sort
  if (hiBound - loBound == 1)
  {
   if (vec[loBound] > vec[hiBound])
   {
    temp = vec[loBound];
    vec[loBound] = vec[hiBound];
    vec[hiBound] = temp;
   }
   return;
  }
 
  // Three or more items to sort
  pivot = vec[parseInt((loBound + hiBound) / 2)];
  vec[parseInt((loBound + hiBound) / 2)] = vec[loBound];
  vec[loBound] = pivot;
  loSwap = loBound + 1;
  hiSwap = hiBound;
 
  do {
   // Find the right loSwap
   while (loSwap <= hiSwap && vec[loSwap] <= pivot)
    loSwap++;
 
   // Find the right hiSwap
   while (vec[hiSwap] > pivot)
    hiSwap--;
 
   // Swap values if loSwap is less than hiSwap
   if (loSwap < hiSwap)
   {
    temp = vec[loSwap];
    vec[loSwap] = vec[hiSwap];
    vec[hiSwap] = temp;
   }
  } while (loSwap < hiSwap);
 
  vec[loBound] = vec[hiSwap];
  vec[hiSwap] = pivot;
 

  // Recursively call function...  the beauty of quicksort
 
  // 2 or more items in first section  
  if (loBound < hiSwap - 1)
   Quicksort(vec, loBound, hiSwap - 1);
 

  // 2 or more items in second section
  if (hiSwap + 1 < hiBound)
   Quicksort(vec, hiSwap + 1, hiBound);
 }

/**
*
*  MD5 (Message-Digest Algorithm)
*  http://www.webtoolkit.info/
*
**/

function MD5(string) {

	function RotateLeft(lValue, iShiftBits) {
		return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
	}

	function AddUnsigned(lX,lY) {
		var lX4,lY4,lX8,lY8,lResult;
		lX8 = (lX & 0x80000000);
		lY8 = (lY & 0x80000000);
		lX4 = (lX & 0x40000000);
		lY4 = (lY & 0x40000000);
		lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
		if (lX4 & lY4) {
			return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
		}
		if (lX4 | lY4) {
			if (lResult & 0x40000000) {
				return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
			} else {
				return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
			}
		} else {
			return (lResult ^ lX8 ^ lY8);
		}
 	}

 	function F(x,y,z) { return (x & y) | ((~x) & z); }
 	function G(x,y,z) { return (x & z) | (y & (~z)); }
 	function H(x,y,z) { return (x ^ y ^ z); }
	function I(x,y,z) { return (y ^ (x | (~z))); }

	function FF(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};

	function GG(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};

	function HH(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};

	function II(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};

	function ConvertToWordArray(string) {
		var lWordCount;
		var lMessageLength = string.length;
		var lNumberOfWords_temp1=lMessageLength + 8;
		var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
		var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
		var lWordArray=Array(lNumberOfWords-1);
		var lBytePosition = 0;
		var lByteCount = 0;
		while ( lByteCount < lMessageLength ) {
			lWordCount = (lByteCount-(lByteCount % 4))/4;
			lBytePosition = (lByteCount % 4)*8;
			lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition));
			lByteCount++;
		}
		lWordCount = (lByteCount-(lByteCount % 4))/4;
		lBytePosition = (lByteCount % 4)*8;
		lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
		lWordArray[lNumberOfWords-2] = lMessageLength<<3;
		lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
		return lWordArray;
	};

	function WordToHex(lValue) {
		var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
		for (lCount = 0;lCount<=3;lCount++) {
			lByte = (lValue>>>(lCount*8)) & 255;
			WordToHexValue_temp = "0" + lByte.toString(16);
			WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
		}
		return WordToHexValue;
	};

	function Utf8Encode(string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {

			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	};

	var x=Array();
	var k,AA,BB,CC,DD,a,b,c,d;
	var S11=7, S12=12, S13=17, S14=22;
	var S21=5, S22=9 , S23=14, S24=20;
	var S31=4, S32=11, S33=16, S34=23;
	var S41=6, S42=10, S43=15, S44=21;

	string = Utf8Encode(string);

	x = ConvertToWordArray(string);

	a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;

	for (k=0;k<x.length;k+=16) {
		AA=a; BB=b; CC=c; DD=d;
		a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
		d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
		c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
		b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
		a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
		d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
		c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
		b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
		a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
		d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
		c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
		b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
		a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
		d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
		c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
		b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
		a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
		d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
		c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
		b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
		a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
		d=GG(d,a,b,c,x[k+10],S22,0x2441453);
		c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
		b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
		a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
		d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
		c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
		b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
		a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
		d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
		c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
		b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
		a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
		d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
		c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
		b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
		a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
		d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
		c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
		b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
		a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
		d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
		c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
		b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
		a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
		d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
		c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
		b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
		a=II(a,b,c,d,x[k+0], S41,0xF4292244);
		d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
		c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
		b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
		a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
		d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
		c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
		b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
		a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
		d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
		c=II(c,d,a,b,x[k+6], S43,0xA3014314);
		b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
		a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
		d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
		c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
		b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
		a=AddUnsigned(a,AA);
		b=AddUnsigned(b,BB);
		c=AddUnsigned(c,CC);
		d=AddUnsigned(d,DD);
	}

	var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);

	return temp.toLowerCase();
}
/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
//var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
//var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
//var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}

/*
 * Calculate the MD5 of an array of little-endian words, and a bit length
 */
function core_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);

}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Calculate the HMAC-MD5, of a key and some data
 */
function core_hmac_md5(key, data)
{
  var bkey = str2binl(key);
  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
  return core_md5(opad.concat(hash), 512 + 128);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
  return bin;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
  return str;
}

/*
 * Convert an array of little-endian words to a hex string.
 */
function binl2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
  }
  return str;
}

/*
 * Convert an array of little-endian words to a base-64 string
 */
function binl2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}
