Advertisement
Guest User

greasemonkey killfile 20091002.1 plus ftb+ct

a guest
Jul 11th, 2012
1,492
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @namespace     http://snowplow.org/martin/greasemonkey
  3. // @name          killfile
  4. // @description   Provides a killfile for certain blogs.  Covers livejournal, haloscan comments, most typepad blogs, most blogspot blogs, scienceblogs.com, and more as I add them.  Trolls can be killfiled with a single click. +++ user edit: support added for freethoughtblogs and crooked timber
  5. // ==/UserScript==
  6. //
  7. // This is a script for greasemonkey 0.6, a plugin for the FireFox
  8. // web browser. (http://greasemonkey.mozdev.org/)
  9. // It likely will NOT work in earlier versions of
  10. // greasemonkey, nor will it work in any other browser.
  11. //
  12. // The intention of this script is to hide the comments of commentors
  13. // you, the reader, do not wish to hear from.  In that respect, it's
  14. // like an old usenet killfile.  It does not affect what other
  15. // visitors to the site will see, nor does it affect what you see
  16. // before the page finishes loading (due to some limitations in
  17. // Mozilla, you really don't want to rewrite HTML that's not entirely
  18. // there yet).  This is not a tool meant for handling spam, only for
  19. // an individual comment reader to avoid having to see comments they
  20. // don't wish to see.
  21. //
  22. // This script is maintained by martin@snowplow.org (Daniel Martin)
  23. // This script is provided under the terms of the GNU General
  24. // Public License, version 2, which can be found at
  25. //   http://www.gnu.org/copyleft/gpl.html
  26. // Specifically, note that this script comes with NO guarantees,
  27. // not even the implied guarantee of merchantibility or fitness for a
  28. // specific purpose.
  29. //
  30. // Please let me know of other blogs you'd like covered with this,
  31. // and I welcome patches.  Future versions of this script may be
  32. // released under a license different from the GPL.
  33. //
  34. // Future enhancements of this script will hopefully include a
  35. // configuration page so that the user can add their own blog
  36. // configurations themselves.
  37.  
  38. // Acknowledgements:
  39. // The GreaseMonkey folks, for putting together GM.
  40. // Stan Dyck, whose original killfile script for
  41. //   the blog pharyngula was the inspiration for this script
  42. //   (Although no actual code was borrowed)
  43. //   You can see his script at
  44. //   http://sgd.homelinux.net/greasemonkey/pharyngulakillfile.user.js
  45. // Christina Schelin, http://christina267.wordpress.com/
  46. //   for village voice code and wordpress updates\
  47.  
  48. const kf_debug = false;
  49.  
  50. var ourVersion = "20091002.1";
  51.  
  52. var ourURL = 'http://snowplow.org/martin/greasemonkey/killfile.user.js';
  53.  
  54. function upgrade() {
  55.   window.open(ourURL,"killfile");
  56. }
  57.  
  58. //GM_registerMenuCommand("Get latest killfile script", upgrade);
  59.  
  60. function checkVersion() {
  61.   GM_xmlhttpRequest({
  62.     method:"GET",
  63.         url:"http://snowplow.org/martin/greasemonkey/versions.xml?killfile-"+ourVersion,
  64.         headers:{
  65.         "User-Agent":"Greasemonkey/0.6",
  66.           "Accept":"application/xml,text/xml",
  67.           },
  68.         onload:
  69.       function(responseDetails) {
  70.         var parser = new DOMParser();
  71.         var dom = parser.parseFromString(responseDetails.responseText,
  72.                                          "application/xml");
  73.         var programNodes = dom.getElementsByTagName('program');
  74.         if (!programNodes) {
  75.           GM_log("Can't find program: " + responseDetails.responseText);
  76.         }
  77.         for (var i=0; i < programNodes.length; i++) {
  78.           if (programNodes[i].getAttribute('id') == 'killfile') {
  79.             var programNode = programNodes[i];
  80.             var version = programNode.getElementsByTagName("version")[0];
  81.             if (version.textContent > ourVersion)
  82.               {
  83.                 alert("There's a new version of killfile available.\n" +
  84.                       "You are running version " + ourVersion + "\n" +
  85.                       "The latest available version is " + version.textContent + "\n" +
  86.                       "You can upgrade from the 'Tools->User script commands' menu");
  87.               }
  88.             GM_setValue('lastVersionCheckTime',Math.round((new Date()).getTime()/1000));
  89.           }
  90.         }
  91.       }
  92.     });
  93. }
  94.  
  95. function autoCheckVersion() {
  96.   var nowsecs = Math.round((new Date()).getTime()/1000);
  97.   var prevcheck = GM_getValue('lastVersionCheckTime',0);
  98.   if (nowsecs > prevcheck + 60*60*24*3) {
  99.     checkVersion();
  100.   }
  101. }
  102.  
  103. function showComment(spot) {
  104.   spot.childNodes[0].style.display = 'block';
  105.   spot.childNodes[1].style.display = 'none';
  106. }
  107. function hideComment(spot) {
  108.   spot.childNodes[0].style.display = 'none';
  109.   spot.childNodes[1].style.display = 'block';
  110. }
  111.  
  112. function escapeHTML(s) {
  113.     var div = document.createElement('div');
  114.     var text = document.createTextNode(s);
  115.     div.appendChild(text);
  116.     return div.innerHTML;
  117. }
  118.  
  119. function reviewContent() {
  120.   var trolllist = GM_getValue("Trolllist", ';');
  121.   var snap = document.evaluate("//div[@class='dtm_killfile_commentholder']", document, null,
  122.                                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
  123.   for (var i=0; i < snap.snapshotLength; i++) {
  124.     var spot = snap.snapshotItem(i);
  125.     if (trolllist.indexOf(";" + spot.getAttribute("dtm_killfile_user") + ";") > -1) {
  126.       hideComment(spot);
  127.     } else {
  128.       showComment(spot);
  129.     }
  130.   }
  131. }
  132.  
  133. function addTroll(troll) {
  134.   var trolllist = GM_getValue("Trolllist", ';');
  135.   if (trolllist.indexOf(";" + troll + ";") < 0) {
  136.     trolllist = trolllist + troll + ";";
  137.     GM_setValue("Trolllist", trolllist);
  138.   }
  139.   reviewContent();
  140. }
  141.  
  142. function delTroll(troll) {
  143.   var trolllist = GM_getValue("Trolllist", ';');
  144.   if (trolllist.indexOf(";" + troll + ";") > -1) {
  145.     trolllist = trolllist.replace(";" + troll + ";",";");
  146.     GM_setValue("Trolllist", trolllist);
  147.   }
  148.   reviewContent();
  149. }
  150.  
  151. function handleClick(evt) {
  152.   var holderdiv = this;
  153.   var evtar = evt.target;
  154.   var clazz = evtar.getAttribute('class');
  155.   if (clazz) {
  156.       if (clazz == "dtm_killfile_show") {
  157.         evt.stopPropagation();
  158.         evt.preventDefault();
  159.         showComment(holderdiv);
  160.       } else if (clazz == "dtm_killfile_hide") {
  161.         evt.stopPropagation();
  162.         evt.preventDefault();
  163.         hideComment(holderdiv);
  164.       } else if (clazz == "dtm_killfile_kill") {
  165.         evt.stopPropagation();
  166.         evt.preventDefault();
  167.         addTroll(holderdiv.getAttribute('dtm_killfile_user'));
  168.       } else if (clazz == "dtm_killfile_unkill") {
  169.         evt.stopPropagation();
  170.         evt.preventDefault();
  171.         delTroll(holderdiv.getAttribute('dtm_killfile_user'));
  172.       }
  173.   }
  174. }
  175.  
  176. // the basic scenario is essentially wordpress-like, so the
  177. // wordpress scenarios below are pretty short
  178. function basicScenario() {
  179.   return {
  180.   commenttopxpath: "//ol[contains(concat(' ',@class,' '),' commentlist ')]/li",
  181.       sigbit: ".//cite[1]",
  182.       replaceXpath: "child::node()",
  183.       mangleBefore: null,
  184.       mangleAppend: null,
  185.       tabXpath: null,
  186.       precedingBit: '',
  187.       followingBit: '',
  188.       sigUserMatch: '$2',
  189.       sigHrefMatch: '$1',
  190.       aHrefAttribute: 'href', // sometimes title
  191.       get sigpat() {
  192.         var s = ('^ *' + this.precedingBit +
  193.                  ' *(?:<a [^>]*?(?:\\b(?:' + this.aHrefAttribute +
  194.                  ') *="([^>"]*)")?[^>]*>)?(\\S[^<]*[^ <]|[^ <]) *(?:</a>)? *' +
  195.                  this.followingBit + '.*');
  196.         return new RegExp(s,"");
  197.       },
  198.       foreachComment:
  199.     function(loopBody) {
  200.       if (!loopBody) {return null;}
  201.       var snap = document.evaluate(this.commenttopxpath,
  202.                                    document, null,
  203.                                    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
  204.       for (var i=0; i < snap.snapshotLength; i++) {
  205.         loopBody(snap.snapshotItem(i));
  206.       }
  207.     },
  208.       getUserspec:
  209.     function(commentNode) {
  210.       if (!commentNode) {return null;}
  211.       var sigsnap = document.evaluate(this.sigbit, commentNode, null,
  212.                                       XPathResult.ANY_UNORDERED_NODE_TYPE, null);
  213.       var nd = sigsnap.singleNodeValue;
  214.       if (nd == null) {progresslog("xpath " + this.sigbit + " gave null: " + commentNode);return null;}
  215.       var sigHTML = nd.innerHTML;
  216.       if (!sigHTML) {sigHTML = nd.textContent;}
  217.       else {sigHTML = sigHTML.replace(/\&nbsp;/g, ' ');}
  218.       sigHTML = sigHTML.replace(/\s+/g, ' ');
  219.       sigHTML = sigHTML.replace(/\s+$/, '');
  220.       sigHTML = sigHTML.replace(/^\s+/, '');
  221.       // in case people match on attribute tags
  222.       var sigre = this.sigpat;
  223.       if (! sigre.test(sigHTML)) {
  224.         progresslog("Didn't match: html " + sigHTML.toSource() + " and regexp " + sigre.toSource());
  225.         return null;
  226.       }
  227.       var user = sigHTML.replace(sigre,this.sigUserMatch);
  228.       var href = sigHTML.replace(sigre,this.sigHrefMatch);
  229. //      progresslog("user: html {" + sigHTML.toSource() + "} and regexp {" + sigre.toSource() + "} gave " + escape(user) + "!" + escape(href));
  230.       return [user, escape(user) + "!" + escape(href)];
  231.     },
  232.       divHTML:
  233.     '<div class="dtm_killfile_shown"></div>' +
  234.       '<div class="dtm_killfile_hidden" style="display:none"><p>Comment by __SHORTUSER__ blocked.' +
  235.       ' [<a href="tag:remove%20user%20from%20killfile" class="dtm_killfile_unkill">unkill</a>]' +
  236.       '&#8203;[<a href="tag:show%20comment" class="dtm_killfile_show">show&nbsp;comment</a>]' +
  237.       '</p></div>',
  238.       divHTMLuser:
  239.     function(user, userspec) {
  240.       return this.divHTML.replace(/__SHORTUSER__/g,escapeHTML(user)).replace(/__USER__/g,escapeHTML(userspec));
  241.     },
  242.       getEmptyHolder:
  243.     function(commentNode, user, userspec) {
  244.       var ddiv = document.createElement('div');
  245.       ddiv.innerHTML = this.divHTMLuser(user, userspec);
  246.       if (this.tabXpath) {
  247.         var tabNode = document.evaluate(this.tabXpath, commentNode, null,
  248.                                         XPathResult.ANY_UNORDERED_NODE_TYPE, null);
  249.         tabNode = tabNode.singleNodeValue;
  250.         if (tabNode) {
  251.           var tabtarget = ddiv.childNodes[1].childNodes[0];
  252.           tabtarget.insertBefore(tabNode.cloneNode(true),tabtarget.firstChild);
  253.         }
  254.       }
  255.       ddiv.setAttribute("class", "dtm_killfile_commentholder");
  256.       ddiv.setAttribute("dtm_killfile_user", userspec);
  257.       return ddiv;
  258.     },
  259.       spanHTML: '[<a href="tag:killfile%20user" class="dtm_killfile_kill">kill</a>]' +
  260.       '&#8203;[<a href="tag:hide%20comment" class="dtm_killfile_hide">hide&nbsp;comment</a>]',
  261.       spanHTMLuser:
  262.     function(user, userspec) {
  263.       return this.spanHTML.replace(/__SHORTUSER__/g,escapeHTML(user)).replace(/__USER__/g,escapeHTML(userspec));
  264.     },
  265.       mangleCommentContent:
  266.     function(contentNode, user, userspec) {
  267.       if (!contentNode) {return null;}
  268.       var snap3 = null;
  269.       var useBefore = true;
  270.       if (this.mangleBefore) {
  271.         snap3 = document.evaluate(this.mangleBefore, contentNode, null,
  272.                                   XPathResult.ANY_UNORDERED_NODE_TYPE, null);
  273.       } else if (this.mangleAppend) {
  274.         snap3 = document.evaluate(this.mangleAppend, contentNode, null,
  275.                                   XPathResult.ANY_UNORDERED_NODE_TYPE, null);
  276.         useBefore = false;
  277.       }
  278.       if (snap3 && snap3.singleNodeValue) {
  279.         var target = snap3.singleNodeValue;
  280.         var cspan = document.createElement('span');
  281.         cspan.innerHTML = this.spanHTMLuser(user, userspec);
  282.         if (useBefore) {
  283.           target.parentNode.insertBefore(cspan,target);
  284.         } else {
  285.           target.appendChild(cspan);
  286.         }
  287.         return contentNode;
  288.       }
  289.       progresslog("Can't find insertion point for kill button");
  290.       progresslog(contentNode);
  291.       progresslog(contentNode.childNodes[0]);
  292.       return null;
  293.     },
  294.       insertCommentHolder:
  295.     function(commentNode, holderDiv) {
  296.       if (! (commentNode && holderDiv)) {return null;}
  297.       var snap2 = document.evaluate(this.replaceXpath, commentNode, null,
  298.                                     XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
  299.       if (snap2.snapshotLength == 0) {return null;}
  300.       snap2.snapshotItem(0).parentNode.insertBefore(holderDiv,snap2.snapshotItem(0));
  301.       var contentNode = holderDiv.firstChild;
  302.       for (var j=0; j < snap2.snapshotLength; j++) {
  303.         var removedNode = snap2.snapshotItem(j);//.parentNode.removeChild(snap2.snapshotItem(j));
  304.         contentNode.appendChild(removedNode);
  305.       }
  306.       holderDiv.addEventListener('click', handleClick, true);
  307.       return contentNode;
  308.     },
  309.       handleComment:
  310.     function(commentNode) {
  311.       progresslog("Comment found " + location.href);
  312.       var us = this.getUserspec(commentNode);
  313.       if (! us) {progresslog("Can't find user");return null;}
  314.       var commentTwo = this.mangleCommentContent(commentNode, us[0], us[1]);
  315.       if (!commentTwo) {progresslog("No kill button"); return null;}
  316.       var ddiv = this.getEmptyHolder(commentTwo, us[0], us[1]);
  317.       if (! ddiv) {progresslog("Can't get empty holder");return null;}
  318.       var contentdiv = this.insertCommentHolder(commentTwo, ddiv);
  319.       if (! contentdiv) {progresslog("Can't insert comment holder");return null;}
  320.     },
  321.       manglePage:
  322.     function () {
  323.       var me = this;
  324.       this.foreachComment(function (c) {me.handleComment(c)});
  325.     }
  326.   };
  327. }
  328.  
  329. //var kf_debug = GM_getValue("debug",0);
  330.  
  331. function progresslog(logstr) {
  332.   //if (kf_debug) {GM_log(logstr);}
  333.   if (kf_debug && console) {console.log(logstr);}
  334. }
  335.  
  336. function wordpressScenario() {
  337.   return {
  338.     get mangleBefore() {return this.sigbit + '/following::*'},
  339.       __proto__:basicScenario()
  340.       };
  341. }
  342.  
  343. function freethoughtblogsScenario() {
  344.   return {
  345.     commenttopxpath: "//ol[@class='commentlist']/" +
  346.                      "li[starts-with(@class,'comment')]/" +
  347.                      "div[starts-with(@class,'comment-body')]",
  348.     sigbit: "div[starts-with(@class,'comment-author')]/cite[@class='fn']",
  349.     __proto__:wordpressScenario()
  350.   };
  351. }
  352.  
  353. function crookedTimberScenario() {
  354.   return {
  355.     commenttopxpath: "//dl[@id='comment_list']/dt[starts-with(@class,'comment')]",
  356.     sigbit: "p/span[@class='comment_author']",
  357.     replaceXpath: ".|following-sibling::dd[1]",
  358.     __proto__:wordpressScenario()
  359.   };
  360. }
  361.  
  362. // Thanks to Christina Schelin, http://christina267.wordpress.com
  363. function wordpressScenario2() {
  364.   return {
  365.     commenttopxpath: "//ol[@id='commentlist']/li",
  366.     sigbit: "span[@class='commentauthor']",
  367.     __proto__:wordpressScenario()
  368.   };
  369. }
  370.  
  371. function riotactScenario() {
  372.   return {
  373.     get mangleAppend() {return this.sigbit + '/..'},
  374.     precedingBit: 'Comment by ',
  375.     followingBit: ' [-\u2014] ',
  376.     commenttopxpath: "//ol[@id='commentlist']/li",
  377.     __proto__:basicScenario()
  378.   };
  379. }
  380.  
  381. function pandagonNewScenario() {
  382.   return {
  383.   commenttopxpath: "//div[@id='content']/div[starts-with(@class,'comment-body')]",
  384.   sigbit: "div[@class='comment-posted']",
  385.   precedingBit: 'Comment #\\d*: ',
  386.   followingBit: ' on \\S+ at <a',
  387.   mangleAppend: "div[@class='comment-posted']",
  388.       __proto__:basicScenario()
  389.       };
  390. }
  391.  
  392. function feministingNewScenario() {
  393.   return {
  394.   commenttopxpath: "//div[@id='comments']//" +
  395.       "div[contains(concat(' ', @class, ' '), ' comment ')]/" +
  396.       "div[contains(concat(' ', @class, ' '), ' inner ')]",
  397.   sigbit: ".//div[contains(concat(' ', @class, ' '), ' commentByline ')]/" +
  398.       "span[contains(concat(' ', @class, ' '), ' author ')]",
  399.   precedingBit: '',
  400.   followingBit: '',
  401.   get mangleAppend() {return this.sigbit + '/..'},
  402.       __proto__:basicScenario()
  403.       };
  404. }
  405.  
  406. function feministingNewFrontPageScenario() {
  407.   return {
  408.   commenttopxpath: "//div[@class='mainPadding']/div[@class='entryBody']",
  409.   replaceXpath: ".|preceding-sibling::div[1][@class='entryTitle']",
  410.   sigbit: "div[@class='posted']",
  411.   precedingBit: 'Posted by ',
  412.   followingBit: ' at <a[^<>]*>[^<>]*</a> \\| in.*',
  413.   mangleAppend: "div[@class='posted']",
  414.       __proto__:basicScenario()
  415.       };
  416. }
  417.  
  418. function pandagonScenario() {
  419.   return {
  420.   sigbit: "span[@class='commentauthor']",
  421.       __proto__:wordpressScenario()
  422.       };
  423. }
  424.  
  425. function pandagon2Scenario() {
  426.   return {
  427.   commenttopxpath: "//ol[@id='commentlist']/li",
  428.   sigbit: "child::*[@class='commentauthor']",
  429.       __proto__:wordpressScenario()
  430.       };
  431. }
  432.  
  433. // thanks to Christina Schelin, http://christina267.wordpress.com/
  434. function voiceScenario() {
  435.   return {
  436.   commenttopxpath: "//div[@id='commentsList']/div",
  437.   sigbit: "div[@class='comments_info']/b",
  438.       __proto__:wordpressScenario()
  439.       };
  440. }
  441.  
  442. function livejournalScenario() {
  443.   return {
  444.   commenttopxpath: "//table[starts-with(@id,'ljcmt') and .//span[contains(concat(' ',@class,' '),' ljuser ')]]",
  445.       sigbit: "descendant::span[contains(concat(' ',@class,' '),' ljuser ')][1]",
  446.       replaceXpath: ".",
  447.       get mangleBefore() {return this.sigbit + "/following::*[1]";},
  448.       tabXpath: 'descendant::img[1]',
  449.       precedingBit: '(?:.*?</a>)?',
  450.       __proto__:basicScenario()
  451.       };
  452. }
  453.  
  454. function livejournalScenario_1a() {
  455.   return {
  456.   commenttopxpath: "//span[starts-with(@id,'ljcmt') and ./table[position() = 1 and @class='talk-comment']]",
  457.       __proto__:livejournalScenario()
  458.       };
  459. }
  460.  
  461. function livejournalScenario2() {
  462.   return {
  463.   commenttopxpath: "//div[@class='comment_wrapper']",
  464.       sigbit: ".//div[@class='comment_postedby']/*[1]",
  465.       tabXpath: '',
  466.       __proto__:livejournalScenario()
  467.       };
  468. }
  469.  
  470. function livejournalScenario3() {
  471.   return {
  472.   commenttopxpath: "//div[starts-with(@id,'ljcmt') and child::div[@class='entry']]",
  473.       replaceXpath: "./h3|./div[@class='entry']|./div[@class='talklinks']",
  474.       tabXpath: '',
  475.       __proto__:livejournalScenario()
  476.       };
  477. }
  478.  
  479. function livejournalScenario4() {
  480.   return {
  481.   commenttopxpath: "//div[starts-with(@id,'ljcmt')]",
  482.       replaceXpath: "child::node()",
  483.       tabXpath: '',
  484.       __proto__:livejournalScenario()
  485.       };
  486. }
  487.  
  488. function livejournalScenario5() {
  489.   return {
  490.     commenttopxpath: "//td[@id='content']//table[@class='heading_bar']/following-sibling::div",
  491.     get mangleAppend() {return this.sigbit + "/parent::node()";},
  492.     mangleBefore: null,
  493.     __proto__:livejournalScenario4()
  494.   };
  495. }
  496. function livejournalScenario6() {
  497.   return {
  498.     commenttopxpath: "//table[@class='entrybox'][2]/tbody/tr/td/table/tbody/tr/td/div",
  499.     __proto__:livejournalScenario5()
  500.   };
  501. }
  502.  
  503. function ljfriendsScenario() {
  504.   // If you want this, see the comments in "scenariolist" below
  505.   return {
  506.   commenttopxpath: "//table[not(ancestor::table) and " +
  507.       "(preceding::a[starts-with(@href,'http://www.livejournal.com/userinfo.bml?')]) " +
  508.       "and (descendant::a[substring-after(@href,'.livejournal.com/')=''])]",
  509.       sigbit: "descendant::a[contains(@href,'livejournal.com/') and " +
  510.       "substring-after(@href,'livejournal.com/')=''][1]/@href",
  511.       replaceXpath: ".",
  512.       sigpat: /(http:\/\/(\w+)\..*)/,
  513.       sigUserMatch: '$2',
  514.       sigHrefMatch: '$1',
  515.       mangleAppend: "descendant::a[contains(@href,'livejournal.com/') and " +
  516.       "substring-after(@href,'livejournal.com/')=''][1]/..",
  517.       spanHTML: '<br>[<a href="tag:killfile%20user" class="dtm_killfile_kill">kill</a>]' +
  518.       '&#8203;[<a href="tag:hide%20comment" class="dtm_killfile_hide">hide&nbsp;comment</a>]',
  519.       __proto__:basicScenario()
  520.       };
  521. }
  522.  
  523. function pandasThumbScenario() {
  524.   return {
  525.   commenttopxpath: "//div[@id='comment-panels']//div[contains(concat(' ', @class, ' '), ' comment ') and not(boolean(.//p[@id='comment-update-message']))]",
  526.       sigbit: ".//div[contains(concat(' ', @class, ' '), ' comment-header ')]//span[contains(concat(' ', @class, ' '), ' author ')]",
  527.       precedingBit: '',
  528.       followingBit: '',
  529.       aHrefAttribute: 'title',
  530.       mangleBefore: ".//span[contains(concat(' ', @class, ' '), ' byline ')]",
  531.       __proto__:basicScenario()
  532.   };
  533. }
  534.  
  535. function blogspotDLScenario() {
  536.   return {
  537.     commenttopxpath: "//dl[@id='comments-block']/dt",
  538.       sigbit: ".",
  539.       precedingBit: '(?: *<div class="profile-image-container">.*?</div>)? *',
  540.       followingBit: '\\b *said\\W+',
  541.       mangleAppend: ".",
  542.       replaceXpath: ".|following-sibling::dd[1]",
  543.       __proto__:basicScenario()
  544.   };
  545. }
  546.  
  547. function blogspotDivScenario() {
  548.   return {
  549.   commenttopxpath: "//div[@class='blogComments']/div[@class='blogComment']",
  550.       sigbit: "div[@class='byline']",
  551.       sigUserMatch: '$2$3',
  552.       sigpat: /^ *<a[^>]*>#<\/a> posted by (?:<span[^>]*comment-icon[^>]*>.*?<\/span>)? *(?:(?:<a [^>]*?(?:\b(?:href) *="([^>"]*)")?[^>]*>)?(\S[^<]*[^ <]|[^ <]) *(?:<\/a>)?|<span class=['"]anon[^>]*>([^<]*)<\/span>) +: +[^a-zA-Z]+[AP]M[^a-zA-Z]*$/,
  553.       mangleAppend: "div[@class='byline']",
  554.       __proto__:basicScenario()
  555.   };
  556. }
  557.  
  558. function blogspotTableScenario() {
  559.   return {
  560.   commenttopxpath: "//table[@class='MainTable']//td[@class='MessageCell']/P",
  561.       sigbit: "*[@class='byline']",
  562.       sigUserMatch: '$1',
  563.       sigHrefMatch: '$2',
  564.       sigpat: /^ *(\S[^|]*[^<>]*?\S|\S) *\| *(?:<a href="([^\"]*)"[^>]*>\S+<\/a> *\|)?[^|]*\|[^|]*$/,
  565.       mangleAppend: "*[@class='byline'][last()]",
  566.       __proto__:basicScenario()
  567.   };
  568. }
  569.  
  570.  
  571. function pharyngulaScenario() {
  572.   return {
  573.   commenttopxpath: "//div[@id='comments']//div[@class='commentContent']",
  574.       sigbit: "p[@class='commentFooter']",
  575.       precedingBit: 'Posted by:',
  576.       followingBit: '(?:(?:<a [^>]*>)?<img [^>]*>(?:</a>)?)? *\\|[^|]*$',
  577.       mangleAppend: "p[@class='commentFooter']",
  578.       __proto__:basicScenario()
  579.   };
  580. }
  581.  
  582. function mtScenario2() {
  583.   return {
  584.   commenttopxpath: "//div[@class='content']/div[@class='othercomment']",
  585.       sigbit: "p[@class='posted']",
  586.       followingBit: '(?:<a [^>]*><img [^>]*><\\/a>)? +at +[^\\]]*\\[link\\][^\\]]*$',
  587.       mangleAppend: "p[@class='posted']",
  588.       __proto__:pharyngulaScenario()
  589.   };
  590. }
  591.  
  592. function typepadScenario() {
  593.   return {
  594.       commenttopxpath: "//div[@class='comments-content']/div[contains(concat(' ',@class,' '),' comment ')]",
  595.       sigbit: "p[@class='comment-footer']",
  596.       mangleAppend: "p[@class='comment-footer']",
  597.       __proto__:pharyngulaScenario()
  598.   };
  599. }
  600.  
  601. function haloscanScenario() {
  602.     return {
  603.         commenttopxpath: "//table[@class='MainTable']//td[@class='MessageCell']",
  604.         sigbit: "descendant::span[@class='byline']",
  605.         mangleAppend: "descendant::span[@class='byline']",
  606.         mangleBefore: null,
  607.         replaceXpath: "child::node()[position() > 2 and position() < last()]",
  608.         sigUserMatch: '$1',
  609.         sigHrefMatch: '$2',
  610.         sigpat: /^ *(\S[^|]*.*?\S|\S) *\| *(?:<a href="([^\"]*)"[^>]*>Homepage<\/a> *\|)?[^|]*\|[^|]*$/,
  611.         __proto__:basicScenario()
  612.   };
  613. }
  614.  
  615. function giveemhellharryScenario1() {
  616.   return {
  617.   commenttopxpath: "//h3[@id='comment']/following-sibling::ol[1]/li",
  618.       sigbit: "small[last()]",
  619.       followingBit: ' \\w+ +\\d+,[^,]*<a',
  620.       mangleAppend: "small[last()]",
  621.       precedingBit: '[^<]*',
  622.       __proto__:basicScenario()
  623.   }
  624. }
  625.  
  626. function giveemhellharryScenario2() {
  627.   return {
  628.   commenttopxpath: "//div[@class='comments']//div[@class='comment']",
  629.       sigbit: "div[@class='commentauthor']",
  630.       replaceXpath: "div[@class='commenttitle' or @class='commenttext' or @class='commentauthor']",
  631.       mangleAppend: "div[@class='commentauthor']",
  632.       precedingBit: ' *[Bb][Yy] *',
  633.       __proto__:basicScenario()
  634.   }
  635. }
  636.  
  637. function soapbloxScenario1() {
  638.   return {
  639.   commenttopxpath: "//form[@name='rateForm']/a[@name != 'p0' and starts-with(@name,'p')]/following-sibling::div[1][starts-with(@class,'commentLevel')]",
  640.       sigbit: "div[last()-1]",
  641.       mangleAppend: "div[last()]",
  642.       precedingBit: '<i>by: *',
  643.       followingBit: ' @[^@]*',
  644.       __proto__:basicScenario()
  645.   }
  646. }
  647.  
  648. function truthoutScenario() {
  649.   return {
  650.   commenttopxpath: "//div[@class='commenthead']",
  651.       replaceXpath: ".|following-sibling::div[1]",
  652.       sigbit: "following-sibling::div[1]//a[starts-with(@href,'/blog/user/')][last()]",
  653.       mangleBefore: ".//br[last()]",
  654.       __proto__:basicScenario()
  655.   };
  656. }
  657.  
  658. function athleticsNationScenario() {
  659.   return {
  660.       delayed: true,
  661.       commenttopxpath: "//div[@class='cx' and .//p[@class='cl']]",
  662.       sigbit: ".//p[@class='cb']/a[1]",
  663.       replaceXpath: ".",
  664.       mangleAppend: ".//p[@class='cl']",
  665.       __proto__:basicScenario()
  666.       };
  667. }
  668. function athleticsNationScenario2() {
  669.   return {
  670.       commenttopxpath: "//div[starts-with(@id,'comment_item_')]/div[starts-with(@id,'comment_inner_')]",
  671.       sigbit: "./p[@class='byline' or @class='by']/a[1]",
  672.       mangleAppend: "./p[@class='byline' or @class='by']",
  673.       __proto__:athleticsNationScenario()
  674.       };
  675. }
  676. function athleticsNationOldScenario() {
  677.   return {
  678.       commenttopxpath: "//a[@name='commenttop'][1]/following-sibling::form//table",
  679.       sigbit: "./tbody/tr[last()]//a[1]",
  680.       replaceXpath: ".",
  681.       mangleBefore: "descendant::br[last()]",
  682.       __proto__:basicScenario()
  683.       };
  684. }
  685.  
  686. function tnrScenario() {
  687.     return {
  688.       commenttopxpath: "//div[@class='discuss-header']",
  689.       replaceXpath: ".|following-sibling::div[1]",
  690.       sigbit: "b[1]",
  691.       mangleAppend: ".",
  692.       __proto__:basicScenario()
  693.     };
  694. }
  695.  
  696. // One of the many Moveable Type templates
  697. function mtScenario1() {
  698.   return {
  699.       commenttopxpath: "//div[@class='comments-body']",
  700.       sigbit: "span[@class='comments-post']",
  701.       precedingBit: 'Posted by:',
  702.       followingBit: 'at <a [^>]*>[^<>]*</a> *$',
  703.       mangleAppend: "span[@class='comments-post']",
  704.       __proto__:basicScenario()
  705.   };
  706. }
  707.  
  708. function freeperScenario() {
  709.   return {
  710.       commenttopxpath: "//div[@class='b2'][1]/following-sibling::div[@class='n2']",
  711.       sigbit: "preceding-sibling::*[@class='a2'][1]",
  712.       replaceXpath: ".|preceding-sibling::node()[position() < 7][self::div or self::text()]",
  713.       precedingBit: '<a .*</a> posted on <b>.*?</b> by',
  714.       followingBit: '',
  715.       mangleAppend: ".",
  716.       __proto__:basicScenario()
  717.   };
  718. }
  719.  
  720. function nytimesBlogsScenario() {
  721.   return {
  722.       commenttopxpath: "//ul[@class='commentlist']/li",
  723.       sigbit: ".//p[last()]/cite",
  724.       replaceXpath: ".",
  725.       precedingBit: '\\W* Posted by ',
  726.       followingBit: '',
  727.       mangleAppend: ".//p[last()]",
  728.       __proto__:basicScenario()
  729.   };
  730. }
  731.  
  732. // fetlife.com
  733. // Added thanks to xtina@twilite.org
  734. function fetScenario() {
  735.     return {
  736.         commenttopxpath: "//div[contains(@class,'group_comment')]",
  737.         sigbit: "./*[contains(@class,'span-14')]/div[@class='quiet']",
  738.         precedingBit: '<a href="/users/[0-9]*">',
  739.         followingBit: '</a> responded .*',
  740.         __proto__:wordpressScenario()
  741.     };
  742. }
  743.  
  744. var scenariolist = {
  745.   pamshouseblend:[{scenario:soapbloxScenario1,hrefpat:"^[^/]*//[^/]*/showDiary.do"}],
  746.   escapepod:[{scenario:wordpressScenario,hrefpat:"^[^/]*//[^/]*/[0-9]"}],
  747.   ravelry:[{scenario:wordpressScenario,hrefpat:"^[^/]*//blog\.[^/]*/[0-9]"}],
  748.   blogsome:[{scenario:pandagon2Scenario,hrefpat:"^[^/]*//pandagon[.][^/]*/[0-9]"}],
  749.   pandagon:[{scenario:pandagonScenario,hrefpat:"^[^/]*//[^/]*/[0-9]"},
  750.             {scenario:pandagonNewScenario,hrefpat:"^[^/]*//[^/]*/.*\\bsite/comments/"}],
  751.   fauxrealtho:[{scenario:pandagon2Scenario,hrefpat:"^[^/]*//[^/]*/[0-9]"}],
  752.   billcara:[{scenario:mtScenario2,hrefpat:"^[^/]*//[^/]*/archives/"}],
  753.   // also thanks to http://christina267.wordpress.com
  754.   scalzi:[{scenario:wordpressScenario,hrefpat:"^[^/]*//[^/]*/whatever"}],
  755.   philliesnation:[{scenario:wordpressScenario,hrefpat:"^[^/]*//[^/]*/archives/"}],
  756.   feministe:[{scenario:wordpressScenario,hrefpat:"^[^/]*//[^/]*/blog/archives/"}],
  757.   powweb:[{scenario:wordpressScenario,hrefpat:"^[^/]*//feministe[.][^/]*/blog/archives/"}],
  758.   amptoons:[{scenario:wordpressScenario,hrefpat:"^[^/]*//[^/]*/blog/archives/"}],
  759.   'the-riotact':[{scenario:riotactScenario, // wordpress 1.5 ?
  760.    xpath:"//ol[@id='commentlist']/li[1]//cite"}],
  761.   wordpress:[
  762.   {scenario:wordpressScenario, // wordpress 1.5 ?
  763.    xpath:"//ol[contains(concat(' ',@class,' '),' commentlist ')]/li[1]/descendant::*[self::cite or self::span][1][self::cite]"
  764.    },
  765.   {scenario:pandagonScenario,  // wordpress 2 ?
  766.    xpath:"//ol[contains(concat(' ',@class,' '),' commentlist ')]/li[1]/span[@class='commentauthor']"},
  767.   {scenario:wordpressScenario2,
  768.    xpath:"//ol[@id='commentlist']/li[1]/span[@class='commentauthor']"},
  769.   ],
  770.   blogger:[
  771.   {scenario:blogspotDLScenario,
  772.    hrefpat:"^[^/]*//[^/]*/comment[.]g[?]",
  773.    xpath:"//dl[@id='comments-block']/dt"},
  774.   ],
  775.   blogspot:[
  776.   {scenario:blogspotDLScenario,
  777.    xpath:"//dl[@id='comments-block']/dt"},
  778.   {scenario:blogspotDivScenario,
  779.     xpath:"//div[@class='blogComments']/div[@class='blogComment']"},
  780.   {scenario:blogspotTableScenario,
  781.     xpath:"//table[@class='MainTable']//td[@class='MessageCell']"}
  782.   ],
  783.   livejournal:[
  784.   {scenario:livejournalScenario2,
  785.    hrefpat:"^[^/]*//((syndicated|community)\\.[^/]*/)?[^/]*/[0-9]",
  786.    xpath:"//div[contains(concat(' ',@class,' '),' comment_wrapper ')]",
  787.   },
  788.   {scenario:livejournalScenario3,
  789.    xpath: "//div[@class='box' and starts-with(@id,'ljcmt')]",
  790.    hrefpat:"^[^/]*//((syndicated|community)\\.[^/]*/)?[^/]*/[0-9]",
  791.   },
  792.   {scenario:livejournalScenario4,
  793.    xpath: "//div[starts-with(@id,'ljcmt') and descendant::span[contains(concat(' ',@class,' '),' ljuser ')]]",
  794.    hrefpat:"^[^/]*//((syndicated|community)\\.[^/]*/)?[^/]*/[0-9]",
  795.   },
  796.   {scenario:livejournalScenario5,
  797.    xpath: "//td[@id='content']//table[@class='heading_bar']/following-sibling::div",
  798.    hrefpat:"^[^/]*//((syndicated|community)\\.[^/]*/)?[^/]*/[0-9]",
  799.   },
  800.   {scenario:livejournalScenario6,
  801.    xpath: "//table[@class='entrybox'][2]/tbody/tr/td/table/tbody/tr/td/div",
  802.    hrefpat:"^[^/]*//((syndicated|community)\\.[^/]*/)?[^/]*/[0-9]",
  803.   },
  804.   {scenario:livejournalScenario_1a,
  805.    xpath:"//span[starts-with(@id,'ljcmt')]/table[@class='talk-comment']",
  806.    hrefpat:"^[^/]*//((syndicated|community)\\.[^/]*/)?[^/]*/[0-9]",
  807.   },
  808.   {scenario:livejournalScenario,
  809.    hrefpat:"^[^/]*//((syndicated|community)\\.[^/]*/)?[^/]*/[0-9]",
  810.   },
  811.   // Commented out because I think it's ugly.  Your taste may vary, so you can
  812.   // uncomment this next line to kill/unkill people on your lj friends list.
  813.   // Intended primarily for communities you like mostly, but one or two
  814.   // posters get on your nerves.
  815.   // {scenario:ljfriendsScenario,hrefpat:"^[^/]*//[^/]*/friends"},
  816.   ],
  817.   journalfen:   // Basically, old livejournal code
  818.   [
  819.   {scenario:livejournalScenario2,
  820.    hrefpat:".*/[0-9]*\\.html",
  821.    xpath:"//div[@class='comment_wrapper']",
  822.   },
  823.   {scenario:livejournalScenario3,
  824.    xpath: "//div[@class='box' and starts-with(@id,'ljcmt')]",
  825.    hrefpat:".*/[0-9]*\\.html",
  826.   },
  827.   {scenario:livejournalScenario4,
  828.    xpath: "//div[starts-with(@id,'ljcmt') and descendant::span[@class='ljuser']]",
  829.    hrefpat:".*/[0-9]*\\.html",
  830.   },
  831.   {scenario:livejournalScenario5,
  832.    xpath: "//td[@id='content']//table[@class='heading_bar']/following-sibling::div",
  833.    hrefpat:".*/[0-9]*\\.html",
  834.   },
  835.   {scenario:livejournalScenario6,
  836.    xpath: "//table[@class='entrybox'][2]/tbody/tr/td/table/tbody/tr/td/div",
  837.    hrefpat:".*/[0-9]*\\.html",
  838.   },
  839.   {scenario:livejournalScenario,
  840.    hrefpat:".*/[0-9]*\\.html",
  841.   },
  842.   // Commented out because I think it's ugly.  Your taste may vary, so you can
  843.   // uncomment this next line to kill/unkill people on your lj friends list.
  844.   // Intended primarily for communities you like mostly, but one or two
  845.   // posters get on your nerves.
  846.   // {scenario:ljfriendsScenario,hrefpat:".*/friends\\b"},
  847.   ],
  848.   scienceblogs:
  849.   [
  850.   {scenario:pharyngulaScenario,hrefpat:"^[^/]*//[^/]*/\\w+/[0-9]"},
  851.   // Do all blogs at scienceblogs.com fit this format?
  852.   ],
  853.   freethoughtblogs:
  854.   [
  855.   {scenario:freethoughtblogsScenario,hrefpat:"^[^/]*//[^/]*/\\w+/[0-9]"},
  856.   ],
  857.   crookedtimber:
  858.   [
  859.   {scenario:crookedTimberScenario,hrefpat:"^[^/]*//[^/]*/[0-9]"},
  860.   ],
  861.   feministing:
  862.   [
  863.   {scenario:feministingNewScenario,
  864.    xpath:"//div[@id='comments']//div[@class='commentByline']/span[contains(concat(' ', @class, ' '), ' author ')]"},
  865.   {scenario:typepadScenario,hrefpat:"^[^/]*//[^/]*/archives/[0-9]"},
  866.   {scenario:feministingNewFrontPageScenario,hrefpat:"^[^/]*//community\\.[^/]*/[^/]*$"},
  867.   ],
  868.   typepad:
  869.   [
  870.   {scenario:typepadScenario,hrefpat:"^[^/]*//[^/]*/\\w+/[0-9]",
  871.    xpath:"//div[@class='comments-content'][1]/div[contains(concat(' ',@class,' '),' comment ')][1]/p[@class='comment-footer']"},
  872.   // There are some other typepad pages with a VERY BAD comment structure
  873.   // (no surrounding element to grab ahold of, so that you have to
  874.   // divide up the comment stream where-ever you see P's of a certain class)
  875.   // Not that I won't get to them eventually, but it's going to take some
  876.   // serious xpath voodoo
  877.   ],
  878.   pandasthumb:[
  879.   {scenario:pandasThumbScenario,hrefpat:"^[^/]*//[^/]*/archives/"}
  880.   ],
  881.   haloscan:[
  882.   {scenario:haloscanScenario,
  883.    xpath:"//body/table[@class='MainTable']//td[@class='MessageCell']"}
  884.   ],
  885.   giveemhellharry:[
  886.   {scenario:giveemhellharryScenario1, hrefpat:"^[^/]*//[^/]*/blog/"},
  887.   {scenario:giveemhellharryScenario2, hrefpat:"^[^/]*//[^/]*/page/community/",
  888.    xpath: "//div[@class='comments']"}
  889.   ],
  890.   truthout:[
  891.   {scenario:truthoutScenario,
  892.    hrefpat:"^https?://forum.truthout.org/blog/story/",}
  893.   ],
  894.   athleticsnation:[
  895.   {scenario:athleticsNationScenario,
  896.   xpath:"//div[@class='cx']//p[@class='cl']",
  897.   hrefpat:"^[^?]*$"
  898.   },
  899.   {scenario:athleticsNationScenario2,
  900.   xpath:"//div[@id='comments_list']/div[starts-with(@id,'comment_item_')]/div[starts-with(@id,'comment_inner_')]",
  901.   hrefpat:"^[^?]*$",
  902.   },
  903.   {scenario:athleticsNationOldScenario,
  904.   xpath:"//a[@name='commenttop']/following-sibling::form//table[1]",
  905.   }  
  906.   ],
  907.   tnr:[{scenario:tnrScenario,hrefpat:"^[^/]*//[^/]*/doc_posts",}],
  908.   smalldeadanimals:[{scenario:mtScenario1,hrefpat:"^[^/]*//[^/]*/\\w*/[0-9]",}],
  909.   freerepublic:[{scenario:freeperScenario, hrefpat:"^[^/]*//[^/]*/focus/f-(news|chat)/[0-9]*/(replies|posts)",}],
  910.   nytimes:[{scenario:nytimesBlogsScenario, hrefpat:"^[^/]*//[^/]*\\.blogs\\.[^/]*/([0-9]*/){3}[\\w-]+/",}],
  911.   villagevoice:[{scenario:voiceScenario,hrefpat:"^[^/]*//[^/]*/news/[0-9]",}],
  912.   fetlife:[{scenario:fetScenario,hrefpat:"[^/]*//[^/]*/groups/[0-9]*/group_posts/"}],
  913. };
  914.  
  915. // sbNation.com is really a family of related blogs
  916. var sbNation =
  917.    ("beyondtheboxscore buffalorumblings minorleagueball halosheaven lookoutlanding "+
  918.     "lonestarball bluebirdbanter draysbay camdenchat overthemonster pinstripealley letsgotribe " +
  919.     "royalsreview blessyouboys twinkietown southsidesox azsnakepit truebluela mccoveychronicles " +
  920.     "gaslampball purplerow talkingchop fishstripes amazinavenue federalbaseball thegoodphight " +
  921.     "crawfishboxes brewcrewball vivaelbirdos bleedcubbieblue bucsdugout redreporter blogabull " +
  922.     "clipsnation sactownroyalty mavsmoneyball poundingtherock blazersedge goldenstateofmind badlefthook " +
  923.     "sundaymorningqb cornnation burntorangenation crimsonandcreammachine dawgsports rollbamaroll " +
  924.     "swampball andthevalleyshook rockytoptalk aseaofblue conquestchronicles bruinsnation udubdish " +
  925.     "buildingthedam aroundtheoval blackshoediaries schembechlerhall blacksburgbeacon tomahawknation " +
  926.     "carolinamarch ramblinracket provopride blocku rakesofmallow podiumcafe faketeams bloggingtheboys " +
  927.     "bleedinggreennation hogshaven windycitygridiron prideofdetroit dailynorseman fieldgulls patspulpit " +
  928.     "cincyjungle dawgsbynature behindthesteelcurtain stampedeblue bigcatcountry musiccitymiracles " +
  929.     "arrowheadpride milehighreport globalfutbol").split(" ");
  930. for (c in sbNation) {
  931.   scenariolist[sbNation[c]] = scenariolist['athleticsnation'];
  932. }
  933.  
  934. function findScenario() {
  935.   var domain = location.host.replace(/^(?:.*[.])?((?!www\.)[a-zA-Z0-9-]+)(?:\.\w{3}|(?:\.\w{2})+)$/,'$1');
  936.   domain = domain.toLowerCase();
  937.   var matchl = scenariolist[domain];
  938.   var re;
  939.   if (matchl) {
  940.     var matchind;
  941.     for (matchind in matchl) {
  942.       var match = matchl[matchind];
  943.       var found = true;
  944.       if (found && match.hrefpat) {
  945.         re = new RegExp(match.hrefpat);
  946.         found = re.test(location.href);
  947.       }
  948.       if (found && match.xpath) {
  949.         found = document.evaluate(match.xpath, document, null, XPathResult.BOOLEAN_TYPE,null);
  950.         if (found) {found = found.booleanValue;}
  951.         if (found) {progresslog("matched. " + location.href);} else {progresslog("Not matched.  " + location.href);}
  952.       }
  953.       if (found) {
  954.         return match.scenario();
  955.       }
  956.     }
  957.   }
  958.   return null;
  959. }
  960. // main.  i.e. "onload"
  961. var model = findScenario();
  962. if (model) {
  963.   if (model.delayed) {
  964.     window.addEventListener("load", function () {model.manglePage();}, false);
  965.   } else {
  966.     model.manglePage();
  967.   }
  968.   window.addEventListener("load", reviewContent, false);
  969.   window.addEventListener("load", autoCheckVersion, false);
  970. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement