2 // @name Thread Rebuilder
3 // @namespace http://tampermonkey.net/
5 // @description try to take over the world!
7 // @match https://boards.4chan.org/*/thread/*
8 // @match http://boards.4chan.org/*/thread/*
9 // @grant GM_xmlhttpRequest
10 // @updateURL https://github.com/ECHibiki/4chan-UserScripts/raw/master/Thread-Rebuilder.user.js
11 // @downloadURL https://github.com/ECHibiki/4chan-UserScripts/raw/master/Thread-Rebuilder.user.js
15 var thread_data = [['Comment'], ['Image URLs'], ['Image Names'] ,['Post No.']];
17 var semaphore_posts = 1;
21 //set listener to build interface in 4chanX
22 //set listeners to build interface in 4chanX
23 document.addEventListener("4chanXInitFinished", function(e){
24 var len = document.links.length;
25 for(var links = 0 ; links < len ; links++){
26 var class_name = document.links[links].parentNode.className ;
27 if(class_name == "postNum desktop" || class_name == "qr-link-container"
28 || class_name == "brackets-wrap qr-link-container-bottom")
29 document.links[links].addEventListener("click", enhance4ChanX);
32 //ENHANCE DUMP TABS (COVER, 482PX - 482PX)
33 //DUMP LIST MAX-HEIGHT TO 490
35 document.getElementById("fourchanx-css").textContent += ".qr-preview { height: 482px; width: 482px; background-size: cover;}";
36 document.getElementById("fourchanx-css").textContent += "#dump-list { min-height: 400px; width: 509px;}";
41 var enhance4ChanX = function(){
42 var qr_window = document.getElementById("qr");
44 if(document.getElementById("qrRebuilder") !== null) qr_window.removeChild(document.getElementById("qrRebuilder"));
46 var thread_rebuilder_table = document.createElement("TABLE");
47 thread_rebuilder_table.setAttribute("id", "qrRebuilder");
48 thread_rebuilder_table.setAttribute("style", "text-align:center");
49 qr_window.appendChild(thread_rebuilder_table);
51 var instruction_row = document.createElement("TR");
52 var top_row_nodes = [document.createElement("BR"),
53 document.createTextNode("Insert the thread number of the post to rebuild"),
54 document.createElement("BR"),
55 document.createTextNode("Must be in the 4chan archives"),
56 document.createElement("BR"),
58 top_row_nodes.forEach(
60 instruction_row.appendChild(node);
62 thread_rebuilder_table.appendChild(instruction_row);
64 var thread_row = document.createElement("TR");
65 var second_row_nodes = [
66 document.createTextNode("Thread: "),
67 document.createElement("INPUT"),
68 document.createElement("INPUT"),
70 second_row_nodes.forEach(
72 thread_row.appendChild(node);
74 thread_rebuilder_table.appendChild(thread_row);
76 second_row_nodes[1].setAttribute("ID", "threadInput");
77 second_row_nodes[1].setAttribute("style", "width:44.9%");
79 second_row_nodes[2].setAttribute("ID", "threadButton");
80 second_row_nodes[2].setAttribute("type", "button");
81 second_row_nodes[2].setAttribute("value", "Set Rebuild Queue");
83 second_row_nodes[2].addEventListener("click", function(){
84 getThread(second_row_nodes[1].value);
85 postID = setInterval(postRoutine, 1000);
86 if(timeListen === undefined) timeListen = setInterval(timeListenerFunction, 1000);
90 var thread_data_length = 0;
91 var posts_created = 0;
93 var postRoutine = function(){
96 thread_data_length = thread_data[0].length;
97 fillID = setInterval(fillRoutine, 10);
102 var stopRoutine = function(){
103 clearInterval(postID);
107 var fillRoutine = function(){
108 if(posts_created >= thread_data_length) {semaphore_posts = 0 ; stopFillRoutine();}
109 else if(semaphore_posts == 1){
111 createPost(thread_data[0][posts_created], thread_data[1][posts_created], thread_data[2][posts_created]);
116 var stopFillRoutine = function(){
117 clearInterval(fillID);
120 var setPropperLinking = function(text){
121 var search_regex = RegExp(">>\\d+", "g");
124 var link_arr = Array();
125 while((result = search_regex.exec(text)) != null){
126 var end_index = search_regex.lastIndex;
127 var post_no = result.toString().replace(/>/g, "");
128 link_arr.push([post_no, end_index]);
130 //hunt down the text of what it linked to
131 var responding_text = Array();
132 URL = "https://a.4cdn.org/" + board + "/thread/" + document.getElementById("threadInput").value + ".json";
133 var xhr = new GM_xmlhttpRequest(({
136 responseType : "json",
137 onload: function(data){
138 data = data.response["posts"];
139 if(data == undefined){
140 alert("Invalid Thread ID: " + threadNo + ". ");
143 link_arr.forEach(function(link_item){
144 for(var data_entry = 0 ; data_entry < data.length ; data_entry++){
145 if(parseInt(link_item[0]) == parseInt(data[data_entry]["no"])){
146 responding_text.push([ [post_no, end_index], data[data_entry]["com"].replace(/(>>|#p)\d+/g, ""), data[data_entry]["md5"]]);
152 var current_url = window.location.href;
153 var hash_index = current_url.lastIndexOf("#") != -1 ? current_url.lastIndexOf("#"): window.location.href.length;
154 var current_thread = window.location.href.substring(current_url.lastIndexOf("/")+1, hash_index);
155 //open current thread to hunt down links
156 URL = "https://a.4cdn.org/" + board + "/thread/" + current_thread + ".json";
157 var xhr = new GM_xmlhttpRequest(({
160 responseType : "json",
161 onload: function(data){
162 data = data.response["posts"];
163 if(data == undefined){
164 alert("Invalid Thread ID: " + threadNo + ". ");
167 responding_text.forEach(function(response_item){
168 for(var data_entry = 0 ; data_entry < data.length ; data_entry++){
169 if((response_item[1] == data[data_entry]["com"].replace(/(>>|#p)\d+/g, "") || response_item[1] == null)
170 && (response_item[2] == data[data_entry]["md5"] || response_item[2] == null)){
171 var start_index = response_item[0][0].legth - response_item[0][1];
172 text = text.substring(0, start_index) + ">>" + data[data_entry]["no"] + text.substring(response_item[0][1]);
177 document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value = text;
178 document.getElementById("add-post").click();
188 //2) GET ARCHIVED THREAD
189 var getThread = function(threadNo){
190 thread_data = [[], [], [], []];
192 URL = "https://a.4cdn.org/" + board + "/thread/" + threadNo + ".json";
193 ////console.log(URL);
194 var xhr = new GM_xmlhttpRequest(({
197 responseType : "json",
198 onload: function(data){
199 data = data.response;
200 ////console.log(data);
201 if(data == undefined){
202 alert("Invalid Thread ID: " + threadNo + ".\n4chan Archive ");
203 //draw from desu instead
206 var len = data["posts"].length;
207 for(var i = 1 ; i < len ; i++){
208 var comment = data["posts"][i]["com"];
209 if(comment !== undefined)
210 thread_data[0].push(comment);
212 thread_data[0].push(-1);
214 var filename = "" + data["posts"][i]["tim"] + data["posts"][i]["ext"];
215 if(filename !== undefined && filename.indexOf("undefined") == -1)
216 thread_data[1].push("https://i.4cdn.org/" + board + "/" + filename);
217 else thread_data[1].push(-1);
219 thread_data[2].push(data["posts"][i]["filename"]);
221 thread_data[3].push(data["posts"][i]["no"]);
229 //3) RIP POSTS AND IMAGES
230 var createPost = function(text, imageURL, imageName){
232 var xhr = new GM_xmlhttpRequest(({
235 responseType : "arraybuffer",
236 onload: function(response)
240 if(imageURL.indexOf(".jpg") > -1){
241 blob = new Blob([response.response], {type:"image/jpeg"});
244 else if(imageURL.indexOf(".png") > -1){
245 blob = new Blob([response.response], {type:"image/png"});
248 else if(imageURL.indexOf(".gif") > -1){
249 blob = new Blob([response.response], {type:"image/gif"});
252 else if(imageURL.indexOf(".webm") > -1){
253 blob = new Blob([response.response], {type:"video/webm"});
257 var name = imageName + ext;
259 //SEND RESULTING RESPONSE TO 4CHANX FILES === QRSetFile
260 var detail = {file:blob, name:name};
261 if (typeof cloneInto === 'function') {
262 detail = cloneInto(detail , document.defaultView);
265 document.dispatchEvent(new CustomEvent('QRSetFile', {bubbles:true, detail}));
267 if(text !== "" && text !== undefined && text !== -1) {
268 text = createPostComment(text);
269 setPropperLinking(text);
272 document.getElementById("add-post").click();
279 text = createPostComment(text);
280 setPropperLinking(text);
284 //4) CREATE POST QUEUE
285 var createPostComment = function(text){
286 text = text.replace(/<a href="\/[a-zA-Z]+\/" class="quotelink">/g, "");
287 text = text.replace(/<span class="deadlink">/g, "");
289 var quote_regex = /<a href="#p[0-9]+" class="quotelink">>>[0-9]+/g;
290 var find = text.match(quote_regex);
292 find.forEach(function(match){
293 var index_start = text.indexOf(match);
294 var match_len = match.length;
295 var index_len = index_start + match_len;
296 var first_quote = match.indexOf('"');
297 var second_quote = match.indexOf('"', first_quote + 1);
298 var post_no = match.substring(first_quote + 3, second_quote);
300 match = ">>" + post_no;
302 text = text.substr(0, index_start) + match + text.substr(index_len);
306 text = text.replace(/<span class="quote">/g, "");
307 text = text.replace(/<br>/g, "\n");
308 text = text.replace(/'/g, "'");
309 text = text.replace(/>/g, ">");
310 text = text.replace(/<\/a>/g, "");
311 text = text.replace(/<wbr>/g, "");
312 text = text.replace(/<\/span>/g, "");
318 var timeListenerFunction = function(){
319 var time = document.getElementById("qr-filename-container").nextSibling.value.replace(/[a-zA-Z]+/g, "");
329 document.addEventListener('QRPostSuccessful', function(e) {
330 document.getElementById("dump-list").childNodes[1].click();
331 setPropperLinking(document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value);