RaaW

Reddit as a Weapon script. Parts and idea by /u/noeatnosleep, enhanced by /u/enim, /u/creesch, /u/skeeto, and /u/djimbob. RaaW adds links for page-wide voting and reporting. It adds a 'report to /r/spam' link, an 'analyze user submission domains' link, and a 'report to /r/botwatchman' link to userpages. RaaW disables the np. domain. RaaW Adds a 'show source' button for comments. DISCLIAMER: Use this at your own risk. If the report button is misued, you could be shadowbanned.

当前为 2014-10-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name RaaW
  3. // @version 3.1.1
  4. // @namespace RaaW
  5. // @description Reddit as a Weapon script. Parts and idea by /u/noeatnosleep, enhanced by /u/enim, /u/creesch, /u/skeeto, and /u/djimbob. RaaW adds links for page-wide voting and reporting. It adds a 'report to /r/spam' link, an 'analyze user submission domains' link, and a 'report to /r/botwatchman' link to userpages. RaaW disables the np. domain. RaaW Adds a 'show source' button for comments. DISCLIAMER: Use this at your own risk. If the report button is misued, you could be shadowbanned.
  6. // @include http://www.reddit.com/user/*
  7. // @include http://www.reddit.com/r/*
  8. // @include http://*reddit.com/*
  9. // @include https://www.reddit.com/user/*
  10. // @include https://www.reddit.com/r/*
  11. // @include https://*reddit.com/*
  12. // @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
  13. // ==/UserScript==
  14.  
  15. this.jQuery = this.jQuery = jQuery.noConflict(true);
  16.  
  17. // define a basic object that we can extend with our functions so we do not accidentally
  18. // override other stuff
  19. var RaaW = {
  20. // ////////////////////////////////////////////////////////////////////////
  21. // constants
  22. // ////////////////////////////////////////////////////////////////////////
  23.  
  24. // some css properties for the links in the toolbox
  25. LINK_CSS: {
  26. 'color': '#000',
  27. },
  28.  
  29. // ////////////////////////////////////////////////////////////////////////
  30. // instance variables
  31. // ////////////////////////////////////////////////////////////////////////
  32.  
  33. raawToolbar: false,
  34.  
  35. // true if we are moderator on the current page (by checking if .moderator is present)
  36. // in <body class="...">
  37. isModerator: false,
  38.  
  39. currentPage: 'user',
  40.  
  41. modHash: false,
  42.  
  43. // ////////////////////////////////////////////////////////////////////////
  44. // various helper functions
  45. // ////////////////////////////////////////////////////////////////////////
  46.  
  47. /**
  48. * Function grabs the username of the current viewed profile.
  49. *
  50. * Returns:
  51. * (string) username or undefined if not found
  52. */
  53. _getUsername: function() {
  54. return jQuery(document).find('.pagename.selected').text();
  55. },
  56.  
  57. // ////////////////////////////////////////////////////////////////////////
  58. // initialization
  59. // ////////////////////////////////////////////////////////////////////////
  60.  
  61. /**
  62. * Initialize RaaW. Will fetch some values like current page and after
  63. * that initialize the toolbar.
  64. */
  65. init: function() {
  66. // first gather all the information needed
  67. this._loadEnvironment();
  68.  
  69. // now add some elements we will need
  70. this._injectElements();
  71.  
  72. // add the toolbar
  73. this._generateToolbar();
  74.  
  75. // after we created everything connect it
  76. this._registerListener();
  77. },
  78.  
  79. /**
  80. * Load environment values like current page.
  81. */
  82. _loadEnvironment: function() {
  83. // set current page
  84. this.currentPage = document.URL.split('reddit.com')[1].split('/')[1];
  85.  
  86. // check if we are moderator
  87. this.isModerator = jQuery('body').hasClass('moderator');
  88. },
  89.  
  90. /**
  91. * Adds/modifies needed elements to the reddit page (e.g. 'toggle source' links).
  92. */
  93. _injectElements: function() {
  94. // add links 'toogle source' to comments
  95. var toggleSourceCodeEl = jQuery('<li><a class="raawToggleSourceCode" href="#">view source</a></li>');
  96. jQuery('.entry .flat-list').append(toggleSourceCodeEl);
  97.  
  98. //disable .np
  99. if (document.documentElement.lang === 'np') {
  100. document.documentElement.lang = 'en-us';
  101. }
  102.  
  103. // add subscriber class to body tag
  104. jQuery('body').addClass('subscriber');
  105.  
  106. // replace links on the page
  107. Array.forEach( document.links, function(a) {
  108. a.href = a.href.replace( "https://i.imgur.com", "http://imgur.com");
  109. a.href = a.href.replace( "https://imgur.com", "http://imgur.com");
  110. });
  111.  
  112. // set checkbox 'limit my search to /r/...' checked
  113. jQuery('form#search input[name="restrict_sr"]').prop('checked', true);
  114.  
  115. // add 'save as mod' button
  116. if(this.isModerator === true) {
  117. this._injectSaveAsMod();
  118. }
  119. },
  120.  
  121. /**
  122. * Register all click listener for the RaaW toolbar links. We do not distingish if we
  123. * are on /user or something else. There should be no noticeable impact on performance
  124. * and we save some maintenance effort.
  125. */
  126. _registerListener: function() {
  127. // we don't want js to bind 'this' to the global object. therefore we use a trick.
  128. // whenever you need a 'this' reference inside one of the functions pointing to
  129. // the RaaW object use 'that'
  130. var that = this;
  131.  
  132. // register click handler for the user toolbar links
  133. jQuery('#raawReportComment').click(function(e) {
  134. that.reportAll(e);
  135. });
  136. jQuery('#raawBotwatchmanSend').click(function(e) {
  137. that.botwatchmanSend(e)
  138. });
  139. jQuery('#raawAnalyzeSend').click(function(e) {
  140. that.analyzeSend(e)
  141. });
  142. jQuery('#raawReportUserToSpam').click(function(e) {
  143. that.reportUserToSpam(e)
  144. });
  145. jQuery('#raawAdminSend').click(function(e) {
  146. that.adminSend(e)
  147. });
  148.  
  149. // register handler for the other toolbar links
  150. jQuery('#raawDownvoteComment').click(function(e) {
  151. that.downvoteAll(e)
  152. });
  153. jQuery('#raawUpvoteComment').click(function(e) {
  154. that.upvoteAll(e)
  155. });
  156. jQuery('#raawComposeNew').click(function(e) {
  157. that.composeNew(e)
  158. });
  159.  
  160. //jQuery('.raawToggleSourceCode').click(this.toggleSourceCode);
  161. jQuery('.raawToggleSourceCode').click(function(e) {
  162. that.toggleSourceCode(e);
  163. });
  164. },
  165.  
  166. // ////////////////////////////////////////////////////////////////////////
  167. // toolbar stuff
  168. // ////////////////////////////////////////////////////////////////////////
  169.  
  170. /**
  171. * Helper function used to create an a-element.
  172. *
  173. * Parameters:
  174. * id (string) - id attribute value
  175. * href (string) - href attribute value
  176. * text (string) - elements text
  177. *
  178. * Returns:
  179. * jQuery element instance
  180. */
  181. _generateToolbarLink: function(id, href, text) {
  182. var link = jQuery('<a id="' + id + '" href="' + href + '">' + text + '</a>');
  183. jQuery(link).css(this.LINK_CSS);
  184. return link;
  185. },
  186.  
  187. /**
  188. * Generate the toolbar on top of the page.
  189. */
  190. _generateToolbar: function() {
  191. // apply some styles to the header
  192. jQuery('#header').css({
  193. 'paddingTop': '18px'
  194. });
  195.  
  196. // create the new raaw toolbar and insert into body
  197. this.raawToolbar = jQuery('<div id="raawToolbar"></div>');
  198. jQuery('body').prepend(this.raawToolbar);
  199.  
  200. // apply style to the new toolbar
  201. jQuery(this.raawToolbar).css({
  202. 'color': '#000'
  203. , 'background-color': '#f0f0f0'
  204. , 'border-bottom': '1px solid #000'
  205. , 'font-family': 'erdana, arial, helvetica, sans-serif'
  206. , 'font-size': '90%'
  207. , 'height': '12px'
  208. , 'padding': '3px 0px 3px 6px'
  209. , 'text-transform': 'uppercase'
  210. , 'width': '100%'
  211. , 'z-index': '+999999'
  212. , 'position': 'fixed'
  213. , 'top': '0'
  214. });
  215.  
  216. // fill toolbar with content depending on parsed page
  217. var toolbarLinks = new Array();
  218. if(this.currentPage === 'user') {
  219. toolbarLinks.push(this._generateToolbarLink('raawReportComment', '#', 'REPORT ALL'));
  220. toolbarLinks.push(this._generateToolbarLink('raawBotwatchmanSend', '#', ' | /R/BOTWATCHMAN'));
  221. toolbarLinks.push(this._generateToolbarLink('raawAnalyzeSend', '#', ' | ANALYZE'));
  222. toolbarLinks.push(this._generateToolbarLink('raawReportUserToSpam', '#', ' | /R/SPAM'));
  223. toolbarLinks.push(this._generateToolbarLink('raawAdminSend', '#', ' | ADMIN'));
  224. } else {
  225. toolbarLinks.push(this._generateToolbarLink('raawDownvoteComment', '#', 'DOWNVOTE ALL'));
  226. toolbarLinks.push(this._generateToolbarLink('raawUpvoteComment', '#', ' | UPVOTE ALL'));
  227. toolbarLinks.push(this._generateToolbarLink('raawComposeNew', '#', ' | COMPOSE'));
  228. }
  229.  
  230. for(i = 0; i < toolbarLinks.length; i++) {
  231. jQuery(this.raawToolbar).append(toolbarLinks[i]);
  232. }
  233. },
  234.  
  235. // ////////////////////////////////////////////////////////////////////////
  236. // functions for user toolbar
  237. // ////////////////////////////////////////////////////////////////////////
  238. /**
  239. * Report a given item using the reddit api.
  240. *
  241. * Parameters:
  242. * fullname (string) - fullname of item to report
  243. * modhash (string) - modhash to use for reporting
  244. */
  245. _reportItem: function(fullname, modhash) {
  246. var modhash = reddit.modhash;
  247. jQuery.post('http://www.reddit.com/api/report', {'id': fullname, 'uh': modHash});
  248. },
  249.  
  250. /**
  251. * Report all items on the page.
  252. *
  253. * Parameters:
  254. * click (jQuery click event) - the jQuery click event.
  255. */
  256. reportAll: function(click) {
  257. click.preventDefault();
  258.  
  259. var isConfirmed = confirm("This will report all items on the page.");
  260. if (isConfirmed === true) {
  261. var that = this;
  262. var modhash = reddit.modhash;
  263.  
  264. // load all fullname of the comments on the page
  265. jQuery('.commentarea .thing').each(function(index, el) {
  266. this._reportItem(fullname, modhash);
  267. fullnames.push(jQuery(el).attr('data-fullname'));
  268. });
  269.  
  270. // not accurate but will do
  271. alert('All items on this page were reported.');
  272. } else {
  273. alert('Report canceled');
  274. }
  275. },
  276.  
  277. /**
  278. * Open a new window to submit a user to /r/botwatchman.
  279. *
  280. * Parameters:
  281. * click (jQuery click event) - the jQuery click event.
  282. */
  283. botwatchmanSend: function(click) {
  284. click.preventDefault();
  285. var username = this._getUsername();
  286. window.open('http://www.reddit.com/r/botwatchman/submit?title=overview for ' + username + '&url=http://www.reddit.com/user/' + username);
  287. },
  288.  
  289. /**
  290. * Send a new message to /u/analyzereddit with subject 'analyze' and a username
  291. * as message.
  292. *
  293. * Parameters:
  294. * click (jQuery click event) - the jQuery click event.
  295. */
  296. analyzeSend: function(click) {
  297. click.preventDefault();
  298. var username = this._getUsername();
  299. window.open('http://www.reddit.com/message/compose/?to=analyzereddit&subject=analyze&message=' + username);
  300. },
  301.  
  302.  
  303. /**
  304. * Open a new window to report a user to /r/spam.
  305. *
  306. * Parameters:
  307. * click (jQuery click event) - the jQuery click event.
  308. */
  309. reportUserToSpam: function(click) {
  310. click.preventDefault();
  311. var username = this._getUsername();
  312. window.open('http://www.reddit.com/r/spam/submit?title=overview for '+ username + '&resubmit=true&url=http://www.reddit.com/user/' + username);
  313. },
  314.  
  315. /**
  316. * Open a new window to send a new message to /r/reddit.com.
  317. */
  318. adminSend: function(click){
  319. click.preventDefault();
  320. var username = this._getUsername();
  321. window.open('http://www.reddit.com/message/compose/?to=/r/reddit.com&message=/u/'+ username);
  322. },
  323.  
  324. // ////////////////////////////////////////////////////////////////////////
  325. // functions for default toolbar
  326. // ////////////////////////////////////////////////////////////////////////
  327.  
  328. /**
  329. * Downvote various comments on all kinds of pages.
  330. *
  331. * Returns:
  332. * (boolean)
  333. */
  334. downvoteAll: function(click) {
  335. click.preventDefault();
  336.  
  337. // we are on a user page
  338. if(this.currentPage === 'user') {
  339. var items = jQuery('#siteTable').find('.arrow.down');
  340. Array.prototype.forEach.call(items, function(el, i){
  341. setTimeout(function() {
  342. el.click();
  343. },100 + ( i * 400 ));
  344. });
  345. return false;
  346. } else {
  347. // all div.comment elements on the page
  348. var comments = jQuery('.comment');
  349.  
  350. // no comments found
  351. if(comments.length == 0) {
  352. var items = jQuery('#siteTable').find('.arrow.down');
  353. Array.prototype.forEach.call(items, function(el, i){
  354. setTimeout(function(){
  355. el.click();
  356. },100 + ( i * 400 ));
  357. });
  358. return false;
  359. } else {
  360. var items = jQuery('.commentarea').find('.arrow.down');
  361. Array.prototype.forEach.call(items, function(el, i){
  362. setTimeout(function(){
  363. el.click();
  364. },100 + ( i * 400 ));
  365. });
  366. return false;
  367. }
  368. }
  369. },
  370.  
  371. /**
  372. * Upvote various comments on all kinds of pages.
  373. *
  374. * Returns:
  375. * (boolean)
  376. */
  377. upvoteAll: function(click) {
  378. click.preventDefault();
  379.  
  380. // we are on a user page
  381. if (this.currentPage === 'user'){
  382. var items = jQuery('#siteTable').find('.arrow.up');
  383. Array.prototype.forEach.call(items, function(el, i){
  384. setTimeout(function(){
  385. el.click();
  386. },100 + ( i * 400 ));
  387. });
  388. return false;
  389. }
  390. else {
  391. // all div.comment elements on the page
  392. var comments = jQuery('.comment');
  393.  
  394. // no comments found
  395. if (comments.length === 0) {
  396. var items = jQuery('#siteTable').find('.arrow.up');
  397. Array.prototype.forEach.call(items, function(el, i){
  398. setTimeout(function() {
  399. el.click();
  400. },100 + ( i * 400 ));
  401. });
  402. return false;
  403. } else {
  404. var items = jQuery('.commentarea').find('.arrow.up');
  405. Array.prototype.forEach.call(items, function(el, i){
  406. setTimeout(function() {
  407. el.click();
  408. },100 + ( i * 400 ));
  409. });
  410. return false;
  411. }
  412. }
  413. },
  414.  
  415. /**
  416. * Open a new window to compose a new message.
  417. *
  418. * Parameters:
  419. * click (jQuery click event) - the jQuery click event.
  420. */
  421. composeNew: function(click) {
  422. click.preventDefault();
  423. window.open('http://www.reddit.com/message/compose/');
  424. },
  425.  
  426. // ////////////////////////////////////////////////////////////////////////
  427. // 'view source' related functions
  428. // ////////////////////////////////////////////////////////////////////////
  429.  
  430. /**
  431. * Helper function to fetch the sourcecode of comments/links/messages using the
  432. * reddit api.
  433. *
  434. * Parameters:
  435. * url (string) - because of the diversity of the api provide a url with the needed attributes
  436. * fullname (string) - fullname needed to search if loading messages
  437. * callback (function(source)) - callback function to call when api call is done
  438. */
  439. _fetchSourceCode: function(url, fullname, callback) {
  440. jQuery.getJSON(url).done(function(response) {
  441. // check what type of posting we're looking at (check api for more information)
  442. var postingType = response.data.children[0].kind;
  443.  
  444. // unfortunately the returned json object has no unified structure so
  445. // we need a bit more logic here
  446. var source;
  447. if(postingType === 't1') { // comment
  448. source = response.data.children[0].data.body;
  449. } else if(postingType === 't3') { // link (post); will be empty for videos or similiar
  450. source = response.data.children[0].data.selftext;
  451. } else if(postingType === 't4') { // message
  452. // the current api url loads a message thread so we need to find the
  453. // desired message
  454. rawData = response.data.children[0].data;
  455. if(rawData.name === fullname) {
  456. source = rawData.body;
  457. } else {
  458. // search through replies
  459. var replies = rawData.replies.data.children;
  460. for(var i = 0; i < replies.length; i++) {
  461. var replyRaw = replies[i].data;
  462. if(replyRaw.name === fullname) {
  463. source = replyRaw.body;
  464. break;
  465. }
  466. }
  467. }
  468. }
  469.  
  470. callback(source);
  471. });
  472. },
  473.  
  474. /**
  475. * Create a textarea to display source code
  476. *
  477. * Parameters:
  478. * source (string) - source code to display
  479. * fullname (string) - fullname of link/comment/message so we can later identify if we already loaded the source
  480. * prependTo (jQuery element) - element to prepend the textarea to
  481. */
  482. _createSourceCodeTextarea: function(source, fullname, prependTo) {
  483. // create a textarea to display source and add it to the dom
  484. var textAreaEl = jQuery('<textarea class="'+fullname+'">'+source+'</textarea>');
  485. jQuery(textAreaEl).css({
  486. 'display': 'block'
  487. , 'width': '90%'
  488. , 'height': '100px'
  489. });
  490.  
  491. // insert textarea
  492. jQuery(prependTo).prepend(textAreaEl);
  493. },
  494.  
  495. /**
  496. * Toggle source code.
  497. *
  498. * Parameters:
  499. * click (jQuery click event) - the jQuery click event.
  500. */
  501. toggleSourceCode: function(click) {
  502. click.preventDefault();
  503.  
  504. // grab the clicked link element
  505. var linkEl = jQuery(click.target);
  506.  
  507. // get the data-fullname attribute to provide an id to 'throw at the api'
  508. var dataFullname = jQuery(linkEl).closest('.thing').attr('data-fullname');
  509.  
  510. var isTextAreaPresent = jQuery('textarea.'+dataFullname);
  511. if(isTextAreaPresent.length == 1) {
  512. jQuery(isTextAreaPresent).toggle();
  513. } else {
  514. // figure out the element where we're going to insert the textarea
  515. var prependTo = jQuery(linkEl).parent().parent();
  516.  
  517. // do an ajax request to fetch the data from the api
  518. // because we cannot fetch a message and a link/comment with the same api call
  519. // we need to figure out, if we are on a message site
  520. var apiURL;
  521. if(this.currentPage === 'message') {
  522. // cut off t4_ for filtering
  523. messageId = dataFullname.slice(3, dataFullname.length);
  524. apiURL = '/message/messages/.json?mid='+messageId+'&count=1';
  525. } else {
  526. apiURL = '/api/info.json?id=' + dataFullname;
  527. }
  528.  
  529. var that = this;
  530. this._fetchSourceCode(apiURL, dataFullname, function(source) {
  531. that._createSourceCodeTextarea(source, dataFullname, prependTo);
  532. });
  533. }
  534. },
  535.  
  536. // ////////////////////////////////////////////////////////////////////////
  537. // 'save as mod' related stuff
  538. // ////////////////////////////////////////////////////////////////////////
  539.  
  540. /**
  541. * Register a new event listener in the given div.
  542. *
  543. * Parameters:
  544. * div (jQuery element) - div with the class .usertext-buttons
  545. */
  546. _updateSavesAsModListener: function(form) {
  547.  
  548. },
  549.  
  550. /**
  551. * Place a 'save as mod' in the comment forms.
  552. *
  553. * Parameters:
  554. * el (jQuery element) - form element to place the button in; leave out to inject into all
  555. */
  556. _injectSaveAsMod: function(el) {
  557. var that = this;
  558. if(typeof el === 'undefined') {
  559. el = false;
  560. }
  561.  
  562. // no element given -> inject into all forms
  563. var injectHere = new Array();
  564. if(el === false) {
  565. injectHere = jQuery('.usertext-buttons');
  566. } else {
  567. injectHere.push(jQuery(el).find('.usertext-buttons')[0]);
  568. }
  569.  
  570. // inject between save and cancel
  571. for(i = 0; i < injectHere.length; i++) {
  572. // element where the buttons life in
  573. var divEl = injectHere[i];
  574.  
  575. // button to inject; register click function...
  576. var buttonEl = jQuery('<button type="button" class="raawSaveAsMod">save as mod</button>');
  577. jQuery(buttonEl).click(function(e) {
  578. that.saveAsMod(e);
  579. });
  580.  
  581. // find save button and add save as mod after that
  582. jQuery(divEl).find('button[type="submit"]').after(buttonEl);
  583. }
  584.  
  585. // remove the buttons from the edit form
  586. jQuery('div.entry .usertext-buttons button.raawSaveAsMod').remove();
  587.  
  588. // find all reply links
  589. var replyButtons = jQuery('ul.flat-list li a').filter(function(index, el) {
  590. return jQuery(el).text() === 'reply';
  591. });
  592.  
  593. for(i = 0; i < replyButtons.length; i++) {
  594. var button = replyButtons[i];
  595. jQuery(button).click(function(e) {
  596. setTimeout(function() {
  597. var allButtons = jQuery('button.raawSaveAsMod');
  598. for(i = 0; i < allButtons.length; i++) {
  599. var button = allButtons[i];
  600. jQuery(button).off('click');
  601. jQuery(button).click(function(e) {
  602. that.saveAsMod(e);
  603. });
  604. }
  605. }, 500);
  606. });
  607. }
  608. },
  609.  
  610. /**
  611. * Method will prevent a comment form to submit in the first place. Will fetch the
  612. * thing id to use it for distinguishing. After that will submit the form and when the
  613. * submit is finished distinguish the comment itself.
  614. *
  615. * Parameters:
  616. * click (jQuery click event)
  617. */
  618. saveAsMod: function(click) {
  619. click.preventDefault();
  620. var form = jQuery(click.target).closest('form.usertext');
  621.  
  622. // get parent
  623. var hiddenInput = jQuery(form).children('input[name="thing_id"]')[0];
  624. var parent = jQuery(hiddenInput).val();
  625.  
  626. // get comment text
  627. var textarea = jQuery(form).find('textarea')[0];
  628. var commentText = jQuery(textarea).val();
  629.  
  630. var modhash = reddit.modhash;
  631. // post comment
  632. data = {
  633. 'api_type': 'json'
  634. , 'text': commentText
  635. , 'thing_id': parent
  636. , 'uh': modhash
  637. };
  638. jQuery.post('/api/comment', data).done(function(response) {
  639. if(response.json.errors.length > 0) {
  640. console.log('Error while posting comment:');
  641. console.log(response.json.errors);
  642. alert('Error while posting comment. Please check the console for more information!');
  643. return;
  644. }
  645.  
  646. // distinguish
  647. var commentData = response.json.data.things[0].data;
  648. var data = {
  649. 'id': commentData.id
  650. , 'api_type': 'json'
  651. , 'how': 'yes'
  652. , 'uh': modhash
  653. };
  654. jQuery.post('/api/distinguish', data).done(function(response) {
  655. if(response.json.errors.length > 0) {
  656. console.log('Error while posting comment:');
  657. console.log(response.json.errors);
  658. alert('Error while posting comment. Please check the console for more information!');
  659. return;
  660. }
  661.  
  662. location.reload();
  663. });
  664. });
  665. },
  666.  
  667. };
  668.  
  669. RaaW.Nuke = {
  670. init: function() {
  671. if(document.querySelector('body.moderator')) { // only execute if you are a moderator
  672. var nuke_button = new Array();
  673. var divels = document.querySelectorAll('div.noncollapsed');
  674. var comment_ids = new Array();
  675. var use_image = false;
  676.  
  677. // create img DOM element to clone
  678. if(use_image) {
  679. try {
  680. var img_element = document.createElement('img');
  681. img_element.setAttribute('alt', 'Nuke!');
  682. img_element.setAttribute('src', chrome.extension.getURL('nuke.png'));
  683. } catch(e) {
  684. use_image = false;
  685. }
  686. }
  687. for (var i = 0; i < divels.length; i++) {
  688. var author_link = divels[i].querySelector('p.tagline>a.author,p.tagline>span.author,p.tagline>em');
  689. // p.tagline>a.author is normal comment;
  690. // some author deleted comments seem to have either
  691. // p.tagline>span.author or p.tagline>em
  692.  
  693. comment_ids[i] = divels[i].getAttribute('data-fullname');
  694. // console.log(i + ':' + comment_ids);
  695. if(author_link) {
  696. // create link DOM element with img inside link
  697. nuke_button[i] = document.createElement('a')
  698. nuke_button[i].setAttribute('href', 'javascript:void(0)');
  699. nuke_button[i].setAttribute('title', 'Nuke!');
  700. nuke_button[i].setAttribute('id', 'nuke_'+i);
  701. if(use_image) {
  702. nuke_button[i].appendChild(img_element.cloneNode(true));
  703. } else {
  704. nuke_button[i].innerHTML= "[Nuke]";
  705. }
  706.  
  707. // append after the author's name
  708. author_link.parentNode.insertBefore(nuke_button[i], author_link.nextSibling);
  709.  
  710. // Add listener for click; using IIFE to function with _i as value of i when created; not when click
  711. nuke_button[i].addEventListener('click',
  712. (function(_i) {
  713. return function() {
  714. var continue_thread = divels[_i].querySelectorAll('span.morecomments>a');
  715. var comment_str = " comments?";
  716. if(continue_thread.length > 0) {
  717. comment_str = "+ comments (more after expanding collapsed threads; there will be a pause before the first deletion to retrieve more comments)?";
  718. }
  719. var delete_button = divels[_i].querySelectorAll('form input[value="removed"]~span.option.error a.yes,a[onclick^="return big_mod_action(jQuery(this), -1)"]');
  720. // form input[value="removed"]~span.option.error a.yes -- finds the yes for normal deleting comments.
  721. // a.pretty-button.neutral finds the 'remove' button for flagged comments
  722. if (confirm("Are you sure you want to nuke the following " + delete_button.length + comment_str)) {
  723. for (var indx=0; indx < continue_thread.length; indx++) {
  724. var elmnt = continue_thread[indx];
  725. setTimeout(function() {
  726. var event = document.createEvent('UIEvents');
  727. event.initUIEvent('click', true, true, window, 1);
  728. elmnt.dispatchEvent(event);
  729. }, 2000*indx); // wait two seconds before each ajax call before clicking each "load more comments"
  730. }
  731. if(indx > 0) {
  732. setTimeout(function() {
  733. delete_function(comment_ids[_i])
  734. }, 2000*(indx + 2)); // wait 4s after last ajax "load more comments"
  735. } else {
  736. delete_function(comment_ids[_i]); // call immediately if not "load more comments"
  737. }
  738. }
  739. }
  740. })(i)); // end of IIFE (immediately invoked function expression)
  741. }
  742. }
  743. }
  744. },
  745.  
  746. //nuke (djimbob)
  747. delete_function: function(thread_root) {
  748. var elmnts = document.getElementsByClassName('id-'+thread_root)[0].querySelectorAll('form input[value="removed"]~span.option.error a.yes,a[onclick^="return big_mod_action(jQuery(this), -1)"]');
  749. for(var i=0; i < elmnts.length; i++) {
  750. setTimeout((function(_elmnt) {
  751. return function() {
  752. var event = document.createEvent('UIEvents');
  753. event.initUIEvent('click', true, true, window, 1);
  754. _elmnt.dispatchEvent(event);
  755. };
  756. })(elmnts[i]), 1500*i); // 1.5s timeout prevents overloading reddit.
  757. };
  758. }
  759. };
  760.  
  761. // initialize when document loaded
  762. jQuery(document).ready(function() {
  763. RaaW.init();
  764. RaaW.Nuke.init();
  765. });