@@ -5,8 +5,8 @@ import { buildStyle, postSelector } from '../../utils/interface.js';
55import { getPreferences } from '../../utils/preferences.js' ;
66import { memoize } from '../../utils/memoize.js' ;
77
8- const canvasClass = 'xkit-paused-gif-placeholder' ;
98const pausedPosterAttribute = 'data-paused-gif-use-poster' ;
9+ const pausedContentVar = '--xkit-paused-gif-content' ;
1010const pausedBackgroundImageVar = '--xkit-paused-gif-background-image' ;
1111const hoverContainerAttribute = 'data-paused-gif-hover-container' ;
1212const labelAttribute = 'data-paused-gif-label' ;
@@ -16,7 +16,6 @@ const containerClass = 'xkit-paused-gif-container';
1616let loadingMode ;
1717
1818const hovered = `:is(:hover, [${ hoverContainerAttribute } ]:hover *)` ;
19- const parentHovered = `:is(:hover > *, [${ hoverContainerAttribute } ]:hover *)` ;
2019
2120export const styleElement = buildStyle ( `
2221[${ labelAttribute } ]::after {
@@ -46,16 +45,17 @@ export const styleElement = buildStyle(`
4645 transform: translateY(-50%);
4746}
4847
49- .${ canvasClass } {
50- position: absolute;
51- visibility: visible;
48+ [${ labelAttribute } ]${ hovered } ::after,
49+ [${ pausedPosterAttribute } ]:not(${ hovered } ) > div > ${ keyToCss ( 'knightRiderLoader' ) } {
50+ display: none;
51+ }
5252
53- background-color: rgb(var(--white));
53+ ${ keyToCss ( 'blogCard' ) } ${ keyToCss ( 'headerImage' ) } ${ keyToCss ( 'small' ) } [${ labelAttribute } ]::after {
54+ font-size: 0.8rem;
55+ top: calc(140px - 1em - 2.2ch);
5456}
5557
56- .${ canvasClass } ${ parentHovered } ,
57- [${ labelAttribute } ]${ hovered } ::after,
58- [${ pausedPosterAttribute } ]:not(${ hovered } ) > div > ${ keyToCss ( 'knightRiderLoader' ) } {
58+ img:is([${ pausedPosterAttribute } ], [style*="${ pausedContentVar } "]):not(${ hovered } ) ~ div > ${ keyToCss ( 'knightRiderLoader' ) } {
5959 display: none;
6060}
6161${ keyToCss ( 'background' ) } [${ labelAttribute } ]::after {
@@ -73,6 +73,9 @@ ${keyToCss('background')}[${labelAttribute}]::after {
7373 display: none;
7474}
7575
76+ img[style*="${ pausedContentVar } "]:not(${ hovered } ) {
77+ content: var(${ pausedContentVar } );
78+ }
7679[style*="${ pausedBackgroundImageVar } "]:not(${ hovered } ) {
7780 background-image: var(${ pausedBackgroundImageVar } ) !important;
7881}
@@ -94,27 +97,6 @@ const addLabel = (element, inside = false) => {
9497 }
9598} ;
9699
97- /**
98- * Fetches the selected image and tests if it is animated. On older browsers without ImageDecoder
99- * support, GIF images are assumed to be animated and WebP images are assumed to not be animated.
100- */
101- const isAnimated = memoize ( async sourceUrl => {
102- const response = await fetch ( sourceUrl , { headers : { Accept : 'image/webp,*/*' } } ) ;
103- const contentType = response . headers . get ( 'Content-Type' ) ;
104-
105- if ( typeof ImageDecoder === 'function' && await ImageDecoder . isTypeSupported ( contentType ) ) {
106- const decoder = new ImageDecoder ( {
107- type : contentType ,
108- data : response . body ,
109- preferAnimation : true
110- } ) ;
111- await decoder . decode ( ) ;
112- return decoder . tracks . selectedTrack . animated ;
113- } else {
114- return ! sourceUrl . endsWith ( '.webp' ) ;
115- }
116- } ) ;
117-
118100/**
119101 * Fetches the selected image, tests if it is animated, and returns a blob URL with the paused image
120102 * if it is. This is a small memory leak, as the resulting blob URL will never be revoked; avoid
@@ -154,53 +136,29 @@ const createPausedUrlIfAnimated = memoize(async sourceUrl => {
154136 return URL . createObjectURL ( blob ) ;
155137} ) ;
156138
157- const pauseGif = async function ( gifElement ) {
158- if ( gifElement . currentSrc . endsWith ( '.webp' ) && ! ( await isAnimated ( gifElement . currentSrc ) ) ) return ;
159-
160- const image = new Image ( ) ;
161- image . src = gifElement . currentSrc ;
162- image . onload = ( ) => {
163- if ( gifElement . parentNode && gifElement . parentNode . querySelector ( `.${ canvasClass } ` ) === null ) {
164- const canvas = document . createElement ( 'canvas' ) ;
165- canvas . width = image . naturalWidth ;
166- canvas . height = image . naturalHeight ;
167- canvas . className = gifElement . className ;
168- canvas . classList . add ( canvasClass ) ;
169- canvas . setAttribute ( 'style' , gifElement . getAttribute ( 'style' ) ) ;
170- canvas . getContext ( '2d' ) . drawImage ( image , 0 , 0 ) ;
171- gifElement . after ( canvas ) ;
172- addLabel ( gifElement ) ;
173-
174- gifElement . closest ( keyToCss (
175- 'imgLink' // trending tag: https://www.tumblr.com/explore/trending
176- ) ) ?. setAttribute ( hoverFixAttribute , '' ) ;
177- }
178- } ;
179- } ;
180-
181139const processGifs = function ( gifElements ) {
182- gifElements . forEach ( gifElement => {
140+ gifElements . forEach ( async gifElement => {
183141 if ( gifElement . closest ( `${ keyToCss ( 'avatarImage' , 'subAvatarImage' ) } , .block-editor-writing-flow` ) ) return ;
184- const pausedGifElements = [ ...gifElement . parentNode . querySelectorAll ( `.${ canvasClass } ` ) ] ;
185- if ( pausedGifElements . length ) {
186- gifElement . after ( ...pausedGifElements ) ;
187- return ;
188- }
189-
190142 gifElement . decoding = 'sync' ;
191143
192144 const posterElement = gifElement . parentElement . querySelector ( keyToCss ( 'poster' ) ) ;
193145 if ( posterElement ) {
194146 gifElement . parentElement . setAttribute ( pausedPosterAttribute , loadingMode ) ;
195- addLabel ( posterElement ) ;
196- return ;
197- }
198-
199- if ( gifElement . complete && gifElement . currentSrc ) {
200- pauseGif ( gifElement ) ;
201147 } else {
202- gifElement . onload = ( ) => pauseGif ( gifElement ) ;
148+ const sourceUrl = gifElement . currentSrc ||
149+ await new Promise ( resolve => gifElement . addEventListener ( 'load' , ( ) => resolve ( gifElement . currentSrc ) , { once : true } ) ) ;
150+
151+ const pausedUrl = await createPausedUrlIfAnimated ( sourceUrl ) ;
152+ if ( ! pausedUrl ) return ;
153+
154+ gifElement . style . setProperty ( pausedContentVar , `url(${ pausedUrl } )` ) ;
203155 }
156+ addLabel ( gifElement ) ;
157+
158+ gifElement . closest ( keyToCss (
159+ 'albumImage' , // post audio element
160+ 'imgLink' // trending tag: https://www.tumblr.com/explore/trending
161+ ) ) ?. setAttribute ( hoverFixAttribute , '' ) ;
204162 } ) ;
205163} ;
206164
@@ -263,10 +221,18 @@ export const main = async function () {
263221 ${
264222 'figure' // post image/imageset; recommended blog carousel entry; blog view sidebar "more like this"; post in grid view; blog card modal post entry
265223 } ,
224+ ${
225+ 'main.labs' // labs settings header: https://www.tumblr.com/settings/labs
226+ } ,
266227 ${ keyToCss (
267228 'linkCard' , // post link element
229+ 'albumImage' , // post audio element
230+ 'messageImage' , // direct message attached image
231+ 'messagePost' , // direct message linked post
268232 'typeaheadRow' , // modal search dropdown entry
269233 'tagImage' , // search page sidebar related tags, recommended tag carousel entry: https://www.tumblr.com/search/gif, https://www.tumblr.com/explore/recommended-for-you
234+ 'headerBanner' , // blog view header
235+ 'headerImage' , // modal blog card header, activity page "biggest fans" header
270236 'topPost' , // activity page top post
271237 'colorfulListItemWrapper' , // trending tag: https://www.tumblr.com/explore/trending
272238 'takeoverBanner' // advertisement
@@ -314,11 +280,12 @@ export const clean = async function () {
314280 wrapper . replaceWith ( ...wrapper . children )
315281 ) ;
316282
317- $ ( `.${ canvasClass } ` ) . remove ( ) ;
318283 $ ( `[${ labelAttribute } ]` ) . removeAttr ( labelAttribute ) ;
319284 $ ( `[${ pausedPosterAttribute } ]` ) . removeAttr ( pausedPosterAttribute ) ;
320285 $ ( `[${ hoverContainerAttribute } ]` ) . removeAttr ( hoverContainerAttribute ) ;
321286 $ ( `[${ hoverFixAttribute } ]` ) . removeAttr ( hoverFixAttribute ) ;
287+ [ ...document . querySelectorAll ( `img[style*="${ pausedContentVar } "]` ) ]
288+ . forEach ( element => element . style . removeProperty ( pausedContentVar ) ) ;
322289 [ ...document . querySelectorAll ( `[style*="${ pausedBackgroundImageVar } "]` ) ]
323290 . forEach ( element => element . style . removeProperty ( pausedBackgroundImageVar ) ) ;
324291} ;
0 commit comments