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.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name RaaW
// @version 3.2.1
// @namespace RaaW
// @run-at document-end
// @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.
// @include http://www.reddit.com/user/*
// @include http://www.reddit.com/r/*
// @include http://*reddit.com/*
// @include https://www.reddit.com/user/*
// @include https://www.reddit.com/r/*
// @include https://*reddit.com/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
// @grant none
// ==/UserScript==

this.jQuery = jQuery.noConflict(true);

// define a basic object that we can extend with our functions so we do not accidentally
// override other stuff
var RaaW = {
	// ////////////////////////////////////////////////////////////////////////
	// constants
	// ////////////////////////////////////////////////////////////////////////

	// some css properties for the links in the toolbox
	LINK_CSS: {
		'color': '#000',
	},

	// ////////////////////////////////////////////////////////////////////////
	// instance variables
	// ////////////////////////////////////////////////////////////////////////

	// true if we are moderator on the current page (by checking if .moderator is present)
	// in <body class="...">
	isModerator: false,

	currentPage: 'user',

	// ////////////////////////////////////////////////////////////////////////
	// various helper functions
	// ////////////////////////////////////////////////////////////////////////

	/**
	 * Function grabs the username of the current viewed profile.
	 *
	 * Returns:
	 *  (string) username or undefined if not found
	 */
	 _getUsername: function() {
	 	return jQuery(document).find('.pagename.selected').text();
	 },

	 _getModhash: function() {
	 	return unsafeWindow.reddit.modhash;
	 },

	// ////////////////////////////////////////////////////////////////////////
	// initialization
	// ////////////////////////////////////////////////////////////////////////

	/**
	* Initialize RaaW. Will fetch some values like current page and after
	* that initialize the toolbar.
	*/
	init: function() {
		// first gather all the information needed
		this._loadEnvironment();

		// now add some elements we will need
		this._injectElements();

		// add the toolbar
		this._generateToolbar();

		// after we created everything connect it
		this._registerListener();
	},

	/**
	 * Load environment values like current page.
	 */
	 _loadEnvironment: function() {
		// set current page
		this.currentPage = document.URL.split('reddit.com')[1].split('/')[1];

		// check if we are moderator
		this.isModerator = jQuery('body').hasClass('moderator');
	},

	/**
	 * Adds/modifies needed elements to the reddit page (e.g. 'toggle source' links).
	 */
	 _injectElements: function() {
		// add links 'toogle source' to comments
		var toggleSourceCodeEl = jQuery('<li><a class="raawToggleSourceCode" href="#">view source</a></li>');
		jQuery('.entry .flat-list').append(toggleSourceCodeEl);

		//disable .np
		if (document.documentElement.lang === 'np') {
			document.documentElement.lang = 'en-us';
		}

		// add subscriber class to body tag
		jQuery('body').addClass('subscriber');

		// replace links on the page
		Array.forEach( document.links, function(a) {
			a.href = a.href.replace( "https://i.imgur.com", "http://imgur.com");
			a.href = a.href.replace( "https://imgur.com", "http://imgur.com");
		});

		// set checkbox 'limit my search to /r/...' checked
		jQuery('form#search input[name="restrict_sr"]').prop('checked', true);

		// add mod only stuff
		if(this.isModerator === true) {
			this._injectSaveAsMod();
			this._injectNuke();
		}
	},

	/**
	 * Register all click listener for the RaaW toolbar links. We do not distingish if we
	 * are on /user or something else. There should be no noticeable impact on performance
	 * and we save some maintenance effort.
	 */
	 _registerListener: function() {
		// we don't want js to bind 'this' to the global object. therefore we use a trick.
		// whenever you need a 'this' reference inside one of the functions pointing to
		// the RaaW object use 'that'
		var that = this;

		// register click handler for the user toolbar links
		jQuery('#raawReportComment').click(function(e) {
			that.reportAll(e);
		});
		jQuery('#raawBotwatchmanSend').click(function(e) {
			that.botwatchmanSend(e);
		});
		jQuery('#raawAnalyzeSend').click(function(e) {
			that.analyzeSend(e);
		});
		jQuery('#raawReportUserToSpam').click(function(e) {
			that.reportUserToSpam(e);
		});
		jQuery('#raawAdminSend').click(function(e) {
			that.adminSend(e);
		});

		// register handler for the other toolbar links
		jQuery('#raawDownvoteComment').click(function(e) {
			that.voteAll(e, -1);
		});
		jQuery('#raawUpvoteComment').click(function(e) {
			that.voteAll(e, 1);
		});
		jQuery('#raawComposeNew').click(function(e) {
			that.composeNew(e);
		});

		jQuery('.raawToggleSourceCode').click(function(e) {
			that.toggleSourceCode(e);
		});
	},

	// ////////////////////////////////////////////////////////////////////////
	// toolbar stuff
	// ////////////////////////////////////////////////////////////////////////

	/**
	* Helper function used to create an a-element.
	*
	* Parameters:
	*  id (string) - id attribute value
	*  href (string) - href attribute value
	*  text (string) - elements text
	*
	* Returns:
	*  jQuery element instance
	*/
	_generateToolbarLink: function(id, href, text) {
		var link = jQuery('<a id="' + id + '" href="' + href + '">' + text + '</a>');
		jQuery(link).css(this.LINK_CSS);
		return link;
	},

	/**
	 * Generate the toolbar on top of the page.
	 */
	 _generateToolbar: function() {
		// create a wrapper div
		var raawToolbarWrapper = jQuery('<div id="raawToolbarWrapper"><a href="#" id="raawToggleToolbar">RaaW</a></div>');
		jQuery('body .side').append(raawToolbarWrapper);

		jQuery(raawToolbarWrapper).css({
			'position': 'fixed'
			, 'top': '300px'
			, 'right': '-140px'
			, 'width': '180px'
			, 'font-size': '10px'
			, 'z-index': '100000'
		});

		// style the tab and bind the hover event
		jQuery('a#raawToggleToolbar').css({
			'display': 'block'
			, 'background': '#333'
			, 'padding': '0.3em'
			, 'color': '#fff'
			, 'font-weight': 'bold'
		});

		jQuery('a#raawToggleToolbar').click(function(e) {
			e.preventDefault();
		});

		jQuery('a#raawToggleToolbar').mouseenter(function(e) {
			console.log('enter');
			e.stopImmediatePropagation();
			jQuery('#raawToolbarWrapper').animate({'right': '-4px'}, 750);
		});

		jQuery('#raawToolbarWrapper').mouseleave(function(e) {
			e.stopImmediatePropagation();
			jQuery('#raawToolbarWrapper').animate({'right': '-140px'}, 750);
		});

		// create the new raaw toolbar and insert into body
		var raawToolbar = jQuery('<div id="raawToolbar"></div>');
		jQuery(raawToolbarWrapper).append(raawToolbar);

		// apply style to the new toolbar
		jQuery(raawToolbar).css({
			'border': '1px solid #aaa'
			, 'background': '#f0f0f0'
			, 'padding': '0.5em 30px 0.5em 0.5em'
			, 'margin-left': '4em'
		});

		// fill toolbar with content depending on parsed page
		var toolbarLinks = new Array();
		if(this.currentPage === 'user') {
			toolbarLinks.push(this._generateToolbarLink('raawReportComment', '#', 'REPORT ALL'));
			toolbarLinks.push(this._generateToolbarLink('raawBotwatchmanSend', '#', '/R/BOTWATCHMAN'));
			toolbarLinks.push(this._generateToolbarLink('raawAnalyzeSend', '#', 'ANALYZE'));
			toolbarLinks.push(this._generateToolbarLink('raawReportUserToSpam', '#', '/R/SPAM'));
			toolbarLinks.push(this._generateToolbarLink('raawAdminSend', '#', 'ADMIN'));
		} else {
			toolbarLinks.push(this._generateToolbarLink('raawUpvoteComment', '#', 'UPVOTE'));
			toolbarLinks.push(this._generateToolbarLink('raawDownvoteComment', '#', 'DOWNVOTE'));
			toolbarLinks.push(this._generateToolbarLink('raawComposeNew', '#', 'COMPOSE'));
		}

		for(i = 0; i < toolbarLinks.length; i++) {
			jQuery(raawToolbar).append(toolbarLinks[i]);
		}

		// add some css to the new links
		jQuery('#raawToolbar a').css({
			'display': 'block'
			, 'margin': '0.3em 0'
		});
	},

	// ////////////////////////////////////////////////////////////////////////
	// functions for user toolbar
	// ////////////////////////////////////////////////////////////////////////

	/**
	 * Report a given item using the reddit api.
	 *
	 * Parameters:
	 *  fullname (string) - fullname of item to report
	 *  el (jQuery el) - element to report (just for easier coding)
	 *	timeout (int) - timeout before request
	 */
	 _reportItem: function(fullname, el, timeout) {
	 	var that = this;
	 	setTimeout(function() {
	 		var data= {
	 			'api_type': 'json'
	 			, 'thing_id': fullname
	 			, 'uh': that._getModhash()
	 		};

	 		jQuery.post('http://www.reddit.com/api/report', data).done(function(response) {
	 			jQuery(el).hide(1000);
	 		}).error(function(response) {
	 			console.log(response);
	 		});
	 	}, timeout);
	 },

	/**
	 * Report all items on the page.
	 *
	 * Parameters:
	 *  click (jQuery click event) - the jQuery click event.
	 */
	 reportAll: function(click) {
	 	click.preventDefault();

	 	var isConfirmed = confirm("This will report all items on the page.");
	 	if (isConfirmed === true) {
			// load all fullname of the comments on the page
			var i = 0;
			var that = this;
			jQuery('div#siteTable .thing').each(function(index, el) {
				var fullname = jQuery(el).attr('data-fullname');
				that._reportItem(fullname, el, (400 * i) + 100);
				i++;
			});

			// not accurate but will do
			alert('All items on this page were reported.');
		} else {
			alert('Report canceled');
		}
	},

	/**
	 * Open a new window to submit a user to /r/botwatchman.
	 *
	 * Parameters:
	 *  click (jQuery click event) - the jQuery click event.
	 */
	 botwatchmanSend: function(click) {
	 	click.preventDefault();
	 	var username = this._getUsername();
	 	window.open('http://www.reddit.com/r/botwatchman/submit?title=overview for ' + username + '&url=http://www.reddit.com/user/' + username);
	 },

	/**
	 * Send a new message to /u/analyzereddit with subject 'analyze' and a username
	 * as message.
	 *
	 * Parameters:
	 *  click (jQuery click event) - the jQuery click event.
	 */
	 analyzeSend: function(click) {
	 	click.preventDefault();
	 	var username = this._getUsername();
	 	window.open('http://www.reddit.com/message/compose/?to=analyzereddit&subject=analyze&message=' + username);
	 },


	/**
	 * Open a new window to report a user to /r/spam.
	 *
	 * Parameters:
	 *  click (jQuery click event) - the jQuery click event.
	 */
	 reportUserToSpam: function(click) {
	 	click.preventDefault();
	 	var username = this._getUsername();
	 	window.open('http://www.reddit.com/r/spam/submit?title=overview for '+ username + '&resubmit=true&url=http://www.reddit.com/user/' + username);
	 },

	/**
	 * Open a new window to send a new message to /r/reddit.com.
	 */
	 adminSend: function(click){
	 	click.preventDefault();
	 	var username = this._getUsername();
	 	window.open('http://www.reddit.com/message/compose/?to=/r/reddit.com&subject=spammer&message=/u/'+ username);
	 },

	// ////////////////////////////////////////////////////////////////////////
	// functions for default toolbar
	// ////////////////////////////////////////////////////////////////////////

	/**
		 * Makes an asynch ajax call to the reddit API after waiting for the given amount
		 * of time.
		 *
		 * Parameters:
		 *  data (object) - data to send (dir, uh, id)
		 *  thing (jQuery el) - element the vote belongs to
		 *  timeout (int) - time to wait in miliseconds
		 */
		 _voteCallAPI: function(data, thing, timeout, callback) {
		 	setTimeout(function() {
		 		jQuery.post('/api/vote', data).done(function(response) {
		 			callback(data, thing);
		 		}).error(function(response) {
		 			console.log('Error voting on item!');
		 			console.log(response);
		 		});
		 	}, timeout);
		 },

		/**
		 * Up- or downvote all comment on a page.
		 *
		 * Parameters:
		 *  event (jQuery click event)
		 *  dir (int) - 1 upvote, -1 downvote, 0 none
		 */
		 voteAll: function(event, dir) {
		 	event.preventDefault();
		 	var things = jQuery('div.sitetable div.thing');

			// gather the required fullnames to call the API
			for(i = 0; i < things.length; i++) {
				var thing = things[i];
				var fullname = jQuery(thing).attr('data-fullname');
				if(typeof fullname !== 'undefined') {
					// send request to the api
					var data= {
						'dir': dir,
						'id': fullname,
						'uh': this._getModhash()
					};

					this._voteCallAPI(data, thing, 100+(i*400), function(data, thing) {
						var upArrow = jQuery(thing).find('div.arrow[aria-label="upvote"]');
						var downArrow = jQuery(thing).find('div.arrow[aria-label="downvote"]');
						var midcol = jQuery(thing).find('div.midcol');

						// not the fanciest way but prevents unexpected behaivour
						if(data.dir === 1) {
							jQuery(upArrow).addClass('upmod');
							jQuery(upArrow).removeClass('up');

							jQuery(downArrow).addClass('down')
							jQuery(downArrow).removeClass('downmod');

							jQuery(midcol).removeClass('dislikes');
							jQuery(midcol).addClass('likes');
						} else if(data.dir === -1) {
							jQuery(upArrow).addClass('up');
							jQuery(upArrow).removeClass('upmod');

							jQuery(downArrow).removeClass('down');
							jQuery(downArrow).addClass('downmod');

							jQuery(midcol).addClass('dislikes');
							jQuery(midcol).removeClass('likes');
						}
					});
				}
			}
		},

	/**
	 * Open a new window to compose a new message.
	 *
	 * Parameters:
	 *  click (jQuery click event) - the jQuery click event.
	 */
	 composeNew: function(click) {
	 	click.preventDefault();
	 	window.open('http://www.reddit.com/message/compose/');
	 },

	// ////////////////////////////////////////////////////////////////////////
	// 'view source' related functions
	// ////////////////////////////////////////////////////////////////////////

	/**
	 * Helper function to fetch the sourcecode of comments/links/messages using the
	 * reddit api.
	 *
	 * Parameters:
	 *  url (string) - because of the diversity of the api provide a url with the needed attributes
	 *  fullname (string) - fullname needed to search if loading messages
	 *  callback (function(source)) - callback function to call when api call is done
	 */
	 _fetchSourceCode: function(url, fullname, callback) {
	 	jQuery.getJSON(url).done(function(response) {
			// check what type of posting we're looking at (check api for more information)
			var postingType = response.data.children[0].kind;

			// unfortunately the returned json object has no unified structure so
			// we need a bit more logic here
			var source;
			if(postingType === 't1') { // comment
				source = response.data.children[0].data.body;
			} else if(postingType === 't3') { // link (post); will be empty for videos or similiar
				source = response.data.children[0].data.selftext;
			} else if(postingType === 't4') { // message
					// the current api url loads a message thread so we need to find the
					// desired message
					rawData = response.data.children[0].data;
					if(rawData.name === fullname) {
						source = rawData.body;
					} else {
						// search through replies
						var replies = rawData.replies.data.children;
						for(var i = 0; i < replies.length; i++) {
							var replyRaw = replies[i].data;
							if(replyRaw.name === fullname) {
								source = replyRaw.body;
								break;
							}
						}
					}
				}
				callback(source);
			});
},

	/**
	 * Create a textarea to display source code
	 *
	 * Parameters:
	 *  source (string) - source code to display
	 *  fullname (string) - fullname of link/comment/message so we can later identify if we already loaded the source
	 *  prependTo (jQuery element) - element to prepend the textarea to
	 */
	 _createSourceCodeTextarea: function(source, fullname, prependTo) {
		// create a textarea to display source and add it to the dom
		var textAreaEl = jQuery('<textarea class="'+fullname+'">'+source+'</textarea>');
		jQuery(textAreaEl).css({
			'display': 'block'
			, 'width': '90%'
			, 'height': '100px'
		});

		// insert textarea
		jQuery(prependTo).prepend(textAreaEl);
	},

	/**
	 * Toggle source code.
	 *
	 * Parameters:
	 *  click (jQuery click event) - the jQuery click event.
	 */
	 toggleSourceCode: function(click) {
	 	click.preventDefault();

			// grab the clicked link element
			var linkEl = jQuery(click.target);

			// get the data-fullname attribute to provide an id to 'throw at the api'
			var dataFullname = jQuery(linkEl).closest('.thing').attr('data-fullname');

			var isTextAreaPresent = jQuery('textarea.'+dataFullname);
			if(isTextAreaPresent.length == 1) {
				jQuery(isTextAreaPresent).toggle();
			} else {
				// figure out the element where we're going to insert the textarea
				var prependTo = jQuery(linkEl).parent().parent();

				// do an ajax request to fetch the data from the api
				// because we cannot fetch a message and a link/comment with the same api call
				// we need to figure out, if we are on a message site
				var apiURL;
				if(this.currentPage === 'message') {
						// cut off t4_ for filtering
						messageId =  dataFullname.slice(3, dataFullname.length);
						apiURL = '/message/messages/.json?mid='+messageId+'&count=1';
					} else {
						apiURL = '/api/info.json?id=' + dataFullname;
					}

					var that = this;
					this._fetchSourceCode(apiURL, dataFullname, function(source) {
						that._createSourceCodeTextarea(source, dataFullname, prependTo);
					});
				}
			},

	// ////////////////////////////////////////////////////////////////////////
	// 'save as mod' related stuff
	// ////////////////////////////////////////////////////////////////////////

	/**
	 * Place a 'save as mod' in the comment forms.
	 *
	 * Parameters:
	 *  el (jQuery element) - form element to place the button in; leave out to inject into all
	 */
	 _injectSaveAsMod: function(el) {
	 	var that = this;
	 	if(typeof el === 'undefined') {
	 		el = false;
	 	}

		// no element given -> inject into all forms
		var injectHere = new Array();
		if(el === false) {
			injectHere = jQuery('.usertext-buttons');
		} else {
			injectHere.push(jQuery(el).find('.usertext-buttons')[0]);
		}

		// inject between save and cancel
		for(i = 0; i < injectHere.length; i++) {
			// element where the buttons life in
			var divEl = injectHere[i];

			// button to inject; register click function...
			var buttonEl = jQuery('<button type="button" class="raawSaveAsMod">save as mod</button>');
			jQuery(buttonEl).click(function(e) {
				that.saveAsMod(e);
			});

			// find save button and add save as mod after that
			jQuery(divEl).find('button[type="submit"]').after(buttonEl);
		}

		// remove the buttons from the edit form
		jQuery('div.entry .usertext-buttons button.raawSaveAsMod').remove();

		// find all  reply links
		var replyButtons = jQuery('ul.flat-list li a').filter(function(index, el) {
			return jQuery(el).text() === 'reply';
		});

		for(i = 0; i < replyButtons.length; i++) {
			var button = replyButtons[i];
			jQuery(button).click(function(e) {
				setTimeout(function() {
					var allButtons = jQuery('button.raawSaveAsMod');
					for(i = 0; i < allButtons.length; i++) {
						var button = allButtons[i];
						jQuery(button).off('click');
						jQuery(button).click(function(e) {
							that.saveAsMod(e);
						});
					}
				}, 500);
			});
		}
	},

	/**
	 * Method will prevent a comment form to submit in the first place. Will fetch the
	 * thing id to use it for distinguishing. After that will submit the form and when the
	 * submit is finished distinguish the comment itself.
	 *
	 * Parameters:
	 *  click (jQuery click event)
	 */
	 saveAsMod: function(click) {
	 	click.preventDefault();
	 	var form = jQuery(click.target).closest('form.usertext');

		// get parent
		var hiddenInput = jQuery(form).children('input[name="thing_id"]')[0];
		var parent = jQuery(hiddenInput).val();

		// get comment text
		var textarea = jQuery(form).find('textarea')[0];
		var commentText = jQuery(textarea).val();

		var modhash = this._getModhash();
		// post comment
		data = {
			'api_type': 'json'
			, 'text': commentText
			, 'thing_id': parent
			, 'uh': modhash
		};
		jQuery.post('/api/comment', data).done(function(response) {
			if(response.json.errors.length > 0) {
				console.log('Error while posting comment:');
				console.log(response.json.errors);
				alert('Error while posting comment. Please check the console for more information!');
				return;
			}

			// distinguish
			var commentData = response.json.data.things[0].data;
			var data = {
				'id': commentData.id
				, 'api_type': 'json'
				, 'how': 'yes'
				, 'uh': modhash
			};
			jQuery.post('/api/distinguish', data).done(function(response) {
				if(response.json.errors.length > 0) {
					console.log('Error while posting comment:');
					console.log(response.json.errors);
					alert('Error while posting comment. Please check the console for more information!');
					return;
				}

				location.reload();
			});
		});
	},

	// ////////////////////////////////////////////////////////////////////////
	// nuke functions
	// ////////////////////////////////////////////////////////////////////////

	/**
	 * Find the a comment with the given id in a response object.
	 *
	 * Parameters:
	 *	commentId (string) - something like t1_abc
	 *	response (object) - response object returned by a API call
	 *
	 * Returns:
	 *	(object) or undefined
	 */
	 _findCommentInResponse: function(commentId, response) {
	 	var searchedComment;

		// build a search queue to go through
		var search = new Array();
		for(var i = 0; i < response.length; i++) {
			var listing = response[i].data.children;
			for(var n = 0; n < listing.length; n++) {
				var content = listing[n];

				// if data is something else than a comment skip
				if(content.kind !== 't1') {
					continue;
				} else {
					// the comment is the one we search
					if(content.data.id === commentId) {
						return content;
					} else {
						// comment is not what we search but maybe one of his replies?
						// add replies to search queue
						if(typeof content.data.replies !== 'undefined' && content.data.replies !== '') {
							search = search.concat(content.data.replies.data.children);
						}
					}
				}
			}
		}

		while(search.length > 0) {
			var currentObj = search.pop();

			// check if this is the right comment
			if(currentObj.data.id === commentId) {
				return currentObj;
			}

			// add all the replies of this comment to the search array
			if(currentObj.data.replies !== '') {
				search = search.concat(currentObj.data.replies.data.children);
			}
		}

		return searchedComment;
	},

	/**
	 * Will find all replies to the given comment.
	 *
	 * Parameters:
	 *	obj (object) - thing object returned by a API call
	 *
	 * Returns:
	 *	(Array) Maybe an empty array if no replies where found. Array holds the ids as a
	 *		string. E.g. '1234', 'a23pav', ...
	 */
	 _findAllReplies: function(obj) {
	 	var replies = new Array();

		// check if there are replies
		if(obj.data.replies === '') {
			return replies;
		}

		var search = new Array();
		for(var i = 0; i < obj.data.replies.data.children.length; i++) {
			var reply = obj.data.replies.data.children[i];
			replies.push(reply.data.id);
			if(typeof reply.data.replies !== 'undefined' && reply.data.replies !== '') {
				search = search.concat(reply.data.replies.data.children);
			}
		}

		while(search.length > 0) {
			var currentObj = search.pop();

			// 'more' occures if there are more than 10 entries
			if(currentObj.kind === 'more') {
				continue;
			}

			// add the id of the current reply
			replies.push(currentObj.data.id);

			// add all replies to the reply to the search
			if(currentObj.data.replies !== '') {
				search = search.concat(currentObj.data.replies.data.children);
			}
		}

		return replies;
	},

	/**
	 * As found on https://github.com/agentlame/Reddit-Mod-Nuke-Userscript/blob/master/modnuke.user.js
	 *
	 * Only reversed the iteration order and fading out the elements.
	 */
	_nukeComment: function(thread_root) {
		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)"]');
		for(var i=elmnts.length-1; i >= 0; i--) {
		setTimeout(
			(function(_elmnt) {
			return function() {
				jQuery(_elmnt).closest('.thing').hide(750);
				var event = document.createEvent('UIEvents');
				event.initUIEvent('click', true, true, window, 1);
				_elmnt.dispatchEvent(event);
			}}
			)(elmnts[i]), 1500*(elmnts.length-i)); // 1.5s timeout prevents overloading reddit.
		};
	 },

	/**
	 * Inject the nuke function as found on
	 * https://github.com/agentlame/Reddit-Mod-Nuke-Userscript/blob/master/modnuke.user.js
	 */
	_injectNuke: function() {
		var that = this;
		var nuke_button = new Array();
		var divels = document.querySelectorAll('div.noncollapsed');
		var comment_ids = new Array();
		var use_image = false;
		// create img DOM element to clone
		if(use_image) {
			try {
				var img_element =  document.createElement('img');
				img_element.setAttribute('alt', 'Nuke!');
				img_element.setAttribute('src', chrome.extension.getURL('nuke.png'));
			} catch(e) {
				use_image = false;
			}
		}
		for (var i = 0; i < divels.length; i++) {
			var author_link = divels[i].querySelector('p.tagline>a.author,p.tagline>span.author,p.tagline>em');
		// p.tagline>a.author is normal comment;
		// some author deleted comments seem to have either
		// p.tagline>span.author or p.tagline>em

		comment_ids[i] = divels[i].getAttribute('data-fullname');
		// console.log(i + ':' + comment_ids);
		if(author_link) {
			// create link DOM element with img inside link
			nuke_button[i] = document.createElement('a')
			nuke_button[i].setAttribute('href', 'javascript:void(0)');
			nuke_button[i].setAttribute('title', 'Nuke!');
			nuke_button[i].setAttribute('id', 'nuke_'+i);
			if(use_image) {
				nuke_button[i].appendChild(img_element.cloneNode(true));
			} else {
				nuke_button[i].innerHTML= "[Nuke]";
			}
			// append after the author's name
			author_link.parentNode.insertBefore(nuke_button[i], author_link.nextSibling);

			// Add listener for click; using IIFE to function with _i as value of i when created; not when click
			nuke_button[i].addEventListener('click',
				(function(_i) {
					return function() {
						var continue_thread = divels[_i].querySelectorAll('span.morecomments>a');
						var comment_str = " comments?";
						if(continue_thread.length > 0) {
							comment_str = "+ comments (more after expanding collapsed threads; there will be a pause before the first deletion to retrieve more comments)?";
						}
						var delete_button = divels[_i].querySelectorAll('form input[value="removed"]~span.option.error a.yes,a[onclick^="return big_mod_action($(this), -1)"]');
						// form input[value="removed"]~span.option.error a.yes -- finds the yes for normal deleting comments.
						// a.pretty-button.neutral finds the 'remove' button for flagged comments
						if (confirm("Are you sure you want to nuke the following " + delete_button.length + comment_str)) {
							for (var indx=0; indx < continue_thread.length; indx++) {
								var elmnt = continue_thread[indx];
								setTimeout(
									function() {
										var event = document.createEvent('UIEvents');
										event.initUIEvent('click', true, true, window, 1);
										elmnt.dispatchEvent(event);
								}, 2000*indx); // wait two seconds before each ajax call before clicking each "load more comments"
							}
							if(indx > 0) {
								setTimeout(function() {that._nukeComment(comment_ids[_i])},
									2000*(indx + 2)); // wait 4s after last ajax "load more comments"
							} else {
								that._nukeComment(comment_ids[_i]); // call immediately if not "load more comments"
							}
						}
					}
				}
				)(i)); // end of IIFE (immediately invoked function expression)
			}
		}
	}
};

// initialize when document loaded
jQuery(document).ready(function() {
	RaaW.init();
});