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 threadData = [['Comment'], ['Image URLs'], ['Image Names'] ,['Post No.']];
17 var semaphore_posts = 1;
21 //set listener to build interface in 4chanX
22 window.onload = function(){
23 var len = document.links.length;
24 for(var i = 0 ; i < len ; i++){
25 var class_name = document.links[i].parentNode.className ;
26 if(class_name == "postNum desktop" || class_name == "qr-link-container"
27 || class_name == "brackets-wrap qr-link-container-bottom")
28 document.links[i].addEventListener("click", enhance4ChanX);
31 //ENHANCE DUMP TABS (COVER, 482PX - 482PX)
32 //DUMP LIST MAX-HEIGHT TO 490
34 document.getElementById("fourchanx-css").textContent += ".qr-preview { height: 482px; width: 482px; background-size: cover;}";
35 document.getElementById("fourchanx-css").textContent += "#dump-list { min-height: 400px; width: 509px;}";
39 var enhance4ChanX = function(){
41 var qrWindow = document.getElementById("qr");
43 if(document.getElementById("qrRebuilder") !== null) qrWindow.removeChild(document.getElementById("qrRebuilder"));
44 //document.getElementById("dump-button").click();
46 ////console.log(document.getElementById("qr").getElementsByTagName("TEXTAREA")[0]);
47 var dList = document.getElementById("dump-list");
48 var filenamecontainer = document.getElementById("qr-filename-container");
53 var observer = new MutationObserver(function(mutate){
54 BGImg = dList.firstChild.style.backgroundImage;
55 if(BGImg !== oldBGImg && imgURL !== ""){
56 //console.log("CHANGED");
57 dList.firstChild.style.backgroundImage = "url(" + imgURL + ")";
58 //console.log("CHANGED");
59 oldBGImg = dList.firstChild.style.backgroundImage;
60 //console.log("CHANGED");
61 //console.log(imgURL);
63 else if (imgURL == ""){
67 observer.observe(dList , {attributes: true,subtree:true, childList: true, characterData: true });*/
69 if(document.getElementById("qr-filerm") !== null)
70 document.getElementById("qr-filerm").addEventListener("click", function(){imgURL = "";});
73 var qrTable = document.createElement("TABLE");
74 qrTable.setAttribute("id", "qrRebuilder");
75 qrTable.setAttribute("style", "text-align:center");
76 qrWindow.appendChild(qrTable);
78 var instructionRow = document.createElement("TR");
79 var topRowNodes = [document.createElement("BR"),
80 document.createTextNode("Insert the thread number of the post to rebuild"),
81 document.createElement("BR"),
82 document.createTextNode("Must be in the 4chan archives"),
83 document.createElement("BR"),
87 instructionRow.appendChild(node);
89 qrTable.appendChild(instructionRow);
91 var threadRow = document.createElement("TR");
92 var secondRowNodes = [
93 document.createTextNode("Thread: "),
94 document.createElement("INPUT"),
95 document.createElement("INPUT"),
97 secondRowNodes.forEach(
99 threadRow.appendChild(node);
101 qrTable.appendChild(threadRow);
103 secondRowNodes[1].setAttribute("ID", "threadInput");
104 secondRowNodes[1].setAttribute("style", "width:44.9%");
106 secondRowNodes[2].setAttribute("ID", "threadButton");
107 secondRowNodes[2].setAttribute("type", "button");
108 secondRowNodes[2].setAttribute("value", "Set Rebuild Queue");
109 secondRowNodes[2].addEventListener("click", function(){
110 ////console.log("exce");
111 getThread(secondRowNodes[1].value);
113 postID = setInterval(postRoutine, 1000);
114 if(timeListen === undefined) timeListen = setInterval(timeListenerFunction, 1000);
123 var postRoutine = function(){
126 len = threadData[0].length;
127 ////console.log(len);
128 fillID = setInterval(fillRoutine, 10);
132 var stopRoutine = function(){
133 ////console.log("Post Ends");
134 clearInterval(postID);
138 var fillRoutine = function(){
139 ////console.log(semaphore_posts + " " + i);
140 if(i >= len) {semaphore_posts = 0 ; stopFillRoutine();}
141 else if(semaphore_posts == 1){
143 createPost(threadData[0][i], threadData[1][i], threadData[2][i]);
148 var stopFillRoutine = function(){
149 ////console.log("Fill Ends");
150 clearInterval(fillID);
153 var setPropperLinking = function(text){
155 var search_regex = RegExp(">>\\d+", "g");
158 var link_arr = Array();
159 while((result = search_regex.exec(text)) != null){
160 var end_index = search_regex.lastIndex;
161 var post_no = result.toString().replace(/>/g, "");
162 link_arr.push([post_no, end_index]);
164 //hunt down the text of what it linked to
165 var responding_text = Array();
166 URL = "https://a.4cdn.org/" + board + "/thread/" + document.getElementById("threadInput").value + ".json";
167 ////console.log(URL);
168 var xhr = new GM_xmlhttpRequest(({
171 responseType : "json",
172 onload: function(data){
173 data = data.response["posts"];
174 ////console.log(data);
175 if(data == undefined){
176 alert("Invalid Thread ID: " + threadNo + ".\n4chan Archive ");
177 //draw from desu instead
180 link_arr.forEach(function(link_item){
181 for(var data_entry = 0 ; data_entry < data.length ; data_entry++){
182 //console.log(parseInt(link_item[0]));
183 //console.log(parseInt(data[data_entry]["no"]));
184 if(parseInt(link_item[0]) == parseInt(data[data_entry]["no"])){
185 responding_text.push([ [post_no, end_index], data[data_entry]["com"].replace(/(>>|#p)\d+/g, ""), data[data_entry]["md5"]]);
186 //console.log(responding_text);
193 var current_url = window.location.href;
194 var hash_index = current_url.lastIndexOf("#") != -1 ? current_url.lastIndexOf("#"): window.location.href.length;
195 var current_thread = window.location.href.substring(current_url.lastIndexOf("/")+1, hash_index);
196 //open current thread to hunt down links
197 URL = "https://a.4cdn.org/" + board + "/thread/" + current_thread + ".json";
198 ////console.log(URL);
199 var xhr = new GM_xmlhttpRequest(({
202 responseType : "json",
203 onload: function(data){
204 data = data.response["posts"];
205 ////console.log(data);
206 if(data == undefined){
207 alert("Invalid Thread ID: " + threadNo + ".\n4chan Archive ");
208 //draw from desu instead
211 responding_text.forEach(function(response_item){
212 for(var data_entry = 0 ; data_entry < data.length ; data_entry++){
213 //console.log (response_item);
214 //console.log(data[data_entry]);
215 //console.log ("----");
216 if((response_item[1] == data[data_entry]["com"].replace(/(>>|#p)\d+/g, "") || response_item[1] == null)
217 && (response_item[2] == data[data_entry]["md5"] || response_item[2] == null)){
218 var start_index = response_item[0][0].legth - response_item[0][1];
219 text = text.substring(0, start_index) + ">>" + data[data_entry]["no"] + text.substring(response_item[0][1]);
225 document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value = text;
226 document.getElementById("add-post").click();
236 //2) GET ARCHIVED THREAD
237 var getThread = function(threadNo){
238 threadData = [[], [], [], []];
240 URL = "https://a.4cdn.org/" + board + "/thread/" + threadNo + ".json";
241 ////console.log(URL);
242 var xhr = new GM_xmlhttpRequest(({
245 responseType : "json",
246 onload: function(data){
247 data = data.response;
248 ////console.log(data);
249 if(data == undefined){
250 alert("Invalid Thread ID: " + threadNo + ".\n4chan Archive ");
251 //draw from desu instead
254 var len = data["posts"].length;
255 for(var i = 1 ; i < len ; i++){
256 var comment = data["posts"][i]["com"];
257 if(comment !== undefined)
258 threadData[0].push(comment);
260 threadData[0].push(-1);
262 var filename = "" + data["posts"][i]["tim"] + data["posts"][i]["ext"];
263 if(filename !== undefined && filename.indexOf("undefined") == -1)
264 threadData[1].push("https://i.4cdn.org/" + board + "/" + filename);
265 else threadData[1].push(-1);
267 threadData[2].push(data["posts"][i]["filename"]);
269 threadData[3].push(data["posts"][i]["no"]);
272 ////console.log(threadData);
278 //3) RIP POSTS AND IMAGES
279 var createPost = function(text, imageURL, imageName){
280 ////console.log("url: " + imageURL);
282 var xhr = new GM_xmlhttpRequest(({
285 responseType : "arraybuffer",
286 onload: function(response)
290 if(imageURL.indexOf(".jpg") > -1){
291 blob = new Blob([response.response], {type:"image/jpeg"});
294 else if(imageURL.indexOf(".png") > -1){
295 blob = new Blob([response.response], {type:"image/png"});
298 else if(imageURL.indexOf(".gif") > -1){
299 blob = new Blob([response.response], {type:"image/gif"});
302 else if(imageURL.indexOf(".webm") > -1){
303 blob = new Blob([response.response], {type:"video/webm"});
307 var name = imageName + ext;
309 ////console.log("----------------");
310 ////console.log("Blob: "); ////console.log(blob);
311 ////console.log("MIME: " + blob.type);
312 ////console.log("Name: " + name);
314 //SEND RESULTING RESPONSE TO 4CHANX FILES === QRSetFile
315 var detail = {file:blob, name:name};
316 if (typeof cloneInto === 'function') {
317 detail = cloneInto(detail , document.defaultView);
319 ////console.log("Detail: ");////console.log(detail);
320 document.dispatchEvent(new CustomEvent('QRSetFile', {bubbles:true, detail}));
322 if(text !== "" && text !== undefined && text !== -1) {
323 text = createPostComment(text);
324 setPropperLinking(text);
327 document.getElementById("add-post").click();
334 text = createPostComment(text);
335 setPropperLinking(text);
339 //4) CREATE POST QUEUE
340 var createPostComment = function(text){
341 ////console.log("text-Before: " + text);
343 text = text.replace(/<a href="\/[a-zA-Z]+\/" class="quotelink">/g, "");
344 text = text.replace(/<span class="deadlink">/g, "");
346 var quote_regex = /<a href="#p[0-9]+" class="quotelink">>>[0-9]+/g;
347 var find = text.match(quote_regex);
349 find.forEach(function(match){
350 ////console.log("---==");
351 var index_start = text.indexOf(match);
352 var match_len = match.length;
353 var index_len = index_start + match_len;
354 var first_quote = match.indexOf('"');
355 var second_quote = match.indexOf('"', first_quote + 1);
356 var post_no = match.substring(first_quote + 3, second_quote);
358 match = ">>" + post_no;
360 text = text.substr(0, index_start) + match + text.substr(index_len);
364 text = text.replace(/<span class="quote">/g, "");
365 text = text.replace(/<br>/g, "\n");
366 text = text.replace(/'/g, "'");
367 text = text.replace(/>/g, ">");
368 text = text.replace(/<\/a>/g, "");
369 text = text.replace(/<wbr>/g, "");
370 text = text.replace(/<\/span>/g, "");
372 ////console.log("text-After: " + text);
373 //if(text.match(/^>>[0-9]+$/g)) document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value = text + "\n" + Math.floor(Math.random() * 1000).toString(62)/*.replace(/[^a-z]+/g, '')*/;
379 var timeListenerFunction = function(){
380 var time = document.getElementById("qr-filename-container").nextSibling.value.replace(/[a-zA-Z]+/g, "");
383 //console.log(time + "A");
387 //console.log(time + "B");
392 document.addEventListener('QRPostSuccessful', function(e) {
393 document.getElementById("dump-list").childNodes[1].click();
394 setPropperLinking(document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value);