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-11-04 提交的版本,查看 最新版本

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