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;
22 var enhance4ChanX = function(){
24 if(document.getElementById("qrRebuilder") !== null) qrWindow.removeChild(document.getElementById("qrImages"));
25 document.getElementById("dump-button").click();
27 console.log(document.getElementById("qr").getElementsByTagName("TEXTAREA")[0]);
28 var dList = document.getElementById("dump-list");
29 var filenamecontainer = document.getElementById("qr-filename-container");
34 var observer = new MutationObserver(function(mutate){
35 BGImg = dList.firstChild.style.backgroundImage;
36 if(BGImg !== oldBGImg && imgURL !== ""){
37 console.log("CHANGED");
38 dList.firstChild.style.backgroundImage = "url(" + imgURL + ")";
39 console.log("CHANGED");
40 oldBGImg = dList.firstChild.style.backgroundImage;
41 console.log("CHANGED");
44 else if (imgURL == ""){
48 observer.observe(dList , {attributes: true,subtree:true, childList: true, characterData: true });*/
50 document.getElementById("qr-filerm").addEventListener("click", function(){imgURL = "";});
54 var qrWindow = document.getElementById("qr");
56 var qrTable = document.createElement("TABLE");
57 qrTable.setAttribute("id", "qrRebuilder");
58 qrTable.setAttribute("style", "text-align:center");
59 qrWindow.appendChild(qrTable);
60 qrWindow.appendChild(document.createElement("BR"));
62 var instructionRow = document.createElement("TR");
63 var topRowNodes = [document.createElement("BR"),
64 document.createTextNode("Insert the thread number of the post to rebuild"),
65 document.createElement("BR"),
66 document.createTextNode("Must be in the 4chan archives"),
67 document.createElement("BR"),
71 instructionRow.appendChild(node);
73 qrTable.appendChild(instructionRow);
75 var threadRow = document.createElement("TR");
76 var secondRowNodes = [
77 document.createTextNode("Thread: "),
78 document.createElement("INPUT"),
79 document.createElement("INPUT"),
81 secondRowNodes.forEach(
83 threadRow.appendChild(node);
85 qrTable.appendChild(threadRow);
87 secondRowNodes[1].setAttribute("ID", "threadInput");
88 secondRowNodes[1].setAttribute("style", "width:44.9%");
90 secondRowNodes[2].setAttribute("ID", "threadButton");
91 secondRowNodes[2].setAttribute("type", "button");
92 secondRowNodes[2].setAttribute("value", "Set Rebuild Queue");
93 secondRowNodes[2].addEventListener("click", function(){
95 getThread(secondRowNodes[1].value);
97 postID = setInterval(postRoutine, 1000);
106 var postRoutine = function(){
109 len = threadData[0].length;
111 fillID = setInterval(fillRoutine, 10);
115 var stopRoutine = function(){
116 console.log("Post Ends");
117 clearInterval(postID);
121 var fillRoutine = function(){
122 console.log(semaphore_posts + " " + i);
123 if(i >= len) {semaphore_posts = 0 ; stopFillRoutine();}
124 else if(semaphore_posts == 1){
126 createPost(threadData[0][i], threadData[1][i], threadData[2][i]);
131 var stopFillRoutine = function(){
132 console.log("Fill Ends");
133 clearInterval(fillID);
136 //2) GET ARCHIVED THREAD
137 var getThread = function(threadNo){
138 threadData = [[], [], [], []];
140 URL = "https://a.4cdn.org/" + board + "/thread/" + threadNo + ".json";
142 var xhr = new GM_xmlhttpRequest(({
145 responseType : "json",
146 onload: function(data){
147 data = data.response;
149 if(data == undefined) alert("Invalid Thread ID: " + threadNo + ".\n4chan Archive Only");
151 var len = data["posts"].length;
152 for(var i = 1 ; i < len ; i++){
153 var comment = data["posts"][i]["com"];
154 if(comment !== undefined)
155 threadData[0].push(comment);
157 threadData[0].push(-1);
159 var filename = "" + data["posts"][i]["tim"] + data["posts"][i]["ext"];
160 if(filename !== undefined && filename.indexOf("undefined") == -1)
161 threadData[1].push("https://i.4cdn.org/" + board + "/" + filename);
162 else threadData[1].push(-1);
164 threadData[2].push(data["posts"][i]["filename"]);
166 threadData[3].push(data["posts"][i]["no"]);
169 console.log(threadData);
175 //3) RIP POSTS AND IMAGES
176 var createPost = function(text, imageURL, imageName){
177 console.log("url: " + imageURL);
179 var xhr = new GM_xmlhttpRequest(({
182 responseType : "arraybuffer",
183 onload: function(response)
187 if(imageURL.indexOf(".jpg") > -1){
188 blob = new Blob([response.response], {type:"image/jpeg"});
191 else if(imageURL.indexOf(".png") > -1){
192 blob = new Blob([response.response], {type:"image/png"});
195 else if(imageURL.indexOf(".gif") > -1){
196 blob = new Blob([response.response], {type:"image/gif"});
199 else if(imageURL.indexOf(".webm") > -1){
200 blob = new Blob([response.response], {type:"video/webm"});
204 var name = imageName + ext;
206 console.log("----------------");
207 console.log("Blob: "); console.log(blob);
208 console.log("MIME: " + blob.type);
209 console.log("Name: " + name);
211 //SEND RESULTING RESPONSE TO 4CHANX FILES === QRSetFile
212 var detail = {file:blob, name:name};
213 if (typeof cloneInto === 'function') {
214 detail = cloneInto(detail , document.defaultView);
216 console.log("Detail: ");console.log(detail);
217 document.dispatchEvent(new CustomEvent('QRSetFile', {bubbles:true, detail}));
219 if(text !== "" && text !== undefined && text !== -1) createPostComment(text);
221 document.getElementById("add-post").click();
227 createPostComment(text);
228 document.getElementById("add-post").click();
233 //4) CREATE POST QUEUE
234 var createPostComment = function(text){
235 console.log("text-Before: " + text);
237 text = text.replace(/<a href="\/[a-zA-Z]+\/" class="quotelink">>>>/g, ">>>");
238 text = text.replace(/<a href="#p[0-9]+" class="quotelink">>>/g, ">>");
239 text = text.replace(/<span class="quote">>/g, ">");
240 text = text.replace(/<br>/g, "\n");
241 text = text.replace(/'/g, "'");
242 text = text.replace(/<\/a>/g, "");
243 text = text.replace(/<wbr>/g, "");
244 text = text.replace(/<\/span>/g, "");
246 console.log("text-After: " + text);
247 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, '')*/;
248 else document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value = text;
252 window.onload = function(){
253 console.log(document.links);
254 var len = document.links.length;
255 for(var i = 0 ; i < len ; i++){
256 document.links[i].addEventListener("click", enhance4ChanX);
259 //ENHANCE DUMP TABS (COVER, 482PX - 482PX)
260 //DUMP LIST MAX-HEIGHT TO 490
262 document.getElementById("fourchanx-css").textContent += ".qr-preview { height: 482px; width: 482px; background-size: cover;}";
263 //document.getElementById("fourchanx-css").textContent += "#qr { min-width:490px;}";
264 document.getElementById("fourchanx-css").textContent += "#dump-list { min-height: 400px; width: 509px;}";