@total_onion/onion-library 3.0.49 → 3.1.0

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.
@@ -0,0 +1,240 @@
1
+ import youtubeVideo from './youtube-video.mjs';
2
+ import uploadVideo from './upload-video.mjs';
3
+ import vimeoVideo from './vimeo-video.mjs';
4
+ import {revealVideoElement, stopVideos, isWpAdmin} from './vcUtils.mjs';
5
+
6
+ export default class videoController {
7
+ constructor(parentContainer) {
8
+ const SRC_BREAKPOINT = 768;
9
+ let defaultGlobalSettings = {
10
+ srcBreakpoint: SRC_BREAKPOINT,
11
+ bodyClass: 'video-playing',
12
+ playingState: false,
13
+ playingStateClass: 'video-playing',
14
+ originDomain: globalThis.location?.origin,
15
+ enableDebugLogs: false,
16
+ isMobileSize: window.innerWidth < SRC_BREAKPOINT
17
+ };
18
+ if (parentContainer) {
19
+ this.videoContainers =
20
+ parentContainer.querySelectorAll('[data-videoid]');
21
+ }
22
+ this.videoReadyState = false;
23
+ if (videoController.instance instanceof videoController) {
24
+ videoController.instance.setVideoObjects(
25
+ this.videoReadyState,
26
+ this.videoContainers,
27
+ parentContainer
28
+ );
29
+ return videoController.instance;
30
+ }
31
+ videoController.instance = this;
32
+ this.globalSettings = defaultGlobalSettings;
33
+ this.enableDebugLogs = this.globalSettings.enableDebugLogs;
34
+ this.containerCollection = {};
35
+ this.setVideoObjects(
36
+ this.videoReadyState,
37
+ this.videoContainers,
38
+ this.triggers,
39
+ parentContainer
40
+ );
41
+ }
42
+ /**
43
+ * Method for setting up the video container objects and adding the listeners to the triggers
44
+ * @param {array} videoContainers - The video containers you want to add to the controller
45
+ * @param {array} triggers - The triggers you want to add to the controller
46
+ * @param {HTMLElement} parentContainer - The parent container of the video containers/triggers
47
+ */
48
+ setVideoObjects(videoReadyState, videoContainers, parentContainer) {
49
+ this.enableDebugLogs && console.log('Setting video Objects');
50
+ if (!videoContainers) {
51
+ this.enableDebugLogs &&
52
+ console.info(`Did not look for video containers`);
53
+ return;
54
+ }
55
+ if (videoContainers.length === 0) {
56
+ this.enableDebugLogs &&
57
+ console.info(
58
+ `could not find any video containers in ${parentContainer}`
59
+ );
60
+ return;
61
+ }
62
+
63
+ videoContainers.forEach((container) => {
64
+ if (!container) {
65
+ return;
66
+ }
67
+ const videoObject = {
68
+ videocontainer: container,
69
+ videotype: container.dataset?.videotype,
70
+ videoid: container.dataset?.videoid,
71
+ videoReadyState: videoReadyState,
72
+ videoplayer: false,
73
+ youtubeplayer: false,
74
+ vimeoplayer: false,
75
+ parentcontainer: parentContainer,
76
+ modalcontainer: '',
77
+ dataLayerPush: container.dataset.datalayerpush,
78
+ modal: Number(container.dataset?.modal),
79
+ isAdmin: isWpAdmin(),
80
+ fullscreen: Number(container.dataset?.fullscreen),
81
+ autoplay: this.setVideoAutoplay(container),
82
+ controls: Number(container.dataset?.controls),
83
+ loop: Number(container.dataset?.loop) || 0,
84
+ muted: Number(container.dataset?.muted) || 0,
85
+ sources: {
86
+ desktop: container.dataset?.desktopvideo,
87
+ mobile: container.dataset?.mobilevideo
88
+ },
89
+ instance: this,
90
+ globalSettings: this.getGlobalSettings()
91
+ };
92
+
93
+ if (this.containerCollection[videoObject.videoid]) {
94
+ let suffix = 1;
95
+ while (this.containerCollection[videoObject.videoid]) {
96
+ videoObject.videoid = videoObject.videoid + '-' + suffix;
97
+ suffix++;
98
+ }
99
+ }
100
+
101
+ let scopeEl = container.parentElement;
102
+ while (scopeEl && !scopeEl.hasAttribute('parentcontainer')) {
103
+ scopeEl = scopeEl.parentElement;
104
+ }
105
+
106
+ const triggerScope = scopeEl ?? document;
107
+ const triggers = triggerScope.querySelectorAll(
108
+ `[data-triggerid='${container.dataset?.videoid}']`
109
+ );
110
+
111
+ videoObject.triggerScope = triggerScope;
112
+
113
+ if (triggers.length === 0) {
114
+ this.enableDebugLogs &&
115
+ console.log(
116
+ `could not find any triggers in ${parentContainer}`
117
+ );
118
+ }
119
+
120
+ triggers.forEach((trigger) => {
121
+ if (trigger?.dataset?.triggerid) {
122
+ trigger.dataset.triggerid = videoObject.videoid;
123
+ trigger.addEventListener('click', () => {
124
+ this.triggerVideo(
125
+ this.containerCollection[trigger.dataset.triggerid]
126
+ );
127
+ });
128
+ } else {
129
+ this.enableDebugLogs &&
130
+ console.log(
131
+ trigger,
132
+ `was missing an id so the listener could not be attached`
133
+ );
134
+ }
135
+ });
136
+ videoObject.trigger = triggers;
137
+ this.containerCollection[videoObject.videoid] = videoObject;
138
+
139
+ if (!videoObject.videoReadyState) {
140
+ if (videoObject.autoplay == 1) {
141
+ this.loadingSpinner(videoObject);
142
+ }
143
+ if (videoObject.videotype === 'upload') {
144
+ console.log('id', videoObject.videoid);
145
+
146
+ uploadVideo.uploadedVideoInit(videoObject);
147
+ }
148
+ if (videoObject.videotype === 'youtube') {
149
+ youtubeVideo.youtubeInit(videoObject);
150
+ }
151
+ if (videoObject.videotype === 'vimeo') {
152
+ vimeoVideo.vimeoInit(videoObject);
153
+ }
154
+ }
155
+ });
156
+ }
157
+ setVideoReadyState(videoObject, isLoaded) {
158
+ videoObject.videoReadyState = isLoaded;
159
+ }
160
+ setVideoAutoplay(container) {
161
+ if (document.body.classList.contains('wp-admin')) {
162
+ return 0;
163
+ }
164
+
165
+ return this.globalSettings.isMobileSize
166
+ ? Number(container.dataset?.autoplayMobile) || 0
167
+ : Number(container.dataset?.autoplay) || 0;
168
+ }
169
+ /**
170
+ * Method to return the global settings for the video controller.
171
+ * @returns {Object} - the global settings object.
172
+ */
173
+ getGlobalSettings() {
174
+ return this.globalSettings;
175
+ }
176
+ /**
177
+ * Method to add or override the global controller settings.
178
+ * @param {Object} settings - Settings object that will be merged with the controller's existing settings.
179
+ */
180
+ setGlobalSettings(settings = {}) {
181
+ this.globalSettings = {...this.globalSettings, ...settings};
182
+ }
183
+ /**
184
+ * Method for getting the current collection of containers and triggers.
185
+ * @returns {Object} - Object container the container and triggers collections.
186
+ */
187
+ getObjects() {
188
+ const data = {
189
+ videos: this.containerCollection,
190
+ triggers: this.triggers
191
+ };
192
+ return data;
193
+ }
194
+ /**
195
+ * Method to determine which video to play and what type it is.
196
+ * @param {Object} videoObject - The video/trigger object that is used to identify which video element is being triggered.
197
+ */
198
+ triggerVideo(videoObject) {
199
+ if (!videoObject.instance) {
200
+ videoObject.instance = this;
201
+ }
202
+ if (!videoObject.globalSettings) {
203
+ videoObject.globalSettings = this.getGlobalSettings();
204
+ }
205
+ this.loadingSpinner(videoObject);
206
+ stopVideos(videoObject);
207
+ switch (videoObject.videotype) {
208
+ case 'upload':
209
+ uploadVideo.triggerUploadedVideo(videoObject);
210
+ break;
211
+ case 'youtube':
212
+ youtubeVideo.triggerYoutube(videoObject);
213
+ break;
214
+ case 'vimeo':
215
+ vimeoVideo.triggerVimeo(videoObject);
216
+ break;
217
+ default:
218
+ break;
219
+ }
220
+ }
221
+ stopAllVideos() {
222
+ stopVideos({videoid: '', instance: this}, true);
223
+ }
224
+ loadingSpinner(videoObject) {
225
+ const loadingWrapper = document.createElement('div');
226
+ loadingWrapper.className = 'loading-wrapper';
227
+ if (!videoObject.videocontainer) {
228
+ return;
229
+ }
230
+ videoObject.videocontainer.appendChild(loadingWrapper);
231
+ revealVideoElement(videoObject);
232
+
233
+ const checkVideoReadyState = setInterval(() => {
234
+ if (videoObject.videoReadyState === true) {
235
+ clearInterval(checkVideoReadyState);
236
+ videoObject.videocontainer.removeChild(loadingWrapper);
237
+ }
238
+ }, 100);
239
+ }
240
+ }
@@ -0,0 +1,251 @@
1
+ import {
2
+ hideVideoElement,
3
+ revealVideoElement,
4
+ generateModal,
5
+ resizeDebouncer,
6
+ stopVideos,
7
+ dataLayerPush,
8
+ } from "./vcUtils.mjs";
9
+
10
+ function uploadedVideoInit(videoObject) {
11
+ const {
12
+ videocontainer,
13
+ globalSettings,
14
+ fullscreen,
15
+ autoplay,
16
+ controls,
17
+ muted,
18
+ loop,
19
+ } = videoObject;
20
+ globalSettings.enableDebugLogs && console.log("running uploaded init");
21
+
22
+ const videoPlayer = videocontainer.querySelector(
23
+ ".cblvc-video-container__video-player",
24
+ );
25
+ videoObject.videoplayer = videoPlayer;
26
+ videoObject.elementType = "video";
27
+ if (loop) {
28
+ videoPlayer.setAttribute("loop", true);
29
+ }
30
+ if (controls) {
31
+ videoPlayer.setAttribute("controls", true);
32
+ }
33
+ if (muted) {
34
+ videoPlayer.setAttribute("muted", true);
35
+ }
36
+ if (autoplay) {
37
+ videoPlayer.setAttribute("autoplay", true);
38
+ }
39
+
40
+ if (fullscreen) {
41
+ videoPlayer.removeAttribute("playsinline");
42
+ }
43
+
44
+ videoPlayer.addEventListener("play", () => {
45
+ videoObject.instance.setVideoReadyState(videoObject, true);
46
+ if (!autoplay) {
47
+ stopVideos(videoObject);
48
+ }
49
+ revealVideoElement(videoObject, false);
50
+ if (videoObject.dataLayerPush) {
51
+ if (!videoPlayer.duration) {
52
+ videoPlayer.addEventListener("durationchange", () => {
53
+ dataLayerPush({ eventname: "play", videoObject });
54
+ });
55
+ } else {
56
+ dataLayerPush({ eventname: "play", videoObject });
57
+ }
58
+ }
59
+ });
60
+ videoPlayer.addEventListener("pause", (e) => {
61
+ const target = e.target;
62
+ if (videoObject.dataLayerPush) {
63
+ dataLayerPush({ eventname: "pause", videoObject });
64
+ }
65
+ });
66
+
67
+ videoPlayer.addEventListener("ended", () => {
68
+ if (document.fullscreenElement !== null) {
69
+ if (document.exitFullscreen) {
70
+ document.exitFullscreen();
71
+ }
72
+ }
73
+ hideVideoElement(videoObject);
74
+ if (videoObject.dataLayerPush) {
75
+ dataLayerPush({ eventname: "ended", videoObject });
76
+ }
77
+ });
78
+
79
+ let quarterFired = false;
80
+ let halfwayFired = false;
81
+ let threeQuartersFired = false;
82
+ let completedFired = false;
83
+ videoPlayer.addEventListener("timeupdate", () => {
84
+ if (
85
+ !quarterFired &&
86
+ videoPlayer.currentTime >= videoPlayer.duration * 0.25
87
+ ) {
88
+ quarterFired = true;
89
+ if (videoObject.dataLayerPush) {
90
+ dataLayerPush({ eventname: "progress", videoObject });
91
+ }
92
+ }
93
+ if (!halfwayFired && videoPlayer.currentTime >= videoPlayer.duration / 2) {
94
+ halfwayFired = true;
95
+ if (videoObject.dataLayerPush) {
96
+ dataLayerPush({ eventname: "progress", videoObject });
97
+ }
98
+ }
99
+ if (
100
+ !threeQuartersFired &&
101
+ videoPlayer.currentTime >= videoPlayer.duration * 0.75
102
+ ) {
103
+ threeQuartersFired = true;
104
+ if (videoObject.dataLayerPush) {
105
+ dataLayerPush({ eventname: "progress", videoObject });
106
+ }
107
+ }
108
+ if (quarterFired && !completedFired && videoPlayer.currentTime < 0.2) {
109
+ completedFired = true;
110
+ if (videoObject.dataLayerPush) {
111
+ videoObject.completed = true;
112
+ dataLayerPush({ eventname: "progress", videoObject });
113
+ }
114
+ }
115
+ });
116
+
117
+ if (videoObject.autoplay) {
118
+ triggerUploadedVideo(videoObject);
119
+ }
120
+ }
121
+ function triggerUploadedVideo(videoObject) {
122
+ const { videocontainer, modal, sources, globalSettings, isAdmin } =
123
+ videoObject;
124
+ globalSettings.enableDebugLogs && console.log("triggering upload video");
125
+
126
+ if (modal && !isAdmin) {
127
+ console.log("modal video");
128
+
129
+ globalSettings.enableDebugLogs && console.log("triggering modal");
130
+ generateModal(videoObject);
131
+ const modalVideoElement = videoObject.modalcontainer.querySelector("video");
132
+ modalVideoElement.addEventListener("play", () => {
133
+ revealVideoElement(videoObject, true);
134
+ });
135
+ modalVideoElement.addEventListener("ended", () => {
136
+ hideVideoElement(videoObject, true);
137
+ });
138
+ let currentSource = setSrc(modalVideoElement, sources, false, videoObject);
139
+ resizeDebouncer(() => {
140
+ currentSource = setSrc(
141
+ modalVideoElement,
142
+ sources,
143
+ currentSource,
144
+ videoObject,
145
+ );
146
+ });
147
+ togglePlay(videoObject);
148
+ } else {
149
+ const videoPlayer = videocontainer.querySelector(
150
+ ".cblvc-video-container__video-player",
151
+ );
152
+ globalSettings.enableDebugLogs && console.log("triggering inline video");
153
+ let currentSource = setSrc(videoPlayer, sources, false, videoObject);
154
+ resizeDebouncer(() => {
155
+ currentSource = setSrc(videoPlayer, sources, currentSource, videoObject);
156
+ });
157
+ togglePlay(videoObject);
158
+ }
159
+ }
160
+
161
+ function togglePlay(videoObject) {
162
+ const {
163
+ videocontainer,
164
+ fullscreen,
165
+ isAdmin,
166
+ modalcontainer,
167
+ modal,
168
+ muted,
169
+ autoplay,
170
+ globalSettings,
171
+ } = videoObject;
172
+ globalSettings.enableDebugLogs && console.log("running togglePlay");
173
+ let videoPlayer;
174
+ if (modal && !isAdmin) {
175
+ videoPlayer = modalcontainer.querySelector("video");
176
+ } else {
177
+ videoPlayer = videocontainer.querySelector(
178
+ ".cblvc-video-container__video-player",
179
+ );
180
+ }
181
+ if (videoPlayer.paused) {
182
+ if (autoplay) {
183
+ videoPlayer.muted = true;
184
+ }
185
+ if (fullscreen && !isAdmin) {
186
+ document.addEventListener("fullscreenchange", () => {
187
+ if (
188
+ document.fullscreenElement !== null &&
189
+ document.fullscreenElement === videoPlayer
190
+ ) {
191
+ setTimeout(() => {
192
+ videoPlayer.play();
193
+ }, 500);
194
+ }
195
+ });
196
+ if (videoPlayer.requestFullscreen) {
197
+ videoPlayer.requestFullscreen();
198
+ } else {
199
+ videoPlayer.play();
200
+ }
201
+ } else {
202
+ videoPlayer.play();
203
+ }
204
+ } else {
205
+ videoPlayer.pause();
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Set Source function :
211
+ * this function is important for mobile/desktop responsiveness as it checks whether to
212
+ * switch to the mobile or desktop video src when the page loads or
213
+ * the screen size changes.
214
+ *
215
+ * @param {HTMLElement} player - this is the selected video element
216
+ * @param {Object} sources - the mobile and desktop video source urls
217
+ * @param {string} source - this is the currently playing video source url
218
+ * @param {Object} videoObject - the current video and its various properties
219
+ */
220
+ function setSrc(player, sources, source = false, videoObject) {
221
+ const globalSettings = videoObject.instance.getGlobalSettings();
222
+ let newVideoUrl;
223
+ if (window.innerWidth >= globalSettings.srcBreakpoint) {
224
+ newVideoUrl = sources.desktop;
225
+ if (source && source !== newVideoUrl) {
226
+ player.setAttribute("src", newVideoUrl);
227
+ } else if (player.paused) {
228
+ player.setAttribute("src", newVideoUrl);
229
+ player.pause();
230
+ }
231
+ } else {
232
+ newVideoUrl = sources.mobile;
233
+ if (!newVideoUrl) {
234
+ newVideoUrl = sources.desktop;
235
+ }
236
+ if (source && source !== newVideoUrl) {
237
+ player.setAttribute("src", newVideoUrl);
238
+ } else if (player.paused) {
239
+ player.setAttribute("src", newVideoUrl);
240
+ player.pause();
241
+ }
242
+ }
243
+ return newVideoUrl;
244
+ }
245
+
246
+ const api = {
247
+ triggerUploadedVideo,
248
+ uploadedVideoInit,
249
+ };
250
+
251
+ export default api;