| 
									
										
										
										
											2022-06-09 12:51:19 +02:00
										 |  |  | /* | 
					
						
							|  |  |  | 	GoToSocial | 
					
						
							| 
									
										
										
										
											2023-03-12 18:49:06 +01:00
										 |  |  | 	Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | 	SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							| 
									
										
										
										
											2022-06-09 12:51:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | 	it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  | 	the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | 	(at your option) any later version. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | 	GNU Affero General Public License for more details. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  | 	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-07 17:58:01 +02:00
										 |  |  | const Photoswipe = require("photoswipe/dist/umd/photoswipe.umd.min.js"); | 
					
						
							|  |  |  | const PhotoswipeLightbox = require("photoswipe/dist/umd/photoswipe-lightbox.umd.min.js"); | 
					
						
							|  |  |  | const PhotoswipeCaptionPlugin = require("photoswipe-dynamic-caption-plugin").default; | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | const Plyr = require("plyr"); | 
					
						
							| 
									
										
										
										
											2023-12-27 11:23:52 +01:00
										 |  |  | const Prism = require("./prism.js"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Prism.manual = true; | 
					
						
							|  |  |  | Prism.highlightAll(); | 
					
						
							| 
									
										
										
										
											2022-08-07 17:58:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 10:12:16 +02:00
										 |  |  | const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-03 20:35:43 +02:00
										 |  |  | let [_, _user, type, id] = window.location.pathname.split("/"); | 
					
						
							|  |  |  | if (type == "statuses") { | 
					
						
							|  |  |  | 	let firstStatus = document.getElementsByClassName("thread")[0].children[0]; | 
					
						
							|  |  |  | 	if (firstStatus.id != id) { | 
					
						
							|  |  |  | 		document.getElementById(id).scrollIntoView(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-07 17:58:01 +02:00
										 |  |  | const lightbox = new PhotoswipeLightbox({ | 
					
						
							|  |  |  | 	gallery: '.photoswipe-gallery', | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 	children: '.photoswipe-slide', | 
					
						
							| 
									
										
										
										
											2022-08-07 17:58:01 +02:00
										 |  |  | 	pswpModule: Photoswipe, | 
					
						
							| 
									
										
										
										
											2025-03-26 16:59:39 +01:00
										 |  |  | 	// Bit darker than default 0.8.
 | 
					
						
							|  |  |  | 	bgOpacity: 0.9, | 
					
						
							|  |  |  | 	loop: false, | 
					
						
							| 
									
										
										
										
											2022-08-07 17:58:01 +02:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | new PhotoswipeCaptionPlugin(lightbox, { | 
					
						
							|  |  |  | 	type: 'auto', | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 	captionContent(slide) { | 
					
						
							|  |  |  | 		return slide.data.alt; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | lightbox.addFilter('itemData', (item) => { | 
					
						
							|  |  |  | 	const el = item.element; | 
					
						
							| 
									
										
										
										
											2024-08-08 10:12:16 +02:00
										 |  |  | 	if ( | 
					
						
							|  |  |  | 		el && | 
					
						
							|  |  |  | 		el.classList.contains("plyr-video") && | 
					
						
							|  |  |  | 		el._plyrContainer !== undefined | 
					
						
							|  |  |  | 	) { | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 		const parentNode = el._plyrContainer.parentNode; | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 		const loopingAuto = el.classList.contains("gifv"); | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 		return { | 
					
						
							|  |  |  | 			alt: el.getAttribute("alt"), | 
					
						
							|  |  |  | 			_video: { | 
					
						
							|  |  |  | 				open(c) { | 
					
						
							|  |  |  | 					c.appendChild(el._plyrContainer); | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 					if (loopingAuto) { | 
					
						
							|  |  |  | 						// Start playing
 | 
					
						
							|  |  |  | 						// when opened.
 | 
					
						
							|  |  |  | 						el._player.play(); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				close() { | 
					
						
							|  |  |  | 					parentNode.appendChild(el._plyrContainer); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				pause() { | 
					
						
							|  |  |  | 					el._player.pause(); | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				play() { | 
					
						
							|  |  |  | 					el._player.play(); | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			width: parseInt(el.dataset.pswpWidth), | 
					
						
							| 
									
										
										
										
											2025-03-26 16:59:39 +01:00
										 |  |  | 			height: parseInt(el.dataset.pswpHeight), | 
					
						
							|  |  |  | 			parentStatus: el.dataset.pswpParentStatus, | 
					
						
							|  |  |  | 			attachmentId: el.dataset.pswpAttachmentId, | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 			loopingAuto: loopingAuto, | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return item; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | // Open video when user moves to its slide.
 | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | lightbox.on("contentActivate", (e) => { | 
					
						
							|  |  |  | 	const { content } = e; | 
					
						
							|  |  |  | 	if (content.data._video != undefined) { | 
					
						
							|  |  |  | 		content.data._video.open(content.element); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | // Pause + close video when user
 | 
					
						
							|  |  |  | // moves away from its slide.
 | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | lightbox.on("contentDeactivate", (e) => { | 
					
						
							|  |  |  | 	const { content } = e; | 
					
						
							|  |  |  | 	if (content.data._video != undefined) { | 
					
						
							|  |  |  | 		content.data._video.pause(); | 
					
						
							|  |  |  | 		content.data._video.close(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | // Pause video when lightbox is closed.
 | 
					
						
							|  |  |  | lightbox.on("closingAnimationStart", function () { | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 	if (lightbox.pswp.currSlide.data._video != undefined) { | 
					
						
							|  |  |  | 		lightbox.pswp.currSlide.data._video.close(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-08-07 17:58:01 +02:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | lightbox.on("close", function () { | 
					
						
							|  |  |  | 	if (lightbox.pswp.currSlide.data._video != undefined && | 
					
						
							|  |  |  | 		!lightbox.pswp.currSlide.data.loopingAuto) { | 
					
						
							|  |  |  | 		lightbox.pswp.currSlide.data._video.pause(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2022-08-07 17:58:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | // Open video when lightbox is opened.
 | 
					
						
							|  |  |  | lightbox.on("openingAnimationEnd", function () { | 
					
						
							|  |  |  | 	if (lightbox.pswp.currSlide.data._video != undefined) { | 
					
						
							|  |  |  | 		lightbox.pswp.currSlide.data._video.play(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Add "open this post" link to lightbox UI.
 | 
					
						
							| 
									
										
										
										
											2025-03-26 16:59:39 +01:00
										 |  |  | lightbox.on('uiRegister', function() { | 
					
						
							|  |  |  | 	lightbox.pswp.ui.registerElement({ | 
					
						
							|  |  |  | 		name: 'open-post-link', | 
					
						
							|  |  |  | 		ariaLabel: 'Open post', | 
					
						
							|  |  |  | 		order: 8, | 
					
						
							|  |  |  | 		isButton: true, | 
					
						
							|  |  |  | 		tagName: "a", | 
					
						
							|  |  |  | 		html: '<span title="Open post"><span class="sr-only">Open post</span><i class="fa fa-lg fa-external-link-square" aria-hidden="true"></i></span>', | 
					
						
							|  |  |  | 		onInit: (el, pswp) => { | 
					
						
							|  |  |  | 			el.setAttribute('target', '_blank'); | 
					
						
							|  |  |  | 			el.setAttribute('rel', 'noopener'); | 
					
						
							|  |  |  | 			pswp.on('change', () => { | 
					
						
							|  |  |  | 				el.href = pswp.currSlide.data.parentStatus | 
					
						
							|  |  |  | 					? pswp.currSlide.data.parentStatus | 
					
						
							|  |  |  | 					: pswp.currSlide.data.element.dataset.pswpParentStatus; | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		  } | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-07 17:58:01 +02:00
										 |  |  | lightbox.init(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | function dynamicSpoiler(className, updateFunc) { | 
					
						
							|  |  |  | 	Array.from(document.getElementsByClassName(className)).forEach((spoiler) => { | 
					
						
							|  |  |  | 		const update = updateFunc(spoiler); | 
					
						
							|  |  |  | 		if (update) { | 
					
						
							|  |  |  | 			update(); | 
					
						
							|  |  |  | 			spoiler.addEventListener("toggle", update); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | dynamicSpoiler("text-spoiler", (spoiler) => { | 
					
						
							| 
									
										
										
										
											2023-05-12 13:50:37 +02:00
										 |  |  | 	const button = spoiler.querySelector(".button"); | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-12 13:50:37 +02:00
										 |  |  | 	return () => { | 
					
						
							|  |  |  | 		button.textContent = spoiler.open | 
					
						
							|  |  |  | 			? "Show less" | 
					
						
							|  |  |  | 			: "Show more"; | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-12 13:50:37 +02:00
										 |  |  | dynamicSpoiler("media-spoiler", (spoiler) => { | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 	const eye = spoiler.querySelector(".eye.button"); | 
					
						
							| 
									
										
										
										
											2023-05-12 13:50:37 +02:00
										 |  |  | 	const video = spoiler.querySelector(".plyr-video"); | 
					
						
							| 
									
										
										
										
											2024-08-08 10:12:16 +02:00
										 |  |  | 	const loopingAuto = !reduceMotion.matches && video != null && video.classList.contains("gifv"); | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-12 13:50:37 +02:00
										 |  |  | 	return () => { | 
					
						
							|  |  |  | 		if (spoiler.open) { | 
					
						
							|  |  |  | 			eye.setAttribute("aria-label", "Hide media"); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			eye.setAttribute("aria-label", "Show media"); | 
					
						
							| 
									
										
										
										
											2024-08-08 10:12:16 +02:00
										 |  |  | 			if (video && !loopingAuto) { | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 				video.pause(); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-05-12 13:50:37 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Array.from(document.getElementsByClassName("plyr-video")).forEach((video) => { | 
					
						
							| 
									
										
										
										
											2024-08-08 10:12:16 +02:00
										 |  |  | 	const loopingAuto = !reduceMotion.matches && video.classList.contains("gifv"); | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 	let player = new Plyr(video, { | 
					
						
							|  |  |  | 		title: video.title, | 
					
						
							| 
									
										
										
										
											2025-03-26 16:59:39 +01:00
										 |  |  | 		settings: [], | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 		// Only show controls for video and audio,
 | 
					
						
							|  |  |  | 		// not looping soundless gifv. Don't show
 | 
					
						
							|  |  |  | 		// volume slider as it's unusable anyway
 | 
					
						
							|  |  |  | 		// when the video is inside a lightbox,
 | 
					
						
							|  |  |  | 		// mute toggle will have to be enough.
 | 
					
						
							|  |  |  | 		controls: loopingAuto | 
					
						
							|  |  |  | 			? [] | 
					
						
							|  |  |  | 			: [ | 
					
						
							|  |  |  | 				'play-large',   // The large play button in the center
 | 
					
						
							|  |  |  | 				'restart',      // Restart playback
 | 
					
						
							|  |  |  | 				'rewind',       // Rewind by the seek time (default 10 seconds)
 | 
					
						
							|  |  |  | 				'play',         // Play/pause playback
 | 
					
						
							|  |  |  | 				'fast-forward', // Fast forward by the seek time (default 10 seconds)
 | 
					
						
							|  |  |  | 				'current-time', // The current time of playback
 | 
					
						
							|  |  |  | 				'duration',     // The full duration of the media
 | 
					
						
							|  |  |  | 				'mute',         // Toggle mute
 | 
					
						
							|  |  |  | 				'fullscreen',   // Toggle fullscreen
 | 
					
						
							|  |  |  | 			], | 
					
						
							| 
									
										
										
										
											2025-03-26 16:59:39 +01:00
										 |  |  | 		tooltips: { controls: true, seek: true }, | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 		iconUrl: "/assets/plyr.svg", | 
					
						
							| 
									
										
										
										
											2025-03-26 16:59:39 +01:00
										 |  |  | 		invertTime: false, | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 		hideControls: false, | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 		listeners: { | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 			play: (_) => { | 
					
						
							|  |  |  | 				if (!inLightbox(video)) { | 
					
						
							|  |  |  | 					// If the video isn't open in the lightbox
 | 
					
						
							|  |  |  | 					// as the current photoswipe slide, clicking
 | 
					
						
							|  |  |  | 					// on it to play it opens it in the lightbox.
 | 
					
						
							| 
									
										
										
										
											2025-03-26 16:59:39 +01:00
										 |  |  | 					lightbox.loadAndOpen(parseInt(video.dataset.pswpIndex), { | 
					
						
							|  |  |  | 						gallery: video.closest(".photoswipe-gallery") | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 				} else if (!loopingAuto) { | 
					
						
							|  |  |  | 					// If the video *is* open in the lightbox,
 | 
					
						
							|  |  |  | 					// and it's not a looping gifv, clicking
 | 
					
						
							|  |  |  | 					// play just plays or pauses the video.
 | 
					
						
							|  |  |  | 					player.togglePlay(); | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-06-09 12:51:19 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2022-12-17 05:38:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 17:46:32 +02:00
										 |  |  | 	player.elements.container.title = video.title; | 
					
						
							|  |  |  | 	video._player = player; | 
					
						
							|  |  |  | 	video._plyrContainer = player.elements.container; | 
					
						
							| 
									
										
										
										
											2022-06-09 12:51:19 +02:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2025-03-01 18:41:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 15:51:17 +02:00
										 |  |  | // Return true if the photoswipe lightbox is
 | 
					
						
							|  |  |  | // open with this element as the current slide.
 | 
					
						
							|  |  |  | function inLightbox(element) { | 
					
						
							|  |  |  | 	if (lightbox.pswp === undefined) { | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (lightbox.pswp.currSlide === undefined) { | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return element.dataset.pswpAttachmentId === | 
					
						
							|  |  |  | 		lightbox.pswp.currSlide.data.attachmentId; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-02 11:27:30 +01:00
										 |  |  | Array.from(document.getElementsByTagName('time')).forEach(timeTag => { | 
					
						
							|  |  |  | 	const datetime = timeTag.getAttribute('datetime'); | 
					
						
							|  |  |  | 	const currentText = timeTag.textContent.trim(); | 
					
						
							|  |  |  | 	// Only format if current text contains precise time.
 | 
					
						
							|  |  |  | 	if (currentText.match(/\d{2}:\d{2}/)) { | 
					
						
							|  |  |  | 		const date = new Date(datetime); | 
					
						
							|  |  |  | 		timeTag.textContent = date.toLocaleString( | 
					
						
							|  |  |  | 			undefined, | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2025-03-01 18:41:32 +08:00
										 |  |  | 				year: 'numeric', | 
					
						
							|  |  |  | 				month: 'short', | 
					
						
							|  |  |  | 				day: '2-digit', | 
					
						
							|  |  |  | 				hour: '2-digit', | 
					
						
							|  |  |  | 				minute: '2-digit', | 
					
						
							|  |  |  | 				hour12: false | 
					
						
							| 
									
										
										
										
											2025-03-02 11:27:30 +01:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-03-01 18:41:32 +08:00
										 |  |  | }); |