Greasy Fork is available in English.

Disqus Click Automate

Automate repetitive clicks on those sites that use Disqus as commenting system.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

  1. // ==UserScript==
  2. // @grant GM_setValue
  3. // @grant GM_getValue
  4. // @grant GM_registerMenuCommand
  5. // @name Disqus Click Automate
  6. // @author aaferrari@eml.cc, updated by flatline4400@gmail.com (thanks woxxom at greasyfork)
  7. // @namespace Disqus Click Automate
  8. // @description Automate repetitive clicks on those sites that use Disqus as commenting system.
  9. // @include *://*.disqus.com/*
  10. // @include *://disqus.com/*
  11. // @version 0.7
  12. // ==/UserScript==
  13.  
  14. // This open a dialog to customize some parameters of the script, add the click events in
  15. // the respective radioboxs, checkboxes and set the way that this dialogue disappears from
  16. // the page when the user wants to close
  17. function config_dialog(){
  18. var i18n = {
  19. "en": {
  20. "SORTING_LABEL": "Sort comments by",
  21. "RECENT_LABEL": "Most recent",
  22. "BEST_LABEL": "The best",
  23. "OLDEST_LABEL": "Oldest",
  24. "AUTOLOAD_LABEL": "Autoload all comments",
  25. "EXPAND_LABEL": "Expand shortened comments",
  26. "GUEST_LABEL": "I am not interested in log in/create an account, I just want to comment",
  27. "CLOSE_TITLE": "Close",
  28. "AUTOLOAD_TITLE": "Load all comments available automatically instead of showing the 50 first",
  29. "EXPAND_TITLE": 'Expand automatically shortened comment containing the link "See more"',
  30. "GUEST_TITLE": 'Check the option "I prefer commenting as guest" and hidden the password field on the comment form'
  31. },
  32. "es": {
  33. "SORTING_LABEL": "Ordenar comentarios por",
  34. "RECENT_LABEL": "Mas nuevos",
  35. "BEST_LABEL": "El mejor",
  36. "OLDEST_LABEL": "Mas antiguos",
  37. "AUTOLOAD_LABEL": "Autocargar todos los comentarios",
  38. "EXPAND_LABEL": "Expandir comentarios acortados",
  39. "GUEST_LABEL": "No me interesa iniciar sesión/crear una cuenta, solo quiero comentar",
  40.  
  41. "CLOSE_TITLE": "Cerrar",
  42. "AUTOLOAD_TITLE": "Carga automáticamente todos los comentarios disponibles en vez de mostrar los 50 primeros",
  43. "EXPAND_TITLE": 'Expande de forma automática los comentarios acortados que contienen el enlace "Ver más"',
  44. "GUEST_TITLE": 'Marca la opcion "Prefiero comentar como invitado" y oculta el campo de contraseña en el formulario para comentar'
  45. }
  46. };
  47.  
  48. function translate(string){
  49. var lang = navigator.userLanguage || navigator.language;
  50. if (i18n[lang]) {
  51. if (i18n[lang][string]) { return i18n[lang][string]; }
  52. }
  53. return i18n["en"][string];
  54. }
  55.  
  56. // Changed the inline html to Javascript DOM (special thanks to
  57. // http://rick.measham.id.au/paste/html2dom.htm for saving so much work) to
  58. // avoid problems with the Content-Security-Policy on pages that have embedded
  59. // the Disqus comments, as well as other sites that also possess these policies.
  60. configDialogStyle = document.createElement('style');
  61. configDialogStyle.id = "config-dialog-style";
  62. configDialogStyle.appendChild(document.createTextNode("\
  63. #div_Content{position: fixed;\
  64. top: 50%; left: 50%;\
  65. transform: translate(-50%, -50%);\
  66. -ms-transform:translate(-50%, -50%); /* IE 9 */\
  67. webkit-transform:translate(-50%, -50%); /* Safari and Chrome */\
  68. z-index: 999; background-color: #ffffff;\
  69. border-color: black; border: 2px solid #000;}\
  70. #div_Content:focus{display:none;}\
  71. .dsq-options li {float: left; margin: 0px 5px;}"));
  72. document.body.appendChild(configDialogStyle);
  73.  
  74. div_Content = document.createElement('div');
  75. div_Content.id = "div_Content";
  76.  
  77. var table_0 = document.createElement('table');
  78. table_0.style.textAlign = "left";
  79.  
  80. var tbody_0 = document.createElement('tbody');
  81.  
  82. var tr_0 = document.createElement('tr');
  83. tr_0.style.color = "white";
  84.  
  85. var td_0 = document.createElement('td');
  86. td_0.style.verticalAlign = "top";
  87. td_0.style.textAlign = "right";
  88.  
  89. var btnClose = document.createElement('span');
  90. btnClose.style.background = "red none repeat scroll 0% 50%";
  91. btnClose.style.cursor = "pointer";
  92. btnClose.style.fontWeight = "bold";
  93. btnClose.style.MozBackgroundClip = "-moz-initial";
  94. btnClose.style.MozBackgroundOrigin = "-moz-initial";
  95. btnClose.style.MozBackgroundInlinePolicy = "-moz-initial";
  96. btnClose.id = "btn close";
  97. btnClose.title = translate("CLOSE_TITLE");
  98. btnClose.innerHTML = " X ";
  99.  
  100. td_0.appendChild(btnClose);
  101.  
  102. tr_0.appendChild(td_0);
  103.  
  104. tbody_0.appendChild(tr_0);
  105.  
  106. var tr_1 = document.createElement('tr');
  107.  
  108. var td_1 = document.createElement('td');
  109. td_1.appendChild(document.createTextNode(translate("SORTING_LABEL") + ":"));
  110.  
  111. tr_1.appendChild(td_1);
  112.  
  113. tbody_0.appendChild(tr_1);
  114.  
  115. var tr_2 = document.createElement('tr');
  116.  
  117. var td_2 = document.createElement('td');
  118.  
  119. var ul_0 = document.createElement('ul');
  120. ul_0.style.listStyle = "none outside none";
  121. ul_0.className = "dsq-options";
  122.  
  123. var li_0 = document.createElement('li');
  124.  
  125. var dsq_sorting_desc = document.createElement('input');
  126. dsq_sorting_desc.name = "sorting";
  127. dsq_sorting_desc.type = "radio";
  128. dsq_sorting_desc.id = "dsq-sorting-desc";
  129. li_0.appendChild(dsq_sorting_desc);
  130.  
  131. li_0.appendChild(document.createTextNode(translate("RECENT_LABEL")));
  132. ul_0.appendChild(li_0);
  133.  
  134. var li_1 = document.createElement('li');
  135.  
  136. var dsq_sorting_popular = document.createElement('input');
  137. dsq_sorting_popular.name = "sorting";
  138. dsq_sorting_popular.type = "radio";
  139. dsq_sorting_popular.id = "dsq-sorting-popular";
  140. li_1.appendChild(dsq_sorting_popular);
  141.  
  142. li_1.appendChild(document.createTextNode(translate("BEST_LABEL")));
  143. ul_0.appendChild(li_1);
  144.  
  145. var li_2 = document.createElement('li');
  146.  
  147. var dsq_sorting_asc = document.createElement('input');
  148. dsq_sorting_asc.name = "sorting";
  149. dsq_sorting_asc.type = "radio";
  150. dsq_sorting_asc.id = "dsq-sorting-asc";
  151. li_2.appendChild(dsq_sorting_asc);
  152.  
  153. li_2.appendChild(document.createTextNode(translate("OLDEST_LABEL")));
  154. ul_0.appendChild(li_2);
  155.  
  156. td_2.appendChild(ul_0);
  157.  
  158. tr_2.appendChild(td_2);
  159.  
  160. tbody_0.appendChild(tr_2);
  161.  
  162. var tr_3 = document.createElement('tr');
  163.  
  164. var td_3 = document.createElement('td');
  165.  
  166. var br_2 = document.createElement('br');
  167. td_3.appendChild(br_2);
  168.  
  169. tr_3.appendChild(td_3);
  170.  
  171. tbody_0.appendChild(tr_3);
  172.  
  173. var tr_4 = document.createElement('tr');
  174.  
  175. var td_4 = document.createElement('td');
  176. td_4.title = translate("AUTOLOAD_TITLE");
  177.  
  178. var autoload = document.createElement('input');
  179. autoload.type = "checkbox";
  180. autoload.id = "autoload_comments";
  181. autoload.title = translate("AUTOLOAD_TITLE");
  182. td_4.appendChild(autoload);
  183.  
  184. td_4.appendChild(document.createTextNode(translate("AUTOLOAD_LABEL")));
  185. tr_4.appendChild(td_4);
  186.  
  187. tbody_0.appendChild(tr_4);
  188.  
  189.  
  190. var tr_5 = document.createElement('tr');
  191.  
  192. var td_5 = document.createElement('td');
  193. td_5.title = translate("EXPAND_TITLE");
  194.  
  195. var autoexpand = document.createElement('input');
  196. autoexpand.type = "checkbox";
  197. autoexpand.id = "autoexpand_comments";
  198. autoexpand.title = translate("EXPAND_TITLE");
  199. td_5.appendChild(autoexpand);
  200.  
  201. td_5.appendChild(document.createTextNode(translate("EXPAND_LABEL")));
  202. tr_5.appendChild(td_5);
  203.  
  204. tbody_0.appendChild(tr_5);
  205.  
  206. var tr_6 = document.createElement('tr');
  207.  
  208. var td_6 = document.createElement('td');
  209. td_6.title = translate("GUEST_TITLE");
  210.  
  211. var without_login = document.createElement('input');
  212. without_login.type = "checkbox";
  213. without_login.id = "guest-comment";
  214. without_login.title = translate("GUEST_TITLE");
  215. td_6.appendChild(without_login);
  216.  
  217. td_6.appendChild(document.createTextNode(translate("GUEST_LABEL")));
  218. tr_6.appendChild(td_6);
  219. tbody_0.appendChild(tr_6);
  220.  
  221. table_0.appendChild(tbody_0);
  222.  
  223. div_Content.appendChild(table_0);
  224.  
  225. document.body.appendChild(div_Content);
  226.  
  227. // This function is only added for the purpose of facilitate the process of
  228. // inserting click events in radiobuttons and checkboxes (both current and
  229. // new) of the dialog config
  230. function click_check_setter(element, gm_value, pseudo_global_var, getted_value){
  231. var superior_scope = this; //Necessary to modify the "pseudo_global_var" from click event
  232. create_click_event(element, function () {
  233. GM_setValue(gm_value, getted_value === "" ? element.checked: getted_value);
  234. superior_scope[pseudo_global_var] = getted_value === "" ? element.checked: getted_value;
  235. });
  236. }
  237. // Init config dialog
  238. var sorting = {"desc": dsq_sorting_desc, "popular": dsq_sorting_popular, "asc": dsq_sorting_asc};
  239. sorting[default_order].checked= true;
  240. autoload.checked = autoload_comments;
  241. click_check_setter(autoload, 'autoload', "autoload_comments", "");
  242. autoexpand.checked = expand_comments;
  243. click_check_setter(autoexpand, 'expand', "expand_comments", "");
  244. without_login.checked = guest_comment;
  245. click_check_setter(without_login, 'guest', "guest_comment", "");
  246. // Set onclick event in radiobuttons
  247. click_check_setter(dsq_sorting_desc, 'sorting', "default_order", "desc");
  248. click_check_setter(dsq_sorting_popular, 'sorting', "default_order", "popular");
  249. click_check_setter(dsq_sorting_asc, 'sorting', "default_order", "asc");
  250. // Click event to close de config dialog
  251. create_click_event(document, function (e) {
  252. e = e || event
  253. var target = e.target || e.srcElement
  254. do {
  255. if (div_Content == target) {
  256. // Click occured inside the box, do nothing.
  257. return
  258. }
  259. target = target.parentNode
  260. } while (target)
  261. // Click was outside the box, destroy it.
  262. del_element(div_Content);
  263. del_element(configDialogStyle);
  264. });
  265.  
  266. create_click_event(btnClose, function(){
  267. del_element(div_Content);
  268. del_element(configDialogStyle);
  269. });
  270. }
  271.  
  272. function clicker(link){
  273. if (link.click) {link.click();}
  274. else{
  275. var new_event = document.createEvent("MouseEvents");
  276. new_event.initMouseEvent("click", true, true, window,0, 0, 0, 0, 0, false, false, false, false, 0, null);
  277. try { link.dispatchEvent(new_event); }
  278. // Do nothing in this catch, this is a problem with JQuery compatibility, more details in
  279. // http://stackoverflow.com/questions/980697/element-dispatchevent-is-not-a-function-js-error-caught-in-firebug-of-ff3-0
  280. catch(error) {}
  281. }
  282. }
  283.  
  284. function finder(tag, attrib, value, one_element){
  285. if (document.querySelector || document.querySelectorAll){
  286. var selectors = {true: "querySelector", false: "querySelectorAll"}
  287. return document[selectors[one_element]](tag + '['+ attrib+ '="'+ value + '"]');
  288. }
  289. else {
  290. var elems = document.getElementsByTagName(tag);
  291. var results = Array();
  292. for (i in elems){
  293. if(elems[i].getAttribute(attrib) == value) {
  294. if (one_element == true) { return elems[i]; }
  295. else { results.push(elems[i]); }
  296. }
  297. }
  298. return results;
  299. }
  300. }
  301.  
  302. function is_class_name(elem, matchClass){
  303. //Adapted from http://stackoverflow.com/a/3808886
  304. if (elem == null) { return null; }
  305. else if (typeof(matchClass) == 'string') {
  306. return (' ' + elem.className + ' ').indexOf(' ' + matchClass + ' ') > -1;
  307. }
  308. else if (matchClass instanceof RegExp) { return matchClass.test(elem.className); }
  309. }
  310.  
  311. function create_click_event(element, func) {
  312. if (element.addEventListener) {
  313. element.addEventListener("click", func, false); }
  314. else { elemen.attachEvent("onclick", func); } //Internet Explorer
  315. }
  316.  
  317. function del_element(id){
  318. if (typeof(id) == "string") { var element = document.getElementById(id); }
  319. else { element = id; }
  320. if (element){
  321. var ancestry = element.parentNode;
  322. ancestry.removeChild(element);
  323. }
  324. }
  325.  
  326. //saved values
  327. default_order = GM_getValue("sorting", "desc");
  328. autoload_comments = GM_getValue("autoload", true);
  329. expand_comments = GM_getValue("expand", true);
  330. guest_comment = GM_getValue("guest", true);
  331.  
  332. //register GM menu command
  333. GM_registerMenuCommand("[Disqus Click Automate] Configuration", config_dialog);
  334.  
  335. // This line (with the @include *) is needed to open the configuration dialog at any time from any page
  336. if (window.location.href.search("disqus.com/embed/comments") == -1 ) {return;}
  337.  
  338. all_comments_retrieved = false;
  339. //var JSON_Disqus = eval("("+document.getElementById("disqus-jsonData").innerHTML+")");
  340. //var actual_comments = JSON_Disqus["response"]["posts"].length;
  341. total_comments = null;
  342. comments = null;
  343. howmany = null;
  344. racecond = 0;
  345. actual_sort = null;
  346.  
  347. // Ordering comments and get some extra data
  348. sorted_comments = false;
  349.  
  350. initial_timer = window.setInterval(function (){
  351.  
  352. // console.log("Racecond: ",racecond);
  353.  
  354. if (actual_sort == null) {
  355. var actual_sort = finder("li", 'class', "selected", true);
  356. // var actual_sort = finder("li", 'active', "data-tab", false);
  357. racecond = racecond + 1;
  358. if (racecond > 100) {
  359. console.log("Race!");
  360. actual_sort = "unknown";
  361. window.clearInterval(initial_timer);
  362. }
  363. }
  364. console.log("actual_sort:",actual_sort);
  365. if (actual_sort != null) {
  366. var choosen_sort = finder("a", 'data-sort', default_order, true);
  367. if (actual_sort.children[0].getAttribute('data-sort') != default_order){
  368. clicker(choosen_sort);
  369. }
  370. sorted_comments = true;
  371. total_comments = parseInt(finder("li", 'data-role', "post-count", true).innerHTML.match(/([0-9]+) /));
  372. comments = document.getElementById("post-list");
  373. window.clearInterval(initial_timer);
  374. console.log("Finished first timer");
  375. console.log("sorted_comments:",sorted_comments);
  376. }
  377. }, 100);
  378.  
  379. if (autoload_comments == true){
  380. timer1 = window.setInterval(function (){
  381. // if (is_class_name(comments, "post-list loading") == false && sorted_comments == true) {
  382. if (1 == 1) {
  383. console.log("Loading button: ",is_class_name(comments, "post-list loading"));
  384. console.log("Autoload please...");
  385. console.log("Window location: ",window.location.hostname);
  386. // Autoload all comments
  387. var more_comments = finder("a", "data-action", "more-posts", true);
  388. if (is_class_name(more_comments, "btn busy") == false && more_comments.parentNode.style.display != "none"){
  389. clicker(more_comments);
  390. }
  391. else if ((finder("li", "class", "post", false).length + finder("li", "class", "post minimized", false).length) >= (total_comments-5)) {
  392. all_comments_retrieved = true;
  393. window.clearInterval(timer1);
  394. console.log("Finished second timer");
  395. console.log("Counted comments:",finder("li", "class", "post", false).length + finder("li", "class", "post minimized", false).length);
  396. console.log("Total comments:",total_comments);
  397.  
  398. // Clean Disqus mangled links
  399. var links = document.evaluate("//a[contains(@href, 'https://disq.us/url?url=') or contains(@href, 'http://disq.us/url?url=')]", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  400. console.log("Links to fix:",links.snapshotLength);
  401. for (var i=0; i < links.snapshotLength; i++)
  402. {
  403. var thisLink = links.snapshotItem(i);
  404. var str = decodeURIComponent(thisLink);
  405. str = str.substring(str.indexOf("=") + 1);
  406. thisLink.href = (str.substring(0, str.lastIndexOf(":")));
  407. console.log("Fixed link: ",thisLink.href);
  408. }
  409. }
  410. }
  411. // }, 100); changed because Disqus freaking out on shorter timer
  412. }, 1000);
  413. }
  414. else { all_comments_retrieved = true; } //Necessary to run the next timer
  415.  
  416. // This timer is held in execution because normally the "long" comments often contain many
  417. // images and/or videos, that when the user read this, then Disqus load comments with this
  418. // kind of content and the system at that moment decides whether shorten (or not) the comment
  419. // based on the resultant height (in pixels) of the div where is the comment.
  420. if (expand_comments == true){
  421. timer2 = window.setInterval(function (){// Unhide long comments
  422. var comments = finder("div", "class", "post-body-inner", false);
  423. for (var i=0; i < comments.length; i++) {
  424. var nodes = comments[i].childNodes;
  425. for (var j=0; j < nodes.length; j++) {
  426. if (is_class_name(nodes[j], "post-message-container") == true) {
  427. nodes[j].style.height = "";
  428. }
  429. else if (is_class_name(nodes[j], /message-shadow|more-button|see-more/gi) == true) {
  430. comments[i].removeChild(nodes[j]);
  431. }
  432. }
  433. }
  434. // Adjust the height of the iframe to match with the unshorted comments
  435. // height with media content that are loaded when the user checks them
  436. // (for some reason does not work or only works sometimes, if anyone
  437. // knows how to fix this, then notify on the forum)
  438. if (top.location != self.location) {
  439. var dsq_iframe = top.document.getElementById("dsq-2");
  440. if (dsq_iframe != null){
  441. dsq_iframe.style.height = document.getElementsByTagName("body")[0].clientHeight + "px";
  442. }
  443. }
  444. //console.log("Finished third timer");
  445. }, 500);
  446. }
  447.  
  448. // Check the option "I prefer commenting as guest" when you click in the textbox to write the nick
  449. if (guest_comment == true){
  450. timer3 = window.setInterval(function (){
  451. var nick_textbox = finder("input", "name", "author-name", true);
  452. if (nick_textbox != null){
  453. create_click_event(nick_textbox, function(){
  454. finder("input", "name", "author-guest", true).checked = true;
  455. del_element(finder("input", "name", "author-password", true));
  456. });
  457. window.clearInterval(timer3);
  458. }
  459. }, 500);
  460. }