PageSourceSearch

https://www.oslohostel.com/modules/custom/common/wx_vendor_libraries/js/video.uploaded.controls.js?tlv33l

js oslohostel.com collected 2026-09-26 16:42:14 UTC 10,721 bytes, 295 lines download raw bytes

1/*----------------------------------------------------
2 * Description: Video controls for uploaded videos
3 * attach_library('wx_vendor_libraries/videoControls')
4 * For youtube video controls see library 'youtube_background/video'
5 * ----------------------------------------------------*/
6(function(Drupal, drupalSettings) {
7  'use strict';
8
9  function handleUploadedVideo(videoContainer, uploadedVideo) {
10    // Handle unmuting videos with not-muted class
11    if (videoContainer.classList.contains('not-muted')) {
12      const videoTags = videoContainer.querySelectorAll('video');
13      videoTags.forEach((video) => {
14        video.muted = false;
15        video.removeAttribute('muted');
16      });
17    }
18
19    // Add controls to videos if class .show-native-controls is present
20    // Override other custom settings such as rendered image on pause and custom play/pause buttons
21    if (videoContainer.classList.contains('show-native-controls')) {
22      const videoTags = videoContainer.querySelectorAll('video');
23      const shouldStartPausedWithControls = videoContainer.classList.contains('pausedAllDevices');
24      const hasImageOverlay = uploadedVideo.classList.contains('puasedImageRendered');
25
26      videoTags.forEach((video) => {
27
28        if (shouldStartPausedWithControls) {
29          video.pause();
30          uploadedVideo.classList.add('paused');
31        }
32
33        video.setAttribute('controls', 'controls');
34
35        // If there's an image overlay and video should start paused, make controls non-focusable
36        if (shouldStartPausedWithControls && hasImageOverlay) {
37          video.setAttribute('tabindex', '-1');
38        }
39
40        // Start video when clicking on the YTPlayer-play button on top of image
41        const playButton = uploadedVideo.querySelector('.YTPlayer-play');
42        if (playButton) {
43          playButton.addEventListener('click', function(event) {
44            video.play();
45            uploadedVideo.classList.remove('paused');
46            uploadedVideo.classList.add('playing');
47
48            // Make video controls focusable again when video starts playing
49            if (hasImageOverlay) {
50              video.removeAttribute('tabindex');
51            }
52          });
53        }
54
55        // Also handle when video is played programmatically or by other means
56        video.addEventListener('play', function() {
57          if (hasImageOverlay) {
58            video.removeAttribute('tabindex');
59          }
60        });
61
62        // Make controls non-focusable again when video is paused and image overlay should show
63        video.addEventListener('pause', function() {
64          if (hasImageOverlay && uploadedVideo.classList.contains('paused')) {
65            video.setAttribute('tabindex', '-1');
66          }
67        });
68      });
69      return;
70    }
71
72    const playButton = uploadedVideo.querySelector('.YTPlayer-play');
73    const pauseButton = uploadedVideo.querySelector('.YTPlayer-pause');
74    const muteButton = uploadedVideo.querySelector('.YTPlayer-mute');
75    const unmuteButton = uploadedVideo.querySelector('.YTPlayer-unmute');
76    const videoElement = uploadedVideo.querySelector('video');
77
78    if (!playButton || !pauseButton || !videoElement) return;
79
80    // Track if last interaction was via keyboard
81    let lastInteractionWasKeyboard = false;
82
83    const videoContentPosition = videoContainer.querySelector('.video-content-position');
84
85    if (videoContentPosition) {
86      const videoContent = videoContentPosition.querySelector('.video-content');
87      const interactiveElements = videoContent.querySelectorAll('a, button, input, select, textarea, [role="button"]');
88
89      if (interactiveElements.length > 0) {
90        interactiveElements.forEach((element) => {
91          element.style.position = 'relative';
92          element.style.zIndex = '21';
93        });
94      }
95    }
96
97    const shouldStartPaused = videoContainer.classList.contains('pausedAllDevices');
98
99    if (shouldStartPaused) {
100      // Ensure video starts paused
101      videoElement.pause();
102      playButton.style.display = '';
103      pauseButton.style.display = 'none';
104      uploadedVideo.classList.add('paused');
105
106      // Show placeholder image if it exists
107      const mobilePlaceholder = uploadedVideo.querySelector('.video-mobile-img');
108      if (mobilePlaceholder && uploadedVideo.classList.contains('puasedImageRendered')) {
109        mobilePlaceholder.style.opacity = '1';
110        mobilePlaceholder.style.visibility = 'visible';
111      }
112    } else {
113      // Autoplay if no pausedAllDevices class
114      // Note: This might be blocked by browsers that require user interaction
115      videoElement.play()
116        .then(() => {
117          updatePlayState(true);
118        })
119        .catch(() => {
120          // Autoplay was prevented
121          updatePlayState(false);
122        });
123    }
124
125    // Handle click on the video to toggle play/pause
126    uploadedVideo.addEventListener('click', function(event) {
127      // Skip if user clicked directly on a button
128      if (event.target.closest('.YTPlayer-play') ||
129          event.target.closest('.YTPlayer-pause') ||
130          event.target.closest('.YTPlayer-mute') ||
131          event.target.closest('.YTPlayer-unmute')) {
132        return;
133      }
134
135      // Skip if user clicked in the content area (contains links)
136      if (videoContentPosition && (event.target.closest('.video-content-position') || event.target.closest('.video-content'))) {
137        return;
138      }
139
140      togglePlayState();
141    });
142
143    // Handle play button click
144    playButton.addEventListener('click', function(event) {
145      lastInteractionWasKeyboard = event.detail === 0; // detail is 0 for keyboard events
146      videoElement.play();
147      updatePlayState(true);
148    });
149
150    // Handle pause button click
151    pauseButton.addEventListener('click', function(event) {
152      lastInteractionWasKeyboard = event.detail === 0; // detail is 0 for keyboard events
153      videoElement.pause();
154      updatePlayState(false);
155    });
156
157    // Handle hover and focus effects for pause, play and mute button visibility
158    function showUploadedVideoControlsWhenPlaying() {
159      if (!uploadedVideo.classList.contains('playing')) return;
160      pauseButton.style.opacity = '1';
161      if (muteButton) muteButton.style.opacity = '1';
162      if (unmuteButton) unmuteButton.style.opacity = '1';
163    }
164
165    uploadedVideo.addEventListener('mouseenter', showUploadedVideoControlsWhenPlaying);
166    uploadedVideo.addEventListener('focus', showUploadedVideoControlsWhenPlaying, true);
167
168    // Handle mute button click
169    if (muteButton) {
170      muteButton.addEventListener('click', function () {
171        videoElement.muted = true;
172        updateMuteState(true);
173      });
174    }
175
176    // Handle unmute button click
177    if (unmuteButton) {
178      unmuteButton.addEventListener('click', function () {
179        videoElement.muted = false;
180        updateMuteState(false);
181      });
182    }
183
184    // Toggle play/pause state
185    function togglePlayState() {
186      if (videoElement.paused) {
187        videoElement.play();
188        updatePlayState(true);
189      } else {
190        videoElement.pause();
191        updatePlayState(false);
192      }
193    }
194
195    // Update UI based on play state
196    function updatePlayState(isPlaying) {
197      if (isPlaying) {
198        uploadedVideo.classList.remove('paused');
199        uploadedVideo.classList.add('playing');
200        playButton.style.display = 'none';
201        pauseButton.classList.add('visible');
202        pauseButton.style.display = '';
203
204        // Transfer focus to pause button if needed (only for keyboard interactions)
205        if (lastInteractionWasKeyboard) {
206          pauseButton.focus();
207        }
208
209        if (muteButton && unmuteButton) {
210          muteButton.classList.add('visible');
211          unmuteButton.classList.add('visible');
212        }
213
214        // Hide video placeholder image if it exists
215        const mobilePlaceholder = uploadedVideo.querySelector('.video-mobile-img');
216        if (mobilePlaceholder) {
217          mobilePlaceholder.style.opacity = '0';
218          mobilePlaceholder.style.visibility = 'hidden';
219        }
220      } else {
221        uploadedVideo.classList.remove('playing');
222        uploadedVideo.classList.add('paused');
223        pauseButton.classList.remove('visible');
224        pauseButton.style.display = 'none';
225        playButton.style.display = '';
226
227        // Transfer focus to play button if needed (only for keyboard interactions)
228        if (lastInteractionWasKeyboard) {
229          playButton.focus();
230        }
231
232        if (muteButton && unmuteButton) {
233          muteButton.classList.remove('visible');
234          unmuteButton.classList.remove('visible');
235        }
236
237        // Show video placeholder image if it exists
238        const mobilePlaceholder = uploadedVideo.querySelector('.video-mobile-img');
239        if (mobilePlaceholder && uploadedVideo.classList.contains('puasedImageRendered')) {
240          mobilePlaceholder.style.opacity = '1';
241          mobilePlaceholder.style.visibility = 'visible';
242        }
243      }
244    }
245
246    // Update UI based on mute state
247    function updateMuteState(isMuted) {
248      if (muteButton && unmuteButton) {
249        if (isMuted) {
250          muteButton.style.display = 'none';
251          unmuteButton.style.display = '';
252          unmuteButton.focus();
253        } else {
254          muteButton.style.display = '';
255          unmuteButton.style.display = 'none';
256          muteButton.focus();
257        }
258      }
259    }
260  }
261
262  document.addEventListener('DOMContentLoaded', function () {
263    const containerSelectors = ['.node--type-video', '.paragraph--type--video', '.video-area'];
264    let videoContainers = [];
265    containerSelectors.forEach((selector) => {
266      document.querySelectorAll(selector).forEach((container) => {
267        videoContainers.push(container);
268      });
269    });
270
271    if (!videoContainers.length) return;
272
273    videoContainers.forEach(function(videoContainer) {
274      const uploadedVideos = videoContainer.querySelectorAll('.uploaded-video');
275
276      // Handle videos with .uploaded-video class
277      if (uploadedVideos.length) {
278        uploadedVideos.forEach(function(uploadedVideo) {
279          handleUploadedVideo(videoContainer, uploadedVideo);
280        });
281      }
282
283      // Handle videos without .uploaded-video class (direct video elements)
284      const directVideoElements = videoContainer.querySelectorAll('video');
285      directVideoElements.forEach(function(videoElement) {
286        // Skip if this video is already wrapped in .uploaded-video
287        if (!videoElement.closest('.uploaded-video')) {
288          // Pass the video-area container instead of parent element
289          handleUploadedVideo(videoContainer, videoContainer);
290        }
291      });
292    });
293  });
294
295})(Drupal, drupalSettings);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.