1 declare var cloneInto: any;
2 declare var GM_xmlhttpRequest: any;
4 class ThreadRebuilder extends FeatureInterface{
7 thread_data = [['Comment'], ['Image URLs'], ['Image Names'] ,['Post No.']];
12 use_offsite_archive = false;
13 window_displayed = false;
15 tool_top_visible = false;
23 var board_uproces = window.location.pathname;
24 this.board = board_uproces.substring(1, board_uproces.length - 1);
28 retrieveStates():void{
29 this.use_offsite_archive = localStorage.getItem("ArchiveType") == "0" ? true : false;
31 storeStates(...items:any[]):void{}
33 document.addEventListener("QRDialogCreation", (e) => this.enhance4ChanX());
34 document.addEventListener('QRPostSuccessful', (e) => {
36 (<HTMLElement>document.getElementById("dump-list").childNodes[1]).click();
37 this.setPropperLinking((<HTMLInputElement>document.getElementById("qr").getElementsByTagName("TEXTAREA")[0]).value);
42 decideAction(node:any):void{}
45 var qr_window = document.getElementById("qr");
47 if(document.getElementById("qrRebuilder") !== null) qr_window.removeChild(document.getElementById("qrRebuilder"));
49 var thread_rebuilder_table = document.createElement("TABLE");
50 thread_rebuilder_table.setAttribute("id", "qrRebuilder");
51 thread_rebuilder_table.setAttribute("style", "text-align:center");
52 qr_window.appendChild(thread_rebuilder_table);
54 var thread_row = document.createElement("TR");
55 var option_text_size = 18;
56 var help_icon_container = document.createElement("A");
57 (<HTMLLinkElement>help_icon_container).href = "javascript:void(0)";
58 help_icon_container.title = "Click to View Help!";
59 var help_icon = document.createElement("IMG");
60 help_icon.setAttribute("style", "height:" + option_text_size * 1.25 + "px;margin:-4px 10px");
61 (<HTMLImageElement>help_icon).src = Constants.HELP_ICON_SOURCE;
63 help_icon_container.appendChild(help_icon);
64 thread_row.appendChild(help_icon_container);
66 var tooltip_div = document.createElement("DIV");
67 tooltip_div.innerHTML = "Insert the thread number of the post to rebuild<br/>Must be in either the 4chan archives or archived.moe<hr/>Submit bugs to <a href='https://github.com/ECHibiki/4chan-UserScripts'>my Github</a>";
68 tooltip_div.setAttribute("style", "z-index:9;padding:5px;border:1px solid black;background-color:white;word-wrap:break-word;display:none;position:absolute;");
69 help_icon_container.addEventListener("click", (evt)=>{
70 if(this.tool_top_visible)
71 tooltip_div.setAttribute("style", "z-index:9;padding:5px;border:1px solid black;background-color:white;word-wrap:break-word;display:none;position:absolute;");
73 tooltip_div.setAttribute("style", "z-index:9;padding:5px;border:1px solid black;background-color:white;word-wrap:break-word;display:block;position:absolute;"
74 + "left:" + ((<MouseEvent>evt).clientX - (<DOMRect>qr_window.getBoundingClientRect()).x) +
75 "px;top:" + ((<MouseEvent>evt).clientY - (<DOMRect>qr_window.getBoundingClientRect()).y ) + "px;");
76 this.tool_top_visible = !this.tool_top_visible;
78 qr_window.appendChild(tooltip_div);
80 var second_row_nodes = [
81 document.createTextNode("Thread: "),
82 document.createElement("INPUT"),
83 document.createElement("INPUT"),
85 second_row_nodes.forEach(
87 thread_row.appendChild(node);
89 thread_rebuilder_table.appendChild(thread_row);
91 (<HTMLInputElement>second_row_nodes[1]).setAttribute("ID", "threadInput");
92 (<HTMLInputElement>second_row_nodes[1]).setAttribute("style", "width:35.0%");
94 (<HTMLInputElement>second_row_nodes[2]).setAttribute("ID", "threadButton");
95 (<HTMLInputElement>second_row_nodes[2]).setAttribute("type", "button");
96 (<HTMLInputElement>second_row_nodes[2]).setAttribute("value", "Set Rebuild Queue");
98 second_row_nodes[2].addEventListener("click", () => {
99 this.in_sequence = true;
101 this.getThread((<HTMLInputElement>second_row_nodes[1]).value);
102 this.postID = setInterval(() => this.postRoutine(), 1000);
103 if(this.timeListen === undefined) this.timeListen = setInterval(() => this.timeListenerFunction(), 1000);
105 qr_window.appendChild(document.createElement("hr"));
108 thread_data_length = 0;
112 if(this.semaphore == 0){
114 this.thread_data_length = this.thread_data[0].length;
115 this.fillID = setInterval(() => this.fillRoutine(), 10);
121 clearInterval(this.postID);
126 if (this.posts_created >= this.thread_data_length)
127 { this.semaphore_posts = 0; this.stopFillRoutine(); }
128 else if(this.semaphore_posts == 1){
129 this.semaphore_posts--;
130 this.createPost(this.thread_data[0][this.posts_created],
131 this.thread_data[1][this.posts_created],
132 this.thread_data[2][this.posts_created]);
133 this.posts_created++;
137 stopFillRoutine():void{
138 clearInterval(this.fillID);
141 setPropperLinking (text):void{
142 var search_regex = RegExp(">>\\d+", "g");
145 var link_arr = Array();
146 while((result = search_regex.exec(text)) != null){
147 var end_index = search_regex.lastIndex;
148 var post_no = result.toString().replace(/>/g, "");
149 link_arr.push([post_no, end_index]);
151 //hunt down the text of what it linked to
152 //Get the links inside of the origonal message to show text contents
154 var responding_text = Array();
155 if(this.use_offsite_archive)
156 var URL = "https://www.archived.moe/_/api/chan/thread/?board=" + this.board + "&num=" + (<HTMLInputElement>document.getElementById("threadInput")).value;
158 var URL = "https://a.4cdn.org/" + this.board + "/thread/" + (<HTMLInputElement>document.getElementById("threadInput")).value + ".json";
159 var xhr = new GM_xmlhttpRequest(({
162 responseType : "json",
164 if(this.use_offsite_archive)
165 data = data.response["" + (<HTMLInputElement>document.getElementById("threadInput")).value]["posts"];
167 data = data.response["posts"];
168 if(data == undefined){
169 alert("Invalid Thread ID: " + (<HTMLInputElement>document.getElementById("threadInput")).value + ". ");
172 link_arr.forEach((link_item)=>{
173 for(var data_entry = 0 ; data_entry < data.length ; data_entry++){
174 if(parseInt(link_item[0]) == parseInt(data[data_entry]["no"])){
175 if(this.use_offsite_archive && data[data_entry]["comment_processed"] !== undefined)
176 responding_text.push([ [post_no, end_index], data[data_entry]["comment_processed"].replace(/(>>|https:\/\/www\.archived\.moe\/.*\/thread\/.*\/#)\d+/g, ""), link_item["media"]["safe_media_hash"] ]);
177 else if(data[data_entry]["com"] !== undefined)
178 responding_text.push([ [post_no, end_index], data[data_entry]["com"].replace(/(>>|#p)\d+/g, ""), data[data_entry]["md5"] ]);
179 else responding_text.push([ [post_no, end_index], undefined, data[data_entry]["md5"] ]);
185 var current_url = window.location.href;
186 var hash_index = current_url.lastIndexOf("#") != -1 ? current_url.lastIndexOf("#"): window.location.href.length;
187 var current_thread = window.location.href.substring(current_url.lastIndexOf("/")+1, hash_index);
188 var current_url = "https://a.4cdn.org/" + this.board + "/thread/" + current_thread + ".json";
189 //open current thread to hunt down the text found in links
190 var xhr = new GM_xmlhttpRequest(({
193 responseType : "json",
195 data = data.response["posts"];
196 if(data == undefined){
197 alert("Invalid Thread ID: " + (<HTMLInputElement>document.getElementById("threadInput")).value + ". ");
200 responding_text.forEach((response_item)=>{
201 for(var data_entry = 0 ; data_entry < data.length ; data_entry++){
202 if(data[data_entry]["com"] !== undefined && (response_item[1] == data[data_entry]["com"].replace(/(>>|#p)\d+/g, "") || response_item[1] == null)
203 && (response_item[2] == data[data_entry]["md5"] || response_item[2] == null)){
204 var start_index = response_item[0][0].legth - response_item[0][1];
205 text = text.substring(0, start_index) + ">>" + data[data_entry]["no"] + text.substring(response_item[0][1]);
208 else if(response_item[2] !== undefined && response_item[2] == data[data_entry]["md5"]){
209 var start_index = response_item[0][0].legth - response_item[0][1];
210 text = text.substring(0, start_index) + ">>" + data[data_entry]["no"] + text.substring(response_item[0][1]);
215 (<HTMLInputElement>document.getElementById("qr").getElementsByTagName("TEXTAREA")[0]).value = text;
216 document.getElementById("add-post").click();
217 this.semaphore_posts++;
228 //2) GET ARCHIVED THREAD
229 getThread(threadNo):void{
230 this.thread_data = [[], [], [], []];
232 if(this.use_offsite_archive)
233 var URL = "https://www.archived.moe/_/api/chan/thread/?board=" + this.board + "&num=" + (<HTMLInputElement>document.getElementById("threadInput")).value;
235 var URL = "https://a.4cdn.org/" + this.board + "/thread/" + (<HTMLInputElement>document.getElementById("threadInput")).value + ".json";
236 var xhr = new GM_xmlhttpRequest(({
239 responseType : "json",
241 var starting_post = -1;
242 if(this.use_offsite_archive){
244 data = data.response["" + (<HTMLInputElement>document.getElementById("threadInput")).value];
248 data = data.response;
250 if(data == undefined){
251 alert("Invalid Thread ID: " + threadNo + ".\n4chan Archive ");
254 if(this.use_offsite_archive) data["posts"] = data.values(data["posts"]);
256 var len = data["posts"].length;
258 for(var post_number = starting_post ; post_number < len ; post_number++){
259 var comment = undefined;
260 if(this.use_offsite_archive)
261 comment = data["posts"][post_number]["comment"];
263 comment = data["posts"][post_number]["com"];
264 if(comment !== undefined && comment !== null)
265 this.thread_data[0].push(comment);
267 this.thread_data[0].push("");
269 var filename = undefined;
270 if(this.use_offsite_archive){
271 if(data["posts"][post_number]["media"] !== null)
272 filename = "" + data["posts"][post_number]["media"]["media_filename"];
275 filename = "" + data["posts"][post_number]["tim"] + data["posts"][post_number]["ext"];
277 if(filename !== undefined && filename !== null && filename.indexOf("undefined") == -1)
278 if(this.use_offsite_archive)
279 if(data["posts"][post_number]["media"] !== null)
280 this.thread_data[1].push(data["posts"][post_number]["media"]["remote_media_link"]);
281 else this.thread_data[1].push("");
283 this.thread_data[1].push("https://i.4cdn.org/" + this.board + "/" + filename);
284 else this.thread_data[1].push("");
285 if(this.use_offsite_archive){
286 if(data["posts"][post_number]["media"] !== null)
287 this.thread_data[2].push(data["posts"][post_number]["media"]["media_id"]);
290 this.thread_data[2].push(data["posts"][post_number]["filename"]);
292 if(this.use_offsite_archive)
293 this.thread_data[3].push(data["posts"][post_number]["num"]);
295 this.thread_data[3].push(data["posts"][post_number]["no"]);
302 //3) RIP POSTS AND IMAGES
303 createPost(text, imageURL, imageName):void{
305 var response_type = "arraybuffer";
306 if(this.use_offsite_archive) response_type = "text"
307 var xhr = new GM_xmlhttpRequest(({
310 responseType : response_type,
311 onload: (response) =>
313 if(this.use_offsite_archive){
314 var parser = new DOMParser();
315 var content_attribute = parser.parseFromString(response.response, "text/html").getElementsByTagName("META")[0].getAttribute("content");
316 var redirect_url = content_attribute.substring(content_attribute.indexOf("http"));
317 var xhr = new GM_xmlhttpRequest(({method:"GET", url: redirect_url, responseType:"arraybuffer",
318 onload:(response) => {
319 this.inputImage(response, text, imageURL, imageName);
324 this.inputImage(response, text, imageURL, imageName);
330 text = this.createPostComment(text);
331 this.setPropperLinking(text);
335 inputImage(response, text, imageURL, imageName):void{
338 if(imageURL.indexOf(".jpg") > -1){
339 blob = new Blob([response.response], {type:"image/jpeg"});
342 else if(imageURL.indexOf(".png") > -1){
343 blob = new Blob([response.response], {type:"image/png"});
346 else if(imageURL.indexOf(".gif") > -1){
347 blob = new Blob([response.response], {type:"image/gif"});
350 else if(imageURL.indexOf(".webm") > -1){
351 blob = new Blob([response.response], {type:"video/webm"});
355 var name = imageName + ext;
357 //SEND RESULTING RESPONSE TO 4CHANX FILES === QRSetFile
358 var detail = {file:blob, name:name};
359 detail = cloneInto(detail , document.defaultView);
361 document.dispatchEvent(new CustomEvent('QRSetFile', {bubbles:true, detail}));
363 if(text !== "" && text !== undefined) {
364 text = this.createPostComment(text);
365 this.setPropperLinking(text);
368 document.getElementById("add-post").click();
369 this.semaphore_posts++;
373 //4) CREATE POST QUEUE
374 createPostComment (text):string{
375 var dummy = document.createElement("DIV");
376 dummy.innerHTML = text;
377 var inside_node = dummy.firstChild;
378 var return_text = "";
380 if((<HTMLElement>inside_node).tagName == "BR")
383 return_text += inside_node.textContent;
384 }while((inside_node = inside_node.nextSibling));
390 timeListenerFunction():void{
391 var time = parseInt((<HTMLInputElement>document.getElementById("qr-filename-container").nextSibling).value.replace(/[a-zA-Z]+/g, ""));
393 this.checked = false;
401 this.thread_data_length = 0;
402 this.posts_created = 0;
404 this.postID = undefined;
406 this.semaphore_posts = 1;
407 this.stopFillRoutine();
408 this.fillID = undefined;
409 this.thread_data = [['Comment'], ['Image URLs'], ['Image Names'] ,['Post No.']];
411 var qr_dumplist = document.getElementById("dump-list").childNodes;
412 var qr_dumplist_len = qr_dumplist.length;
413 var current_preview = 0;
414 while(qr_dumplist_len - current_preview > 1){
415 (<HTMLElement>qr_dumplist[0].firstChild).click();