/* Global functions */ function htmlspecialchars(str){ var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' } return str.replace(/[&<>"']/g, function(m){return map[m];}); } function htmlspecialchars_decode(str){ var map = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'" } return str.replace(/&|<|>|"|'/g, function(m){return map[m];}); } function is_click_within(elem, classname, is_id = false){ while(true){ if(elem === null){ return false; } if( ( is_id === false && elem.className == classname ) || ( is_id === true && elem.id == classname ) ){ return elem; } elem = elem.parentElement; } } /* Prevent GET parameter pollution */ var form = document.getElementsByTagName("form"); if( form.length !== 0 && window.location.pathname != "/" && window.location.pathname != "/settings.php" && window.location.pathname != "/settings" ){ form = form[0]; var scraper_dropdown = document.getElementsByName("scraper")[0]; scraper_dropdown.addEventListener("change", function(choice){ submit(form); }); form.addEventListener("submit", function(e){ e.preventDefault(); submit(e.srcElement); }); } function submit(e){ var GET = ""; var first = true; if((s = document.getElementsByName("s")).length !== 0){ GET += "?s=" + encodeURIComponent(s[0].value).replaceAll("%20", "+"); first = false; } Array.from( e.getElementsByTagName("select") ).concat( Array.from( e.getElementsByTagName("input") ) ).forEach(function(el){ var firstelem = el.getElementsByTagName("option"); if( ( ( firstelem.length === 0 || firstelem[0].value != el.value ) && el.name != "" && el.value != "" && el.name != "s" ) || el.name == "scraper" || el.name == "nsfw" ){ if(first){ GET += "?"; first = false; }else{ GET += "&"; } GET += encodeURIComponent(el.name).replaceAll("%20", "+") + "=" + encodeURIComponent(el.value).replaceAll("%20", "+"); } }); window.location.href = GET; } /* Hide show more button when it's not needed on answers */ var answer_div = document.getElementsByClassName("answer"); if(answer_div.length !== 0){ answer_div = Array.from(answer_div); var spoiler_button_div = Array.from(document.getElementsByClassName("spoiler-button")); // execute on pageload hide_show_more(); window.addEventListener("resize", hide_show_more); function hide_show_more(){ var height = window.innerWidth >= 1000 ? 600 : 200; for(i=0; i<answer_div.length; i++){ if(answer_div[i].scrollHeight < height){ spoiler_button_div[i].style.display = "none"; document.getElementById(spoiler_button_div[i].htmlFor).checked = true; }else{ spoiler_button_div[i].style.display = "block"; } } } } switch(document.location.pathname){ case "/web": case "/web.php": var image_class = "image"; break; case "/images": case "/images.php": var image_class = "thumb"; break; default: var image_class = null; } if(image_class !== null){ /* Add popup to document */ var popup_bg = document.createElement("div"); popup_bg.id = "popup-bg"; document.body.appendChild(popup_bg); // enable/disable pointer events if(!document.cookie.includes("bg_noclick=yes")){ popup_bg.style.pointerEvents = "none"; } var popup_status = document.createElement("div"); popup_status.id = "popup-status"; document.body.appendChild(popup_status); var popup_body = document.createElement("div"); popup_body.id = "popup"; document.body.appendChild(popup_body); // import popup var popup_body = document.getElementById("popup"); var popup_status = document.getElementById("popup-status"); var popup_image = null; // is set later on popup click // image metadata var collection = []; // will contain width, height, image URL var collection_index = 0; // event handling helper variables var is_popup_shown = false; var mouse_down = false; var mouse_move = false; var move_x = 0; var move_y = 0; var target_is_popup = false; var mirror_x = false; var mirror_y = false; var rotation = 0; /* Image dragging (mousedown) */ document.addEventListener("mousedown", function(div){ if(div.buttons !== 1){ return; } mouse_down = true; mouse_move = false; if(is_click_within(div.target, "popup", true) === false){ target_is_popup = false; }else{ target_is_popup = true; var pos = popup_body.getBoundingClientRect(); move_x = div.x - pos.x; move_y = div.y - pos.y; } }); /* Image dragging (mousemove) */ document.addEventListener("mousemove", function(pos){ if( target_is_popup && mouse_down ){ mouse_move = true; movepopup(popup_body, pos.clientX - move_x, pos.clientY - move_y); } }); /* Image dragging (mouseup) */ document.addEventListener("mouseup", function(){ mouse_down = false; }); /* Image popup open */ document.addEventListener("click", function(click){ // should our click trigger image open? if( elem = is_click_within(click.target, image_class) || click.target.classList.contains("openimg") ){ event.preventDefault(); is_popup_shown = true; // reset position params mirror_x = false; mirror_y = false; rotation = 0; scale = 60; collection_index = 0; // get popup data if(elem === true){ // we clicked a simple image preview elem = click.target; var image_url = elem.getAttribute("src"); if(image_url.startsWith("/proxy")){ var match = image_url.match(/i=([^&]+)/); if(match !== null){ image_url = decodeURIComponent(match[1]); } }else{ image_url = htmlspecialchars_decode(image_url); } var w = Math.round(click.target.naturalWidth); var h = Math.round(click.target.naturalHeight); if( w === 0 || h === 0 ){ w = 100; h = 100; } collection = [ { "url": image_url, "width": w, "height": h } ]; var title = "No description provided"; if(click.target.title != ""){ title = click.target.title; }else{ if(click.target.alt != ""){ title = click.target.alt; } } }else{ if(image_class == "thumb"){ // we're inside image.php elem = elem .parentElement .parentElement; var image_url = elem.getElementsByTagName("a")[1].href; }else{ // we're inside web.php var image_url = elem.href; } collection = JSON.parse( elem.getAttribute("data-json") ); var imagesize = elem.getElementsByTagName("img")[0]; var imagesize_w = 0; var imagesize_h = 0; if(imagesize.complete){ imagesize_w = imagesize.naturalWidth; imagesize_h = imagesize.naturalHeight; } if( imagesize_w === 0 || imagesize_h === 0 ){ imagesize_w = 100; imagesize_h = 100; } for(var i=0; i<collection.length; i++){ if(collection[i].width === null){ collection[i].width = imagesize_w; collection[i].height = imagesize_h; } } var title = elem.title; } // prepare HTML var html = '<div id="popup-num">(' + collection.length + ')</div>' + '<div id="popup-dropdown">' + '<select name="viewer-res" onchange="changeimage(event)">'; for(i=0; i<collection.length; i++){ if(collection[i].url.startsWith("data:")){ var domain = "<Base64 Data>"; }else{ var domain = new URL(collection[i].url).hostname; } html += '<option value="' + i + '">' + '(' + collection[i].width + 'x' + collection[i].height + ') ' + domain + '</option>'; } popup_status.innerHTML = html + '</select></div>' + '<a href="' + htmlspecialchars(image_url) + '" rel="noreferrer nofollow "id="popup-title">' + htmlspecialchars(title) + '</a>'; popup_body.innerHTML = '<img src="' + getproxylink(collection[0].url) + '" draggable="false" id="popup-image">'; // make changes to DOM popup_body.style.display = "block"; popup_bg.style.display = "block"; popup_status.style.display = "table"; // store for rotation functions & changeimage() popup_image = document.getElementById("popup-image"); scalepopup(collection[collection_index], scale); centerpopup(); }else{ // click inside the image viewer // resize image if(is_click_within(click.target, "popup", true)){ if(mouse_move === false){ scale = 80; scalepopup(collection[collection_index], scale); centerpopup(); } }else{ if(is_click_within(click.target, "popup-status", true) === false){ // click outside the popup while its open // close it if(is_popup_shown){ hidepopup(); } } } } }); /* Scale image viewer */ popup_body.addEventListener("wheel", function(scroll){ event.preventDefault(); if( scroll.altKey || scroll.ctrlKey || scroll.shiftKey ){ var increment = 7; }else{ var increment = 14; } if(scroll.wheelDelta > 0){ // scrolling up scale = scale + increment; }else{ // scrolling down if(scale - increment > 7){ scale = scale - increment; } } // calculate relative size before scroll var pos = popup_body.getBoundingClientRect(); var x = (scroll.x - pos.x) / pos.width; var y = (scroll.y - pos.y) / pos.height; scalepopup(collection[collection_index], scale); // move popup to % we found pos = popup_body.getBoundingClientRect(); movepopup( popup_body, scroll.clientX - (x * pos.width), scroll.clientY - (y * pos.height) ); }); /* Keyboard controls */ document.addEventListener("keydown", function(key){ // close popup if( is_popup_shown && key.keyCode === 27 ){ hidepopup(); return; } if(is_popup_shown === false){ return; } if( key.altKey || key.ctrlKey || key.shiftKey ){ // mirror image switch(key.keyCode){ case 37: // left key.preventDefault(); mirror_x = true; break; case 38: // up key.preventDefault(); mirror_y = false; break; case 39: // right key.preventDefault(); mirror_x = false; break; case 40: // down key.preventDefault(); mirror_y = true; break; } }else{ // rotate image switch(key.keyCode){ case 37: // left key.preventDefault(); rotation = -90; break; case 38: // up key.preventDefault(); rotation = 0; break; case 39: // right key.preventDefault(); rotation = 90; break; case 40: // down key.preventDefault(); rotation = -180; break; } } popup_image.style.transform = "scale(" + (mirror_x ? "-1" : "1") + ", " + (mirror_y ? "-1" : "1") + ") " + "rotate(" + rotation + "deg" + ")"; }); } function getproxylink(url){ if(url.startsWith("data:")){ return htmlspecialchars(url); }else{ return '/proxy?i=' + encodeURIComponent(url); } } function hidepopup(){ is_popup_shown = false; popup_status.style.display = "none"; popup_body.style.display = "none"; popup_bg.style.display = "none"; } function scalepopup(size, scale){ var ratio = Math.min( (window.innerWidth * (scale / 100)) / collection[collection_index].width, (window.innerHeight * (scale / 100)) / collection[collection_index].height ); popup_body.style.width = size.width * ratio + "px"; popup_body.style.height = size.height * ratio + "px"; } function centerpopup(){ var size = popup_body.getBoundingClientRect(); var size = { "width": parseInt(size.width), "height": parseInt(size.height) }; movepopup( popup_body, (window.innerWidth / 2) - (size.width / 2), (window.innerHeight / 2) - (size.height / 2) ); } function movepopup(popup_body, x, y){ popup_body.style.left = x + "px"; popup_body.style.top = y + "px"; } function changeimage(event){ // reset rotation params mirror_x = false; mirror_y = false; rotation = 0; scale = 60; collection_index = parseInt(event.target.value); // we set innerHTML otherwise old image lingers a little popup_body.innerHTML = '<img src="' + getproxylink(collection[collection_index].url) + '" draggable="false" id="popup-image">'; // store for rotation functions & changeimage() popup_image = document.getElementById("popup-image"); scalepopup(collection[collection_index], scale); centerpopup(); } var searchbox_wrapper = document.getElementsByClassName("searchbox"); if(searchbox_wrapper.length !== 0){ searchbox_wrapper = searchbox_wrapper[0]; var searchbox = searchbox_wrapper.getElementsByTagName("input")[1]; /* Textarea shortcuts */ document.addEventListener("keydown", function(key){ switch(key.keyCode){ case 191: // 191 = / if(document.activeElement.tagName == "INPUT"){ // already focused, ignore break; } if( typeof is_popup_shown != "undefined" && is_popup_shown ){ hidepopup(); } window.scrollTo(0, 0); searchbox.focus(); key.preventDefault(); break; } }); /* Autocompleter */ if( // make sure the user wants it document.cookie.includes("scraper_ac=") && document.cookie.includes("scraper_ac=disabled") === false ){ var autocomplete_cache = []; var focuspos = -1; var list = []; var autocomplete_div = document.getElementsByClassName("autocomplete")[0]; if( document.cookie.includes("scraper_ac=auto") && typeof scraper_dropdown != "undefined" ){ var ac_req_appendix = "&scraper=" + scraper_dropdown.value; }else{ var ac_req_appendix = ""; } function getsearchboxtext(){ var value = searchbox.value .trim() .replace( / +/g, " " ) .toLowerCase(); return value; } searchbox.addEventListener("input", async function(){ // ratelimit on input only // dont ratelimit if we already have res if(typeof autocomplete_cache[getsearchboxtext()] != "undefined"){ await getac(); }else{ await getac_ratelimit(); } }); async function getac(){ var curvalue = getsearchboxtext(); if(curvalue == ""){ // hide autocompleter autocomplete_div.style.display = "none"; return; } if(typeof autocomplete_cache[curvalue] == "undefined"){ /* Fetch autocomplete */ // make sure we dont fetch same thing twice autocomplete_cache[curvalue] = []; var res = await fetch("/api/v1/ac?s=" + (encodeURIComponent(curvalue).replaceAll("%20", "+")) + ac_req_appendix); if(!res.ok){ return; } var json = await res.json(); autocomplete_cache[curvalue] = json[1]; if(curvalue == getsearchboxtext()){ render_ac(curvalue, autocomplete_cache[curvalue]); } return; } render_ac(curvalue, autocomplete_cache[curvalue]); } var ac_func = null; function getac_ratelimit(){ return new Promise(async function(resolve, reject){ if(ac_func !== null){ clearTimeout(ac_func); }//else{ // no ratelimits //getac(); //} ac_func = setTimeout(function(){ ac_func = null; getac(); // get results after 100ms of no keystroke resolve(); }, 200); }); } function render_ac(query, list){ if(list.length === 0){ autocomplete_div.style.display = "none"; return; } html = ""; // prepare regex var highlight = query.split(" "); var regex = []; for(var k=0; k<highlight.length; k++){ // espace regex regex.push( highlight[k].replace(/[.*+?^${}()|[\]\\]/g, "\\$&") ); } regex = new RegExp(highlight.join("|"), "gi"); for(var i=0; i<list.length; i++){ html += '<div tabindex="0" class="entry" onclick="handle_entry_click(this);">' + htmlspecialchars( list[i] ).replace( regex, '<u>$&</u>' ) + '</div>'; } autocomplete_div.innerHTML = html; autocomplete_div.style.display = "block"; } var should_focus = false; document.addEventListener("keydown", function(event){ if(event.key == "Escape"){ document.activeElement.blur(); focuspos = -1; autocomplete_div.style.display = "none"; return; } if( is_click_within(event.target, "searchbox") === false || typeof autocomplete_cache[getsearchboxtext()] == "undefined" ){ return; } switch(event.key){ case "ArrowUp": event.preventDefault(); focuspos--; if(focuspos === -2){ focuspos = autocomplete_cache[getsearchboxtext()].length - 1; } break; case "ArrowDown": case "Tab": event.preventDefault(); focuspos++; if(focuspos >= autocomplete_cache[getsearchboxtext()].length){ focuspos = -1; } break; case "Enter": should_focus = true; if(focuspos !== -1){ // replace input content event.preventDefault(); searchbox.value = autocomplete_div.getElementsByClassName("entry")[focuspos].innerText; break; } break; default: focuspos = -1; break; } if(focuspos === -1){ searchbox.focus(); return; } autocomplete_div.getElementsByClassName("entry")[focuspos].focus(); }); window.addEventListener("blur", function(){ autocomplete_div.style.display = "none"; }); document.addEventListener("keyup", function(event){ // handle ENTER key on entry if(should_focus){ should_focus = false; searchbox.focus(); } }); document.addEventListener("mousedown", function(event){ // hide input if click is outside if(is_click_within(event.target, "searchbox") === false){ autocomplete_div.style.display = "none"; return; } }); function handle_entry_click(event){ searchbox.value = event.innerText; focuspos = -1; searchbox.focus(); } searchbox.addEventListener("focus", function(){ focuspos = -1; getac(); }); } }