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 var qrWindow = document.getElementById("qr");
26 if(document.getElementById("qrRebuilder") !== null) qrWindow.removeChild(document.getElementById("qrRebuilder"));
27 //document.getElementById("dump-button").click();
29 //console.log(document.getElementById("qr").getElementsByTagName("TEXTAREA")[0]);
30 var dList = document.getElementById("dump-list");
31 var filenamecontainer = document.getElementById("qr-filename-container");
36 var observer = new MutationObserver(function(mutate){
37 BGImg = dList.firstChild.style.backgroundImage;
38 if(BGImg !== oldBGImg && imgURL !== ""){
39 console.log("CHANGED");
40 dList.firstChild.style.backgroundImage = "url(" + imgURL + ")";
41 console.log("CHANGED");
42 oldBGImg = dList.firstChild.style.backgroundImage;
43 console.log("CHANGED");
46 else if (imgURL == ""){
50 observer.observe(dList , {attributes: true,subtree:true, childList: true, characterData: true });*/
52 if(document.getElementById("qr-filerm") !== null)
53 document.getElementById("qr-filerm").addEventListener("click", function(){imgURL = "";});
56 var qrTable = document.createElement("TABLE");
57 qrTable.setAttribute("id", "qrRebuilder");
58 qrTable.setAttribute("style", "text-align:center");
59 qrWindow.appendChild(qrTable);
61 var instructionRow = document.createElement("TR");
62 var topRowNodes = [document.createElement("BR"),
63 document.createTextNode("Insert the thread number of the post to rebuild"),
64 document.createElement("BR"),
65 document.createTextNode("Must be in the 4chan archives"),
66 document.createElement("BR"),
70 instructionRow.appendChild(node);
72 qrTable.appendChild(instructionRow);
74 var threadRow = document.createElement("TR");
75 var secondRowNodes = [
76 document.createTextNode("Thread: "),
77 document.createElement("INPUT"),
78 document.createElement("INPUT"),
80 secondRowNodes.forEach(
82 threadRow.appendChild(node);
84 qrTable.appendChild(threadRow);
86 secondRowNodes[1].setAttribute("ID", "threadInput");
87 secondRowNodes[1].setAttribute("style", "width:44.9%");
89 secondRowNodes[2].setAttribute("ID", "threadButton");
90 secondRowNodes[2].setAttribute("type", "button");
91 secondRowNodes[2].setAttribute("value", "Set Rebuild Queue");
92 secondRowNodes[2].addEventListener("click", function(){
93 //console.log("exce");
94 getThread(secondRowNodes[1].value);
96 postID = setInterval(postRoutine, 1000);
97 if(timeListen === undefined) timeListen = setInterval(timeListenerFunction, 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){
150 alert("Invalid Thread ID: " + threadNo + ".\n4chan Archive ");
151 //draw from desu instead
154 var len = data["posts"].length;
155 for(var i = 1 ; i < len ; i++){
156 var comment = data["posts"][i]["com"];
157 if(comment !== undefined)
158 threadData[0].push(comment);
160 threadData[0].push(-1);
162 var filename = "" + data["posts"][i]["tim"] + data["posts"][i]["ext"];
163 if(filename !== undefined && filename.indexOf("undefined") == -1)
164 threadData[1].push("https://i.4cdn.org/" + board + "/" + filename);
165 else threadData[1].push(-1);
167 threadData[2].push(data["posts"][i]["filename"]);
169 threadData[3].push(data["posts"][i]["no"]);
172 //console.log(threadData);
178 //3) RIP POSTS AND IMAGES
179 var createPost = function(text, imageURL, imageName){
180 //console.log("url: " + imageURL);
182 var xhr = new GM_xmlhttpRequest(({
185 responseType : "arraybuffer",
186 onload: function(response)
190 if(imageURL.indexOf(".jpg") > -1){
191 blob = new Blob([response.response], {type:"image/jpeg"});
194 else if(imageURL.indexOf(".png") > -1){
195 blob = new Blob([response.response], {type:"image/png"});
198 else if(imageURL.indexOf(".gif") > -1){
199 blob = new Blob([response.response], {type:"image/gif"});
202 else if(imageURL.indexOf(".webm") > -1){
203 blob = new Blob([response.response], {type:"video/webm"});
207 var name = imageName + ext;
209 //console.log("----------------");
210 //console.log("Blob: "); //console.log(blob);
211 //console.log("MIME: " + blob.type);
212 //console.log("Name: " + name);
214 //SEND RESULTING RESPONSE TO 4CHANX FILES === QRSetFile
215 var detail = {file:blob, name:name};
216 if (typeof cloneInto === 'function') {
217 detail = cloneInto(detail , document.defaultView);
219 //console.log("Detail: ");//console.log(detail);
220 document.dispatchEvent(new CustomEvent('QRSetFile', {bubbles:true, detail}));
222 if(text !== "" && text !== undefined && text !== -1) createPostComment(text);
224 document.getElementById("add-post").click();
230 createPostComment(text);
231 document.getElementById("add-post").click();
236 //4) CREATE POST QUEUE
237 var createPostComment = function(text){
238 //console.log("text-Before: " + text);
240 text = text.replace(/<a href="\/[a-zA-Z]+\/" class="quotelink">>>>/g, ">>>");
242 var quote_regex = /<a href="#p[0-9]+" class="quotelink">>>[0-9]+/g;
243 var find = text.match(quote_regex);
245 find.forEach(function(match){
246 //console.log("---==");
247 var index_start = text.indexOf(match);
248 var match_len = match.length;
249 var index_len = index_start + match_len;
250 var first_quote = match.indexOf('"');
251 var second_quote = match.indexOf('"', first_quote + 1);
252 var post_no = match.substring(first_quote + 3, second_quote);
254 match = ">>" + post_no;
256 text = text.substr(0, index_start) + match + text.substr(index_len);
260 text = text.replace(/<span class="quote">>/g, ">");
261 text = text.replace(/<br>/g, "\n");
262 text = text.replace(/'/g, "'");
263 text = text.replace(/<\/a>/g, "");
264 text = text.replace(/<wbr>/g, "");
265 text = text.replace(/<\/span>/g, "");
267 //console.log("text-After: " + text);
268 //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, '')*/;
270 document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value = text;
274 var timeListenerFunction = function(){
276 var time = document.getElementById("qr-filename-container").nextSibling.value.replace(/[a-zA-Z]+/g, "");
279 console.log(time + "A");
283 console.log(time + "B");
288 window.onload = function(){
289 //console.log(document.links);
290 var len = document.links.length;
291 for(var i = 0 ; i < len ; i++){
292 document.links[i].addEventListener("click", enhance4ChanX);
295 //ENHANCE DUMP TABS (COVER, 482PX - 482PX)
296 //DUMP LIST MAX-HEIGHT TO 490
298 document.getElementById("fourchanx-css").textContent += ".qr-preview { height: 482px; width: 482px; background-size: cover;}";
299 //document.getElementById("fourchanx-css").textContent += "#qr { min-width:490px;}";
300 document.getElementById("fourchanx-css").textContent += "#dump-list { min-height: 400px; width: 509px;}";