AdBlock for Facebook

Block / Hide "Sponsored" posts on Facebook

  1. // ==UserScript==
  2. // @name AdBlock for Facebook
  3. // @namespace fb-adblock
  4. // @version 1.6
  5. // @description Block / Hide "Sponsored" posts on Facebook
  6. // @author Thor Lancaster
  7. // @match https://www.facebook.com/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. // Add abDbg=true as a URL parameter to debug ad classification
  12.  
  13. (function() {
  14. 'use strict';
  15. var initialWait = 1500;
  16. setTimeout(function(){
  17. if(document.getElementById("stream_pagelet") == null){
  18. console.log("AdBlock for Facebook currently only works on news feeds. Aborting.");
  19. return;
  20. }
  21. console.log("AdBlock for Facebook running");
  22. document.abDbg = window.location.href.includes("abDbg=true");
  23. document.abCheckAll = false; // If true, check every post every time. Comsumes more CPU
  24. document.abTestCheck = null; // Set to an element for debugging to determine if this script thinks it's an ad
  25. var el = (document.getElementById("stream_pagelet"));
  26. var feed = el.querySelectorAll('[role="feed"]')[0];
  27. setInterval(function(f){
  28. try{
  29. checkAds(f);
  30. if(document.abTestCheck != null){ // Check test element to determine whether it is an article and/or an and
  31. var el = document.abTestCheck;
  32. document.abTestCheck = null;
  33. if(!el.getAttribute("role") == "article"){
  34. console.log("Element is not an article");
  35. }
  36. if(isAd(el)){
  37. console.log("Element is an ad");
  38. } else {
  39. console.log("Element is not an ad");
  40. }
  41. }
  42. } catch(e){ // Log error before it is swallowed by FB's obfusicated debugging maw
  43. console.error(e);
  44. }
  45. }, 2000, document);
  46. // TODO check for ads when feed updates rather than every 2 seconds
  47. }, initialWait);
  48.  
  49. function checkAds(feed){
  50. document.lastABRun = new Date().getTime();
  51. var posts = feed.querySelectorAll('[role="article"]');
  52. for(var x = 0; x < posts.length; x++){
  53. var post = posts[x];
  54. // Calculate a quick checksum so that if text changes, the post will be re-scanned
  55. // I suspect that FB was creating non-ad posts and inserting them after a delay
  56. // To spoof the previous version of this script
  57. if(!post.abCheckSum || post.abCheckSum != abCheckSum(post) || document.abCheckAll){
  58. post.abCheckSum = abCheckSum(post);
  59. var isAdType = isAd(post);
  60. if(isAdType == 1){
  61. if(document.abDbg){
  62. markPost(post, "#F70");
  63. } else {
  64. removePost(post);
  65. }
  66. }
  67. // Blank post, should never happen
  68. else if(isAdType == -1){
  69. //console.log("ABCHECK blank post");
  70. }
  71. else if(isAdType == 0 && document.abDbg){
  72. markPost(post, "#0F7"); // for debugging
  73. }
  74. }
  75. }
  76. }
  77. // Color a FB post for debugging purposes
  78. function markPost(post, color){
  79. var els = post.querySelectorAll("*");
  80. for(var x = 0; x < els.length; x++){
  81. els[x].style.background = color;
  82. }
  83. }
  84. function removePost(post){
  85. post.parentElement.removeChild(post);
  86. //post.style.height = "0px";
  87. //post.style.opacity = "0";
  88. }
  89. function isAd(post){
  90. if(post.getAttribute("aria-label") == "Comment"){ // Don't scan comments for ads
  91. return 0;
  92. }
  93. var scan = post.innerText.substring(0, 128);
  94. if(scan.length == 0){
  95. return -1; // Blank post
  96. }
  97. if(scan.includes("·")){
  98. var start = scan.substring(0, scan.indexOf("·"));
  99. if(isSpnsrd(start)){
  100. //console.log("IS AN AD");
  101. return 1;
  102. }
  103. }
  104. //console.log("IS NOT AN AD");
  105. return 0;
  106. }
  107. // Scan for a string that represents "Sponsored"
  108. var SPONSORED = "Sponsored";
  109. function isSpnsrd(str){
  110. var spPtr = 0;
  111. for(var x = 0; x < str.length; x++){
  112. var chr = str[x];
  113. if(chr == SPONSORED[spPtr]){
  114. spPtr++;
  115. if(spPtr == SPONSORED.length){
  116. return true;
  117. }
  118. } else {
  119. var toReset = true;
  120. if(chr == "\n"){
  121. toReset = false;
  122. } else{
  123. for(var y = spPtr; y >= 0; y--){
  124. if(chr == SPONSORED[y]){
  125. toReset = false;
  126. }
  127. }
  128. }
  129. if(toReset){
  130. spPtr = 0;
  131. }
  132. }
  133. }
  134. return false;
  135. }
  136.  
  137. // Calculate the checksum of an element through it's innerText
  138. function abCheckSum(el){
  139. return checkSum(el.innerText);
  140. }
  141.  
  142. // Fast Checksum algorithm. Works great, open-source
  143. // See https://stackoverflow.com/questions/811195/fast-open-source-checksum-for-small-strings
  144. function checkSum(s)
  145. {
  146. var chk = 0x12345678;
  147. var len = s.length;
  148. for (var i = 0; i < len; i++) {
  149. chk += (s.charCodeAt(i) * (i + 1));
  150. }
  151. return (chk & 0xffffffff).toString(16);
  152. }
  153. })();