132 lines
6.4 KiB
JavaScript
132 lines
6.4 KiB
JavaScript
|
|
// Helper for shrinking images in entry/comment content. The actual Squish is
|
||
|
|
// pure CSS; this just handles adding and removing classes to support:
|
||
|
|
// - Zooming (on click)
|
||
|
|
// - Exempting decorative markup from Squish (should be pure CSS, but can't yet)
|
||
|
|
// - Hiding zoom cursors for images that won't zoom
|
||
|
|
// - Marking tall images so they get modified squish behavior
|
||
|
|
jQuery(function($) {
|
||
|
|
// Entry-like things that might have images in them we want to possibly shrink. Should be in CSS-selector format
|
||
|
|
const imgHolders = ['.entry-content', '.comment-content', '.InboxItem_Content .Body'];
|
||
|
|
// Containers to observe for changes. Should be a plain id name.
|
||
|
|
const observedContainers = ['entries', 'comments', 'inbox_message_list'];
|
||
|
|
|
||
|
|
// The next two constants turn imgHolders into single CSS selector strings.
|
||
|
|
const imgSelector = imgHolders.map(function(holder) {return `${holder} img`}).join(", ");
|
||
|
|
|
||
|
|
// Only shrink casual pics; leave artisanal HTML alone. Can't predict
|
||
|
|
// everything, but 99% of the time that's a <div style="..."> or a table.
|
||
|
|
// Expanded cut tags have style='display: block', but aren't decorations.
|
||
|
|
const exemptSelector = imgHolders.map(function(holderName) {
|
||
|
|
return `${holderName} div[style]:not(.cuttag-open) img, ${holderName} table img`;
|
||
|
|
}).join(", ");
|
||
|
|
|
||
|
|
// First: Basic click-to-zoom. (.imageshrink-expanded on/off)
|
||
|
|
$(document).on('click', imgSelector, function(e) {
|
||
|
|
var $that = $(e.target);
|
||
|
|
if ( ! $that.is('a img, .poll-response img, .imageshrink-actualsize') ) {
|
||
|
|
$that.toggleClass('imageshrink-expanded');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Second: Exempt codes from the squish. (.imageshrink-exempt on/off)
|
||
|
|
function protectTheCodes(container) {
|
||
|
|
container.querySelectorAll( exemptSelector ).forEach(function(img) { img.classList.add('imageshrink-exempt') });
|
||
|
|
|
||
|
|
// Feeds (.journal-type-Y) always make a mess, so no mercy.
|
||
|
|
container.querySelectorAll('.journal-type-Y img').forEach( function(img) { img.classList.remove('imageshrink-exempt')})
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check the whole document to start with.
|
||
|
|
protectTheCodes(document);
|
||
|
|
|
||
|
|
// Dummied out observeImages function, to simplify browser support in (Fifth).
|
||
|
|
var observeImages = function(container) {return;};
|
||
|
|
|
||
|
|
if (typeof ResizeObserver === 'function') {
|
||
|
|
var imageShrinkResizeObserver = new ResizeObserver(function(resizeList, observer) {
|
||
|
|
resizeList.forEach(function(entry) {
|
||
|
|
var img = entry.target;
|
||
|
|
if (img.tagName !== 'IMG') { return; }
|
||
|
|
|
||
|
|
// Third: Skip zoom cursors for images that won't zoom (.imageshrink-actualsize on/off)
|
||
|
|
// "Won't zoom" means it's in its "shrunk" state and is already
|
||
|
|
// either "natural" full size or "requested" full size (as per
|
||
|
|
// the height/width attributes).
|
||
|
|
// "Natural" is easy:
|
||
|
|
var isNaturalSize = img.width === img.naturalWidth && img.height === img.naturalHeight;
|
||
|
|
// "Requested" is hard. `object-fit: contain` w/ max-height means
|
||
|
|
// layout width might be wider than visible width. There's no way
|
||
|
|
// to directly measure visible width, and of course that's the one
|
||
|
|
// we care about. Plus maybe they only set one attribute. So we
|
||
|
|
// measure indirectly: if the layout aspect ratio doesn't match the
|
||
|
|
// natural aspect ratio (+/- some slop bc floats), it CAN'T be the
|
||
|
|
// requested size. (And at least one dimension has to match.)
|
||
|
|
// And yes, this ignores the use case of deliberately mutilating the
|
||
|
|
// aspect ratio for comedy; use something other than the height/width
|
||
|
|
// attrs for that.
|
||
|
|
var attrWidth = parseInt(img.getAttribute('width'));
|
||
|
|
var attrHeight = parseInt(img.getAttribute('height'));
|
||
|
|
var isRequestedSize =
|
||
|
|
( ( !isNaN(attrWidth) && attrWidth === img.width )
|
||
|
|
|| ( !isNaN(attrHeight) && attrHeight === img.height )
|
||
|
|
)
|
||
|
|
&& Math.abs( (img.height / img.width) - (img.naturalHeight / img.naturalWidth) ) < 0.004;
|
||
|
|
// Might want to tune that slop later. Or hey, maybe I nailed it. -NF
|
||
|
|
|
||
|
|
var isActualSize = isNaturalSize || isRequestedSize;
|
||
|
|
|
||
|
|
if ( isActualSize && ! img.classList.contains('imageshrink-expanded') ) {
|
||
|
|
img.classList.add('imageshrink-actualsize');
|
||
|
|
} else {
|
||
|
|
img.classList.remove('imageshrink-actualsize');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fourth: Mark tall images so we can let them scroll. Doing
|
||
|
|
// this in the ResizeObserver because images have unknown natural
|
||
|
|
// aspect ratios until load & decode; they'll fire a resize once
|
||
|
|
// the browser sorts it out.
|
||
|
|
if ( (img.naturalHeight / img.naturalWidth) >= 2 ) {
|
||
|
|
img.classList.add('imageshrink-tall');
|
||
|
|
} else {
|
||
|
|
img.classList.remove('imageshrink-tall');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// And now the real version:
|
||
|
|
observeImages = function(container) {
|
||
|
|
let images = container.querySelectorAll(imgSelector);
|
||
|
|
// Anything with ResizeObserver definitely has NodeList.forEach.
|
||
|
|
images.forEach(function(img) {
|
||
|
|
imageShrinkResizeObserver.observe(img);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check the whole document to start with.
|
||
|
|
observeImages(document);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fifth: Repeat (Second), (Third), (Fourth) when adding images to the page.
|
||
|
|
// (Via cut tags, comment expansion, or image placeholders.)
|
||
|
|
if (typeof MutationObserver === 'function') {
|
||
|
|
var imageUpdater = new MutationObserver(function(mutationList, observer) {
|
||
|
|
mutationList.forEach(function(mutation) {
|
||
|
|
if (mutation.addedNodes.length > 0) {
|
||
|
|
protectTheCodes(mutation.target);
|
||
|
|
observeImages(mutation.target);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
var opts = {childList: true, subtree: true};
|
||
|
|
observedContainers.forEach(function (conName) {
|
||
|
|
var container = document.getElementById(conName);
|
||
|
|
if (container) {
|
||
|
|
imageUpdater.observe(container, opts);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
});
|