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

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