PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0
Elementor Website Builder – more than just a page builder v4.3.0
4.3.2 4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 All 455 releases
elementor / modules / atomic-widgets / elements / atomic-background-video / handlers / background-video-handler.js

background-video-handler.js in Elementor Website Builder – more than just a page builder 4.3.0, at modules/atomic-widgets/elements/atomic-background-video/handlers/background-video-handler.js

160 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { register } from '@elementor/frontend-handlers';
2 import { Alpine } from '@elementor/alpinejs';
3 import { getEditorState, isEditorPreview, resolveDesignTimeState } from './editor-background-video-state';
4
5 const PLAYING_CLASS = 'e--playing';
6 const PAUSED_CLASS = 'e--paused';
7 // Functional state classes on the root element that drive play/pause button visibility via CSS.
8 // The root re-renders (client-side twig) on every settings change and always carries the correct
9 // class, so button visibility survives the editor tearing down and re-creating the child DOM nodes.
10 const ROOT_PLAYING_CLASS = 'e-background-video--playing';
11 const ROOT_PAUSED_CLASS = 'e-background-video--paused';
12 const PLAY_ELEMENT_TYPE = 'e-background-video-play';
13 const PAUSE_ELEMENT_TYPE = 'e-background-video-pause';
14 const CONTROLS_ELEMENT_TYPE = 'e-background-video-controls';
15
16 function applyVideoSettings( video, settings ) {
17 if ( ! video ) {
18 return;
19 }
20
21 video.muted = Boolean( settings.mute );
22 video.loop = Boolean( settings.loop );
23 video.autoplay = Boolean( settings.autoplay );
24
25 if ( settings.start_time ) {
26 video.currentTime = Number( settings.start_time );
27 }
28
29 if ( settings.autoplay ) {
30 video.play().catch( () => {} );
31 }
32 }
33
34 // Enforces the user-defined end_time boundary during playback. Browsers apply the `#t=start,end`
35 // media fragment inconsistently, and with `loop` enabled they typically ignore the end boundary,
36 // so we mirror the legacy background-video behavior and clamp playback in JS.
37 function enforceEndTime( video, settings, signal ) {
38 if ( ! video ) {
39 return;
40 }
41
42 const endTime = Number( settings.end_time );
43 const startTime = Number( settings.start_time ) || 0;
44
45 if ( ! endTime || endTime <= startTime ) {
46 return;
47 }
48
49 video.addEventListener( 'timeupdate', () => {
50 if ( video.currentTime < endTime ) {
51 return;
52 }
53
54 if ( settings.loop ) {
55 video.currentTime = startTime;
56 return;
57 }
58
59 video.pause();
60 }, { signal } );
61 }
62
63 register( {
64 elementType: 'e-background-video',
65 id: 'e-background-video-handler',
66 callback: ( { element, settings, signal } ) => {
67 const elementId = element.dataset.id;
68 const video = element.querySelector( '.e-background-video__media' );
69
70 applyVideoSettings( video, settings );
71 enforceEndTime( video, settings, signal );
72
73 Alpine.data( `eBackgroundVideo${ elementId }`, () => ( {
74 isPlaying: video ? ! video.paused : false,
75 isEditor: isEditorPreview(),
76 get editorState() {
77 return getEditorState( elementId, resolveDesignTimeState( settings.state ) );
78 },
79
80 get previewState() {
81 if ( this.isEditor ) {
82 return this.editorState;
83 }
84
85 return this.isPlaying ? 'playing' : 'paused';
86 },
87 get showPlayButton() {
88 return 'paused' === this.previewState;
89 },
90 get showPauseButton() {
91 return 'playing' === this.previewState;
92 },
93 play() {
94 // In the editor, playback state is design-time and controlled by the States
95 // toggle in the panel; the on-canvas buttons are visual only, so ignore clicks
96 // to avoid pausing/resuming the actual <video> while editing.
97 if ( this.isEditor || ! video ) {
98 return;
99 }
100
101 video.play().catch( () => {} );
102 },
103 pause() {
104 if ( this.isEditor || ! video ) {
105 return;
106 }
107
108 video.pause();
109 },
110 init() {
111 if ( video ) {
112 video.addEventListener( 'play', () => {
113 this.isPlaying = true;
114 }, { signal } );
115 video.addEventListener( 'pause', () => {
116 this.isPlaying = false;
117 }, { signal } );
118 }
119 },
120 rootState: {
121 ':class'() {
122 // In the editor the state is design-time and rendered onto the root by twig on
123 // every re-render; let that class stand rather than fighting it with a possibly
124 // stale Alpine value. On the frontend, drive it from real playback.
125 if ( this.isEditor ) {
126 return {};
127 }
128
129 return {
130 [ ROOT_PLAYING_CLASS ]: this.isPlaying,
131 [ ROOT_PAUSED_CLASS ]: ! this.isPlaying,
132 };
133 },
134 },
135 controlsWrapper: {
136 ':class'() {
137 return {
138 [ PLAYING_CLASS ]: 'playing' === this.previewState,
139 [ PAUSED_CLASS ]: 'paused' === this.previewState,
140 };
141 },
142 },
143 playButton: {
144 '@click'() {
145 this.play();
146 },
147 },
148 pauseButton: {
149 '@click'() {
150 this.pause();
151 },
152 },
153 } ) );
154
155 return () => {};
156 },
157 } );
158
159 export { PLAY_ELEMENT_TYPE, PAUSE_ELEMENT_TYPE, CONTROLS_ELEMENT_TYPE };
160