Uncensor GameFAQs: G*** Edition

Uncensors "g***" formatted swears on GameFAQs.

Verzia zo dňa 05.08.2014. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript== 
// @name           Uncensor GameFAQs: G*** Edition
// @namespace      [email protected] 
// @description    Uncensors "g***" formatted swears on GameFAQs.
// @include        http://www.gamefaqs.com/boards/*
// @version        1.61

//-------------------------------------------------------------------
//
//    Uncensor GameFAQs
//
//    Author: The Amazing One ([email protected])
//            Vague Rant (minor shit, no support, go away)
//
//    Version 1.61 notes:
//    - Now implements some rudimentary all-caps guessing. It will
//      check the current word for capitals at either end of the
//      swear, e.g. ube[R]G*** will capitalize GRIS; otherwise it
//      will check the previous or next /two/ letters from adjacent
//      words, e.g. G*** [IT] or suc[K A] Gris. It's simpler than it
//      sounds. Just try it.
//    - Extremely inefficient! Really needs someone who actually
//      knows JavaScript and regex to take over!
//
//
//    Version 1.6 notes:
//    - G*** Edition means "Gris Edition". But "Gold" also fits. See,
//      it's clever. Bet you were all "Is that for 'Gris' or 'Gold'?"
//    - Updated to add support for new GameFAQs censorship rules
//      (first character no longer needs to be starred out). Covers a
//      few extra words over the old version, see the list below.
//    - I don't know either regex or JavaScript. This is a total hack
//      job. If anything doesn't work, it's because I hate you.
//
//
//    ----- About Uncensor GameFAQs -----
//      VR NOTE! RULES HAVE CHANGED SOME OF THIS IS WRONG BUT I'M
//      LAZY SO THE OLD DESCRIPTION IS STILL HERE. BRARUARBAURHAUH.
//
//      Uncensor GameFAQs is a script that finds series of asterisks
//      on the GameFAQs message boards, and tries to figure out what
//      bad word the person meant. It makes its best guess, and fills
//      that word in. For convenience, it also replaces the bad words
//      in your own post with asterisks. Note that the Uncensor GameFAQs
//      script will always censor your own post with asterisks,
//      never by first letter. Therefore it is in your best interest
//      not to post something like "F is for fuck" since this will
//      be censored as "F is for ****", and that's a censor bypass. The
//      exception to this rule is the word "nigger", which is always
//      censored as "n-word" or "the n-word" (rather than six asterisks).
//
//      Supported curse words:
//      - arsehole
//      - asshole
//      - bitch
//      - cunt
//      - fag
//      - fuck
//      - jizz
//      - nigger
//      - pussy
//      - pussies
//      - shit
//      - tits
//      - titties
//      - titty
//
//      VR NOTE! Some of these words are really shitty to use as
//      descriptors for things, people, etc. Don't be a douchebag.
//
//-------------------------------------------------------------------


// This script was initially adapted from IronicSans's "Uncensor the
// Internet" script found at:
//   http://userscripts.org/scripts/show/8891 

// ==/UserScript==

var badSp = [], goodSp = [];
var bad = [], good = [];
var badFlip = [], goodFlip = [];

var replacements = {
 
        "Da.\\*\\*\\*Wiz": "DaShizWiz",

        "A\\*\\*\\*\\*\\*\\*\\*(?!\\*)": "Arsehole",
        "a\\*\\*\\*\\*\\*\\*\\*(?!\\*)": "arsehole",
        "A\\*\\*\\*\\*\\*\\*(?!\\*)": "Asshole",
        "a\\*\\*\\*\\*\\*\\*(?!\\*)": "asshole",
        "B\\*\\*\\*\\*(?!\\*)": "Bitch",
        "b\\*\\*\\*\\*(?!\\*)": "bitch",
        "C\\*\\*\\*(?!\\*)": "Cunt",
        "c\\*\\*\\*(?!\\*)": "cunt",
        "F\\*\\*(?!\\*)": "Fag",
        "f\\*\\*(?!\\*)": "fag",
        "F\\*\\*\\*(?!\\*)": "Fuck",
        "f\\*\\*\\*(?!\\*)": "fuck",
        "J\\*\\*\\*(?!\\*)": "Jizz",
        "j\\*\\*\\*(?!\\*)": "jizz",
        "N\\*\\*\\*\\*\\*(?!\\*)": "Nigger",
        "n\\*\\*\\*\\*\\*(?!\\*)": "nigger",
        "P\\*\\*\\*\\*(?!\\*)": "Pussy",
        "p\\*\\*\\*\\*(?!\\*)": "pussy",
        "P\\*\\*\\*\\*\\*\\*(?!\\*)": "Pussies",
        "p\\*\\*\\*\\*\\*\\*(?!\\*)": "pussies",
        "S\\*\\*\\*(?!\\*)": "Shit",
        "s\\*\\*\\*(?!\\*)": "shit",
        "T\\*\\*\\*(?!\\*)": "Tits",
        "t\\*\\*\\*(?!\\*)": "tits",
        "T\\*\\*\\*\\*\\*\\*(?!\\*)": "Titties",
        "t\\*\\*\\*\\*\\*\\*(?!\\*)": "titties",
        "T\\*\\*\\*\\*(?!\\*)": "Titty",
        "t\\*\\*\\*\\*(?!\\*)": "titty",
		
		"([A-Z] ?[A-Z] |[A-Z])Arsehole": "$1ARSEHOLE",
		"Arsehole(?=(?:([A-Z]| [A-Z])))": "ARSEHOLE",
		"([A-Z] ?[A-Z] |[A-Z])Asshole": "$1ASSHOLE",
		"Asshole(?=(?:([A-Z]| [A-Z])))": "ASSHOLE",
		"([A-Z] ?[A-Z] |[A-Z])Bitch": "$1BITCH",
		"Bitch(?=(?:([A-Z]| [A-Z])))": "BITCH",
		"([A-Z] ?[A-Z] |[A-Z])Cunt": "$1CUNT",
		"Cunt(?=(?:([A-Z]| [A-Z])))": "CUNT",
		"([A-Z] ?[A-Z] |[A-Z])Fag": "$1FAG",
		"Fag(?=(?:([A-Z]| [A-Z])))": "FAG",
		"([A-Z] ?[A-Z] |[A-Z])Fuck": "$1FUCK",
		"Fuck(?=(?:([A-Z]| [A-Z] ?[A-Z])))": "FUCK",
		"([A-Z] ?[A-Z] |[A-Z])Jizz": "$1JIZZ",
		"Jizz(?=(?:([A-Z]| [A-Z])))": "JIZZ",
		"([A-Z] ?[A-Z] |[A-Z])Nigger": "$1NIGGER",
		"Nigger(?=(?:([A-Z]| [A-Z])))": "NIGGER",
		"([A-Z] ?[A-Z] |[A-Z])Pussy": "$1PUSSY",
		"Pussy(?=(?:([A-Z]| [A-Z])))": "PUSSY",
        "([A-Z] ?[A-Z] |[A-Z])Pussies": "$1PUSSIES",
		"Pussies(?=(?:([A-Z]| [A-Z])))": "PUSSIES",
		"([A-Z] ?[A-Z] |[A-Z])Shit": "$1SHIT",
		"Shit(?=(?:([A-Z]| [A-Z])))": "SHIT",
		"([A-Z] ?[A-Z] |[A-Z])Tits": "$1TITS",
		"Tits(?=(?:([A-Z]| [A-Z])))": "TITS",
		"([A-Z] ?[A-Z] |[A-Z])Titties": "$1TITTIES",
		"Titties(?=(?:([A-Z]| [A-Z])))": "TITTIES",
		"([A-Z] ?[A-Z] |[A-Z])Titty": "$1TITTY",
		"Titty(?=(?:([A-Z]| [A-Z])))": "TITTY",
		
		"\\*\\*\\*\\*(?=(?:ING|ER)\\b)": "FUCK",
		"(^|\\n|\\t|[\\.!\\?] +)\\*\\*\\*\\*(?=(?:ing|er)\\b)": "$1Fuck",
		"([^\\*])\\*\\*\\*\\*(?=(?:ing|er)\\b)": "$1fuck",
		"THIS \\*\\*\\*\\*": "THIS SHIT",
		"This \\*\\*\\*\\*": "This shit",
		"this \\*\\*\\*\\*": "this shit",
		"(^|\\n|\\t|[^\\*])\\*\\*\\*\\*(?=(?:TY|TING| NO)\\b)": "$1SHIT",
		"(^|\\n|\\t|[\\.!\\?] +)\\*\\*\\*\\*(?=(?:ty|ting)\\b)": "$1Shit",
        "([^\\*])\\*\\*\\*\\*(?=(?:ty|ting)\\b)": "$1shit"

    };

var specialReplacements = {  
        "\\bthe n-word\\b": "\"nigger\"",
        "\\bn-word\\b": "nigger"
    };

var flipRules = {
        "fuck": "shit",
        "Fuck": "Shit",
        "FUCK": "SHIT",
        "shit": "fuck",
        "Shit": "Fuck",
        "SHIT": "FUCK",
        "bitch": "pussy",
        "Bitch": "Pussy",
        "BITCH": "PUSSY",
        "pussy": "bitch",
        "Pussy": "Bitch",
        "PUSSY": "BITCH"
    };

for (var key in specialReplacements) { 
  badSp.push(new RegExp(key, "g")); 
  goodSp.push(specialReplacements[key]); 
}
for (var key in replacements) { 
  bad.push(new RegExp(key, "g")); 
  good.push(replacements[key]); 
}
for (var key in flipRules) { 
  badFlip.push(new RegExp(key)); 
  goodFlip.push(flipRules[key]); 
} 

function doceval(q) {
  return document.evaluate(q, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}

function uncensor(s,sp) { 
  if(sp)
    for(var i=0; i<badSp.length; i++)
      s = s.replace(badSp[i],goodSp[i]);
  for(var i=0; i<bad.length; i++)
    s = s.replace(bad[i],good[i]);
  return s;  
}

function sanitize(s) {
  var flips = [], doubles = [];
  if(!s) return s;
  if(s.charAt(0)==" " && s.charAt(1)!=" ") {
    flips.push(0);
    doubles.push(false);
  }
  for(var p = s.indexOf("  ");p!=-1;p=s.indexOf("  ",p+1)) {
    if(s.charAt(p+2)==" ") continue;
    if(p+2==s.length) break;
    flips.push(p);
    var isDouble = (p==0||s.charAt(p-1)==" ");
    doubles.push(isDouble);
    if(p!=0) s = removeAt(s,p,(isDouble?2:1));
  }
  s = uncensor(s,false);
  for(var i=0; i<flips.length; i++)
    s = flipAt(s,flips[i],doubles[i]);
  s = uncensor(s,true);
  return s;
}

function flipAt(s,i,dbl) {
  while(i<s.length && s.charAt(i)==" ")
    i++;
  if(i>=s.length) return s;
  var p = i;
  while(p<s.length && s.charAt(p)!=" ")
    p++;
  var sub = s.slice(i,p);
  sub = flipWord(sub);
  if(dbl) sub = flipWord(sub);
  s = removeAt(s,i,p-i);
  s = insertAt(s,sub,i);
  return s;
}

function flipWord(s) {
  for(var i=0; i<badFlip.length; i++)
    if(badFlip[i].test(s)) {
      s=s.replace(badFlip[i],goodFlip[i]);
      return s;
    }
  return s;
}

// replace in title 
if(document.title) {
  var gameFoxTitle = false;
  if(/^GameFAQs:M: /.test(document.title)) {
    gameFoxTitle = true;
    document.title = document.title.replace(/^GameFAQs:M: /,"");
  }
  document.title = sanitize(document.title);
  if(gameFoxTitle)
    document.title = "GameFAQs:M: "+document.title;
}
var h2s = doceval("//h2[@class='title']//text()"); 
for(var i = 0; i < h2s.snapshotLength; i++) { 
  node = h2s.snapshotItem(i); 
  node.data = sanitize(node.data); 
}
if(/post\.php/.test(window.location.href)) {
  var ttPreview = doceval("//div[@class='details']/p");
  if(ttPreview.snapshotLength>0) {
    node = ttPreview.snapshotItem(0);
    node.innerHTML = sanitize(node.innerHTML);
  }
  ttPreview = doceval("//div[@class='details']//a");
  if(ttPreview.snapshotLength>0) {
    node = ttPreview.snapshotItem(0);
    node.innerHTML = sanitize(node.innerHTML);
  }
}

// replace in body text
var gentopic = /\/boards\/[0-9]+-[^\/]*$|myposts|search/.test(window.location.href);
var textnodes;
if(gentopic)
  textnodes = doceval("//table[@class=\"board topics\"]//td//text()");
else
  textnodes = doceval("//table[@class=\"board message\"]//td//text()");
for (var i = 0; i < textnodes.snapshotLength; i++) { 
  node = textnodes.snapshotItem(i); 
  node.data = sanitize(node.data); 
}

function censor(text, sp) {
    if (sp) text =
        text.replace(/ARSEHOLE/g, "A*******").replace(/Arsehole/g, "A*******").replace(/arsehole/g, "a*******")
        .replace(/ASSHOLE/g, "A******").replace(/Asshole/g, "A******").replace(/asshole/g, "a******")
	    .replace(/BITCH/g, "B****").replace(/Bitch/g, "B****").replace(/bitch/g, "b****")
	    .replace(/CUNT/g, "C***").replace(/Cunt/g, "C***").replace(/cunt/g, "c***")
	    .replace(/FAG/g, "F**").replace(/Fag/g, "F**").replace(/fag/g, "f**")
        .replace(/FUCK/g, "F***").replace(/Fuck/g, "F***").replace(/fuck/g, "f***")
	    .replace(/JIZZ/g, "J***").replace(/Jizz/g, "J***").replace(/jizz/g, "j***")
	    .replace(/NIGGER/g, "N*****").replace(/Nigger/g, "N*****").replace(/nigger/g, "n*****")
	    .replace(/PUSSY/g, "P****").replace(/Pussy/g, "P****").replace(/pussy/g, "p****")
	    .replace(/PUSSIES/g, "P******").replace(/Pussies/g, "P******").replace(/pussies/g, "p******")
	    .replace(/SHIT/g, "S***").replace(/Shit/g, "S***").replace(/shit/g, "s***")
	    .replace(/SHIZ/g, "S***").replace(/Shiz/g, "S***").replace(/shiz/g, "s***")
	    .replace(/TITS/g, "T***").replace(/Tits/g, "T***").replace(/tits/g, "t***")
	    .replace(/TITTIES/g, "T******").replace(/Titties/g, "T******").replace(/titties/g, "t******")
	    .replace(/TITTY/g, "T****").replace(/Titty/g, "T****").replace(/titty/g, "t****");
    return text;
}

function insertAt(string,sub,index) {
  return string.slice(0,index)+sub+string.slice(index);
}
function removeAt(string,index,count) {
  return string.slice(0,index)+string.slice(index+count);
}

function prevWhitespace(string,index) {
  for(;index>0 && string.charAt(index-1)!="\n"
               && string.charAt(index-1)!="\t"
               && string.charAt(index-1)!=" "; index--);
  return index;
}

function cleanUp(text) {
  var text2=text.toLowerCase();
  var text3=uncensor(censor(text,false)).toLowerCase();
  for(var p = text3.indexOf("fuck"); p!=-1; p = text3.indexOf("fuck",p+3)) {
    var p2 = prevWhitespace(text,p);
    if(text2.substr(p,4)=="shit") {
      text=insertAt(text," ",p2);
      text2=insertAt(text2," ",p2);
      text3=insertAt(text3," ",p2);
    }
  }
  for(var p = text3.indexOf("shit"); p!=-1; p = text3.indexOf("shit",p+3)) {
    var p2 = prevWhitespace(text,p);
    if(text2.substr(p,4)=="fuck") {
      text=insertAt(text," ",p2);
      text2=insertAt(text2," ",p2);
      text3=insertAt(text3," ",p2);
    }
  }
  for(var p = text3.indexOf("bitch"); p!=-1; p = text3.indexOf("bitch",p+3)) {
    var p2 = prevWhitespace(text,p);
    if(text2.substr(p,5)=="pussy") {
      text=insertAt(text," ",p2);
      text2=insertAt(text2," ",p2);
      text3=insertAt(text3," ",p2);
    }
  }
  for(var p = text3.indexOf("pussy"); p!=-1; p = text3.indexOf("pussy",p+3)) {
    var p2 = prevWhitespace(text,p);
    if(text2.substr(p,5)=="bitch") {
      text=insertAt(text," ",p2);
      text2=insertAt(text2," ",p2);
      text3=insertAt(text3," ",p2);
    }
  }   
  return censor(text,true);
}

function initRecensor() {
  var textareas = doceval("//div[@id=\"content\"]//textarea");
  if(textareas && textareas.snapshotLength>0) {
    var postBox = textareas.snapshotItem(0);
    var timedValidate = function(event) {
      setTimeout(validateFunction,10);
    };
    var validateFunction = function() {
      var selStart = postBox.selectionStart;
      var newText1 = cleanUp(postBox.value.substring(0,selStart));
      var newText2 = cleanUp(postBox.value.substring(selStart));
      if(newText1+newText2!=postBox.value) {
        postBox.value=newText1+newText2;
        postBox.setSelectionRange(newText1.length,newText1.length);
      }
    };
    var validateFunction2 = function(event) {
      postBox.value = cleanUp(postBox.value);
    }
    postBox.addEventListener("focus",timedValidate,true);
    postBox.addEventListener("blur",validateFunction2,true);
    return 0;
  } else
    setTimeout(initRecensor,1000); // GameFOX hasn't added the quickpost box yet
}

function initRecensorTitle() {
  var topicTitle = doceval("//input[@name=\"topictitle\"]");
  if(topicTitle&&topicTitle.snapshotLength>0) {
    topicTitle = topicTitle.snapshotItem(0);
    var validateTitle = function(event) {
      topicTitle.value = cleanUp(topicTitle.value);
    }
    topicTitle.addEventListener("focus",validateTitle,true);
    topicTitle.addEventListener("blur",validateTitle,true);
  } else
    setTimeout(initRecensorTitle,1000);
}

initRecensor();
initRecensorTitle();


// --------------------------------------------------------------------------
// Check for updates
// 
// Much thanks to Jarett - http://userscripts.org/users/38602 - for the code
//---------------------------------------------------------------------------
var version_scriptNum = 97311; // Change this to the number given to the script by userscripts.org (check the address bar)
var version_timestamp = 1298091840552; // Used to differentiate one version of the script from an older one. Use the Date.getTime() function to get a value for this.
function updateCheck(forced) {if((forced)||(parseInt(GM_getValue("lastUpdate", "0")) + 86400000 <= (new Date().getTime()))) {try {GM_xmlhttpRequest({method: "GET",url: "http://userscripts.org/scripts/review/" + version_scriptNum + "?" + new Date().getTime(),headers: {'Cache-Control': 'no-cache'},onload: function(xhrResponse) {GM_setValue("lastUpdate", new Date().getTime() + ""); var rt = xhrResponse.responseText.replace(/&nbsp;?/gm, " ").replace(/&#x000A;/g,"\n").replace(/<li>/gm, "\n").replace(/<[^>]*>/gm, ""); var scriptName = (/@name\s*(.*?)\s*$/m.exec(rt))[1]; GM_setValue("targetScriptName", scriptName); if (parseInt(/version_timestamp\s*=\s*([0-9]+)/.exec(rt)[1]) > version_timestamp) {if (confirm("There is an update available for the Greasemonkey script \"" + scriptName + ".\"\nWould you like to go to the install page now?")) {GM_openInTab("http://userscripts.org/scripts/show/" + version_scriptNum);}} else if (forced) {alert("No update is available for \"" + scriptName + ".\"");}}});} catch (err) {if (forced) {alert("An error occurred while checking for updates:\n" + err);}}}} GM_registerMenuCommand(GM_getValue("targetScriptName", "???") + " - Manual Update Check", function() {updateCheck(true);}); updateCheck(false);