Update README.md
[4Free-FSE.git] / Danbooru-Image-Adder.user.js
blob27aab0fef6c3a14ff9914b893a8b4b88d4408f6f
1 // ==UserScript==
2 // @name         Danbooru-Image-Adder
3 // @namespace    http://tampermonkey.net/
4 // @version      1.0
5 // @description  Add images to posts
6 // @author       ECHibiki /qa/
7 // @match *://boards.4chan.org/*
8 // @grant         GM_xmlhttpRequest
9 // @updateURL    https://github.com/ECHibiki/4chan-UserScripts/raw/master/Danbooru-Image-Adder.user.js
10 // @downloadURL  https://github.com/ECHibiki/4chan-UserScripts/raw/master/Danbooru-Image-Adder.user.js
11 // @run-at document-end
12 // ==/UserScript==
15 I(CREATING THE POSTS)
16      1) DO JSON SEARCH FOR TAGS:
17             search[name]=TAG1,TAG2 &&&&&& search[order]=count
18      2) PICK THE SMALLEST ONES AS BASE
19      3) GENERATE A RANDOM NUMBER BETWEEN 0 AND FINAL_PAGE-1
20      4) ITTERATE THROUGH POSTS WITH TAGS UNTIL:
21             A) IT TIMESOUT
22             B) IT GOES FROM START TO END AND NOT FOUND
23      5) DO A 4CHANX CreateNotification***
25 II(AUTO COMPLETE)
26     1) ON FIELD CHANGE READ THE CURSOR LEFT AND DO A  search[name_matches]=n* & search[order]=count
27     2) PHONE STYLED AUTO COMPLETE
28     3) CLICK ON THE GIVEN ITEM TO ADD
29     4) GETS PLACED IN THE GIVEN FIELD
31 III(CHECKBOXES)
32    1) CHECKS FOR RATINGS
33    2) DROPDOWN FOR ORDER
35    3)***   (TODO)
38 //update 0.8.9 single tag warning and number_of_posts bug.
41 function alert4ChanX(message, type){
42     var detail = {type: type, content: message, lifetime: 10};
43     if (typeof cloneInto === 'function') {
44         detail = cloneInto(detail, document.defaultView);
45     }
46     var event = new CustomEvent('CreateNotification', {bubbles: true, detail: detail});
47     document.dispatchEvent(event);
50 var number_of_posts = 0;
51 var pageNo;
52 var JSONPage;
53 var JSONTag;
54 var pagesLoaded = 0;
55 var smallestTag;
57 var top_page_max =  10000000;
58 var top_page = top_page_max;
59 var attemptMax = 20;
60 var attemptCounter = attemptMax ;
62 var imgURL = "";
63 var sendURL = "";
64 var oldVal = "";
66 var timeout = false;
67 var fail_state = false;
68 var tag_incorrect_state = false;
69 var time_max = 10;
70 var time = time_max;
71 var intervalFunction;
72 var timeout_functions = [];
73 var tries = Array();
75 var taggingFunction;
77 var interfaceSet = false;
79 //set listener to build interface in 4chanX
80 window.onload = function(){
81     var len = document.links.length;
82     for(var i = 0 ; i < len ; i++){
83         document.links[i].addEventListener("click", enhance4ChanX);
84     }
86     //ENHANCE DUMP TABS (COVER, 482PX - 482PX)
87     //DUMP LIST MAX-HEIGHT TO 490
89     document.getElementById("fourchanx-css").textContent += ".qr-preview { height: 482px; width: 482px; background-size: cover;}";
90     document.getElementById("fourchanx-css").textContent += "#dump-list { min-height: 400px; width: 509px;}";
94 //Alter 4chanX
95 var enhance4ChanX = function(){
96     var qrWindow = document.getElementById("qr");
97     //check if elements already made upon opening a qr window
98     if(document.getElementById("qrImages") !== null){
99         qrWindow.removeChild(document.getElementById("qrImages"));
100         clearInterval(taggingFunction);
101         //4chanx autodeletes images
102         clearImage();
103     }
104     var dButton = document.getElementById("dump-button");
105     if(dButton !== null){dButton.click();}
106     else{return;}
108     var dList = document.getElementById("dump-list");
109     var filenamecontainer = document.getElementById("qr-filename-container");
111     //used for setting and unsetting high resolution thumbs for dump list.
112     var BGImg = "";
113     var oldBGImg = "";
114     var observer = new MutationObserver(function(mutate){
115         BGImg = dList.firstChild.style.backgroundImage;
116         if(BGImg !== oldBGImg && imgURL !== ""){
117             dList.firstChild.style.backgroundImage = "url(" + imgURL + ")";
118             oldBGImg = dList.firstChild.style.backgroundImage;
119         }
120         else if (imgURL == ""){
121         }
122     });
123     observer.observe(dList , {attributes: true,subtree:true, childList: true, characterData: true });
124     //make the image clear button clear images;
125     document.getElementById("qr-filerm").addEventListener("click", clearImage);
127     //image setting html elements.
128     var qrTable = document.createElement("TABLE");
129     qrTable.setAttribute("id", "qrImages");
130     qrTable.setAttribute("style", "text-align:center");
131     qrWindow.appendChild(qrTable);
132     qrWindow.appendChild(document.createElement("BR"));
134     var instructionRow = document.createElement("TR");
135     var topRowNodes = [document.createElement("BR"),
136                        document.createTextNode("Insert Tags to search from danbooru bellow."),
137                        document.createElement("BR"),
138                        document.createTextNode("Do Not Use \"order:\" tags"),
139                        document.createElement("BR"),
140                        document.createTextNode("Do Not Use \"rating:\" tags"),
141                        document.createElement("BR"),
142                        document.createTextNode("For more speed uncheck all boxes!"),
143                       ];
144     topRowNodes.forEach(
145         function(node){
146             instructionRow.appendChild(node);
147         });
148     qrTable.appendChild(instructionRow);
150     var optionsRow = document.createElement("TR");
151     optionsRow.setAttribute("ID", "or");
152     optionsRow.setAttribute("style", "margin:5px;");
153     qrTable.appendChild(optionsRow);
154     var checkSafe = document.createElement("INPUT");
155     checkSafe.setAttribute("id", "safe");
156     checkSafe.setAttribute("type", "checkbox");
157     var safeText  = document.createTextNode("Safe");
158     var checkQuest= document.createElement("INPUT");
159     checkQuest.setAttribute("id", "questionable");
160     checkQuest.setAttribute("type", "checkbox");
161     var questText= document.createTextNode("Questionable");
162     var checkExplicit = document.createElement("INPUT");
163     checkExplicit.setAttribute("id", "explicit");
164     checkExplicit.setAttribute("type", "checkbox");
165     var explText = document.createTextNode("Explicit");
167     optionsRow.appendChild(safeText);
168     optionsRow.appendChild(checkSafe);
169     optionsRow.appendChild(questText);
170     optionsRow.appendChild(checkQuest);
171     optionsRow.appendChild(explText);
172     optionsRow.appendChild(checkExplicit);
174     var tagRow = document.createElement("TR");
175     var secondRowNodes = [
176         document.createTextNode("Tags: "),
177         document.createElement("INPUT"),
178         document.createElement("INPUT"),
179         document.createElement("A"),
180         document.createElement("INPUT"),
181     ];
182     secondRowNodes.forEach(
183         function(node){
184             tagRow.appendChild(node);
185         });
186     qrTable.appendChild(tagRow);
188     var autoCompleteRow = document.createElement("TR");
189     autoCompleteRow.setAttribute("ID", "acr");
190     autoCompleteRow.setAttribute("style", "margin:5px;");
191     qrTable.appendChild(autoCompleteRow);
193     secondRowNodes[1].setAttribute("ID", "tags");
194     secondRowNodes[1].setAttribute("style", "width:44.9%");
195     secondRowNodes[3].setAttribute("ID", "timer");
196     secondRowNodes[3].setAttribute("style", "width:20%;margin:0 5px");
197     secondRowNodes[4].setAttribute("ID", "urlContainer");
198     secondRowNodes[4].setAttribute("style", "width:20%;margin:0 5px");
199     secondRowNodes[4].setAttribute("disabled", "");
200     var tags = "";
201     var tagNode = document.getElementById("tags");
202     var rightMost;
203     var leftMost;
205     secondRowNodes[2].setAttribute("ID", "imageButton");
206     secondRowNodes[2].setAttribute("type", "button");
207     secondRowNodes[2].setAttribute("value", "Set Image");
210     //event listener logic
211     secondRowNodes[2].addEventListener("click", buttonClickFunction);
213     //ping ever 0.5s for changes
214     taggingFunction = setInterval(
215         function(){setTagInterface(tagNode, autoCompleteRow, secondRowNodes);},
216         500);
220 function buttonClickFunction(){
221         tries = Array();
222         primed_for_fail = false;
223         for(var i = 0 ; i < timeout_functions.length; i++){
224                 clearInterval(timeout_functions[i]);
225         }
226         tag_incorrect_state = false;
227         timeout = false;
228         document.getElementById("tags").setAttribute("disabled", 1);
229         document.getElementById("imageButton").setAttribute("disabled", 1);
230         time = time_max;
231         timeout_functions.push(setInterval(counterFunction, 1000));
232         setImage();
235 function clearImage(){
236     var dList = document.getElementById("dump-list");
237     dList.firstChild.style.backgroundImage = "url()";//trigger mutation event
238     imgURL = ""; //get mutation to set to dead
241 var setTagInterface =  function(tagNode, autoCompleteRow, secondRowNodes){
242     tags = tagNode.value;
243     if(oldVal !== tags){
244         var cursorPos = tagNode.selectionStart - 1;
245         var currentTag =  (function(){
246             var currentChar = tags.charAt(cursorPos);
247             var i = 0;
248             rightMost = cursorPos;
249             while(currentChar != " " && currentChar != "" && currentChar !== undefined){
250                 i++;
251                 currentChar = tags.charAt(cursorPos + i);
252                 if(currentChar != " " && currentChar != "") rightMost = cursorPos + i;
253             }
254             rightMost += 1;
255             currentChar = tags.charAt(cursorPos);
256             i = 0;
257             leftMost = cursorPos;
258             while(currentChar != " " && currentChar != ""  && currentChar !== undefined){
259                 i++;
260                 currentChar = tags.charAt(cursorPos - i);
261                 if(currentChar != " " && currentChar != "") leftMost = cursorPos - i;
262             }
263             return tags.substring(leftMost, rightMost);
264         })();
265         var xhr = new GM_xmlhttpRequest(({
266             method: "GET",
267             url: "https://danbooru.donmai.us/tags.json?search[name_matches]=" + currentTag + "*&search[order]=count",
268             responseType : "json",
269             onload: function(data){
270                 data = data.response;
271                 var tagArray = tags.split(" ");
272                 while (autoCompleteRow.hasChildNodes()) {
273                     autoCompleteRow.removeChild(autoCompleteRow.lastChild);
274                 }
275                 for (var i = 0 ; i < 5 ; i++){
276                     var a  = document.createElement("A");
277                     a.setAttribute("style", "padding:5px;padding-top:0px;font-size:15px;font-weight:bold;border:1px solid black;");
278                     var tagText = data["" + i];
279                     if(tagText == "" || tagText === undefined) break;
280                     tagText = tagText["name"];
282                     var aTxt  = document.createTextNode(data[i]["name"]);
284                     autoCompleteRow.appendChild(a);
285                     a.appendChild(aTxt);
286                     a.addEventListener("click", function(evt){
287                         tagArray[tagArray.indexOf(currentTag)] = this.textContent;
288                         secondRowNodes[1].value = tagArray.join(" ");
289                     });
290                 }
291             }}));
292     }
293     oldVal =  tagNode.value;
296 var setImage = function(){
297     //Set image tags.
298     var tags = document.getElementById("tags").value;
300     //TODO 4cx notification of warning(no error)
301     if(tags.indexOf(":") > -1) {
302         alert4ChanX("Character ':' not used for file characteristic searches", "warning");
303     }
304     tags = tags.split(" ");
306     var xhr_image_load = new GM_xmlhttpRequest(({
307         method: "GET",
308         //returns a list of all tags and their properties
309         url: "https://danbooru.donmai.us/tags.json?search[name]=" + tags.join() + "&search[order]=count",
310         responseType : "json",
311         onload: function(data)
312         {
313             verifyTags(data, tags);
314                         if(fail_state) return;
315                         
316             //set the end
317             var endURL = ratingURL(tags, JSONTag);
319             var URL = setPostAndPage(endURL, tags);
320             sendURL = URL;
321             //final check, sends final request after function or calls this function again
322             getJSON(URL, checkPageFromDanbooru, tags);
323         }}));
326 function verifyTags(data, tags){
327     data = data.response;
328     if(tags.length == 1 && tags[0] == "") JSONTag = [{"name":""}];
329     else JSONTag = data;
330         fail_state = false;
331     if(data.length == 0){
332         //TODO 4cx notification of error)
333         alert4ChanX("All tags incorrect", "error");
334                 fail_state = true;
335                 document.getElementById("timer").textContent = "";
336                 document.getElementById("tags").removeAttribute("disabled");
337                 document.getElementById("imageButton").removeAttribute("disabled");
338         return;
339     }
340         else if(data.length != tags.length && !tag_incorrect_state){
341                 tag_incorrect_state = true;
342                 if(document.getElementById("tags").value.trim() == "") alert4ChanX("No Tags", "info");
343                 else alert4ChanX("One Tag Incorrect", "warning");
344         }
345     //tag size. Smallest tag is placed at bottom of JSON
346     smallestTag = parseInt(data[data.length-1]["post_count"]);
349 var setPostAndPage = function(endURL, tags){
350         //posts
351         if(number_of_posts > 0)
352     number_of_posts = 0;
353    //page
354         if(top_page != top_page_max) smallestTag = top_page * 20;
355     if(smallestTag == 0) smallestTag = 100;
356         do{     
357                 escape_cond = true;
358                 pageNo = ((Math.floor(Math.random() * 10000)) % Math.ceil(smallestTag / 20)) % 1000;    //1000 is max page search limit
359                 tries.forEach(function(page){
360                         if(page == 0){
361                                 primed_for_fail = true;
362                                 escape_cond = true;
363                                 return;
364                         }
365                         else if(page == pageNo){
366                                 escape_cond = false;
367                                 return;
368                         }
369                 });
370         } while(!escape_cond);
371         tries.push(pageNo);
373     var URL = "https://danbooru.donmai.us/posts.json?page=" + pageNo + endURL;
375     loopOne = false;
376     loopPage = pageNo;
377     loopPost = number_of_posts;
378     sameTrigger = 0;
379     return URL;
382 var ratingURL = function(tags, data){
383     var URL = "";
384     if(document.getElementById("safe").checked){
385         if(document.getElementById("questionable").checked){
386             if(document.getElementById("explicit").checked){
387                 if(data.length > 1)  URL =  "&utf8=%E2%9C%93&tags=" + data[data.length-2]["name"] + "+" + data[data.length-1]["name"];
388                 else  URL =  "&utf8=%E2%9C%93&tags=" + data[data.length-1]["name"];
389             }
390             else{
391                 URL =  "&utf8=%E2%9C%93&tags=" + "-rating%3Aexplicit" + "+" + data[data.length-1]["name"];
392             }
393         }
394         else if(document.getElementById("explicit").checked){
395             URL = "&utf8=%E2%9C%93&tags=" + "-rating%3Aquestionable" + "+" + data[data.length-1]["name"];
396         }
397         else{
398             URL = "&utf8=%E2%9C%93&tags=" + "rating%3Asafe" + "+" + data[data.length-1]["name"];
399         }
400     }
401     else if(document.getElementById("questionable").checked){
402         if(document.getElementById("explicit").checked){
403             URL =  "&utf8=%E2%9C%93&tags=" + "-rating%3Asafe" + "+" + data[data.length-1]["name"];
404         }
405         else{
406             URL =  "&utf8=%E2%9C%93&tags=" + "rating%3Aquestionable" + "+" + data[data.length-1]["name"];
407         }
408     }
409     else if(document.getElementById("explicit").checked){
410         URL =  "&utf8=%E2%9C%93&tags=" + "rating%3Aexplicit" + "+" + data[data.length-1]["name"];
411     }
412     else{
413         if(data.length > 1)  URL =  "&utf8=%E2%9C%93&tags=" + data[data.length-2]["name"] + "+" + data[data.length-1]["name"];
414         else  URL = "&utf8=%E2%9C%93&tags=" + data[data.length-1]["name"];
415     }
416     return URL;
419 //check if valid url location
420 var primed_for_fail = false;
421 var checkPageFromDanbooru = function(err, data, tags){
422         if (err != null) {
423                 console.log('Something went wrong: ' + err);
424                 alert4ChanX("Danbooru Server Did Not Perform request -- Error: "  + err, "error");
425                 top_page = top_page_max;
426                 attemptCounter = attemptMax;
427                 document.getElementById("timer").textContent = "";
428                 document.getElementById("tags").removeAttribute("disabled");
429                 document.getElementById("imageButton").removeAttribute("disabled");
430                 pageNo = 0;
431                 //number_of_posts = 0;
432         }
433         else {
434                 if(primed_for_fail){
435                         alert4ChanX("No Results", "error");
436                         top_page = top_page_max;
437                         attemptCounter = attemptMax;
438                         document.getElementById("timer").textContent = "";
439                         document.getElementById("tags").removeAttribute("disabled");
440                         document.getElementById("imageButton").removeAttribute("disabled");
441                         return;
442                 }
443                 //redo
444                 else if(data.length < number_of_posts+1 && attemptCounter > 0) {
445                         if(top_page > pageNo){
446                                 top_page = pageNo + number_of_posts / 20;
447                         }
448                         attemptCounter--;
449                         document.getElementById("timer").textContent = attemptCounter + "|" + time;
450                         setImage();
451                 }
452                 //process page
453                 else if (attemptCounter > 0){
454                         //ALL PARAMETERS WILL BE RESET INSIDE JSON
455                         document.getElementById("timer").textContent =  attemptCounter + "|" + time;
456                         getJSON(sendURL, setImageFromDanbooru, tags);
457                 }
458                 else{
459                         alert4ChanX("Not found", "error");
460                         top_page = top_page_max;
461                         attemptCounter = attemptMax;
462                         document.getElementById("timer").textContent = "";
463                         document.getElementById("tags").removeAttribute("disabled");
464                         document.getElementById("imageButton").removeAttribute("disabled");     
465                         return;
466                 }
467         }
470 var setImageFromDanbooru = function(err, data, tags){
471     if (err != null) {
472         console.log('Something went wrong: ' + err);
473         alert4ChanX("Danbooru Server Did Not Perform request -- Error: "  + err, "error");
474         top_page = top_page_max;
475         attemptCounter = attemptMax;
476         document.getElementById("timer").textContent = "";
477         document.getElementById("tags").removeAttribute("disabled");
478         document.getElementById("imageButton").removeAttribute("disabled");
480     }
481     else {
482                 JSONPage = data;
483                 var image_found = false;
484                 for (number_of_posts = 0; number_of_posts < 20 ; number_of_posts++){
485                         if(timeout){
486                                 alert4ChanX("timeout after " + time +" seconds", "error");
487                                 clearInterval(counterFunction);
488                                 document.getElementById("timer").textContent = "";
489                                 document.getElementById("tags").removeAttribute("disabled");
490                                 document.getElementById("imageButton").removeAttribute("disabled");
491                                 top_page = top_page_max;
492                                 attemptCounter = attemptMax;
493                                 return;
494                         }
495                         else if(JSONPage["" + number_of_posts] == undefined){
496                                 top_page = pageNo;
497                                 attemptCounter--;
498                                 setImage();
499                                 return;
500                         }
502                         var endURL = JSONPage["" + number_of_posts].file_url;
503                         var URL = "https://danbooru.donmai.us" + endURL;
505                         urlContainterFunction(URL);
507                         var fail = false;
509                         if(endURL === undefined ||
510                            endURL.indexOf(".mp4") > -1 || endURL.indexOf(".webm") > -1 || endURL.indexOf(".swf") > -1 || endURL.indexOf(".zip") > -1){
511                                 // top_page = pageNo;
512                                 // attemptCounter--;
513                                 // setImage();
514                                 // return;
515                                 continue;
516                         }
517                         else{
518                                 tags.forEach(function(tag){
519                                         if(tag.indexOf("order:") > -1);
520                                         else if(tag.indexOf("rating:") > -1){
521                                                 if(tag.charAt(7) !== JSONPage["" + number_of_posts]["rating"]){
522                                                         fail = true;
523                                                         return;
524                                                 }
525                                         }
526                                         else if(JSONPage["" + number_of_posts]["tag_string"].indexOf(tag) == -1){
527                                                 fail = true;
528                                                 return;
529                                         }
530                                 });
531                         }
532                         if(fail){
533                                 // top_page = pageNo;
534                                 // attemptCounter--;
535                                 // setImage();
536                                 // return;
537                                 continue;
538                         }
539                         else{
540                                 if(JSONPage["" + number_of_posts].file_size >= 4000000){
541                                         var endURL = JSONPage["" + number_of_posts].large_file_url;
542                                         var URL = "https://danbooru.donmai.us" + endURL;
543                                 }
544                                 document.getElementById("timer").textContent = "...";
545                                 imgURL = URL;
546                                 var xhr = new GM_xmlhttpRequest(({
547                                         method: "GET",
548                                         url: URL,
549                                         responseType : "arraybuffer",
550                                         onload: function(response)
551                                         {
552                                                 top_page = top_page_max;
553                                                 attemptCounter = attemptMax;
554                                                 document.getElementById("tags").removeAttribute("disabled");
555                                                 document.getElementById("imageButton").removeAttribute("disabled");
556                                                 loopOne = false;
557                                                 clearInterval(intervalFunction);
558                                                 time = time_max;
559                                                 var counter = document.getElementById("timer");
560                                                 while(counter.hasChildNodes())
561                                                         document.getElementById("timer").removeChild(document.getElementById("timer").lastChild);
563                                                 var blob;
564                                                 if(endURL.indexOf(".jpg") > -1)
565                                                         blob = new Blob([response.response], {type:"image/jpeg"});
566                                                 else if(endURL.indexOf(".png") > -1)
567                                                         blob = new Blob([response.response], {type:"image/png"});
568                                                 else if(endURL.indexOf(".gif") > -1)
569                                                         blob = new Blob([response.response], {type:"image/gif"});
572                                                 var name = endURL.replace(/(data|cached)/g, "");
573                                                 name = name.replace(/\//g, "");
575                                                 //SEND RESULTING RESPONSE TO 4CHANX FILES === QRSetFile
576                                                 var detail = {file:blob, name:name};
577                                                 if (typeof cloneInto === 'function') {
578                                                         detail  = cloneInto(detail , document.defaultView);
579                                                 }
580                                                 document.getElementById("dump-list").firstChild.click();
581                                                 document.dispatchEvent(new CustomEvent('QRSetFile', {bubbles:true, detail}));
582                                         }
583                                 }));
584                                                                                                 //end function;
585                                 image_found = true;
586                                 number_of_posts = 9001;
587                         }
588                 }
589                 if(!image_found){
590                         top_page = pageNo;
591                         attemptCounter--;
592                         setImage();
593                 }
594     }
597 var urlContainterFunction = function(url){
598     var urlBox = document.getElementById("urlContainer");
599     urlBox.value = url;
602 var counterFunction  = function(){
603     if(!timeout){
604         time--;
605         if(time < 0){
606             timeout = true;
607             time = time_max;
608         }
609     }
612 var getJSON = function(url, callback, extra) {
613     var xhr = new XMLHttpRequest();
614     xhr.open('GET', url, true);
615     xhr.responseType = 'json';
616     xhr.onload = function() {
617         var status = xhr.status;
618         if (status == 200) {
619             callback(null, xhr.response, extra);
620         } else {
621             callback(status);
622         }
623     };
624     xhr.send();