Filter, Highlight & Delete

Highlights, Lowlights, or Deletes page elements based on their text.

  1. // ==UserScript==
  2. // @name Filter, Highlight & Delete
  3. // @namespace https://github.com/erickRecai
  4. // @version 1.04.07b
  5. // @description Highlights, Lowlights, or Deletes page elements based on their text.
  6. // @author guyRicky
  7.  
  8. // @match *://*/*
  9.  
  10. // @exclude *://docs.google.com/*
  11. // @require https://code.jquery.com/jquery-3.4.1.min.js
  12. // @require https://greasyfork.org/scripts/5392-waitforkeyelements/code/WaitForKeyElements.js?version=115012
  13.  
  14. // @licence CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/
  15. // @licence GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt
  16. // ==/UserScript==
  17. /* jshint esversion: 6 */
  18.  
  19. (function () {
  20. 'use strict';
  21.  
  22. const scriptPrefix = "fhd-"; // for text elements excluding element classes.
  23. const classPrefix = "fhd-"; // exclusively for element classes.
  24. const scriptTag = "FHD"; // used to mark script sources in console log.
  25.  
  26. let runScript = 1;
  27. runScript = getOptionState("enable-"+ scriptPrefix +"script", runScript);
  28.  
  29. // = getOptionState(, );
  30. // used to update option if 'script option' is set.
  31. function getOptionState(idName, currentState) {
  32. if (document.getElementById(idName)) {
  33. return document.getElementById(idName).checked;
  34. }
  35. return currentState;
  36. }
  37.  
  38. if (runScript) {
  39.  
  40. if(0){/*
  41.  
  42. == last update: 7/05/2020 ==
  43. == todo ==
  44. 5. B2, (custom rules script), generate selector block from selectors made.
  45. 10. C1, change autohide notifs to apply per notif, not to all notifs.
  46. ==== how it works =====================================================================|
  47. - checks for "block selectors" from current href.
  48. - if found and custom selectors are found (custom rules script), adds selectors set to list of selectors.
  49. if there are block selectors
  50. checkBlocks() {
  51. foreach (selector set) {
  52. checkblock() {
  53. if (not checked previously) {
  54. check against delete1
  55. check against lowlight1
  56. * check against short1
  57. * check against short2
  58. check against highlight1
  59. check against highlight2
  60. check other things
  61. create notif if enabled.
  62. }
  63. } end checkblock()
  64. } end foreach (selector set)
  65. add 'checked' class to all checked elements
  66. } end checkBlocks()
  67. == other notes
  68. - applies checked marker to a block after a block is fully checked.
  69. ==== version log ======================================================================|
  70. == 1.04.07b ==
  71. - fullCheck on manual runs.
  72. == 1.04.07 ==
  73. - getOptionState()
  74. - retrieveLocalRules()
  75. */}
  76.  
  77. if(0){/*
  78. ==== code markers =====================================================================|
  79.  
  80. A0. console messages
  81. AA. filter lists
  82. AB. selectors by site list
  83. AC. script options
  84. AD. setting parent selectors block
  85. AE. main code start
  86. AF. script CSS
  87. AD. notif block
  88.  
  89. BA. checkBlock()
  90. BB. checkBlocks()
  91. BC. checkRegex()
  92. BD. createNotif()
  93.  
  94. CA. script execution block
  95. CB. script button
  96. ZZ. script end
  97.  
  98. */}
  99.  
  100. // ==== A0. console messages ==============================================================|
  101. // controls if messages are made to the browser's console.
  102.  
  103. let enableLogMessages = 1; // set to 1 to show console messages, required to enable the following message rules.
  104. enableLogMessages = getOptionState("log-"+ scriptPrefix +"msg", enableLogMessages);
  105.  
  106. let enabledMessages =
  107. "DLT|"+
  108. "LL1|"+
  109. //"-MA|"+ // all matches
  110. "selectors|"+
  111.  
  112. "local-rules|"+
  113. //"secondText|"+
  114. "blockHref|"+
  115.  
  116. "RUNT|"+ // run time messages.
  117. "EXEC|"+ // execution messages.
  118. "1"; // high priority messages.
  119.  
  120. let logAll = 0; // if 1, logs all titles from blocks.
  121. logAll = getOptionState("log-"+ scriptPrefix +"all", logAll);
  122. if (logAll) {
  123. enabledMessages = enabledMessages.concat("|title");
  124. }
  125. const enabledMessagesRegex = new RegExp(enabledMessages,"i"); // used in consolelog().
  126.  
  127. consolelog("#### ("+ scriptTag +") start. ####", "EXEC");
  128.  
  129. // ==== AA. filter lists ==================================================================|
  130. let delete1, delete2, lowlight1, lowlight2, highlight1, highlight2;
  131. if (1) {
  132.  
  133. // ==== AAA. delete1 ==================================================================|
  134. delete1 = [
  135. ];
  136.  
  137. // ==== AAB. delete2 ==================================================================|
  138. delete2 = [
  139. ];
  140.  
  141. // ==== AAC. lowlight1 ================================================================|
  142. //strong lowlight
  143. lowlight1 = [
  144.  
  145. ];
  146.  
  147. // ==== AAD. lowlight2 ================================================================|
  148. lowlight2 = [
  149.  
  150. ];
  151.  
  152. // ==== AAE. highlight1 ===============================================================|
  153. highlight1 = [
  154. /apple/i,
  155. ];
  156.  
  157. // ==== AAF. highlight2 ===============================================================|
  158. highlight2 = [
  159. ];
  160.  
  161. // ==== AAG. local rules ==============================================================|
  162. let localRules = retrieveLocalRules("Delete1");
  163. if (localRules) {
  164. delete1 = delete1.concat(localRules);
  165. }
  166. localRules = retrieveLocalRules("Lowlight1");
  167. if (localRules) {
  168. lowlight1 = lowlight1.concat(localRules);
  169. }
  170. localRules = retrieveLocalRules("Highlight1");
  171. if (localRules) {
  172. highlight1 = highlight1.concat(localRules);
  173. }
  174. function retrieveLocalRules(keyName) {
  175. if (window.localStorage.getItem(keyName)) {
  176. let localRules = window.localStorage.getItem(keyName);
  177. localRules = localRules.split(";");
  178. for (let index = 0; index < localRules.length; index++) { // converts a string of rules to an array of regex.
  179. localRules[index] = localRules[index].split("##")[0].trim();
  180. if (localRules[index]) {
  181. let ruleFlags = localRules[index].split("/")[1];
  182. if (ruleFlags) {
  183. ruleFlags = ruleFlags.trim();
  184. }
  185. localRules[index] = localRules[index].split("/")[0].replace(/^\//, "");
  186. localRules[index] = new RegExp(localRules[index], ruleFlags);
  187. ruleFlags = "";
  188. }
  189. }
  190. localRules = localRules.filter(Boolean);
  191. if (localRules) {
  192. consolelog(localRules, "local-rules");
  193. return localRules;
  194. }else {
  195. return false;
  196. }
  197. } // end if (localStorage.getItem(keyName))
  198. return false;
  199. } // end function retrieveLocalRules()
  200. } // end if(1) of filter rules.
  201.  
  202. // ==== AB. selectors by site list ========================================================|
  203. // needs to be set and matched to function on a site.
  204.  
  205. const domainName = window.location.hostname;
  206. const hrefString = window.location.href; //href lets you be more specific
  207.  
  208. const selectorsList = [
  209. {hrefRegex: /reddit\.com/i,
  210. elementSelectors: [
  211. {superParentSelector: "div.ListingLayout-outerContainer div:nth-child(5)>div",
  212. textSelector: 'span>a>h2',
  213. secondTextSelector: "",
  214. hrefSelector: "",
  215. thirdTextSelector: ""},
  216. {superParentSelector: ".Comment",
  217. textSelector: 'div[data-test-id="comment"]>div>p',
  218. secondTextSelector: "",
  219. hrefSelector: "",
  220. thirdTextSelector: ""},
  221.  
  222. ]},
  223.  
  224. {hrefRegex: /www\.youtube\.com/i,
  225. elementSelectors: [
  226. {superParentSelector: "ytd-compact-video-renderer",
  227. textSelector: 'span#video-title',
  228. secondTextSelector: "",
  229. hrefSelector: "",
  230. thirdTextSelector: ""},
  231. ]},
  232.  
  233. {hrefRegex: /news\.google\.com/i,
  234. elementSelectors: [
  235. {superParentSelector: "main>c-wiz>div>div",
  236. textSelector: 'div>article>h3>a',
  237. secondTextSelector: "article:nth-child(2)>div:nth-child(4)>div>a",
  238. hrefSelector: "",
  239. thirdTextSelector: ""},
  240. ]},
  241. ];
  242. if(0){/*
  243. {hrefRegex: ,
  244. elementSelectors: [
  245. {superParentSelector: "", // required; selector for the parent block;
  246. textSelector: "", // ex: title
  247. secondTextSelector: "", //ex: source/author
  248. hrefSelector: "",
  249. thirdTextSelector: ""}, //ex: description
  250. // other than the parent selector, other selectors are optional, order does not need to be kept.
  251. ]},
  252. */}
  253.  
  254. // ==== AC. script options ================================================================|
  255.  
  256. const generalizeSpace = 0; // default 0; if 1, the " " character in Regexs are replaced with (\W|_).
  257.  
  258. // ==== script options with local storage =================================================|
  259. // these options are used as default if the option is not set by the user in (script options).
  260.  
  261. let markCheckedBlocks = 1; // default 1; does all possible checks before applying a special checked class.
  262. //checked in checkBlocks().
  263.  
  264. let dynamicChecking = 1; // default 1; set to 1 to run the script automatically when new image elements are detected.
  265.  
  266. let logRuntimes = 1; // set to 1 to log to console how long this takes to run.
  267.  
  268. let disableDelete = 0; // if 1, delete matches are lowlight1 instead.
  269. let deleteDelete2 = 0; // default 0;
  270.  
  271. // ==== AD. identifying parent selectors block ============================================|
  272. let parentSelectors;
  273. for (let index = 0; index < selectorsList.length; index++) { //if href match, sets custom filters
  274. if (selectorsList[index]["hrefRegex"].test(hrefString)) {
  275.  
  276. let selectorsMessage =
  277. "("+ scriptTag +") href match: "+
  278. selectorsList[index]["hrefRegex"]
  279. ;
  280. consolelog(selectorsMessage, "selectors");
  281.  
  282. parentSelectors = selectorsList[index]["elementSelectors"]; // array of selector sets, usually 1.
  283. break;
  284. }
  285. }
  286. // ==== local selectors ====
  287. let keyList = ["parentSelector", "textSelector", "secondTextSelector", "hrefSelector", "thirdTextSelector"];
  288. if (window.localStorage.getItem(keyList[0])) {
  289. let customSelectors = {
  290. superParentSelector: window.localStorage.getItem(keyList[0]),
  291. textSelector: window.localStorage.getItem(keyList[1]),
  292. secondTextSelector: window.localStorage.getItem(keyList[2]),
  293. hrefSelector: window.localStorage.getItem(keyList[3]),
  294. thirdTextSelector: window.localStorage.getItem(keyList[4])
  295. };
  296. if (parentSelectors) {
  297. parentSelectors.push(customSelectors);
  298. }else {
  299. parentSelectors = [customSelectors];
  300. }
  301. }
  302.  
  303. // ==== AE. main code start ===============================================================|
  304. if (parentSelectors) {
  305. consolelog(parentSelectors, "selectors");
  306.  
  307. // ==== AF. script CSS ================================================================|
  308. let enableScriptCSS = 1;
  309. enableScriptCSS = getOptionState("log-"+ scriptPrefix +"all", enableScriptCSS);
  310.  
  311. if (enableScriptCSS) {
  312. const scriptCSS =
  313. `<style type="text/css">
  314. .`+classPrefix+`dlt1,
  315. .`+classPrefix+`dlt2,
  316. .`+classPrefix+`lowlight1
  317. {opacity: .2;}
  318.  
  319. .`+classPrefix+`dlt1,
  320. .`+classPrefix+`dlt2,
  321. .`+classPrefix+`lowlight1
  322. {background: #f004 !important;}
  323.  
  324. .`+classPrefix+`lowlight2 {
  325. opacity: .5;
  326. background: #ff9800 !important;
  327. }
  328.  
  329. .`+classPrefix+`highlight1
  330. {background: #62bb66 !important;}
  331. .`+classPrefix+`highlight2
  332. {background: #fff281 !important;}
  333. </style>`;
  334. jQuery(document.body).append(scriptCSS);
  335. }
  336.  
  337. // ==== AD. notif block ===============================================================|
  338.  
  339. // 'script options' options
  340. let enableBlockCounter = 0;
  341. enableBlockCounter = getOptionState("enable-"+ scriptPrefix +"counter", enableBlockCounter);
  342. let enableNotifications = 0;
  343. enableNotifications = getOptionState("enable-"+ scriptPrefix +"notifs", enableNotifications);
  344. let autohideNotifs = 0; // default 0; notifs disappear after a set period of time. used in createNotif().
  345. let startCollapsed = 1; // default 1;
  346.  
  347. // notif css variables.
  348. const notifsHex = "#ddd";
  349. const notifsOpacity = .4; // default .4; set to a value between 0 and 1, 1 is no transparency, .5 is 50% transparency.
  350. const notifsWidth = 120; // default 120; width in pixels of each notification.
  351.  
  352. let notifContainerId = "notif-main-container";
  353.  
  354. // generate notif container if needed.
  355. if ((enableBlockCounter || enableNotifications) && !jQuery("#"+ notifContainerId).length) {
  356.  
  357. // ==== setting/checking initial visual state of notifs ===========================|
  358.  
  359. // controlled exclusively by local storage or the default value.
  360. const localStorageName = "notif start collapsed";
  361. if (window.localStorage.getItem(localStorageName)) {
  362. startCollapsed = window.localStorage.getItem(localStorageName);
  363. startCollapsed = (startCollapsed == "true");
  364. }
  365.  
  366. const visibleClass = "notif-visible";
  367. const hiddenClass = "notif-hidden1";
  368. let startingStateClass = visibleClass;
  369. let otherStartingStateClass = hiddenClass;
  370. if (startCollapsed) {
  371. startingStateClass = hiddenClass;
  372. otherStartingStateClass = visibleClass;
  373. }
  374.  
  375. // ==== create container ==========================================================|
  376. /*
  377. [ notif main container
  378. [notif1] - content
  379. [hide] - button
  380. [open] - button
  381. [close] - button
  382. [clear] - button
  383. [notif2] - content
  384. ]
  385. - hide: makes visible open | hides close, clear, notif2
  386. - open: makes visible hide, close, clear, notif2 | hides open
  387. - close: deletes notif main container.
  388. - clear: empties notif-container2
  389. */
  390.  
  391. const openButtonId = "notif-open";
  392. const hideButtonId = "notif-hide";
  393.  
  394. let notificationsElement =
  395. "<div id='"+ notifContainerId +"'>"+
  396. "<div id='notif-container1'></div>"+
  397. "<div id='"+ hideButtonId +"' class='notif-red notif-rounded-block "+ startingStateClass +"'>notif hide</div>"+
  398. "<div id='"+ openButtonId +"' class='notif-green notif-rounded-block "+ otherStartingStateClass +"'>notif open</div>"+
  399. "<div id='notif-close' class='notif-gray notif-rounded-block "+ startingStateClass +"'>close notif[]</div>"+
  400. "<div id='notif-clear' class='notif-orange notif-rounded-block "+ startingStateClass +"'>clear notif</div>"+
  401. "<div id='notif-container2' class=' "+ startingStateClass +"'>"+
  402. "<div id='dlt-container'></div>"+
  403. "<div id='ll-container' class='notif-hidden1'></div>"+
  404. "<div id='ot-container' class='notif-hidden1'></div>"+
  405. "</div>"+
  406. "</div>";
  407. jQuery("body").prepend(notificationsElement);
  408.  
  409. let textReaderElement =
  410. "<div id='notif-text-overlay' class='notif-text-hidden'></div>";
  411. jQuery("body").prepend(textReaderElement);
  412.  
  413. $('#notif-container2').on( {
  414. mouseenter: function () {
  415. let notifText = $(this).find(".notif-text").text();
  416. let notifClassList = this.className;
  417. if (/red/.test(notifClassList)) {
  418. jQuery("#notif-text-overlay").addClass("notif-red");
  419. }else if (/orange/.test(notifClassList)) {
  420. jQuery("#notif-text-overlay").addClass("notif-orange");
  421. }else if (/yellow/.test(notifClassList)) {
  422. jQuery("#notif-text-overlay").addClass("notif-yellow");
  423. }else {
  424. jQuery("#notif-text-overlay").addClass("notif-gray");
  425. }
  426. jQuery("#notif-text-overlay").text(notifText);
  427. jQuery("#notif-text-overlay").addClass("notif-text-visible");
  428. },
  429. mouseleave: function () {
  430. jQuery("#notif-text-overlay").removeClass("notif-text-visible");
  431. jQuery("#notif-text-overlay").removeClass("notif-red");
  432. jQuery("#notif-text-overlay").removeClass("notif-orange");
  433. }
  434. }, '.notif-instance');
  435.  
  436. // ==== close ====
  437. jQuery("#notif-close").click(function(){jQuery("#"+ notifContainerId).remove();});
  438.  
  439. // ==== clears container2 which contains notif instances. ====
  440. function clearNotif(){
  441. jQuery("#notif-container2").empty();
  442. }
  443. jQuery("#notif-clear").click(clearNotif);
  444.  
  445. // ==== open/hide events ==========================================================|
  446.  
  447. const mainSelector = "#notif-container2, #"+ hideButtonId +", #notif-close, #notif-clear";
  448.  
  449. jQuery("#"+ hideButtonId).click(function () {
  450. //console.log(hideButtonId);
  451. window.localStorage.setItem(localStorageName, true);
  452.  
  453. switchClasses(
  454. mainSelector,
  455. "#"+ openButtonId,
  456. visibleClass,
  457. hiddenClass
  458. );
  459. });
  460.  
  461. jQuery("#"+ openButtonId).click(function () {
  462. //console.log(openButtonId);
  463. window.localStorage.setItem(localStorageName, false);
  464.  
  465. switchClasses(
  466. mainSelector,
  467. "#"+ openButtonId,
  468. hiddenClass,
  469. visibleClass
  470. );
  471. });
  472.  
  473. function switchClasses(mainSelector, subSelector, removedClass, newClass) {
  474. jQuery(mainSelector).removeClass(removedClass);
  475. jQuery(mainSelector).addClass(newClass);
  476. jQuery(subSelector).removeClass(newClass);
  477. jQuery(subSelector).addClass(removedClass);
  478. }
  479.  
  480. // ==== notif CSS =================================================================|
  481. if(1){var notifsCss =
  482. `<style type="text/css">
  483. #`+ notifContainerId +` {
  484. width: `+ notifsWidth +`px;
  485. max-height: 50%;
  486. margin: 0 2px 2px;
  487. display: block;
  488.  
  489. line-height: initial;
  490. color: #000;
  491. opacity: `+ notifsOpacity +`;
  492. position: fixed;
  493. top: 0px;
  494. right: 0px;
  495. z-index: 9999;
  496. overflow-y: auto;
  497. }
  498. #`+ notifContainerId +`:hover {
  499. opacity: 1;
  500. }
  501.  
  502. .notif-rounded-block {
  503. display: block;
  504. padding: 2px;
  505. border-radius: 3px;
  506. margin-top: 2px;
  507.  
  508. font-size: 11px !important;
  509. font-weight: bold;
  510. text-align: center;
  511. cursor: pointer;
  512. }
  513.  
  514. .s-counter {
  515. display: block;
  516. padding: 2px;
  517. border-radius: 4px;
  518. margin-top: 2px;
  519.  
  520. background: #ddd;
  521. font-size: 11px !important;
  522. font-weight: bold;
  523. text-align: center;
  524. }
  525.  
  526. .notif-text-hidden {
  527. display:none;
  528. }
  529. .notif-text-visible {
  530. display: block;
  531. max-width: 50%;
  532. padding: 5px;
  533. border: #999 solid 2px;
  534. border-radius: 10px;
  535.  
  536. position: fixed;
  537. top: 5px;
  538. left: 5px;
  539. z-index: 999999;
  540.  
  541.  
  542. font-size: 15px !important;
  543. font-weight: bold !important;
  544. text-align: center !important;
  545. color: black !important;
  546. }
  547.  
  548. .notif-instance {
  549. display: block;
  550. padding: 2px;
  551. border-radius: 4px;
  552. margin-top: 2px;
  553.  
  554. background: `+ notifsHex +`;
  555. font-size: 11px !important;
  556. font-weight: bold;
  557. text-align: center;
  558. cursor: pointer;
  559. }
  560.  
  561. .notif-instance div{/* div holding the rule.*/
  562. max-height: 12px;
  563. padding: 0px;
  564. margin: 0px;
  565. border: 0px;
  566.  
  567. overflow: hidden;
  568. word-break: break-all;
  569. }
  570. .notif-hidden{ /* meant to hide the rule */
  571. opacity: .1;
  572. }
  573. .notif-hidden:hover {
  574. opacity: 1;
  575. }
  576.  
  577. .notif-red {
  578. background: #f67066;
  579. }
  580. .notif-orange {
  581. background: #ffc107; //yellowish
  582. }
  583. .notif-green {
  584. background: #62bb66;
  585. }
  586. .notif-gray {
  587. background: #777;
  588. }
  589.  
  590. /* collapsible classes */
  591. .notif-hidden1 {
  592. display: none !important;
  593. }
  594. .notif-visible {
  595. display: block !important;
  596. }
  597.  
  598. div#ll-container, div#ot-container {
  599. border-top: solid black 3px;
  600. }
  601. </style>`;
  602. }
  603. jQuery(document.body).append(notifsCss);
  604. }
  605. if (enableBlockCounter) {
  606. jQuery("#notif-container1").prepend("<div id='"+ scriptTag +"-counter' class='s-counter .notif-rounded-block'>B No Blocks Matched.</div>");
  607. }
  608.  
  609. // ==== BA. checkBlock() ==============================================================|
  610.  
  611. // ==== checkBlock globals ====
  612. var currentElementSelectors = "";
  613. let elementCounter = 0;
  614. let elementsDeleted = 0;
  615. let lowlight1Counter = 0;
  616. //internal state, checks all blocks regardless if previously, changes state when function is ran manually.
  617. let fullCheck = 0;
  618.  
  619. // used to find if classlist contains the checked class marker.
  620. const checkedClassRegex = new RegExp(classPrefix +"element");
  621.  
  622. // checks values within a specific block to choose what class to add to it.
  623. function checkBlock() {
  624.  
  625. if(!fullCheck){elementCounter++;}
  626.  
  627. let parentClassList = "";
  628. if (jQuery(this).attr("class")) {
  629. parentClassList = jQuery(this).attr("class");
  630. //consolelog(classList, 1); // test: doublecheck parent class list
  631. }
  632.  
  633. // main check block, only run if not checked or a check is forced.
  634. if (!parentClassList ||
  635. !checkedClassRegex.test(parentClassList) ||
  636. fullCheck) {
  637.  
  638. // ==== retrieve block data ===================================================|
  639. let blockTitle, secondText, thirdText, blockHref;
  640. if (currentElementSelectors && currentElementSelectors["textSelector"]) {
  641. blockTitle = jQuery(this).find(currentElementSelectors["textSelector"]).text();
  642. if(blockTitle){consolelog("("+ scriptTag +") t1 ["+ blockTitle +"]", "title");}
  643. }
  644. if (currentElementSelectors && currentElementSelectors["secondTextSelector"]) {
  645. secondText = jQuery(this).find(currentElementSelectors["secondTextSelector"]).text();
  646. if(secondText){consolelog("("+ scriptTag +") t2 ["+ secondText +"]", "secondText");}
  647. }
  648. if (currentElementSelectors && currentElementSelectors["thirdTextSelector"]) {
  649. thirdText = jQuery(this).find(currentElementSelectors["thirdTextSelector"]).text();
  650. }
  651. if (currentElementSelectors && currentElementSelectors["hrefSelector"]) {
  652. blockHref = jQuery(this).find(currentElementSelectors["hrefSelector"]).attr("href");
  653. if(blockHref){consolelog("("+ scriptTag +") href ["+ blockHref +"]", "blockHref");}
  654. }
  655.  
  656. if (blockTitle || blockHref) {
  657.  
  658. if (blockTitle) {
  659. blockTitle = blockTitle.trim();
  660. }
  661.  
  662. let matchCode = "n0";
  663. let filterType = "txt";
  664. disableDelete = getOptionState("disable-delete", disableDelete);
  665.  
  666. // stops after the first rule matched.
  667. // ## delete 1 ## =========================================================|
  668. if (checkRegex(blockTitle, delete1) ||
  669. /dlt1/i.test(blockTitle) || //cases caught by text replace script.
  670. (secondText && (checkRegex(secondText, delete1) || /dlt1/i.test(secondText))) ||
  671. (blockHref && checkRegex(blockHref, delete1))) {
  672.  
  673. matchCode = "dlt1";
  674. if (secondText && checkRegex(secondText, delete1)) {
  675. filterType = "2nd";
  676. }else if (blockHref && checkRegex(blockHref, delete1)) {
  677. filterType = "href";
  678. }
  679.  
  680. if (!disableDelete) {
  681. jQuery(this).remove();
  682. elementsDeleted++;
  683. }else {
  684. jQuery(this).addClass(classPrefix + matchCode);
  685. lowlight1Counter++;
  686. }
  687. // ## delete 2 ## =========================================================|
  688.  
  689. }else if (checkRegex(blockTitle, delete2) || /dlt2/i.test(blockTitle) || (blockHref && checkRegex(blockHref, delete2))) {
  690. matchCode = "dlt2";
  691.  
  692. deleteDelete2 = getOptionState("delete-delete2", deleteDelete2);
  693.  
  694. if (!disableDelete && deleteDelete2) {
  695. jQuery(this).remove();
  696. elementsDeleted++;
  697. }else {
  698. jQuery(this).addClass(classPrefix + matchCode);
  699. lowlight1Counter++;
  700. }
  701. // ## lowlight 1 ## =======================================================|
  702.  
  703. }else if (checkRegex(blockTitle, lowlight1) ||
  704. checkRegex(blockTitle, lowlight1) ||
  705. /fr1/i.test(blockTitle) ||
  706. (blockHref && checkRegex(blockHref, lowlight1))) {
  707.  
  708. matchCode = "ll1";
  709. jQuery(this).addClass(classPrefix +"lowlight1");
  710. lowlight1Counter++;
  711. // ## lowlight 2 ## =======================================================|
  712.  
  713. }else if (checkRegex(blockTitle, lowlight2)) {
  714. matchCode = "ll2";
  715. jQuery(this).addClass(classPrefix +"lowlight2");
  716. }
  717.  
  718. // ## highlight 1/2 ## ====================================================|
  719.  
  720. if (matchCode == "n0") {
  721. if (checkRegex(blockTitle, highlight1)) {
  722. matchCode = "h1";
  723. jQuery(this).addClass(classPrefix +"highlight1");
  724. }else if (checkRegex(blockTitle, highlight2)) {
  725. matchCode = "h2";
  726. jQuery(this).addClass(classPrefix +"highlight2");
  727. }
  728. //additionalCheck(searchText);
  729. }
  730.  
  731. createNotif(elementCounter +" ("+ filterType +")"+ matchCode, regexLog, blockTitle);
  732.  
  733. if (enableLogMessages) {
  734. let printMsg;
  735. printMsg = "("+ scriptTag +") ";
  736. printMsg += elementCounter;
  737. printMsg += " ("+ matchCode +")";
  738. printMsg += ": "+ blockTitle;
  739. if(regexLog){printMsg += "("+ regexLog +")";}
  740. if(thirdText){printMsg += "("+ thirdText.trim() +")";}
  741. //printMsg += "("+ groupLink +")";
  742. consolelog(printMsg, matchCode +"-MA");
  743. }
  744. if (regexLog) {regexLog = "";}
  745. }else {
  746. //consolelog("("+ classPrefix +"element-"+ elementCounter +")("+ selectorType +") no text found.");
  747. }
  748. }
  749.  
  750. } // end function checkBlock()
  751.  
  752. // ==== BB. checkBlocks() =============================================================|
  753.  
  754. let printedSelectors = 0; // used to print parentSelectors just once.
  755. function checkBlocks() {
  756.  
  757. logRuntimes = getOptionState("log-"+ scriptPrefix +"runtimes", logRuntimes);
  758.  
  759. if (enableLogMessages && logRuntimes) {
  760. var startTime = performance.now();
  761. }
  762. // on sites with multiple block selectors, checks against all block selectors
  763. for (let index = 0; index < parentSelectors.length; index++) {
  764. currentElementSelectors = parentSelectors[index];
  765. let superParentSelector = currentElementSelectors["superParentSelector"];
  766. let textSelector = currentElementSelectors["textSelector"];
  767. let secondTextSelector = currentElementSelectors["secondTextSelector"];
  768. let hrefSelector = currentElementSelectors["hrefSelector"];
  769. let thirdTextSelector = currentElementSelectors["thirdTextSelector"];
  770. let selectorsMessage =
  771. "("+ scriptTag +") selectors: "+
  772. superParentSelector +" | "+
  773. textSelector +" | "+
  774. secondTextSelector +" | "+
  775. hrefSelector +" | "+
  776. thirdTextSelector +" ##";
  777. if (!printedSelectors) {
  778. printedSelectors = 1;
  779. consolelog( selectorsMessage, "selectors");
  780. }
  781.  
  782. jQuery(superParentSelector).each(checkBlock);
  783. }
  784.  
  785. // ==== update block counter ====
  786. if (elementCounter && !fullCheck) {
  787. let counterText = "B DLT: "+ elementsDeleted +" | LL1: "+ lowlight1Counter +" | B: "+ elementCounter;
  788. jQuery("#"+ scriptTag +"-counter").text(counterText);
  789. jQuery("#"+ scriptTag +"-counter").addClass("notif-green");
  790. }
  791.  
  792. // adds a marker as a class that identifies what was checked.
  793. let elementLog = "";
  794. markCheckedBlocks = getOptionState(scriptPrefix +"mark-checked", markCheckedBlocks);
  795. if (markCheckedBlocks && !fullCheck) {
  796. // for each selector
  797. elementCounter = 0;
  798. for (let index = 0; index < parentSelectors.length; index++) {
  799. //for each block selected
  800. jQuery(parentSelectors[index]["superParentSelector"]).each(function(){
  801. let classList = "";
  802. if (jQuery(this).attr("class")) {
  803. classList = jQuery(this).attr("class");
  804. }
  805. if (!classList || !checkedClassRegex.test(classList)){
  806. elementCounter++;
  807. jQuery(this).addClass(classPrefix +"element-"+ elementCounter);
  808. elementLog = elementLog +" "+ elementCounter;
  809. }
  810.  
  811. });
  812. }
  813. //console.log(elementLog); //test: check checked elements
  814. }
  815.  
  816. if (fullCheck) {
  817. fullCheck = 0;
  818. }
  819.  
  820. // logs the run time of the script.
  821. if (enableLogMessages && logRuntimes) {
  822. const endTime = performance.now();
  823. const runTime = ((endTime - startTime) / 1000).toFixed(2);
  824. if (runTime < 1) {
  825. consolelog("("+ scriptTag +") finished in less than 1 second.", "RUNT");
  826. }else {
  827. consolelog("("+ scriptTag +") finished after " + runTime + " seconds.", "RUNT");
  828. }
  829. }
  830. }
  831.  
  832. // ==== BC. checkRegex() ==============================================================|
  833.  
  834. // checks string against given list of regular expressions.
  835. // resets lastIndex of global regex
  836. let regexLog = "";
  837. function checkRegex(string, regexList) {
  838. for (let i = 0; i < regexList.length; i++) {
  839. let regexRule = regexList[i];
  840. if (generalizeSpace && / /.test(regexRule.toString())) {
  841. let regexFlags = regexRule.flags;
  842. regexRule = new RegExp(regexRule.toString().replace(/ /g,"(\\W|_)").replace(/(^\/|\/$|\/\w*)/g,''), regexFlags); // converts /ab cd/i to /ab(\W|_)cd)i
  843. }
  844. if (regexRule.test(string)) {
  845. if (regexLog) {
  846. regexLog = regexLog +", "+ regexList[i];
  847. }else {
  848. regexLog = regexList[i];
  849. }
  850. if (/\/i?g/.test(regexList[i])) {//regex global modifier needs to be reset.
  851. regexList[i].lastIndex = 0;
  852. }
  853. return true;
  854. }
  855. }
  856. return false;
  857. }
  858.  
  859. // ==== BD. createNotif() =============================================================|
  860.  
  861. let scriptChar = "b"; // indicator of which script created this notif.
  862. function createNotif(notifLabel, notifRule, notifText) { //notifRule needs to match notifTypes
  863. enableNotifications = getOptionState("enable-"+ scriptPrefix +"notifs", enableNotifications);
  864. if (enableNotifications) {
  865. let additionalClass, notifContainer;
  866. if (/dlt/i.test(notifLabel) && /dlt/i.test(notifText)) { // caught by text replace
  867. additionalClass = "notif-yellow";
  868. notifContainer = "dlt-container";
  869. }else if (/dlt/i.test(notifLabel)) {
  870. additionalClass = "notif-red";
  871. notifContainer = "dlt-container";
  872. }else if (/ll/i.test(notifLabel)) {
  873. additionalClass = "notif-orange";
  874. notifContainer = "ll-container";
  875. }else {
  876. additionalClass = "notif-gray";
  877. notifContainer = "ot-container";
  878. }
  879.  
  880. let newNotif =
  881. "<div class='notif-instance "+ additionalClass +"'><div>"+ scriptChar +" "+ notifLabel +"</div>"+
  882. "<div class='notif-hidden'>"+ notifRule +"</div>"+
  883. "<div class='notif-text' hidden>"+ notifText+"</div>"+ // to be displayed at the bottom left.
  884. "</div>";
  885.  
  886. const enabledNotifTypesString = // set to only create notifs for dlt1 and ll1.
  887. "dlt1"+
  888. "|ll1";
  889. const enabledNotifTypesRegex = new RegExp(enabledNotifTypesString, "i");
  890. if (enabledNotifTypesRegex.test(notifLabel)) {
  891. jQuery("#"+ notifContainer).append(newNotif);
  892. jQuery(".notif-instance").click(function(){
  893. jQuery("#notif-container2").empty();
  894. });
  895.  
  896. if (/ll1/i.test(notifLabel)) {
  897. jQuery("#ll-container").removeClass("notif-hidden1");
  898. }
  899.  
  900. autohideNotifs = getOptionState("autohide-notifications", autohideNotifs);
  901. if (autohideNotifs) {
  902. const notifDuration = 10; // default 10; amount of seconds notifications are displayed before disappearing.
  903. setTimeout(function() {
  904. jQuery(".notif-instance").remove();
  905. }, notifDuration*1000);
  906. }
  907. }
  908. }
  909. } // end function createNotif()
  910.  
  911. // ==== CA. script execution block ====================================================|
  912.  
  913. consolelog("("+ scriptTag +") EXEC: Initial run.", "EXEC");
  914. checkBlocks();
  915. let runWhenReady = 0;
  916. runWhenReady = getOptionState("run-when-ready", runWhenReady);
  917. if (runWhenReady) {
  918. jQuery(document).ready(function() { //after DOM has loaded.
  919. consolelog("("+ scriptTag +") EXEC: document.ready()", "EXEC");
  920. //fullCheck = 1;
  921. checkBlocks();
  922. });
  923. }
  924.  
  925. let runWhenLoaded = 0;
  926. runWhenLoaded = getOptionState("run-when-loaded", runWhenLoaded);
  927. if (runWhenLoaded) {
  928. jQuery(window).on("load", function() { //after all initial images are loaded.
  929. consolelog("("+ scriptTag +") EXEC: window.load()", "EXEC");
  930. //fullCheck = 1;
  931. checkBlocks();
  932. });
  933. }
  934.  
  935. dynamicChecking = getOptionState("dynamic-checking", dynamicChecking);
  936. if (dynamicChecking) {
  937. let allSelectors = "";
  938. for (let index = 0; index < parentSelectors.length; index++) {
  939. if (allSelectors) {
  940. allSelectors += ","+ parentSelectors[index]["superParentSelector"];
  941. }else {
  942. allSelectors = parentSelectors[index]["superParentSelector"];
  943. }
  944. }
  945. //consolelog(allSelectors, "selectors");
  946.  
  947. waitForKeyElements(allSelectors, function(){
  948. //consolelog("("+ scriptTag +") EXEC: waitForKeyElements().", "EXEC");
  949. checkBlocks();
  950. });
  951. }
  952.  
  953. // ==== CB. script button =============================================================|
  954. let buttonContainerId = "ctb-container1";
  955.  
  956. if (jQuery("#"+ buttonContainerId).length) {
  957. jQuery("#"+ buttonContainerId).prepend("<div id='"+ scriptTag +"-reset' class='ctb-blue ctb-rounded-block'>run "+ scriptTag +"</div>"); //added to beginning
  958. jQuery("#"+ scriptTag +"-reset").click(function () {
  959. consolelog("("+ scriptTag +") EXEC: Script button.", "EXEC");
  960. elementCounter = 0;
  961. fullCheck = 1;
  962. checkBlocks();
  963. });
  964. }
  965. } //end if (parentSelectors)
  966.  
  967. function consolelog(text, messageType) {
  968. if (enableLogMessages && enabledMessagesRegex.test(messageType)) { // script option; defined at top.
  969. console.log(text);
  970. }
  971. }
  972.  
  973. // ==== ZZ. script end ====================================================================|
  974. consolelog("#### ("+ scriptTag +") script finished. ####", "EXEC");
  975.  
  976. } // end if (runScript)
  977.  
  978. })();