Skip to content

Instantly share code, notes, and snippets.

@marktaiwan
Last active December 23, 2023 02:25
Show Gist options
  • Save marktaiwan/474262db6cdac8b4e7411cd5471654a5 to your computer and use it in GitHub Desktop.
Save marktaiwan/474262db6cdac8b4e7411cd5471654a5 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Desuarchive Image Expansion
// @description Inline image expansion
// @version 1.0.0
// @author Marker
// @license MIT
// @namespace https://github.com/marktaiwan/
// @homepageURL https://github.com/marktaiwan/
// @match https://desuarchive.org/*
// @inject-into content
// @noframes
// @require https://raw.githubusercontent.com/soufianesakhi/node-creation-observer-js/master/release/node-creation-observer-latest.js
// @grant GM_addStyle
// ==/UserScript==
(function () {
'use strict';
/* Shorthands */
function $(selector, root = document) {
return root.querySelector(selector);
}
function $$(selector, root = document) {
return root.querySelectorAll(selector);
}
function onLeftClick(callback, root = document) {
root.addEventListener('click', e => {
if (e instanceof MouseEvent && e.button === 0) callback(e);
});
}
const SCRIPT_ID = 'desu-image-expansion';
const EXPANDED_ATTR = `${SCRIPT_ID}-expanded`;
const PENDING_ATTR = `${SCRIPT_ID}-pending`;
const LOADED_ATTR = `${SCRIPT_ID}-loaded`;
const fileExtensionRegExp = new RegExp('\\.(?:jpg|png|gif)$');
const CSS = `
.thread_image_link[${PENDING_ATTR}] .post_image {
opacity: 0.75;
}
article.thread > .thread_image_box[${EXPANDED_ATTR}],
aside.posts .post_wrapper > .thread_image_box[${EXPANDED_ATTR}] {
float: none;
text-align: unset;
}
#backlink .thread_image_link .expanded_image,
.thread_image_link .expanded_image,
.thread_image_link[${EXPANDED_ATTR}] .post_image {
display: none;
}
#backlink .thread_image_link .post_image,
.thread_image_link .post_image,
.thread_image_link[${EXPANDED_ATTR}] .expanded_image {
display: block;
}
#backlink .post_image {
opacity: 1;
}
#backlink .text .spoiler {
color: unset;
background: #ccc;
}
`;
function toggle(anchor) {
const expanded = anchor.hasAttribute(EXPANDED_ATTR);
const pending = anchor.hasAttribute(PENDING_ATTR);
const loaded = anchor.hasAttribute(LOADED_ATTR);
if (loaded) {
const article = anchor.closest('article');
const container = anchor.closest('.thread_image_box');
const padding = 5;
const scrollTop = article.getBoundingClientRect().top;
if (scrollTop < 0) window.scrollBy({top: scrollTop - padding, left: 0});
container.toggleAttribute(EXPANDED_ATTR, !expanded);
anchor.toggleAttribute(EXPANDED_ATTR, !expanded);
} else if (pending) {
anchor.removeAttribute(PENDING_ATTR);
} else {
anchor.setAttribute(PENDING_ATTR, '');
if (!$('.expanded_image', anchor)) {
const img = document.createElement('img');
img.src = anchor.href;
img.style.maxWidth = '100%';
img.classList.add('expanded_image');
anchor.append(img);
checkLoadStart(anchor, img);
}
}
}
function checkLoadStart(root, expandedImage) {
if (expandedImage.naturalWidth) {
root.setAttribute(LOADED_ATTR, '');
if (root.hasAttribute(PENDING_ATTR)) {
root.removeAttribute(PENDING_ATTR);
toggle(root);
}
} else {
window.setTimeout(checkLoadStart, 100, root, expandedImage);
}
}
GM_addStyle(CSS);
onLeftClick(e => {
const target = e.target;
const anchor = target.closest('a.thread_image_link');
if (!anchor || !fileExtensionRegExp.test(anchor.href)) return;
e.preventDefault();
toggle(anchor);
});
NodeCreationObserver.init(SCRIPT_ID);
NodeCreationObserver.onCreation('.post_backlink > .backlink', backlink => {
const postId = Number.parseInt(backlink.parentElement.dataset.post);
const previewId = Number.parseInt(backlink.dataset.post);
const selector = `#backlink [id="${previewId}"]`;
backlink.addEventListener('mouseenter', () => {
$$(`${selector} a.backlink[data-post="${postId}"]`).forEach(ele => {
ele.classList.add('post_level', 'post_level_administrator');
});
});
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment