Advertisement
creamygoat

Harmonator Greasemonkey script

Sep 21st, 2013
361
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name        Harmonator
  3. // @namespace   Dalek-Human-Alliance
  4. // @description Harmonator makes some paragraphs pink.
  5. // @include     http://*
  6. // @include     https://*
  7. // @grant       none
  8. // @version     1.0.0.0
  9. // ==/UserScript==
  10.  
  11.  
  12. //------------------------------------------------------------------------------
  13. // Index
  14. //------------------------------------------------------------------------------
  15.  
  16.  
  17. // Debugging functions
  18. //   FunctionName(): String
  19. //   Defined(x): Boolean
  20. //   Assigned(x): Boolean
  21. //   Assert(Assertion, ErrorMsg)
  22.  
  23. // Object helper functions
  24. //   IsEmpty(ObjectOrArray): Boolean
  25.  
  26. // String helper functions
  27. //   String.TrimLeft(): String
  28. //   String.TrimRight(): String
  29. //   String.Trim(): String
  30. //   String.TrimDS(): String
  31. //   String.HasWord(Word): Boolean
  32. //   String.AddWord(Word): String
  33. //   String.IncludeWord(Word): String
  34. //   String.ExcludeWord(Word): String
  35.  
  36. // HTML/CSS helper functions
  37. //   GetFirstElementByTagName(Root, TagName): Element
  38. //   GetFirstElementByTagNameAndClass(Root, TagName, ClassName): Element
  39. //   AddCSS(CSSText): Element
  40. //   HasClass(Element, ClassName): Boolean
  41. //   IncludeClass(Element, ClassName)
  42. //   ExcludeClass(Element, ClassName)
  43. //   SetClassBoolean(Element, ClassName, DoInclude)
  44.  
  45. // Harmonator implementation
  46. //   AddCustomStyles(): Element
  47. //   StyleUnsightlyText(Node, VisitedClassName, MarkedClassName, Keywords)
  48. //   Harmonate(Node, Keywords)
  49. //   Main()
  50.  
  51.  
  52. //------------------------------------------------------------------------------
  53. // Debugging functions
  54. //------------------------------------------------------------------------------
  55.  
  56.  
  57. // FunctionName(): String
  58. //
  59. // Returns the name of whatever function calls this. The call operator "()" is
  60. // excluded.
  61.  
  62. function FunctionName() {
  63.  
  64.   return arguments.callee.caller.name;
  65.  
  66. }
  67.  
  68.  
  69. //------------------------------------------------------------------------------
  70.  
  71.  
  72. // Defined(x): Boolean
  73. //
  74. // Returns true iff x is defined. A defined value may be an empty string, zero,
  75. // a NaN or even null.
  76.  
  77. function Defined(x) {
  78.   return typeof x != 'undefined';
  79. }
  80.  
  81.  
  82. //------------------------------------------------------------------------------
  83.  
  84.  
  85. // Assigned(x): Boolean
  86. //
  87. // Returns true iff x is both defined and assigned any value but null. The
  88. // name "Assigned" is intended to mirror the Delphi function for testing if
  89. // a pointer is not nil, but is augmented to fit with Javascript's inclusion
  90. // of a distinct 'undefined' datatype.
  91. //
  92. // This function is useful for object references. It enhances code clarity and
  93. // lessens the risk of the relational operator used to test for assignment being
  94. // mistakenly applied to surrounding expression terms.
  95.  
  96. function Assigned(x) {
  97.  
  98.   return Defined(x) && (x != null);
  99.  
  100. }
  101.  
  102.  
  103. //------------------------------------------------------------------------------
  104.  
  105.  
  106. // Assert(Assertion, ErrorMsg)
  107. //
  108. // If Assertion is false, an exception with the given error message is raised.
  109.  
  110. function Assert(Assertion, ErrorMsg) {
  111.  
  112.   if (!Assertion) {
  113.     throw new Error(ErrorMsg);
  114.   }
  115.  
  116. }
  117.  
  118.  
  119. //------------------------------------------------------------------------------
  120. // Object helper functions
  121. //------------------------------------------------------------------------------
  122.  
  123.  
  124. // IsEmpty(ObjectOrArray): Boolean
  125. //
  126. // Returns true iff the array or object has no elements or properties (key-value
  127. // pairs), not counting those that are a part of the property chain, the means
  128. // by which new methods are defined for new objects created with their usual
  129. // constructors.
  130.  
  131. function IsEmpty(ObjectOrArray) {
  132.  
  133.   var Result;
  134.   var Key;
  135.  
  136.   Assert(
  137.     typeof ObjectOrArray == 'object',
  138.     'IsEmpty() cannot be used on anything of the "' +
  139.       (typeof ObjectOrArray) + '" data type.'
  140.   )
  141.  
  142.   if (ObjectOrArray instanceof Array) {
  143.  
  144.     Result = ObjectOrArray.length == 0;
  145.  
  146.   } else {
  147.  
  148.     Result = true;
  149.  
  150.     for (Key in ObjectOrArray) {
  151.  
  152.       if (ObjectOrArray.hasOwnProperty(Key)) {
  153.  
  154.         // This object has a property it truly owns, not just something
  155.         // that's just a part of the property chain. Therefore, the object
  156.         // is not empty.
  157.  
  158.         Result = false;
  159.         break;
  160.  
  161.       }
  162.  
  163.     }
  164.  
  165.   }
  166.  
  167.   return Result;
  168.  
  169. }
  170.  
  171.  
  172. //------------------------------------------------------------------------------
  173. // String helper functions
  174. //------------------------------------------------------------------------------
  175.  
  176.  
  177. // String.TrimLeft(): String
  178. //
  179. // Returns the string with leading spaces removed.
  180.  
  181. String.prototype.TrimLeft = function() {
  182.   return this.replace(/^ +/, '');
  183. }
  184.  
  185.  
  186. //------------------------------------------------------------------------------
  187.  
  188.  
  189. // String.TrimRight(): String
  190. //
  191. // Returns the string with trailing spaces removed.
  192.  
  193. String.prototype.TrimRight = function() {
  194.   return this.replace(/ +$/, '');
  195. }
  196.  
  197.  
  198. //------------------------------------------------------------------------------
  199.  
  200.  
  201. // String.Trim(): String
  202. //
  203. // Returns the string with both leading and trailing spaces removed.
  204.  
  205. String.prototype.Trim = function() {
  206.   return this.TrimRight().TrimLeft();
  207. }
  208.  
  209.  
  210. //------------------------------------------------------------------------------
  211.  
  212.  
  213. // String.TrimDS(): String
  214. //
  215. // Returns the string with leading and trailing spaces removed and with any
  216. // run of spaces replaced with just one space. "DS" refers to doubled spaces,
  217.  
  218. String.prototype.TrimDS = function() {
  219.   return this.replace(/ {2,}/g, ' ').Trim();
  220. }
  221.  
  222.  
  223. //------------------------------------------------------------------------------
  224.  
  225.  
  226. // String.HasWord(Word): Boolean
  227. //
  228. // Returns true iff the string contains an occurrence of Word delimited
  229. // at each end by either a space or the start or end of the string.
  230. //
  231. // Trailing or leading spaces in Word are ignored.
  232. //
  233. // For convenience, Word may be undefined, unassigned, an empty string or
  234. // a string containing nothing but spaces. In those cases this method will
  235. // return false.
  236.  
  237. String.prototype.HasWord = function(Word) {
  238.  
  239.   var Result = false;
  240.   var ValWord = Defined(Word) ? Word.Trim() : '';
  241.  
  242.   if (ValWord) {
  243.  
  244.     var Padded = ' ' + this + ' ';
  245.     var IsFound = Padded.search(ValWord) >= 0;
  246.     return IsFound;
  247.  
  248.   }
  249.  
  250.   return Result;
  251.  
  252. }
  253.  
  254.  
  255. //------------------------------------------------------------------------------
  256.  
  257.  
  258. // String.AddWord(Word): String
  259. //
  260. // Returns the original string with Word appended to it, separated by a space,
  261. // stripping trailing and leading spaces from (copies of) both the original
  262. // string and Word.
  263. //
  264. // For convenience, word may be unassigned, an empty string or a string
  265. // containing only spaces. In those cases this method will return the
  266. // (trimmed) original string.
  267. //
  268. // The original string may be empty or full of spaces, in which case the
  269. // result is Word (or an empty string is Word is empty or undefined).
  270. //
  271. // It's permissible for Word to consist of multiple space-delimited words. This
  272. // function is really a space-delimited word string concatenation function. The
  273. // name "AddWord" reflects the anticipated use.
  274.  
  275. String.prototype.AddWord = function(Word) {
  276.  
  277.   var Result;
  278.   var ValStr = this.Trim();
  279.   var ValWord = Defined(Word) ? Word.Trim() : '';
  280.  
  281.   if (!ValStr) {
  282.     Result = ValWord;
  283.   } else if (ValWord) {
  284.     Result = ValStr + ' ' + ValWord;
  285.   } else {
  286.     Result = ValStr;
  287.   }
  288.  
  289.   Result = Result.TrimDS();
  290.  
  291.   return Result;
  292.  
  293. }
  294.  
  295.  
  296. //------------------------------------------------------------------------------
  297.  
  298.  
  299. // String.IncludeWord(Word): String
  300. //
  301. // Returns the existing string with Word appended to it, but only if Word
  302. // does not already appear in the space-delimited string of words. Else the
  303. // original string is returned (with leading or trailing spaces removed).
  304. //
  305. // Words are considered distinct even if they only differ by letter case.
  306. //
  307. // As with AddWord(), the existing string may be empty and Word may be empty
  308. // or undefined.
  309.  
  310. String.prototype.IncludeWord = function(Word) {
  311.  
  312.   if (this.HasWord(Word)) {
  313.     return this.TrimDS();
  314.   } else {
  315.     return this.AddWord(Word)
  316.   }
  317.  
  318. }
  319.  
  320.  
  321. //------------------------------------------------------------------------------
  322.  
  323.  
  324. // String.ExcludeWord(Word): String
  325. //
  326. // Removes any occurrence of Word in the space-delimited string of words.
  327. // Words are considered distinct even if they only differ by letter case.
  328. //
  329. // As with IncludeWord(), the existing string may be empty and Word may be
  330. // empty or undefined.
  331.  
  332. String.prototype.ExcludeWord = function(Word) {
  333.  
  334.   var ValWord;
  335.   var re;
  336.   var Result;
  337.  
  338.   ValWord = Defined(Word) ? Word.Trim() : '';
  339.  
  340.   if (ValWord) {
  341.     re = RegExp('(?:^| )' + ValWord + '(?= |$)', 'g');
  342.     Result = this.replace(re, '');
  343.   } else {
  344.     Result = this;
  345.   }
  346.  
  347.   Result = Result.TrimDS();
  348.  
  349.   return Result;
  350.  
  351. }
  352.  
  353.  
  354. //------------------------------------------------------------------------------
  355. // HTML/CSS helper functions
  356. //------------------------------------------------------------------------------
  357.  
  358.  
  359. // GetFirstElementByTagName(Root, TagName): Element
  360. //
  361. // Returns the first HTML element with the given tag name if such an
  362. // element exists, otherwise null. The matching is case-insensitive.
  363. //
  364. // The search is performed recursively from Root. Usually, "document"
  365. // is a good place to start.
  366. //
  367. // Examples of tag names include "BODY", "H2", "P", and "DIV".
  368.  
  369. function GetFirstElementByTagName(Root, TagName) {
  370.  
  371.   var Elements;
  372.   var Result = null;
  373.  
  374.   Elements = Root.getElementsByTagName(TagName);
  375.  
  376.   if (Assigned(Elements)) {
  377.     if (Elements.length > 0) {
  378.       Result = Elements[0];
  379.     }
  380.   }
  381.  
  382.   return Result;
  383.  
  384. }
  385.  
  386.  
  387. //------------------------------------------------------------------------------
  388.  
  389.  
  390. // GetFirstElementByTagNameAndClass(Root, TagName, ClassName): Element
  391. //
  392. // Returns the first HTML element with the given tag name and with ClassName
  393. // included in the "class" attribute, if such an element exists, otherwise null.
  394. // The matching of the tag name is case-insensitive but the matching of the
  395. // class name is sensitive to letter case.
  396. //
  397. // The search is performed recursively from Root. Usually, "document"
  398. // is a good place to start.
  399.  
  400. function GetFirstElementByTagNameAndClass(Root, TagName, ClassName) {
  401.  
  402.   var Result = null;
  403.  
  404.   var Elements;
  405.   var Element;
  406.   var i;
  407.  
  408.   Elements = Root.getElementsByTagName(TagName);
  409.  
  410.   if (Assigned(Elements)) {
  411.  
  412.     for (i = 0; i < Elements.length; ++i) {
  413.  
  414.       Element = Elements[i];
  415.  
  416.       if (HasClass(Element, ClassName)) {
  417.         Result = Element;
  418.         break;
  419.       }
  420.  
  421.     }
  422.  
  423.   }
  424.  
  425.   return Result;
  426.  
  427. }
  428.  
  429.  
  430. //------------------------------------------------------------------------------
  431.  
  432.  
  433. // AddCSS(CSSText): Element
  434. //
  435. // Adds to the current HTML/XHTML document a block of CSS code.
  436. //
  437. // A reference to the new styles element is returned so that the caller may
  438. // set its attributes to make it easy to find later.
  439.  
  440. function AddCSS(CSSText) {
  441.  
  442.   var StylesElement; // The newly created CSS stylesheet element
  443.   var Head;
  444.  
  445.   Assert(Assigned(CSSText), 'CSSText is not assigned.');
  446.   StylesElement = document.createElement('style');
  447.   Assert(Assigned(StylesElement), 'Failed to create StylesElement.');
  448.   StylesElement.type = 'text/css';
  449.  
  450.   Head = GetFirstElementByTagName(document, 'HEAD');
  451.  
  452.   Assert(
  453.     Assigned(Head),
  454.     'There is no document HEAD to which a CSS style sheet may be added.'
  455.   );
  456.  
  457.   if (StylesElement.styleSheet) {
  458.     StylesElement.styleSheet.cssText = CSSText;
  459.   } else {
  460.     StylesElement.appendChild(document.createTextNode(CSSText));
  461.   }
  462.  
  463.   return Head.appendChild(StylesElement);
  464.  
  465. }
  466.  
  467.  
  468. //------------------------------------------------------------------------------
  469.  
  470.  
  471. // HasClass(Element, ClassName): Boolean
  472. //
  473. // Returns true iff the "class" attribute of Elements is defined and
  474. // contains ClassName. (Elements can be styled with multiple classes,
  475. // each class delimited from another by a space.)
  476. //
  477. // If ClassName is empty or unassigned, false is returned.
  478.  
  479. function HasClass(Element, ClassName) {
  480.  
  481.   var Result = false;
  482.   var ClassAttr;
  483.  
  484.   Assert(Assigned(Element), 'HasClass(): Element is unassigned.');
  485.  
  486.   if (Assigned(ClassName) && (ClassName != '')) {
  487.  
  488.     ClassAttr = '' + Element.getAttribute('class');
  489.  
  490.     if (ClassAttr) {
  491.       Result = ClassAttr.HasWord(ClassName);
  492.     }
  493.  
  494.   }
  495.  
  496.   return Result;
  497.  
  498. }
  499.  
  500.  
  501. //------------------------------------------------------------------------------
  502.  
  503.  
  504. // IncludeClass(Element, ClassName)
  505. //
  506. // Each elements on an HTML/XHTML page may be styled with multiple classes by
  507. // setting the "class" attribute to a space-delimited list of CSS class names.
  508. //
  509. // This function adds the "class" attribute to the element if it does not exist
  510. // already and adds the given class name if it is not already included in the
  511. // attribute.
  512.  
  513. function IncludeClass(Element, ClassName) {
  514.  
  515.   var ClassAttr;
  516.  
  517.   Assert(Assigned(Element), 'IncludeClass(): Element is unassigned.');
  518.   Assert(Assigned(ClassName), 'IncludeClass(): ClassName is unassigned.');
  519.   Assert(ClassName != '', 'IncludeClass(): ClassName is empty.');
  520.  
  521.   Assert(
  522.     ClassName.search(/' '/) < 0,
  523.     'IncludeClass(): ClassName must not contain any spaces.'
  524.   );
  525.  
  526.   if (!HasClass(Element, ClassName)) {
  527.  
  528.     ClassAttr = Element.getAttribute('class');
  529.  
  530.     if (!Assigned(ClassAttr)) {
  531.       ClassAttr = '';
  532.     }
  533.  
  534.     Element.setAttribute('class', ClassAttr.AddWord(ClassName));
  535.  
  536.   }
  537.  
  538. }
  539.  
  540.  
  541. //------------------------------------------------------------------------------
  542.  
  543.  
  544. // ExcludeClass(Element, ClassName)
  545. //
  546. // Removes any reference to ClassName from the "class" attribute of the element,
  547. // if it exists. If the class attribute is found to be empty, it is removed.
  548.  
  549. function ExcludeClass(Element, ClassName) {
  550.  
  551.   var ClassAttr;
  552.  
  553.   Assert(Assigned(Element), 'ExcludeClass(): Element is unassigned.');
  554.   Assert(Assigned(ClassName), 'ExcludeClass(): ClassName is unassigned.');
  555.   Assert(ClassName != '', 'ExcludeClass(): ClassName is empty.');
  556.  
  557.   Assert(
  558.     ClassName.search(/' '/) < 0,
  559.     'ExcludeClass(): ClassName must not contain any spaces.'
  560.   );
  561.  
  562.   if (HasClass(Element, ClassName)) {
  563.  
  564.     ClassAttr = Element.getAttribute('class');
  565.  
  566.     if (Assigned(ClassAttr)) {
  567.       ClassAttr = ClassAttr.ExcludeWord(ClassName);
  568.     }
  569.  
  570.     ClassAttr = ClassAttr.ExcludeWord(ClassName);
  571.  
  572.     if (ClassAttr) {
  573.       Element.setAttribute('class', ClassAttr);
  574.     } else {
  575.       Element.removeAttribute('class');
  576.     }
  577.  
  578.   }
  579.  
  580. }
  581.  
  582.  
  583. //------------------------------------------------------------------------------
  584.  
  585.  
  586. // SetClassBoolean(Element, ClassName, DoInclude)
  587. //
  588. // Calls IncludeClass() or ExcludeClass() depending on the value of DoInclude,
  589. // thus the presence or absence of a class reference cane be conveniently
  590. // matched to a Boolean.
  591.  
  592. function SetClassBoolean(Element, ClassName, DoInclude) {
  593.  
  594.   if (DoInclude) {
  595.     IncludeClass(Element, ClassName);
  596.   } else {
  597.     ExcludeClass(Element, ClassName);
  598.   }
  599.  
  600. }
  601.  
  602.  
  603. //------------------------------------------------------------------------------
  604. // Harmonator implementation
  605. //------------------------------------------------------------------------------
  606.  
  607.  
  608. // AddCustomStyles(): Element
  609. //
  610. // Colours anything Doctor Who-oriented a fabulous pink.
  611.  
  612. function AddCustomStyles() {
  613.  
  614.   var CSS = '\
  615. /* Fabulous colour accents */\n\
  616. \n\
  617. .donotwant {\n\
  618.  border: 0px solid #fdf !important;\n\
  619.  color: #c7a !important;\n\
  620. }\n\
  621. .donotwant a:link {\n\
  622.  background: transparent !important;\n\
  623.  color: #f7c !important;\n\
  624. }\n\
  625. .donotwant a:visited {\n\
  626.  background: transparent !important;\n\
  627.  color: #f7c !important;\n\
  628. }\n\
  629. \n\
  630. /* Force plain background */\n\
  631. \n\
  632. body {\n\
  633.  /* background-image: none !important; */\n\
  634. }\n\
  635. ';
  636.  
  637.   return AddCSS(CSS);
  638.  
  639. }
  640.  
  641.  
  642. //------------------------------------------------------------------------------
  643.  
  644.  
  645. // StyleUnsightlyText(Node, VisitedClassName, MarkedClassName, Keywords)
  646. //
  647. // For the given node or its children where applicable, unvisited
  648. // paragraphs containing unsightly keywords are marked.
  649.  
  650. function StyleUnsightlyText(Node, VisitedClassName, MarkedClassName, Keywords) {
  651.  
  652.   //----------------------------------------------------------------------------
  653.  
  654.   // HasUnsightlyText(Element): Boolean
  655.   //
  656.   // Returns true iff at least one unsighly word appears within
  657.   // the element's text.
  658.  
  659.   function HasUnsightlyText(Element) {
  660.  
  661.     var Result = false;
  662.     var KIx;
  663.     var Keyword;
  664.     var RE;
  665.     var Text;
  666.  
  667.     if (Assigned(Element)) {
  668.  
  669.       Text = Element.textContent;
  670.  
  671.       for (KIx = 0; KIx < Keywords.length; ++KIx) {
  672.  
  673.         Keyword = Keywords[KIx];
  674.         RE = RegExp('\\b' + Keyword + '\\b', 'i');
  675.  
  676.         if (Text.search(RE) >= 0) {
  677.           Result = true;
  678.           break;
  679.         }
  680.  
  681.       }
  682.  
  683.     }
  684.  
  685.     return Result;
  686.  
  687.   }
  688.  
  689.   //----------------------------------------------------------------------------
  690.  
  691.   var Items;
  692.   var Ix;
  693.   var Item;
  694.  
  695.   //console.log('--BEGIN--');
  696.  
  697.   if (Node.nodeType == 1 && Node.tagName == 'P' &&
  698.       !HasClass(Node, VisitedClassName)) {
  699.  
  700.     //console.log('Direct P');
  701.  
  702.     IncludeClass(Node, VisitedClassName);
  703.  
  704.     if (HasUnsightlyText(Node)) {
  705.       IncludeClass(Node, MarkedClassName);
  706.     }
  707.  
  708.   } else {
  709.  
  710.     Items = Node.getElementsByTagName('P');
  711.  
  712.     if (Items) {
  713.  
  714.       //console.log(Items.length, 'items');
  715.  
  716.       for (Ix = 0; Ix < Items.length; ++Ix) {
  717.  
  718.         Item = Items[Ix];
  719.  
  720.         if (!HasClass(Item, VisitedClassName)) {
  721.  
  722.           //console.log('P');
  723.  
  724.           IncludeClass(Item, VisitedClassName);
  725.  
  726.           if (HasUnsightlyText(Item)) {
  727.             //console.log('  Marked!');
  728.             IncludeClass(Item, MarkedClassName);
  729.           }
  730.  
  731.         }
  732.  
  733.       }
  734.  
  735.     }
  736.  
  737.   }
  738.  
  739. }
  740.  
  741.  
  742. //------------------------------------------------------------------------------
  743.  
  744.  
  745. // Harmonate(Node, Keywords)
  746. //
  747. // Marks all messages in Node containing any word in Keywords.
  748.  
  749. function Harmonate(Node, Keywords) {
  750.  
  751.   StyleUnsightlyText(Node, 'beenthere', 'donotwant', Keywords);
  752.  
  753. }
  754.  
  755.  
  756. //------------------------------------------------------------------------------
  757.  
  758.  
  759. // Main()
  760. //
  761. // Adds custom styles and styles posts containing unsightly words.
  762.  
  763. function Main() {
  764.  
  765.   const Keywords = [
  766.       'pudding', 'baloney', 'sausage', 'cheese',
  767.       'wensleydale', 'harry potter', 'vector',
  768.       'fox', 'moon', 'landing', 'hoax'
  769.     ]
  770.  
  771.   var Stream = null;
  772.  
  773.   AddCustomStyles();
  774.  
  775.   Stream = document.getElementsByTagName('body')[0];
  776.  
  777.   if (Stream) {
  778.  
  779.     Harmonate(Stream, Keywords);
  780.  
  781.     /*window.addEventListener(
  782.       'load',
  783.       function() {
  784.         var Stream = document.getElementsByTagName('body')[0];
  785.         if (Stream) {
  786.           //Harmonate(Stream, Keywords);
  787.           Stream.addEventListener(
  788.             'DOMNodeInserted',
  789.             function(e) {
  790.               Harmonate(e.currentTarget, Keywords);
  791.             },
  792.             false
  793.           );
  794.         }
  795.       },
  796.       false
  797.     );*/
  798.  
  799.   }
  800.  
  801. }
  802.  
  803.  
  804. //------------------------------------------------------------------------------
  805.  
  806.  
  807. Main();
  808.  
  809.  
  810. //------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement