PageSourceSearch

https://www.nodeflip.com/js/video-magnifier.js

js nodeflip.com collected 2026-10-02 00:53:51 UTC 4,646 bytes, 164 lines download raw bytes

1/**
2 * Video Magnifier - Click-to-zoom functionality for videos
3 * Similar to image magnifier but handles video elements
4 */
5
6class VideoMagnifier {
7  constructor() {
8    this.modal = null;
9    this.init();
10  }
11
12  init() {
13    // Wait for DOM to be ready and videos to be loaded
14    if (document.readyState === 'loading') {
15      document.addEventListener('DOMContentLoaded', () => {
16        // Give videos time to initialize
17        setTimeout(() => this.setup(), 100);
18      });
19    } else {
20      // Give videos time to initialize
21      setTimeout(() => this.setup(), 100);
22    }
23  }
24
25  setup() {
26    this.createModal();
27    this.setupVideos();
28    this.bindEvents();
29  }
30
31  createModal() {
32    // Create modal elements
33    this.modal = document.createElement('div');
34    this.modal.className = 'video-magnify-modal';
35    
36    const closeBtn = document.createElement('div');
37    closeBtn.className = 'video-magnify-close';
38    closeBtn.innerHTML = '×';
39    closeBtn.setAttribute('title', 'Close (Press Esc)');
40    
41    const video = document.createElement('video');
42    video.className = 'video-magnify-video';
43    video.controls = true;
44    video.autoplay = true;
45    video.loop = true;
46    video.muted = true;
47    video.playsInline = true;
48    
49    const caption = document.createElement('div');
50    caption.className = 'video-magnify-caption';
51    
52    this.modal.appendChild(closeBtn);
53    this.modal.appendChild(video);
54    this.modal.appendChild(caption);
55    
56    document.body.appendChild(this.modal);
57  }
58
59  setupVideos() {
60    // Find all videos that should be magnifiable
61    const videos = document.querySelectorAll('video');
62    
63    videos.forEach(video => {
64      // Skip if already processed
65      if (video.hasAttribute('data-magnify-enabled')) {
66        return;
67      }
68      
69      console.log('Setting up video magnifier for:', video.src || video.querySelector('source')?.src);
70      console.log('Video loop attribute:', video.hasAttribute('loop'));
71      console.log('Video autoplay attribute:', video.hasAttribute('autoplay'));
72      console.log('Video paused state:', video.paused);
73      
74      // Mark as processed
75      video.setAttribute('data-magnify-enabled', 'true');
76      
77      // Add click handler only - no DOM manipulation
78      video.addEventListener('click', (e) => {
79        e.preventDefault();
80        e.stopPropagation();
81        this.showMagnified(e, video);
82      });
83      
84      console.log('Video magnifier enabled - video should continue natural behavior');
85    });
86  }
87
88  wrapVideo(video) {
89    const container = document.createElement('div');
90    container.className = 'video-magnify-container';
91    
92    const overlay = document.createElement('div');
93    overlay.className = 'video-magnify-overlay';
94    
95    // Insert container before video
96    video.parentNode.insertBefore(container, video);
97    
98    // Move video into container
99    container.appendChild(video);
100    container.appendChild(overlay);
101  }
102
103  showMagnified(event, video) {
104    event.preventDefault();
105    event.stopPropagation();
106    
107    const modalVideo = this.modal.querySelector('.video-magnify-video');
108    const caption = this.modal.querySelector('.video-magnify-caption');
109    
110    // Copy video source
111    modalVideo.innerHTML = video.innerHTML;
112    
113    // Set caption from video title or alt attribute
114    const captionText = video.getAttribute('title') || video.getAttribute('alt') || 'Video preview';
115    caption.textContent = captionText;
116    
117    // Show modal
118    this.modal.classList.add('active');
119    document.body.style.overflow = 'hidden';
120    
121    // Start playing the modal video
122    modalVideo.load();
123    modalVideo.play().catch(e => {
124      console.log('Video autoplay prevented:', e);
125    });
126    
127    // Focus for keyboard accessibility
128    this.modal.focus();
129  }
130
131  hideMagnified() {
132    const modalVideo = this.modal.querySelector('.video-magnify-video');
133    
134    // Pause the modal video
135    modalVideo.pause();
136    
137    this.modal.classList.remove('active');
138    document.body.style.overflow = '';
139  }
140
141  bindEvents() {
142    // Close button click
143    this.modal.querySelector('.video-magnify-close').addEventListener('click', () => {
144      this.hideMagnified();
145    });
146    
147    // Modal background click
148    this.modal.addEventListener('click', (e) => {
149      if (e.target === this.modal) {
150        this.hideMagnified();
151      }
152    });
153    
154    // Escape key
155    document.addEventListener('keydown', (e) => {
156      if (e.key === 'Escape' && this.modal.classList.contains('active')) {
157        this.hideMagnified();
158      }
159    });
160  }
161}
162
163// Initialize when script loads
164new VideoMagnifier();

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.