diff options
-rw-r--r-- | src/content/dependencies/image.js | 180 | ||||
-rw-r--r-- | src/content/dependencies/linkArtTag.js | 8 | ||||
-rw-r--r-- | src/misc-templates.js | 117 | ||||
-rw-r--r-- | tap-snapshots/test/snapshot/image.js.test.cjs | 60 | ||||
-rw-r--r-- | test/snapshot/image.js | 92 |
5 files changed, 340 insertions, 117 deletions
diff --git a/src/content/dependencies/image.js b/src/content/dependencies/image.js new file mode 100644 index 00000000..1f904377 --- /dev/null +++ b/src/content/dependencies/image.js @@ -0,0 +1,180 @@ +import {empty} from '../../util/sugar.js'; + +export default { + extraDependencies: [ + 'getSizeOfImageFile', + 'html', + 'language', + 'thumb', + 'to', + ], + + data(artTags) { + const data = {}; + + if (artTags) { + data.contentWarnings = + artTags + .filter(tag => tag.isContentWarning) + .map(tag => tag.name); + } else { + data.contentWarnings = null; + } + + return data; + }, + + generate(data, { + getSizeOfImageFile, + html, + language, + thumb, + to, + }) { + return html.template(slot => + slot('src', ([src]) => + slot('path', ([...path]) => + slot('thumb', ([thumbKey = '']) => + slot('link', ([link = false]) => + slot('lazy', ([lazy = false]) => + slot('square', ([willSquare = false]) => { + let originalSrc; + + if (src) { + originalSrc = src; + } else if (!empty(path)) { + originalSrc = to(...path); + } else { + originalSrc = ''; + } + + const thumbSrc = + originalSrc && + (thumbKey + ? thumb[thumbKey](originalSrc) + : originalSrc); + + const willLink = typeof link === 'string' || link; + const willReveal = originalSrc && !empty(data.contentWarnings); + + const idOnImg = willLink ? null : slot('id'); + const idOnLink = willLink ? slot('id') : null; + + if (!originalSrc) { + return prepare( + html.tag('div', {class: 'image-text-area'}, + slot('missingSourceContent'))); + } + + let fileSize = null; + if (willLink) { + const mediaRoot = to('media.root'); + if (originalSrc.startsWith(mediaRoot)) { + fileSize = + getSizeOfImageFile( + originalSrc + .slice(mediaRoot.length) + .replace(/^\//, '')); + } + } + + let reveal = null; + if (willReveal) { + reveal = [ + language.$('misc.contentWarnings', { + warnings: language.formatUnitList(data.contentWarnings), + }), + html.tag('br'), + html.tag('span', {class: 'reveal-interaction'}, + language.$('misc.contentWarnings.reveal')), + ]; + } + + const className = slot('class'); + const imgAttributes = { + id: idOnImg, + class: className, + alt: slot('alt'), + width: slot('width'), + height: slot('height'), + 'data-original-size': fileSize, + }; + + const nonlazyHTML = + originalSrc && + prepare( + html.tag('img', { + ...imgAttributes, + src: thumbSrc, + })); + + if (lazy) { + return html.tags([ + html.tag('noscript', nonlazyHTML), + prepare( + html.tag('img', + { + ...imgAttributes, + class: [className, 'lazy'], + 'data-original': thumbSrc, + }), + true), + ]); + } + + return nonlazyHTML; + + function prepare(content, hide = false) { + let wrapped = content; + + wrapped = + html.tag('div', {class: 'image-container'}, + wrapped); + + if (willReveal) { + wrapped = + html.tag('div', {class: 'reveal'}, [ + wrapped, + html.tag('span', {class: 'reveal-text-container'}, + html.tag('span', {class: 'reveal-text'}, + reveal)), + ]); + } + + if (willSquare) { + wrapped = + html.tag('div', + { + class: [ + 'square', + hide && !willLink && 'js-hide' + ], + }, + + html.tag('div', {class: 'square-content'}, + wrapped)); + } + + if (willLink) { + wrapped = html.tag('a', + { + id: idOnLink, + class: [ + 'box', + 'image-link', + hide && 'js-hide', + ], + + href: + (typeof link === 'string' + ? link + : originalSrc), + }, + wrapped); + } + + return wrapped; + } + }))))))); + }, +}; diff --git a/src/content/dependencies/linkArtTag.js b/src/content/dependencies/linkArtTag.js new file mode 100644 index 00000000..7ddb7786 --- /dev/null +++ b/src/content/dependencies/linkArtTag.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, artTag) => + ({link: relation('linkThing', 'localized.tag', artTag)}), + + generate: (relations) => relations.link, +}; diff --git a/src/misc-templates.js b/src/misc-templates.js index 710dbcc3..f2307724 100644 --- a/src/misc-templates.js +++ b/src/misc-templates.js @@ -327,123 +327,6 @@ function unbound_getFlashGridHTML({ }); } -// Images - -function unbound_img({ - getSizeOfImageFile, - html, - to, - - src, - alt, - noSrcText = '', - thumb: thumbKey, - reveal, - id, - class: className, - width, - height, - link = false, - lazy = false, - square = false, -}) { - const willSquare = square; - const willLink = typeof link === 'string' || link; - - const originalSrc = src; - const thumbSrc = src && (thumbKey ? thumb[thumbKey](src) : src); - - const href = - (willLink - ? (typeof link === 'string' - ? link - : originalSrc) - : null); - - let fileSize = null; - const mediaRoot = to('media.root'); - if (href?.startsWith(mediaRoot)) { - fileSize = getSizeOfImageFile(href.slice(mediaRoot.length).replace(/^\//, '')); - } - - const imgAttributes = { - id: link ? '' : id, - class: className, - alt, - width, - height, - 'data-original-size': fileSize, - }; - - const noSrcHTML = - !src && - wrap( - html.tag('div', - {class: 'image-text-area'}, - noSrcText)); - - const nonlazyHTML = - src && - wrap( - html.tag('img', { - ...imgAttributes, - src: thumbSrc, - })); - - const lazyHTML = - src && - lazy && - wrap( - html.tag('img', - { - ...imgAttributes, - class: [className, 'lazy'], - 'data-original': thumbSrc, - }), - true); - - if (!src) { - return noSrcHTML; - } else if (lazy) { - return html.tag('noscript', nonlazyHTML) + '\n' + lazyHTML; - } else { - return nonlazyHTML; - } - - function wrap(input, hide = false) { - let wrapped = input; - - wrapped = html.tag('div', {class: 'image-container'}, wrapped); - - if (reveal) { - wrapped = html.tag('div', {class: 'reveal'}, [ - wrapped, - html.tag('span', {class: 'reveal-text-container'}, - html.tag('span', {class: 'reveal-text'}, reveal)), - ]); - } - - if (willSquare) { - wrapped = html.tag('div', {class: 'square-content'}, wrapped); - wrapped = html.tag('div', - {class: ['square', hide && !willLink && 'js-hide']}, - wrapped); - } - - if (willLink) { - wrapped = html.tag('a', - { - id, - class: ['box', hide && 'js-hide', 'image-link'], - href, - }, - wrapped); - } - - return wrapped; - } -} - // Carousel reels // Layout constants: diff --git a/tap-snapshots/test/snapshot/image.js.test.cjs b/tap-snapshots/test/snapshot/image.js.test.cjs new file mode 100644 index 00000000..9f0e9948 --- /dev/null +++ b/tap-snapshots/test/snapshot/image.js.test.cjs @@ -0,0 +1,60 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/image.js TAP image (snapshot) > content warnings via tags 1`] = ` +<div class="reveal"> + <div class="image-container"><img src="media/album-art/beyond-canon/cover.png"></div> + <span class="reveal-text-container"> + <span class="reveal-text"> + cw: too cool for school + <br> + <span class="reveal-interaction">click to show</span> + </span> + </span> +</div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > id with link 1`] = ` +<a id="banana" class="box image-link" href="foobar"><div class="image-container"><img src="foobar"></div></a> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > id with square 1`] = ` +<div class="square"><div class="square-content"><div class="image-container"><img id="banana" src="foobar"></div></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > id without link 1`] = ` +<div class="image-container"><img id="banana" src="foobar"></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > lazy with square 1`] = ` +<noscript><div class="square"><div class="square-content"><div class="image-container"><img src="foobar"></div></div></div></noscript> +<div class="square js-hide"><div class="square-content"><div class="image-container"><img class=" lazy" data-original="foobar"></div></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > link with file size 1`] = ` +<a class="box image-link" href="media/album-art/pingas/cover.png"><div class="image-container"><img data-original-size="1000000" src="media/album-art/pingas/cover.png"></div></a> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > source missing 1`] = ` +<div class="image-container"><div class="image-text-area">Example of missing source message.</div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > source via path 1`] = ` +<div class="image-container"><img src="media/album-art/beyond-canon/cover.png"></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > source via src 1`] = ` +<div class="image-container"><img src="https://example.com/bananas.gif"></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > square 1`] = ` +<div class="square"><div class="square-content"><div class="image-container"><img src="foobar"></div></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > width & height 1`] = ` +<div class="image-container"><img width="600" height="400" src="foobar"></div> +` diff --git a/test/snapshot/image.js b/test/snapshot/image.js new file mode 100644 index 00000000..eeffb849 --- /dev/null +++ b/test/snapshot/image.js @@ -0,0 +1,92 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'image (snapshot)', async (t, evaluate) => { + await evaluate.load(); + + const quickSnapshot = (message, opts) => + evaluate.snapshot(message, { + name: 'image', + extraDependencies: { + getSizeOfImageFile: () => 0, + }, + ...opts, + }); + + quickSnapshot('source via path', { + postprocess: template => template + .slot('path', ['media.albumCover', 'beyond-canon', 'png']), + }); + + quickSnapshot('source via src', { + postprocess: template => template + .slot('src', 'https://example.com/bananas.gif'), + }); + + quickSnapshot('source missing', { + postprocess: template => template + .slot('missingSourceContent', 'Example of missing source message.'), + }); + + quickSnapshot('id without link', { + postprocess: template => template + .slot('src', 'foobar') + .slot('id', 'banana'), + }); + + quickSnapshot('id with link', { + postprocess: template => template + .slot('src', 'foobar') + .slot('link', true) + .slot('id', 'banana'), + }); + + quickSnapshot('id with square', { + postprocess: template => template + .slot('src', 'foobar') + .slot('square', true) + .slot('id', 'banana'), + }) + + quickSnapshot('width & height', { + postprocess: template => template + .slot('src', 'foobar') + .slot('width', 600) + .slot('height', 400), + }); + + quickSnapshot('square', { + postprocess: template => template + .slot('src', 'foobar') + .slot('square', true), + }); + + quickSnapshot('lazy with square', { + postprocess: template => template + .slot('src', 'foobar') + .slot('lazy', true) + .slot('square', true), + }); + + quickSnapshot('link with file size', { + extraDependencies: { + getSizeOfImageFile: () => 10 ** 6, + }, + + postprocess: template => template + .slot('path', ['media.albumCover', 'pingas', 'png']) + .slot('link', true), + }); + + quickSnapshot('content warnings via tags', { + args: [ + [ + {name: 'Dirk Strider', directory: 'dirk'}, + {name: 'too cool for school', isContentWarning: true}, + ], + ], + + postprocess: template => template + .slot('path', ['media.albumCover', 'beyond-canon', 'png']), + }) +}); |