/* * 4ducky.com client script. * Vanilla port of media/picturelicious.js, js/functions.js, js/jqpr.js, * js/lightbox.js (Lightbox v2.51 by Lokesh Dhakar, CC BY 2.5) and * media/quicktags.js. Same DOM and behaviour, no jQuery, no inline handlers. */ (function () { 'use strict'; var $ = function (id) { return document.getElementById(id); }; function csrf() { var m = document.querySelector('meta[name="csrf-token"]'); return m ? m.content : ''; } function post(url, data) { return fetch(url, { method: 'POST', credentials: 'same-origin', headers: { 'X-CSRF-Token': csrf(), 'X-Requested-With': 'XMLHttpRequest' }, body: new URLSearchParams(data) }).then(function (r) { return r.text(); }); } // ------------------------------------------------------------------ cookies function getCookie(name) { var m = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)')); return m ? decodeURIComponent(m[1]) : null; } function setCookie(name, value) { if (value === null) { document.cookie = name + '=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'; } else { document.cookie = name + '=' + encodeURIComponent(value) + '; path=/; SameSite=Lax'; } } // ------------------------------------------------------------------ fades (jQuery fadeIn/fadeOut) function fadeOut(el, ms) { if (!el || el.style.display === 'none') return; el.style.transition = 'opacity ' + ms + 'ms'; el.style.opacity = '0'; clearTimeout(el._fade); el._fade = setTimeout(function () { el.style.display = 'none'; }, ms); } function fadeIn(el, ms, display) { if (!el) return; clearTimeout(el._fade); if (el.style.display === 'none' || getComputedStyle(el).display === 'none') { el.style.opacity = '0'; el.style.display = display || 'block'; } el.style.transition = 'opacity ' + ms + 'ms'; void el.offsetWidth; el.style.opacity = ''; } function show(el, display) { if (el) el.style.display = display || 'block'; } function hide(el) { if (el) el.style.display = 'none'; } // ------------------------------------------------------------------ AJAX loaders function LoadData(str, divId, imageId) { return post('/ajax_getdata.php', { q: str, imageId: imageId || 0 }).then(function (data) { var d = $(divId); if (!d) return; d.innerHTML = data; d.style.display = 'block'; }); } function Menu(art, er) { var ud = $('UploadD'); if (art === 'upload') ud.style.backgroundPosition = '24px top'; if (art === 'addon') ud.style.backgroundPosition = '90px top'; if (er === 'error') { hide($('UbLoad')); show($('MenuLoad')); return; } show($('MenuLoad')); $('UploadD_d').innerHTML = '
'; LoadData(art, 'UploadD_d', 0); } function loadComments() { var id = document.body.getAttribute('data-image-id'); if (id) LoadData('comments', 'cmts', id); } function SubmitComment(imageId) { var file = $('image_comment'), url = $('url_comment'), content = $('content_comment'); if (content.value.length < 1) { $('CommentError').textContent = 'Your comment is too short!'; show($('CommentError')); return; } if (file && file.value.length > 4) { $('CommentForm').submit(); return; } var fields = [file, url, content, $('SubBut')]; fields.forEach(function (f) { if (f) f.disabled = true; }); show($('SubLoad')); post('/ajax_getdata.php', { q: 'add', imageId: imageId, content: content.value, urlUpload: url ? url.value : '' }).then(function () { fields.forEach(function (f) { if (f) f.disabled = false; }); $('CommentForm').reset(); hide($('SubLoad')); hide($('CommentError')); loadComments(); }); } function toggle(id) { var el = $(id); if (!el) return; el.style.display = el.style.display !== 'block' ? 'block' : 'none'; } function BBcode(code) { var input = prompt('Enter URL / text / image-link / youtube-link'); if (input === null || input === '') return; var x = $('content_comment'); x.value = x.value + '[' + code + ']' + input + '[/' + code + ']'; } function reComment(commentId, user) { var box = $('comment' + commentId); if (box) box.style.border = '1px solid #898989'; var output = $(String(commentId)).textContent; output = '>> ' + user + ': \n"' + output + '"'; var x = $('content_comment'); if (x) x.value = x.value + '[quote]' + output.substr(0, 750) + '[/quote]\n'; } // ------------------------------------------------------------------ admin (json.php) function json(action, params) { return post('/json.php?' + action, params); } var admin = { fame: function (id) { if (!confirm('Move to Hall of Fame?')) return; json('fame', { id: id }); }, ban: function (id) { if (!confirm('Ban this User?')) return; var rsn = prompt('Why?'); json('banUser', { id: id, rsn: rsn || '' }); }, timeblock: function (id) { if (!confirm('Spamlock this user?')) return; var tm = prompt('How long? (in hours)'); var rsn = prompt('Why?'); var now = Math.round(Date.now() / 1000) + 3600 * parseInt(tm || '0', 10); alert(now); json('TimeUser', { id: id, hours: tm || '0', rsn: rsn || '' }); }, close: function (id) { if (!confirm('Close Thread?')) return; json('CloseThread', { id: id }); }, 'delete': function (id) { if (!confirm('Delete this image: ' + id + ' ?')) return; json('delete', { id: id }).then(loadComments); } }; function delComment(id) { if (!confirm('Delete this comment?')) return; json('deleteComment', { id: id }).then(loadComments); } // ------------------------------------------------------------------ grid solver (picturelicious.js) var gridSize = 64, gridMinWidth = 6, gridWidth = 0, grid = null; var boxDef = { b0: { width: 3, height: 3 }, b1: { width: 2, height: 2 }, b2: { width: 1, height: 1 } }; function gridSolve(cid) { var container = $(cid); var newGridWidth = parseInt(container.offsetWidth / gridSize, 10); newGridWidth = newGridWidth < gridMinWidth ? gridMinWidth : newGridWidth; if (gridWidth === newGridWidth) return; gridWidth = newGridWidth; var boxes = []; while (container.hasChildNodes()) { var e = container.removeChild(container.firstChild); if (e.tagName === 'DIV') boxes.push(e); } grid = []; for (var i = 0; i < gridWidth; i++) grid[i] = 0; for (i = 0; i < boxes.length; i++) insertThumb(boxes[i], container); var maxHeight = 0; for (i = 0; i < grid.length; i++) maxHeight = Math.max(maxHeight, grid[i]); container.style.height = (maxHeight * gridSize) + 'px'; $('images').style.height = (maxHeight * gridSize) + 'px'; } function insertThumb(box, container) { var def = boxDef[box.className] || boxDef.b2; var boxWidth = def.width, boxHeight = def.height; var maxHeight = 0, currentHeight = -1, spotWidth = 0, spotLeft = 0, freeSpots = []; for (var i = 0; i < grid.length; i++) { if (currentHeight === grid[i]) spotWidth++; if ((currentHeight !== grid[i] || i + 1 === grid.length) && spotWidth > 0) { freeSpots.push({ width: spotWidth, left: spotLeft, height: currentHeight }); spotWidth = 1; spotLeft = i; if (currentHeight !== grid[i] && i + 1 === grid.length) { freeSpots.push({ width: spotWidth, left: spotLeft, height: grid[i] }); } } else if (spotWidth === 0) { spotWidth = 1; } currentHeight = grid[i]; if (grid[i] > maxHeight) maxHeight = grid[i]; } var targetHeight = 0, targetLeft = 0, bestScore = -1; for (var j = 0; j < boxWidth; j++) { if (grid[j] > targetHeight) targetHeight = grid[j]; } var newHeight = targetHeight + boxHeight; for (i = 0; i < freeSpots.length; i++) { var heightScore = maxHeight - freeSpots[i].height; var widthScore = boxWidth / freeSpots[i].width; var score = heightScore * heightScore + widthScore * 2; if (freeSpots[i].width >= boxWidth && score > bestScore) { targetHeight = freeSpots[i].height; targetLeft = freeSpots[i].left; newHeight = freeSpots[i].height + boxHeight; bestScore = score; } } for (j = 0; j < boxWidth; j++) grid[targetLeft + j] = newHeight; container.appendChild(box); box.style.left = (targetLeft * gridSize) + 'px'; box.style.top = (targetHeight * gridSize) + 'px'; } function gridView(id) { gridSolve(id); gridSolve(id); setInterval(function () { gridSolve(id); }, 250); } // ------------------------------------------------------------------ sidebar & layout (jqpr.js) function hideSidebar() { hide($('sidebar')); hide($('MenuRightBlock')); $('site').style.marginRight = '0px'; var f = $('footer'); if (f) f.style.marginRight = '0px'; $('footer-back-to-top').style.right = '30px'; } // ------------------------------------------------------------------ lightbox (Lightbox v2.51) var lightbox = { album: [], current: 0, built: false, build: function () { var overlay = document.createElement('div'); overlay.id = 'lightboxOverlay'; var lb = document.createElement('div'); lb.id = 'lightbox'; lb.innerHTML = '
' + '
' + '
' + '
' + '
'; document.body.appendChild(overlay); document.body.appendChild(lb); hide(overlay); hide(lb); var that = this; overlay.addEventListener('click', function (e) { e.preventDefault(); that.end(); }); lb.addEventListener('click', function (e) { e.preventDefault(); var t = e.target; if (t.id === 'lightbox') that.end(); else if (t.classList.contains('lb-prev')) that.change(that.current - 1); else if (t.classList.contains('lb-next')) that.change(that.current + 1); else if (t.closest('.lb-loader, .lb-close')) that.end(); }); this.keys = function (e) { that.keyboard(e); }; this.built = true; }, q: function (sel) { return $('lightbox').querySelector(sel); }, sizeOverlay: function () { var o = $('lightboxOverlay'); o.style.width = document.documentElement.scrollWidth + 'px'; o.style.height = document.documentElement.scrollHeight + 'px'; }, start: function (link) { if (!this.built) this.build(); var rel = link.getAttribute('rel'); this.album = []; var number = 0; if (rel === 'lightbox') { this.album.push({ link: link.getAttribute('href'), title: link.getAttribute('title') }); } else { var links = document.querySelectorAll('a[rel="' + rel + '"]'); for (var i = 0; i < links.length; i++) { this.album.push({ link: links[i].getAttribute('href'), title: links[i].getAttribute('title') }); if (links[i] === link) number = i; } } this.sizeOverlay(); fadeIn($('lightboxOverlay'), 500); var lb = $('lightbox'); lb.style.top = (window.scrollY + window.innerHeight / 10) + 'px'; lb.style.left = window.scrollX + 'px'; fadeIn(lb, 500); window.addEventListener('resize', this.sizeOverlay); this.change(number); }, change: function (n) { if (n < 0 || n >= this.album.length) return; var that = this; document.removeEventListener('keyup', this.keys); ['.lb-image', '.lb-nav', '.lb-prev', '.lb-next', '.lb-dataContainer', '.lb-caption'].forEach(function (s) { hide(that.q(s)); }); show(this.q('.lb-loader')); this.q('.lb-outerContainer').classList.add('animating'); var pre = new Image(); pre.onload = function () { that.q('.lb-image').src = that.album[n].link; that.size(pre.width, pre.height); }; pre.src = this.album[n].link; this.current = n; }, size: function (w, h) { var that = this; var outer = this.q('.lb-outerContainer'); var cs = getComputedStyle(this.q('.lb-container')); var nw = w + parseInt(cs.paddingLeft, 10) + parseInt(cs.paddingRight, 10); var nh = h + parseInt(cs.paddingTop, 10) + parseInt(cs.paddingBottom, 10); outer.style.transition = 'width 700ms, height 700ms'; outer.style.width = nw + 'px'; outer.style.height = nh + 'px'; setTimeout(function () { that.q('.lb-dataContainer').style.width = nw + 'px'; hide(that.q('.lb-loader')); fadeIn(that.q('.lb-image'), 600); show(that.q('.lb-nav')); if (that.current > 0) show(that.q('.lb-prev')); if (that.current < that.album.length - 1) show(that.q('.lb-next')); var item = that.album[that.current]; if (item.title) { that.q('.lb-caption').textContent = item.title; fadeIn(that.q('.lb-caption'), 200, 'inline'); } var num = that.q('.lb-number'); if (that.album.length > 1) { num.textContent = 'Image ' + (that.current + 1) + ' of ' + that.album.length; fadeIn(num, 200); } else { hide(num); } outer.classList.remove('animating'); fadeIn(that.q('.lb-dataContainer'), 700); that.sizeOverlay(); [that.current + 1, that.current - 1].forEach(function (i) { if (that.album[i]) new Image().src = that.album[i].link; }); document.addEventListener('keyup', that.keys); }, 700); }, keyboard: function (e) { var key = String.fromCharCode(e.keyCode).toLowerCase(); if (e.keyCode === 27 || /x|o|c/.test(key)) this.end(); else if (key === 'p' || e.keyCode === 37) this.change(this.current - 1); else if (key === 'n' || e.keyCode === 39) this.change(this.current + 1); }, end: function () { document.removeEventListener('keyup', this.keys); window.removeEventListener('resize', this.sizeOverlay); fadeOut($('lightbox'), 500); fadeOut($('lightboxOverlay'), 500); } }; // ------------------------------------------------------------------ quicktagger (quicktags.js) var nextImage = {}; function quickTag(logId, imageId, inputField) { var tags = inputField.value; $('imageLocation').href = nextImage.link || '#'; $('imageLocation').textContent = nextImage.link || ''; $('imageId').value = nextImage.id || 0; $('logId').value = nextImage.logId || 0; if (nextImage.image) $('image').src = nextImage.image; $('tags').textContent = nextImage.tags || ''; $('tagText').value = ''; $('tagText').focus(); if (!nextImage.link) { $('imageInfo').textContent = 'No more Images found'; } json('quickTag', { logId: logId, id: imageId, tags: tags }).then(function (txt) { nextImage = JSON.parse(txt || '{}'); if (nextImage.image) new Image().src = nextImage.image; }); } function quickTagInit() { $('quickTagIntro').className = 'hidden'; $('viewer').className = 'visible'; json('quickTag', {}).then(function (txt) { nextImage = JSON.parse(txt || '{}'); quickTag(0, 0, $('tagText')); }); } // ------------------------------------------------------------------ events document.addEventListener('click', function (e) { // Match the element that carries the hook, not an inside a link. var t = e.target.closest( 'a[rel^="lightbox"], [data-action], [data-menu], [data-toggle], [data-fav], [data-bbcode], ' + '[data-requote], [data-delcomment], [data-admin], [data-bookmarklet], [data-select], ' + '#SubBut, #image, #x, #stfu, #del_cookie, #loadcom, #footer-back-to-top' ); if (!t) return; var d = t.dataset; if (t.matches('a[rel^="lightbox"]')) { e.preventDefault(); lightbox.start(t); return; } if (d.action === 'close-upload') { hide($('MenuLoad')); return; } if (d.menu) { e.preventDefault(); Menu(d.menu); return; } if (d.toggle) { e.preventDefault(); toggle(d.toggle); return; } if (d.fav) { e.preventDefault(); LoadData('fav', 'FavDiv', d.fav); return; } if (d.bbcode) { e.preventDefault(); BBcode(d.bbcode); return; } if (d.requote) { reComment(d.requote, d.name); return; } if (d.delcomment) { e.preventDefault(); delComment(d.delcomment); return; } if (d.admin && admin[d.admin]) { e.preventDefault(); admin[d.admin](d.id); return; } if (d.bookmarklet) { e.preventDefault(); alert('Drag this Link to your Bookmarks!'); return; } if (d.select) { t.focus(); t.select(); return; } if (d.action === 'quicktag-start') { e.preventDefault(); quickTagInit(); return; } if (d.action === 'quicktag') { e.preventDefault(); quickTag($('logId').value, $('imageId').value, $('tagText')); return; } if (t.id === 'SubBut') { SubmitComment(d.imageId); return; } if (t.id === 'image' && /^(scaled|full)$/.test(t.className)) { t.className = t.className === 'scaled' ? 'full' : 'scaled'; return; } if (t.id === 'x') { e.preventDefault(); fadeOut($('sidebar'), 600); hide($('MenuRightBlock')); $('site').style.marginRight = '0px'; $('footer-back-to-top').style.right = '30px'; alert('Sidebar closed. Open it by clicking on your avatar in the header. Best regards, Dolan!'); setCookie('close_sidebar', 'yes'); return; } if (t.id === 'stfu') { fadeOut($('dolan'), 600); setCookie('close_message', 'yes'); return; } if (t.id === 'del_cookie') { e.preventDefault(); setCookie('close_sidebar', null); setCookie('close_message', null); location.reload(); return; } if (t.id === 'loadcom') { e.preventDefault(); LoadData('lastcomments', 'rightpanel'); return; } if (t.id === 'footer-back-to-top') { e.preventDefault(); window.scrollTo({ top: 0, behavior: 'smooth' }); } }); document.addEventListener('submit', function (e) { if (e.target.id === 'addTag') { e.preventDefault(); quickTag($('logId').value, $('imageId').value, $('tagText')); } }); document.addEventListener('mouseover', function (e) { var t = e.target; if (t.classList && t.classList.contains('duckfav')) t.src = '/media/duck.png'; var h = t.closest && t.closest('[data-hover]'); if (h) { var el = $(h.dataset.hover); if (el) el.style.visibility = 'visible'; } }); document.addEventListener('mouseout', function (e) { var t = e.target; if (t.classList && t.classList.contains('duckfav')) t.src = '/media/ducksw.png'; var box = $('Last5Div'); if (box && box.contains(t) && !box.contains(e.relatedTarget)) box.style.visibility = 'hidden'; }); // Broken [img] BBCode images fall back to the dolan 404 picture (was onError="..."). document.addEventListener('error', function (e) { var t = e.target; if (t.tagName === 'IMG' && t.dataset && t.dataset.fallback && !t.dataset.failed) { t.dataset.failed = '1'; t.src = t.dataset.fallback; } }, true); document.documentElement.addEventListener('keyup', function (e) { if (!$('prevBar') || $('lightbox') && $('lightbox').style.display === 'block') return; var a = document.activeElement; if (a && (a.tagName === 'INPUT' || a.tagName === 'TEXTAREA')) return; var c = $('content_comment'); if (c && c.value) return; var bar = e.keyCode === 37 ? $('prevBar') : e.keyCode === 39 ? $('nextBar') : null; if (bar && bar.getAttribute('href')) window.location = bar.getAttribute('href'); }); // ------------------------------------------------------------------ init function init() { var body = document.body; var top = $('footer-back-to-top'); if (/iPhone|iPod|iPad|Android|BlackBerry/i.test(navigator.userAgent)) { hideSidebar(); $('menu').style.position = 'relative'; $('content').style.marginTop = '0.5em'; } if (body.hasAttribute('data-guest')) { hide($('sidebar')); $('site').style.marginRight = '0px'; top.style.right = '30px'; } if (getCookie('close_sidebar') === 'yes') hideSidebar(); if (getCookie('close_message') === 'yes') hide($('dolan')); hide(top); window.addEventListener('scroll', function () { if (window.scrollY > 200) fadeIn(top, 600); else fadeOut(top, 600); }, { passive: true }); if ($('imageGrid')) gridView('imageGrid'); var ml = $('MenuLoad'); if (ml && ml.hasAttribute('data-error')) Menu('', 'error'); if ($('rightpanel')) LoadData('lastcomments', 'rightpanel'); loadComments(); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init(); })();