diff options
Diffstat (limited to 'static/client.js')
-rw-r--r-- | static/client.js | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/static/client.js b/static/client.js new file mode 100644 index 0000000..82ea4df --- /dev/null +++ b/static/client.js @@ -0,0 +1,682 @@ + +/* + 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); + } + + collection = [ + { + "url": image_url, + "width": Math.round(click.target.naturalWidth), + "height": Math.round(click.target.naturalHeight) + } + ]; + + 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 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{ + + console.log(url); + 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(); +} + +/* + Shortcuts +*/ +var searchbox_wrapper = document.getElementsByClassName("searchbox"); + +if(searchbox_wrapper.length !== 0){ + searchbox_wrapper = searchbox_wrapper[0]; + var searchbox = searchbox_wrapper.getElementsByTagName("input")[1]; + + 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; + } + }); +} |