hexo-theme-linen 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/languages/en.yml +3 -0
  2. package/languages/zh-CN.yml +4 -1
  3. package/layout/footer.ejs +62 -0
  4. package/layout/layout.ejs +22 -9
  5. package/layout/partials/header.ejs +1 -1
  6. package/layout/partials/post-item.ejs +5 -5
  7. package/layout/partials/related-posts.ejs +1 -1
  8. package/layout/post.ejs +362 -24
  9. package/layout/series.ejs +277 -4
  10. package/package.json +1 -1
  11. package/scripts/font-version.js +5 -3
  12. package/scripts/helpers/tools.js +187 -191
  13. package/scripts/lazyload/client.js +11 -2
  14. package/scripts/lazyload/index.js +7 -5
  15. package/scripts/lazyload/inject.js +8 -0
  16. package/scripts/lazyload/process.js +202 -163
  17. package/scripts/markdown-it-image-grid.js +74 -10
  18. package/source/linen-theme/css/{mediaquery.scss → _mediaquery.scss} +183 -12
  19. package/source/linen-theme/css/atom.xsl +37 -0
  20. package/source/linen-theme/css/font-version.scss +1 -1
  21. package/source/linen-theme/css/images/layers-2x.png +0 -0
  22. package/source/linen-theme/css/images/layers.png +0 -0
  23. package/source/linen-theme/css/images/marker-icon-2x.png +0 -0
  24. package/source/linen-theme/css/images/marker-icon.png +0 -0
  25. package/source/linen-theme/css/images/marker-shadow.png +0 -0
  26. package/source/linen-theme/css/leaflet.css +661 -0
  27. package/source/linen-theme/css/linen.scss +1198 -114
  28. package/source/linen-theme/css/linkcard.scss +53 -11
  29. package/source/linen-theme/css/noscript.css +70 -0
  30. package/source/linen-theme/css/photoswipe-dynamic-caption-plugin.css +1 -1
  31. package/source/linen-theme/css/photoswipe.css +166 -1
  32. package/source/linen-theme/css/rss-en.xsl +1 -1
  33. package/source/linen-theme/css/rss-zh.xsl +1 -1
  34. package/source/linen-theme/css/vars.scss +1 -1
  35. package/source/linen-theme/img/pattern-randomized.svg +1 -0
  36. package/source/linen-theme/img/travel-map-fallback.svg +30 -0
  37. package/source/linen-theme/js/hdr.js +42 -12
  38. package/source/linen-theme/js/lazyload.js +11 -2
  39. package/source/linen-theme/js/leaflet.js +6 -0
  40. package/source/linen-theme/js/linen.js +114 -83
  41. package/source/linen-theme/js/photoswipe/photoswipe.esm.min.js +13 -0
  42. package/source/linen-theme/js/replace-emoji.js +2 -4
  43. package/source/linen-theme/js/toc.js +71 -121
  44. package/layout/partials/footer.ejs +0 -12
  45. package/source/linen-theme/img/icons/backref.svg +0 -1
  46. package/source/linen-theme/img/icons/more.svg +0 -4
package/layout/post.ejs CHANGED
@@ -89,7 +89,7 @@
89
89
  <div id="page-donates"><%=__('common.donateToAuthor')%>
90
90
  <div id="donate-modal">
91
91
  <% page.donates.forEach(donateMethod => { %>
92
- <img class="qr-code" src="<%=donateMethod.qrcode%>" alt="<%=donateMethod.type%>" />
92
+ <img class="qr-code" src="<%=url_for(donateMethod.qrcode)%>" alt="<%=donateMethod.type%>" />
93
93
  <% }); %>
94
94
  </div>
95
95
  </div>
@@ -130,10 +130,10 @@ const seriesInfo = getSeriesInfo(page?.series);
130
130
  <div class="post-nav-wrap">
131
131
  <div class="post-nav">
132
132
  <% if (previousPost) { %>
133
- <a class="post-nav-item prev" target="_self" href="<%= url_for(previousPost.path) %>"><span class="item-link"><%= previousPost.title %></span><% if (previousPost.subTitle) { %><span class="sub-title"><%=previousPost.subTitle%></span><% } %><span class="nav-indicator-text"><%=__('common.prevPost')%></span></a>
133
+ <a class="post-nav-item prev" target="_self" href="<%=url_for(previousPost.path)%>"><span class="item-link"><%=previousPost.title %></span><% if (previousPost.subTitle) { %><span class="sub-title"><%=previousPost.subTitle%></span><% } %><span class="nav-indicator-text"><%=__('common.prevPost')%></span></a>
134
134
  <% } %>
135
135
  <% if (nextPost) { %>
136
- <a class="post-nav-item next" target="_self" href="<%= url_for(nextPost.path) %>"><span class="item-link"><%= nextPost.title %></span><% if (nextPost.subTitle) { %><span class="sub-title"><%=nextPost.subTitle%></span><% } %><span class="nav-indicator-text"><%=__('common.nextPost')%></a>
136
+ <a class="post-nav-item next" target="_self" href="<%=url_for(nextPost.path)%>"><span class="item-link"><%=nextPost.title %></span><% if (nextPost.subTitle) { %><span class="sub-title"><%=nextPost.subTitle%></span><% } %><span class="nav-indicator-text"><%=__('common.nextPost')%></a>
137
137
  <% } %>
138
138
  </div>
139
139
  </div>
@@ -150,9 +150,21 @@ const seriesInfo = getSeriesInfo(page?.series);
150
150
  </span>
151
151
  </div>
152
152
  <div class="series-items">
153
- <% series.forEach((item, index) => { %>
153
+ <%
154
+ let currentSeriesYear = null;
155
+ series.forEach((item, index) => {
156
+ const itemYear = item.date?.year ? item.date.year() : new Date(item.date).getFullYear();
157
+ const showYearDivider = seriesInfo?.yearDivider && itemYear && itemYear !== currentSeriesYear;
158
+ const showIndex = seriesInfo?.showMobileIndex !== false
159
+ if (showYearDivider) {
160
+ currentSeriesYear = itemYear;
161
+ }
162
+ %>
163
+ <% if (showYearDivider) { %>
164
+ <div class="series-year-divider"><%=itemYear%></div>
165
+ <% } %>
154
166
  <a class="series-item-link<%-item.source === page.source ? ' active': ''%>" href="<%=url_for(item.path)%>">
155
- <span class="item-index"><%-`${seriesInfo?.noOrder === 'asc' ? index + 1 : series.length - index}. `%></span><%=item.title%><% if (item.subTitle) { %><span class="sub-title"><%=item.subTitle%></span><% } %>
167
+ <% if (showIndex) { %><span class="item-index"><%-`${seriesInfo?.noOrder === 'asc' ? index + 1 : series.length - index}. `%></span><% }%><%=item.title%><% if (item.subTitle) { %><span class="sub-title"><%=item.subTitle%></span><% } %>
156
168
  </a>
157
169
  <% }); %>
158
170
  </div>
@@ -189,7 +201,7 @@ const seriesInfo = getSeriesInfo(page?.series);
189
201
  }
190
202
  const link = document.createElement("link");
191
203
  link.rel = "stylesheet";
192
- link.href = "<%=url_for('/linen-theme/css/gitalk/gitalk.css')%>";
204
+ link.href = "<%=url_for('/linen-theme/js/gitalk/gitalk.css')%>";
193
205
  document.head.appendChild(link);
194
206
  const script = document.createElement("script");
195
207
  script.src = "<%=url_for('/linen-theme/js/gitalk/gitalk.min.js')%>";
@@ -236,7 +248,7 @@ const seriesInfo = getSeriesInfo(page?.series);
236
248
  </script>
237
249
  <% } %>
238
250
 
239
- <% if ([config?.theme_config?.photoswipe ?? true, page.photoswipe ?? true, /<img/.test(page?.content)].every(enable=>enable)) {
251
+ <% if ([config?.theme_config?.photoswipe, page.photoswipe ?? true, /<img|\/static\/live-photo\/|live-photo-gallery-item|data-live-photo-src/.test(page?.content)].every(enable=>enable)) {
240
252
  const version = theme.version;
241
253
  const hdrAssetsPrefix = page?.hdrAssetsPrefix || '';
242
254
  %>
@@ -248,10 +260,11 @@ const seriesInfo = getSeriesInfo(page?.series);
248
260
  import PhotoSwipeDynamicCaption from '<%=url_for(`/linen-theme/js/photoswipe/photoswipe-dynamic-caption-plugin.esm.js?v=${version}`)%>';
249
261
  let isPswpOpening = false;
250
262
  let hdrAssetsPrefix = '<%=hdrAssetsPrefix%>';
263
+ const hiddenLivePhotoTriggers = new Map();
251
264
  const getSafeAreaHeight = () => {
252
265
  const el = document.getElementById('safe-area-test');
253
266
  const height = window.getComputedStyle(el).height;
254
- return parseInt(height) ?? 0
267
+ return parseInt(height) || 0;
255
268
  }
256
269
  const smallScreenPadding = {
257
270
  top: 0,
@@ -277,14 +290,14 @@ const seriesInfo = getSeriesInfo(page?.series);
277
290
  });
278
291
 
279
292
  const getExif = (element) => {
280
- const exifString = element?.dataset?.pspwExif || '';
281
- const locationString = element?.dataset?.pspwLocation || '';
293
+ const exifString = (element && element.dataset && element.dataset.pspwExif) || '';
294
+ const locationString = (element && element.dataset && element.dataset.pspwLocation) || '';
282
295
  const matches = [...exifString.matchAll(/\{([^}]*)\}/g)].map(match => match[1])
283
- if (matches?.length) {
296
+ if (matches && matches.length) {
284
297
  const locationMatches = [...locationString.matchAll(/\{([^}]*)\}/g)].map(match => match[1]);
285
298
  const keys = ['camera', 'lens', 'iso', 'focusLength', 'ev', 'aperture', 'shutterSpeed', 'hdr'];
286
299
  const [camera, iso, focusLength, aperture, shutterSpeed, ev, lens, isHDR] = matches;
287
- const isHDRActive = (isHDRScreen && (!!isHDR || element?.href?.includes(hdrAssetsPrefix)) && window.hdrEnabled) ? 'HDR' : ''
300
+ const isHDRActive = (isHDRScreen && (!!isHDR || (element && element.href && element.href.includes(hdrAssetsPrefix))) && window.hdrEnabled) ? 'HDR' : ''
288
301
  const exifObj = {
289
302
  camera,
290
303
  iso,
@@ -294,7 +307,7 @@ const seriesInfo = getSeriesInfo(page?.series);
294
307
  ev,
295
308
  lens,
296
309
  hdr: isHDRActive,
297
- location: locationMatches?.length === 2 ? {
310
+ location: (locationMatches && locationMatches.length === 2) ? {
298
311
  url: locationMatches[0],
299
312
  title: locationMatches[1]
300
313
  } : null
@@ -310,19 +323,253 @@ const seriesInfo = getSeriesInfo(page?.series);
310
323
  hdr: 'HDR Photo',
311
324
  location: 'Location'
312
325
  }
313
- return `<div class="img-exif">${keys.map(key=>exifObj[key]?`<span class="${key}" title="${altTextMap[key]}">${typeof exifObj[key] === 'boolean' ? '' : exifObj[key]}</span>`: '').join('')}${exifObj.location ? `<a href="${exifObj.location.url}"><span class="location">${exifObj.location.title}</span></a>`: ''}</div>`
326
+ return `<div class="img-exif">${keys.map(key=>exifObj[key]?`<span class="${key}" title="${altTextMap[key]}">${typeof exifObj[key] === 'boolean' ? '' : exifObj[key]}</span>`: '').join('')}${exifObj.location ? `<a href="${exifObj.location.url}"><span class="location">${exifObj.location.title}</span></a>`: ''}</div>`;
314
327
  }
315
- return ''
328
+ return '';
316
329
  }
317
330
  const captionPlugin = new PhotoSwipeDynamicCaption(lightbox, {
318
331
  type: 'auto',
319
332
  });
320
333
 
334
+ const escapeHtml = (value) => String(value || '').replace(/[&<>"']/g, (char) => ({
335
+ '&': '&amp;',
336
+ '<': '&lt;',
337
+ '>': '&gt;',
338
+ '"': '&quot;',
339
+ "'": '&#39;'
340
+ }[char]));
341
+
342
+ const normalizeUrl = (url) => {
343
+ try {
344
+ return new URL(url, location.href).href;
345
+ } catch (error) {
346
+ return url;
347
+ }
348
+ }
349
+
350
+ const pauseLivePhotos = () => {
351
+ document.querySelectorAll('.pswp-live-photo-video').forEach((video) => {
352
+ video.pause();
353
+ const livePhotoWrap = video.closest('.pswp-live-photo-wrap');
354
+ if (livePhotoWrap) {
355
+ livePhotoWrap.classList.remove('is-playing');
356
+ }
357
+ });
358
+ }
359
+
360
+ const setPswpLivePhotoClosing = (enabled) => {
361
+ if (lightbox && lightbox.pswp && lightbox.pswp.element) {
362
+ lightbox.pswp.element.classList.toggle('is-closing-live-photo', enabled);
363
+ }
364
+ }
365
+
366
+ const setPswpLivePhotoOpening = (enabled) => {
367
+ if (lightbox && lightbox.pswp && lightbox.pswp.element) {
368
+ lightbox.pswp.element.classList.toggle('is-opening-live-photo', enabled);
369
+ if (!enabled) {
370
+ lightbox.pswp.element.classList.remove('is-live-photo-trigger-visible');
371
+ }
372
+ }
373
+ }
374
+
375
+ const revealOpeningLivePhotoTrigger = () => {
376
+ if (!lightbox || !lightbox.pswp || !lightbox.pswp.element) {
377
+ return;
378
+ }
379
+
380
+ if (!lightbox.pswp.element.classList.contains('is-opening-live-photo')) {
381
+ return;
382
+ }
383
+
384
+ window.requestAnimationFrame(() => {
385
+ if (!lightbox || !lightbox.pswp || !lightbox.pswp.element) {
386
+ return;
387
+ }
388
+ lightbox.pswp.element.classList.add('is-live-photo-trigger-visible');
389
+ window.setTimeout(() => {
390
+ setPswpLivePhotoOpening(false);
391
+ }, 360);
392
+ });
393
+ }
394
+
395
+ const getCurrentGalleryElement = () => (
396
+ lightbox && lightbox.pswp && lightbox.pswp.currSlide && lightbox.pswp.currSlide.data && lightbox.pswp.currSlide.data.element
397
+ );
398
+
399
+ const isLivePhotoGalleryElement = (element) => (
400
+ element && element.classList && element.classList.contains('live-photo-gallery-item')
401
+ );
402
+
403
+ const setPageLivePhotoTriggerHidden = (galleryElement) => {
404
+ const scrollTarget = getScrollTarget(galleryElement);
405
+ const iframe = scrollTarget && scrollTarget.querySelector && scrollTarget.querySelector('iframe[src*="/static/live-photo/"]');
406
+ let iframeDocument = null;
407
+ try {
408
+ iframeDocument = iframe && iframe.contentDocument;
409
+ } catch (error) {
410
+ return;
411
+ }
412
+
413
+ const trigger = iframeDocument && iframeDocument.querySelector('.live-trigger');
414
+ if (!trigger || hiddenLivePhotoTriggers.has(trigger)) {
415
+ return;
416
+ }
417
+
418
+ hiddenLivePhotoTriggers.set(trigger, {
419
+ opacity: trigger.style.opacity,
420
+ transition: trigger.style.transition,
421
+ pointerEvents: trigger.style.pointerEvents,
422
+ visibility: trigger.style.visibility
423
+ });
424
+ trigger.style.opacity = '0';
425
+ trigger.style.transition = 'none';
426
+ trigger.style.pointerEvents = 'none';
427
+ trigger.style.visibility = 'hidden';
428
+ }
429
+
430
+ const restorePageLivePhotoTriggers = () => {
431
+ hiddenLivePhotoTriggers.forEach((previousStyle, trigger) => {
432
+ trigger.style.opacity = previousStyle.opacity;
433
+ trigger.style.transition = previousStyle.transition;
434
+ trigger.style.pointerEvents = previousStyle.pointerEvents;
435
+ trigger.style.visibility = previousStyle.visibility;
436
+ });
437
+ hiddenLivePhotoTriggers.clear();
438
+ }
439
+
440
+ const prepareCurrentLivePhotoClose = () => {
441
+ const currentElement = getCurrentGalleryElement();
442
+ const isLivePhoto = isLivePhotoGalleryElement(currentElement);
443
+ setPswpLivePhotoClosing(isLivePhoto);
444
+ if (isLivePhoto) {
445
+ setPageLivePhotoTriggerHidden(currentElement);
446
+ }
447
+ }
448
+
449
+ const getScrollTarget = (galleryElement) => {
450
+ if (!galleryElement || !galleryElement.classList || !galleryElement.classList.contains('live-photo-gallery-item')) {
451
+ return galleryElement;
452
+ }
453
+
454
+ return galleryElement.closest('.lazyload-outer-wrap') || galleryElement;
455
+ }
456
+
457
+ const getLivePhotoThumbElement = (itemData) => {
458
+ const galleryElement = itemData && itemData.element;
459
+ if (!galleryElement || !galleryElement.classList || !galleryElement.classList.contains('live-photo-gallery-item')) {
460
+ return null;
461
+ }
462
+
463
+ const thumbId = galleryElement.dataset && galleryElement.dataset.pswpThumbId;
464
+ return (thumbId && document.getElementById(thumbId)) ||
465
+ (galleryElement.closest('.lazyload-outer-wrap') && galleryElement.closest('.lazyload-outer-wrap').querySelector('.lazyload-wrap')) ||
466
+ null;
467
+ }
468
+
469
+ const syncSwitcherWithGalleryElement = (galleryElement) => {
470
+ const gridItem = galleryElement && galleryElement.closest && galleryElement.closest('.image-grid.switcher .grid-item');
471
+ const switcher = gridItem && gridItem.closest && gridItem.closest('.image-grid.switcher');
472
+ if (!gridItem || !switcher) {
473
+ return;
474
+ }
475
+
476
+ const items = Array.from(switcher.children).filter((child) => child.classList && child.classList.contains('grid-item'));
477
+ const activeIndex = items.indexOf(gridItem);
478
+ const controls = switcher.querySelector('.image-grid-switcher-controls');
479
+ const buttons = Array.from(switcher.querySelectorAll('.image-grid-switcher-button'));
480
+
481
+ if (activeIndex < 0) {
482
+ return;
483
+ }
484
+
485
+ if (controls && controls.style) {
486
+ controls.style.setProperty('--switcher-active-index', activeIndex);
487
+ }
488
+
489
+ items.forEach((item, index) => {
490
+ const isActive = index === activeIndex;
491
+ item.classList.toggle('active', isActive);
492
+ item.setAttribute('aria-hidden', isActive ? 'false' : 'true');
493
+ });
494
+
495
+ buttons.forEach((button, index) => {
496
+ const isActive = index === activeIndex;
497
+ button.classList.toggle('active', isActive);
498
+ button.setAttribute('aria-selected', isActive ? 'true' : 'false');
499
+ });
500
+ }
501
+
502
+ const getLivePhotoHtml = (iframeSrc) => {
503
+ const livePhotoUrl = new URL(iframeSrc, location.href);
504
+ const picUrl = livePhotoUrl.searchParams.get('picUrl') || livePhotoUrl.searchParams.get('photoSrc') || '';
505
+ const videoUrl = livePhotoUrl.searchParams.get('videoUrl') || livePhotoUrl.searchParams.get('videoSrc') || '';
506
+ const isMuted = livePhotoUrl.searchParams.has('muted');
507
+ const shouldLoop = livePhotoUrl.searchParams.has('loop');
508
+ const volume = Number(livePhotoUrl.searchParams.get('volume') || 100);
509
+
510
+ if (!picUrl || !videoUrl) {
511
+ return `<div class="pswp-live-photo-wrap"><img class="pswp-live-photo-img" src="${escapeHtml(picUrl)}" draggable="false" alt="" /></div>`;
512
+ }
513
+
514
+ return `<div class="pswp-live-photo-wrap" data-volume="${Math.min(Math.max(volume, 0), 100)}">
515
+ <img class="pswp-live-photo-img" src="${escapeHtml(picUrl)}" draggable="false" alt="" />
516
+ <video class="pswp-live-photo-video" src="${escapeHtml(videoUrl)}" playsinline webkit-playsinline preload="metadata"${isMuted ? ' muted' : ''}${shouldLoop ? ' loop' : ''}></video>
517
+ <button class="pswp-live-photo-trigger" type="button" aria-label="Play Live Photo">
518
+ <span class="icon-wrap">
519
+ <span class="trigger-icon ready"></span>
520
+ <span class="progress-circle loaded" aria-hidden="true">
521
+ <svg width="25" height="25" viewBox="0 0 25 25" focusable="false">
522
+ <circle cx="12.5" cy="12.5" r="11" stroke="#eee" stroke-width="3" fill="none"></circle>
523
+ <circle cx="12.5" cy="12.5" r="11" stroke="#232323" stroke-width="3" fill="none" stroke-linecap="round"></circle>
524
+ </svg>
525
+ </span>
526
+ </span>
527
+ <span class="trigger-text">LIVE</span>
528
+ </button>
529
+ </div>`;
530
+ }
531
+
532
+ lightbox.addFilter('itemData', (itemData) => {
533
+ const livePhotoSrc = itemData && itemData.element && itemData.element.dataset && itemData.element.dataset.livePhotoSrc;
534
+ if (!livePhotoSrc) {
535
+ return itemData;
536
+ }
537
+
538
+ const iframeSrc = decodeURIComponent(livePhotoSrc);
539
+ return {
540
+ ...itemData,
541
+ msrc: (itemData.element.dataset && itemData.element.dataset.livePhotoPicUrl) || itemData.msrc || itemData.src,
542
+ thumbCropped: true,
543
+ src: undefined,
544
+ type: 'html',
545
+ html: getLivePhotoHtml(iframeSrc)
546
+ };
547
+ });
548
+
549
+ lightbox.addFilter('thumbEl', (thumbEl, itemData) => {
550
+ return getLivePhotoThumbElement(itemData) || thumbEl;
551
+ });
552
+
553
+ lightbox.addFilter('useContentPlaceholder', (usePlaceholder, content) => {
554
+ const itemData = content && content.data;
555
+ return getLivePhotoThumbElement(itemData) ? true : usePlaceholder;
556
+ });
557
+
321
558
  lightbox.addFilter('placeholderSrc', (placeholderSrc, content) => {
322
- const thumbnail = content?.slide?.data?.msrc || ''
323
- return thumbnail || placeholderSrc;
559
+ const galleryElement = content && content.data && content.data.element;
560
+ if (galleryElement && galleryElement.classList && galleryElement.classList.contains('live-photo-gallery-item')) {
561
+ return (galleryElement.dataset && galleryElement.dataset.livePhotoPicUrl) || placeholderSrc;
562
+ }
563
+ return placeholderSrc;
324
564
  });
325
565
 
566
+ <% if (page.path === 'gallery/index.html') { %>
567
+ lightbox.addFilter('placeholderSrc', (placeholderSrc, content) => {
568
+ const thumbnail = (content && content.slide && content.slide.data && content.slide.data.msrc) || '';
569
+ return thumbnail || placeholderSrc;
570
+ });
571
+ <% } %>
572
+
326
573
  lightbox.on('uiRegister', function() {
327
574
  lightbox.pswp.ui.registerElement({
328
575
  name: 'custom-caption',
@@ -336,7 +583,7 @@ const seriesInfo = getSeriesInfo(page?.series);
336
583
  let captionHTML = '';
337
584
  if (currSlideElement) {
338
585
  const gap = (window.innerHeight - lightbox.pswp.currSlide.data.height / lightbox.pswp.currSlide.data.width * window.innerWidth) / 2;
339
- captionHTML = getExif(lightbox.pswp.currSlide.data?.element) || ''
586
+ captionHTML = getExif(lightbox.pswp.currSlide.data && lightbox.pswp.currSlide.data.element) || '';
340
587
  if (window.innerWidth <= 600) {
341
588
  el.style.top = `${gap}px`;
342
589
  el.classList.add('mobile');
@@ -355,18 +602,109 @@ const seriesInfo = getSeriesInfo(page?.series);
355
602
  isPswpOpening = true;
356
603
  });
357
604
 
605
+ lightbox.on('openingAnimationEnd', () => {
606
+ revealOpeningLivePhotoTrigger();
607
+ });
608
+
609
+ lightbox.on('closingAnimationStart', () => {
610
+ prepareCurrentLivePhotoClose();
611
+ });
612
+
613
+ lightbox.on('closingAnimationEnd', () => {
614
+ setPswpLivePhotoClosing(false);
615
+ window.requestAnimationFrame(restorePageLivePhotoTriggers);
616
+ });
617
+
358
618
  lightbox.on('change', (e) => {
619
+ pauseLivePhotos();
620
+ const currentElement = getCurrentGalleryElement();
621
+ syncSwitcherWithGalleryElement(currentElement);
359
622
  if (isPswpOpening) {
623
+ setPswpLivePhotoOpening(isLivePhotoGalleryElement(currentElement));
360
624
  return isPswpOpening = false;
361
625
  }
362
- const currentImage = lightbox?.pswp?.currSlide?.data?.element;
363
- currentImage && currentImage.scrollIntoView({
364
- block: 'center',
365
- behavior: 'instant'
366
- })
626
+ setPswpLivePhotoOpening(false);
627
+ const currentImage = getScrollTarget(currentElement);
628
+ if (currentImage && currentImage.scrollIntoView) {
629
+ currentImage.scrollIntoView({
630
+ block: 'center',
631
+ behavior: 'instant'
632
+ });
633
+ }
634
+ });
635
+
636
+ lightbox.on('close', () => {
637
+ prepareCurrentLivePhotoClose();
638
+ pauseLivePhotos();
367
639
  });
640
+
368
641
  lightbox.init();
642
+
643
+ document.addEventListener('pointerdown', (event) => {
644
+ if (event.target.closest('.pswp-live-photo-trigger')) {
645
+ event.stopPropagation();
646
+ }
647
+ });
648
+
649
+ document.addEventListener('click', async (event) => {
650
+ const trigger = event.target.closest('.pswp-live-photo-trigger');
651
+ if (!trigger) {
652
+ return;
653
+ }
654
+
655
+ event.preventDefault();
656
+ event.stopPropagation();
657
+
658
+ const livePhoto = trigger.closest('.pswp-live-photo-wrap');
659
+ const video = livePhoto && livePhoto.querySelector && livePhoto.querySelector('.pswp-live-photo-video');
660
+ if (!video) {
661
+ return;
662
+ }
663
+
664
+ if (video.paused) {
665
+ video.volume = Number(livePhoto.dataset.volume || 100) / 100;
666
+ await video.play();
667
+ livePhoto.classList.add('is-playing');
668
+ } else {
669
+ video.pause();
670
+ livePhoto.classList.remove('is-playing');
671
+ }
672
+ });
673
+
674
+ document.addEventListener('pause', (event) => {
675
+ if (event.target.matches('.pswp-live-photo-video')) {
676
+ const livePhotoWrap = event.target.closest('.pswp-live-photo-wrap');
677
+ if (livePhotoWrap) {
678
+ livePhotoWrap.classList.remove('is-playing');
679
+ }
680
+ }
681
+ }, true);
682
+
683
+ window.addEventListener('message', (event) => {
684
+ if (event.origin !== location.origin) {
685
+ return;
686
+ }
687
+
688
+ if (lightbox.pswp) {
689
+ return;
690
+ }
691
+
692
+ const messageUrl = typeof event.data === 'string' ? event.data : (event.data && event.data.picUrl);
693
+ if (!messageUrl) {
694
+ return;
695
+ }
696
+
697
+ const targetUrl = normalizeUrl(messageUrl);
698
+ const target = Array.from(document.querySelectorAll('#post a.gallery-item')).find((element) => {
699
+ const livePhotoPicUrl = element.dataset.livePhotoPicUrl;
700
+ return normalizeUrl(livePhotoPicUrl || element.href) === targetUrl;
701
+ });
702
+
703
+ if (target) {
704
+ target.click();
705
+ }
706
+ });
369
707
  </script>
370
708
  <% } %>
371
709
 
372
- <%-partial('partials/related-posts')%>
710
+ <%-partial('partials/related-posts')%>