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.

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