@total_onion/onion-library 2.0.167 → 2.0.179
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.
- package/.github/workflows/npm-publish.yml +14 -10
- package/components/block-carousel-multi-layout-v3/carousel-multi-layout-v3.scss +1 -1
- package/components/block-post-info-v3/group_6866429531436.json +2673 -821
- package/components/component-nav-menu-v3/nav-menu-v3.twig +10 -10
- package/components/fields-post-info-v3/group_6867bbe167dd2.json +1087 -1104
- package/onion-utils.mjs +700 -0
- package/package.json +1 -1
package/onion-utils.mjs
ADDED
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
/* eslint-disable consistent-return */
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
/**
|
|
4
|
+
* Function for check whether the code is running within the Wordpress admin page. Checks for wp-admin class.
|
|
5
|
+
* @returns {Boolean}
|
|
6
|
+
*/
|
|
7
|
+
export function isWpAdmin() {
|
|
8
|
+
return document.body.classList.contains("wp-admin") ? true : false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ttfb() {
|
|
12
|
+
new PerformanceObserver((entryList) => {
|
|
13
|
+
const [pageNav] = entryList.getEntriesByType("navigation");
|
|
14
|
+
const time = pageNav.responseStart;
|
|
15
|
+
console.log(`TTFB: ${time} new`);
|
|
16
|
+
pushData(time);
|
|
17
|
+
}).observe({ type: "navigation", buffered: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function pushData(time) {
|
|
21
|
+
window.dataLayer = window.dataLayer || [];
|
|
22
|
+
window.dataLayer.push({ ttfb: time });
|
|
23
|
+
console.log("ttfb sent");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* This function accepts an html element and returns the position of that element on the page.
|
|
28
|
+
*
|
|
29
|
+
* @param {htmlElement} element The element whose position you want to get.
|
|
30
|
+
* @returns {object} An object with the x and y pixel values.
|
|
31
|
+
*/
|
|
32
|
+
export function getPosition(element) {
|
|
33
|
+
const scrollElementTag = document.scrollingElement.tagName;
|
|
34
|
+
let xPosition = 0;
|
|
35
|
+
let yPosition = 0;
|
|
36
|
+
while (element) {
|
|
37
|
+
xPosition += element.offsetLeft - element.scrollLeft + element.clientLeft;
|
|
38
|
+
yPosition += element.offsetTop - element.scrollTop + element.clientTop;
|
|
39
|
+
element = element.offsetParent;
|
|
40
|
+
}
|
|
41
|
+
// Quirks mode safety - in case of missing DOCTYPE
|
|
42
|
+
if (scrollElementTag === "BODY") {
|
|
43
|
+
yPosition += document.querySelector("body").scrollTop;
|
|
44
|
+
}
|
|
45
|
+
return { x: xPosition, y: yPosition };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Adds or removes a 'valid' class to an input and button element depending on whether the value of the input is valid.
|
|
50
|
+
* @param {htmlElement} input The input element to check.
|
|
51
|
+
* @param {htmlElement} button A corresponding button to disable if invalid.
|
|
52
|
+
*/
|
|
53
|
+
export function toggleClassByValidity(input, button) {
|
|
54
|
+
try {
|
|
55
|
+
input.addEventListener("input", () => {
|
|
56
|
+
if (input.validity.valid && input.value) {
|
|
57
|
+
if (!button.classList.contains("valid")) {
|
|
58
|
+
button.classList.add("valid");
|
|
59
|
+
}
|
|
60
|
+
if (!input.classList.contains("valid")) {
|
|
61
|
+
input.classList.add("valid");
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
button.classList.remove("valid");
|
|
65
|
+
input.classList.remove("valid");
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error("jsutils.js ~ toggleClassByValidity ~ error", error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Checks to see if the css for the block has already been included in the 'criticalconfig' window object.
|
|
75
|
+
* Will return true if it is.
|
|
76
|
+
* @param {string} assetKey The assetkey string of the block.
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
79
|
+
export function inCriticalCssConfig(assetKey) {
|
|
80
|
+
if (!globalThis.criticalConfig) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (
|
|
84
|
+
globalThis.criticalConfig &&
|
|
85
|
+
globalThis.criticalConfig.indexOf(assetKey) === -1
|
|
86
|
+
) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Dynamically loads the css for the block if it has not already been included in critical css or the css property is set to false.
|
|
94
|
+
* @param {string} assetKey The assetkey string of the block.
|
|
95
|
+
* @param {object} options The options object which will at the very least contain the css property set to true.
|
|
96
|
+
* @returns {promise}
|
|
97
|
+
*/
|
|
98
|
+
export function loadCss(assetKey, options = { css: true }) {
|
|
99
|
+
const promise = new Promise((resolve) => {
|
|
100
|
+
if (options.css === true && !inCriticalCssConfig(assetKey)) {
|
|
101
|
+
import(
|
|
102
|
+
/* webpackChunkName: "[request]" */ `Assets/scss/blocks/${assetKey}.scss`
|
|
103
|
+
).then(() => resolve(true));
|
|
104
|
+
} else {
|
|
105
|
+
return resolve(true);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return promise;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Checks for browser, device and OS and then adds the relevant classes to the html tag.
|
|
113
|
+
*/
|
|
114
|
+
export function checkDevice() {
|
|
115
|
+
try {
|
|
116
|
+
const deviceAgent = navigator.userAgent.toLowerCase();
|
|
117
|
+
const htmlElement = document.querySelector("html");
|
|
118
|
+
if (
|
|
119
|
+
"ontouchstart" in globalThis &&
|
|
120
|
+
window.screen.width * window.devicePixelRatio >= 2048 &&
|
|
121
|
+
window.screen.width < window.screen.height
|
|
122
|
+
) {
|
|
123
|
+
htmlElement.classList.add("highResTabletPortrait");
|
|
124
|
+
}
|
|
125
|
+
if ("ontouchstart" in globalThis) {
|
|
126
|
+
htmlElement.classList.add("touch");
|
|
127
|
+
}
|
|
128
|
+
if (navigator.connection) {
|
|
129
|
+
htmlElement.classList.add(navigator.connection.effectiveType);
|
|
130
|
+
}
|
|
131
|
+
if (navigator.platform) {
|
|
132
|
+
let platform = navigator.platform.toLowerCase();
|
|
133
|
+
let platformArray = [platform];
|
|
134
|
+
if (platform.search("-")) {
|
|
135
|
+
platformArray = platform.split("-");
|
|
136
|
+
}
|
|
137
|
+
if (platform.search(" ")) {
|
|
138
|
+
platformArray = platform.split(" ");
|
|
139
|
+
}
|
|
140
|
+
htmlElement.classList.add(...platformArray);
|
|
141
|
+
}
|
|
142
|
+
if (deviceAgent.match(/(iphone|ipod|ipad)/)) {
|
|
143
|
+
htmlElement.classList.add("ios");
|
|
144
|
+
htmlElement.classList.add("mobile");
|
|
145
|
+
}
|
|
146
|
+
if (deviceAgent.match(/(windows)/)) {
|
|
147
|
+
htmlElement.classList.add("windows");
|
|
148
|
+
}
|
|
149
|
+
if (deviceAgent.match(/(macintosh)/)) {
|
|
150
|
+
htmlElement.classList.add("mac");
|
|
151
|
+
}
|
|
152
|
+
if (deviceAgent.match(/(android)/)) {
|
|
153
|
+
htmlElement.classList.add("android");
|
|
154
|
+
}
|
|
155
|
+
if (navigator.userAgent.search("MSIE") >= 0) {
|
|
156
|
+
htmlElement.classList.add("ie");
|
|
157
|
+
} else if (navigator.userAgent.search("Edge") >= 0) {
|
|
158
|
+
htmlElement.classList.add("edge-legacy");
|
|
159
|
+
} else if (navigator.userAgent.search("Chrome") >= 0) {
|
|
160
|
+
htmlElement.classList.add("chrome");
|
|
161
|
+
} else if (navigator.userAgent.search("Firefox") >= 0) {
|
|
162
|
+
htmlElement.classList.add("firefox");
|
|
163
|
+
} else if (
|
|
164
|
+
navigator.userAgent.search("Safari") >= 0 &&
|
|
165
|
+
navigator.userAgent.search("Chrome") < 0
|
|
166
|
+
) {
|
|
167
|
+
htmlElement.classList.add("safari");
|
|
168
|
+
} else if (navigator.userAgent.search("Opera") >= 0) {
|
|
169
|
+
htmlElement.classList.add("opera");
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check and possibly wait for an image to have downloaded before running an optional function.
|
|
178
|
+
* Returns a resolved promise when the load event has fired.
|
|
179
|
+
* @param {htmlElement} img The image element you want to wait for.
|
|
180
|
+
* @param {function} delayedFunction The optional function you want to run when/if the image has downloaded.
|
|
181
|
+
* @returns {promise}
|
|
182
|
+
*/
|
|
183
|
+
export async function waitForLoad(img, delayedFunction) {
|
|
184
|
+
const loaded = new Promise((resolve) => {
|
|
185
|
+
if (img.complete) {
|
|
186
|
+
if (typeof delayedFunction === "function") {
|
|
187
|
+
delayedFunction();
|
|
188
|
+
}
|
|
189
|
+
return resolve(true);
|
|
190
|
+
}
|
|
191
|
+
img.addEventListener(
|
|
192
|
+
"load",
|
|
193
|
+
() => {
|
|
194
|
+
if (typeof delayedFunction === "function") {
|
|
195
|
+
delayedFunction();
|
|
196
|
+
}
|
|
197
|
+
return resolve(true);
|
|
198
|
+
},
|
|
199
|
+
{ once: true }
|
|
200
|
+
);
|
|
201
|
+
}).catch((error) => console.log(error, "could not load the image", img));
|
|
202
|
+
return loaded;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Check whether the current environment supports the webp image format.
|
|
207
|
+
* Returns true if it does.
|
|
208
|
+
* @returns {boolean}
|
|
209
|
+
*/
|
|
210
|
+
export async function supportsWebp() {
|
|
211
|
+
// eslint-disable-next-line no-restricted-globals
|
|
212
|
+
if (!self.createImageBitmap) return false;
|
|
213
|
+
const webpData =
|
|
214
|
+
"data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=";
|
|
215
|
+
const blob = await fetch(webpData).then((r) => r.blob());
|
|
216
|
+
return createImageBitmap(blob).then(
|
|
217
|
+
() => true,
|
|
218
|
+
() => false
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
// ( async () => {
|
|
222
|
+
// if ( await supportsWebp() ) {
|
|
223
|
+
// return true;
|
|
224
|
+
// }
|
|
225
|
+
// return false;
|
|
226
|
+
// } )();
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* DEPRECATED!! footerIntersection will be removed in a future version.
|
|
230
|
+
*/
|
|
231
|
+
export function footerIntersection(entries, element) {
|
|
232
|
+
entries.forEach((entry) => {
|
|
233
|
+
if (element) {
|
|
234
|
+
if (entry.isIntersecting) {
|
|
235
|
+
const { height } = entry.boundingClientRect;
|
|
236
|
+
element.style.bottom = `${height}px`;
|
|
237
|
+
} else {
|
|
238
|
+
element.style.bottom = "0px";
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Dynamically retrieves the swiper css and js and returns a resolved promise when they have both been successfully fetched.
|
|
246
|
+
* @returns {promise}
|
|
247
|
+
*/
|
|
248
|
+
export function getSwiperAssetsV2(options = { css: "bundle" }) {
|
|
249
|
+
const getSwiperJs = () => import(/* webpackChunkName: 'swiper' */ "swiper");
|
|
250
|
+
|
|
251
|
+
const promisedJs = Promise.all([getSwiperJs()]).then((values) => {
|
|
252
|
+
return values;
|
|
253
|
+
});
|
|
254
|
+
return promisedJs;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Dynamically retrieves the jQuery and returns a resolved promise when it has been successfully fetched.
|
|
259
|
+
* @returns {promise}
|
|
260
|
+
*/
|
|
261
|
+
export function getJquery() {
|
|
262
|
+
if (!window.jQuery) {
|
|
263
|
+
const importCode = () =>
|
|
264
|
+
import(/* webpackChunkName: 'jquery' */ "jquery/dist/jquery.min.js");
|
|
265
|
+
return importCode();
|
|
266
|
+
}
|
|
267
|
+
return Promise.resolve();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Dynamically retrieves GSAP and the scrolltrigger plugin and returns a resolved promise when they have both been successfully fetched.
|
|
272
|
+
* @returns {promise}
|
|
273
|
+
*/
|
|
274
|
+
export function getGsap() {
|
|
275
|
+
const gsapCore = () =>
|
|
276
|
+
import("gsap/dist/gsap.min.js").then((gsapObj) => {
|
|
277
|
+
const { gsap } = gsapObj;
|
|
278
|
+
return gsap;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const gsapPlugin = () =>
|
|
282
|
+
import("gsap/dist/ScrollTrigger.min.js").then((scrollObj) => {
|
|
283
|
+
const ScrollTrigger = scrollObj;
|
|
284
|
+
return ScrollTrigger;
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const prom = Promise.all([gsapCore(), gsapPlugin()]).then((values) => {
|
|
288
|
+
return values;
|
|
289
|
+
});
|
|
290
|
+
return prom;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Returns true if the device is a tablet device.
|
|
295
|
+
* @param {*} userAgent Pass the value of navigator.userAgent.
|
|
296
|
+
* @returns {boolean}
|
|
297
|
+
*/
|
|
298
|
+
export function isTablet(userAgent) {
|
|
299
|
+
return /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/.test(
|
|
300
|
+
userAgent.toLowerCase()
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Returns true if the device is a mobile device. Checks navigator.userAgent||navigator.vendor||window.opera.
|
|
306
|
+
* @returns {boolean}
|
|
307
|
+
*/
|
|
308
|
+
export function mobileCheck() {
|
|
309
|
+
let check = false;
|
|
310
|
+
(function (a) {
|
|
311
|
+
if (
|
|
312
|
+
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
|
|
313
|
+
a
|
|
314
|
+
) ||
|
|
315
|
+
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
|
|
316
|
+
a.substr(0, 4)
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
check = true;
|
|
320
|
+
})(navigator.userAgent || navigator.vendor || window.opera);
|
|
321
|
+
return check;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Run a function after a window resize event, but only after the alloted time has ended.
|
|
326
|
+
* If another resize event occurs it resets the time window.
|
|
327
|
+
* @param {function} debouncedFunction The function you want to run after a window resize event.
|
|
328
|
+
* @param {number} time The time in ms.
|
|
329
|
+
* @param {boolean} ignoreVertical Set to true if you only want to listen for horizontal resizing events.
|
|
330
|
+
*/
|
|
331
|
+
export function resizeDebouncer(
|
|
332
|
+
debouncedFunction,
|
|
333
|
+
time = 250,
|
|
334
|
+
ignoreVertical = false
|
|
335
|
+
) {
|
|
336
|
+
let resizeTimer;
|
|
337
|
+
let screenWidth = window.innerWidth;
|
|
338
|
+
window.addEventListener("resize", () => {
|
|
339
|
+
clearTimeout(resizeTimer);
|
|
340
|
+
resizeTimer = setTimeout(() => {
|
|
341
|
+
if (ignoreVertical) {
|
|
342
|
+
let currentWidth = window.innerWidth;
|
|
343
|
+
if (currentWidth - screenWidth !== 0) {
|
|
344
|
+
debouncedFunction();
|
|
345
|
+
}
|
|
346
|
+
screenWidth = currentWidth;
|
|
347
|
+
} else {
|
|
348
|
+
debouncedFunction();
|
|
349
|
+
}
|
|
350
|
+
}, time);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* General purpose utility for running a function after a particular event fires on a specified element. The function only fires after the alloted time has ended.
|
|
356
|
+
* If another resize event occurs it resets the time window.
|
|
357
|
+
* @param {htmlElement} element The element that emits the event.
|
|
358
|
+
* @param {string} eventType The type of event to listen for.
|
|
359
|
+
* @param {function} debouncedFunction The function to run after the event.
|
|
360
|
+
* @param {number} time The time in ms.
|
|
361
|
+
*/
|
|
362
|
+
export function eventListenerDebouncer(
|
|
363
|
+
element,
|
|
364
|
+
eventType,
|
|
365
|
+
debouncedFunction,
|
|
366
|
+
time = 250
|
|
367
|
+
) {
|
|
368
|
+
let timer;
|
|
369
|
+
element.addEventListener(eventType, (event) => {
|
|
370
|
+
clearTimeout(timer);
|
|
371
|
+
timer = setTimeout(() => {
|
|
372
|
+
debouncedFunction(event);
|
|
373
|
+
}, time);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Injects the youtube iframe api script and waits for the YT object to be instantiated before resolving the promise
|
|
379
|
+
* @returns {promise} Resolves once the YT object is available.
|
|
380
|
+
*/
|
|
381
|
+
export function injectYouTubeIframeScript() {
|
|
382
|
+
const prom = new Promise((resolve) => {
|
|
383
|
+
if (globalThis.YT) {
|
|
384
|
+
return resolve();
|
|
385
|
+
}
|
|
386
|
+
const tag = document.createElement("script");
|
|
387
|
+
tag.id = "iframe-api";
|
|
388
|
+
tag.src = "https://www.youtube.com/iframe_api";
|
|
389
|
+
|
|
390
|
+
const firstScriptTag = document.getElementsByTagName("script")[0];
|
|
391
|
+
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
|
392
|
+
|
|
393
|
+
tag.addEventListener("load", () => {
|
|
394
|
+
if (!YT.loaded) {
|
|
395
|
+
const loadingCheck = setInterval(() => {
|
|
396
|
+
if (YT.loaded) {
|
|
397
|
+
clearInterval(loadingCheck);
|
|
398
|
+
return resolve(true);
|
|
399
|
+
}
|
|
400
|
+
}, 50);
|
|
401
|
+
} else {
|
|
402
|
+
return resolve(true);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
return prom;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export function fallbackCopyToClipboard(text) {
|
|
411
|
+
const textArea = document.createElement("textarea");
|
|
412
|
+
textArea.value = text;
|
|
413
|
+
|
|
414
|
+
// Avoid scrolling to bottom
|
|
415
|
+
textArea.style.top = "0";
|
|
416
|
+
textArea.style.left = "0";
|
|
417
|
+
textArea.style.position = "fixed";
|
|
418
|
+
|
|
419
|
+
document.body.appendChild(textArea);
|
|
420
|
+
textArea.focus();
|
|
421
|
+
textArea.select();
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
const successful = document.execCommand("copy");
|
|
425
|
+
const msg = successful ? "successful" : "unsuccessful";
|
|
426
|
+
console.log(`Fallback: Copying text command was ${msg}`);
|
|
427
|
+
} catch (err) {
|
|
428
|
+
console.error("Fallback: Oops, unable to copy", err);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
document.body.removeChild(textArea);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export function copyToClipboard() {
|
|
435
|
+
const copyLinks = document.querySelectorAll(".copy-to-clipboard");
|
|
436
|
+
let timeout;
|
|
437
|
+
copyLinks.forEach((link) => {
|
|
438
|
+
link.addEventListener("click", function () {
|
|
439
|
+
const copyToClipboardDone = link.nextElementSibling;
|
|
440
|
+
if (!navigator.clipboard) {
|
|
441
|
+
fallbackCopyToClipboard(this.dataset.url);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
navigator.clipboard.writeText(this.dataset.url).then(
|
|
446
|
+
function () {
|
|
447
|
+
console.log("Copying to clipboard was successful!");
|
|
448
|
+
},
|
|
449
|
+
function (err) {
|
|
450
|
+
console.error("Could not copy text: ", err);
|
|
451
|
+
}
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
if (
|
|
455
|
+
copyToClipboardDone &&
|
|
456
|
+
copyToClipboardDone.classList &&
|
|
457
|
+
copyToClipboardDone.classList.contains("sharing__clipboard-done")
|
|
458
|
+
) {
|
|
459
|
+
clearTimeout(timeout);
|
|
460
|
+
copyToClipboardDone.classList.remove("hidden");
|
|
461
|
+
|
|
462
|
+
timeout = setTimeout(() => {
|
|
463
|
+
copyToClipboardDone.classList.add("hidden");
|
|
464
|
+
}, 2000);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Checks whether an element is visible in the viewport.
|
|
472
|
+
* @param {string} element - The dom element to look for.
|
|
473
|
+
* @returns {boolean} True if visible and false if not.
|
|
474
|
+
*/
|
|
475
|
+
export function isInViewport(element) {
|
|
476
|
+
const position = element.getBoundingClientRect();
|
|
477
|
+
return (
|
|
478
|
+
(position.top >= 0 && position.bottom <= window.innerHeight) ||
|
|
479
|
+
(position.top < window.innerHeight && position.bottom >= 0)
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* eslint-disable-next-line */
|
|
484
|
+
const emailRegex =
|
|
485
|
+
/^[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Checks to make sure a passed string has a valid email address structure and characters.
|
|
489
|
+
* @param {RegExp} email - The email address as a string.
|
|
490
|
+
* @returns {boolean} True if valid and false if not.
|
|
491
|
+
*/
|
|
492
|
+
export function isEmailValid(email) {
|
|
493
|
+
return emailRegex.test(email);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Get the value of a css property on a specific element.
|
|
498
|
+
* @param {htmlElement} el The element whose styles you want to get.
|
|
499
|
+
* @param {string} property The name of the property.
|
|
500
|
+
* @returns {(string|number)} The value of the property.
|
|
501
|
+
*/
|
|
502
|
+
export function getElementStyles(el, property) {
|
|
503
|
+
const elementStyles = window.getComputedStyle(el);
|
|
504
|
+
const cssPropertyValue = elementStyles.getPropertyValue(property);
|
|
505
|
+
return cssPropertyValue;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Get a rounded percentage value using two numbers.
|
|
510
|
+
* @param {number} total The value you want get a percentage of.
|
|
511
|
+
* @param {number} part The value that is a fraction of the total.
|
|
512
|
+
* @returns {number} The rounded percentage value.
|
|
513
|
+
*/
|
|
514
|
+
export function getPercent(total, part) {
|
|
515
|
+
return Math.round((part / total) * 100);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Detects swipe actions on a element and runs a callback function.
|
|
520
|
+
*
|
|
521
|
+
* @param {htmlElement} element The element on which you want to detect swipe actions.
|
|
522
|
+
* @param {function} callback The callback function to run when a swipe is detected.
|
|
523
|
+
* @returns {void} Attaches or removes the functionality.
|
|
524
|
+
*/
|
|
525
|
+
export function detectSwipe(element, callback, removeHandlers = false) {
|
|
526
|
+
let touchstartX = 0;
|
|
527
|
+
let touchstartY = 0;
|
|
528
|
+
let touchendX = 0;
|
|
529
|
+
let touchendY = 0;
|
|
530
|
+
|
|
531
|
+
function handleGesture(
|
|
532
|
+
touchstartX,
|
|
533
|
+
touchstartY,
|
|
534
|
+
touchendX,
|
|
535
|
+
touchendY,
|
|
536
|
+
pixels = 50
|
|
537
|
+
) {
|
|
538
|
+
const delx = touchendX - touchstartX;
|
|
539
|
+
const dely = touchendY - touchstartY;
|
|
540
|
+
|
|
541
|
+
if (Math.abs(delx) > pixels || Math.abs(dely) > pixels) {
|
|
542
|
+
if (Math.abs(delx) > Math.abs(dely)) {
|
|
543
|
+
if (delx > 0) return "right";
|
|
544
|
+
else return "left";
|
|
545
|
+
} else if (Math.abs(delx) < Math.abs(dely)) {
|
|
546
|
+
if (dely > 0) return "down";
|
|
547
|
+
else return "up";
|
|
548
|
+
}
|
|
549
|
+
} else return "tap";
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function touchStart(event) {
|
|
553
|
+
touchstartX = event.changedTouches[0].screenX;
|
|
554
|
+
touchstartY = event.changedTouches[0].screenY;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
element.addEventListener("touchstart", () => touchStart(event), false);
|
|
558
|
+
|
|
559
|
+
element.addEventListener(
|
|
560
|
+
"touchend",
|
|
561
|
+
function (event) {
|
|
562
|
+
touchendX = event.changedTouches[0].screenX;
|
|
563
|
+
touchendY = event.changedTouches[0].screenY;
|
|
564
|
+
callback(handleGesture(touchstartX, touchstartY, touchendX, touchendY));
|
|
565
|
+
},
|
|
566
|
+
false
|
|
567
|
+
);
|
|
568
|
+
if (removeHandlers === "remove") {
|
|
569
|
+
element.removeEventListener("touchstart", touchStart);
|
|
570
|
+
element.removeEventListener("touchend", handleGesture);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Function for getting the ios operating system version
|
|
577
|
+
*
|
|
578
|
+
* @returns {number} the ios version
|
|
579
|
+
*/
|
|
580
|
+
export function checkIosVersion() {
|
|
581
|
+
const agent = window.navigator.userAgent,
|
|
582
|
+
start = agent.indexOf("OS ");
|
|
583
|
+
if (
|
|
584
|
+
(agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) &&
|
|
585
|
+
start > -1
|
|
586
|
+
) {
|
|
587
|
+
return window.Number(agent.substr(start + 3, 3).replace("_", "."));
|
|
588
|
+
}
|
|
589
|
+
return 0;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Function for adding a link tag with preconnect to the head of the document. Very useful if you need to add this dynamically.
|
|
594
|
+
* @param {string} domain The domain you wish to preconnect to.
|
|
595
|
+
* @returns {void} The preconnect will be appended to the head tag.
|
|
596
|
+
*/
|
|
597
|
+
export function appendPreconnect(domain) {
|
|
598
|
+
try {
|
|
599
|
+
if (!domain) {
|
|
600
|
+
console.log("The domain was missing or broken...");
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const link = document.createElement("link");
|
|
604
|
+
link.rel = "preconnect";
|
|
605
|
+
link.href = domain;
|
|
606
|
+
document.head.appendChild(link);
|
|
607
|
+
return;
|
|
608
|
+
} catch (error) {
|
|
609
|
+
console.error(error);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
*
|
|
615
|
+
* @param {string} src The URL src to load
|
|
616
|
+
*/
|
|
617
|
+
async function loadScript(src) {
|
|
618
|
+
const script = document.createElement("script");
|
|
619
|
+
script.type = "text/javascript";
|
|
620
|
+
script.src = src;
|
|
621
|
+
script.async = true;
|
|
622
|
+
document.head.appendChild(script);
|
|
623
|
+
await new Promise((resolve, reject) => {
|
|
624
|
+
script.onload = resolve;
|
|
625
|
+
script.onerror = () => reject(new Error(`Script load error for ${src}`));
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
*
|
|
631
|
+
* @param {object} event The click event object
|
|
632
|
+
*/
|
|
633
|
+
async function handleCTBClick(event) {
|
|
634
|
+
event.preventDefault();
|
|
635
|
+
|
|
636
|
+
if (!window.isCTBLoaded) {
|
|
637
|
+
const domain = window.location.hostname;
|
|
638
|
+
// Assuming PR-CLICKTOBUY plugin is enabled to make the script load
|
|
639
|
+
await loadScript(
|
|
640
|
+
`/wp-content/plugins/pr-clicktobuy/public/js/pr-clicktobuy-public.js`
|
|
641
|
+
);
|
|
642
|
+
window.isCTBLoaded = true;
|
|
643
|
+
|
|
644
|
+
// This is important!! Never remove this code
|
|
645
|
+
const clonedEvent = new event.constructor(event.type, event);
|
|
646
|
+
setTimeout(() => {
|
|
647
|
+
event.target.dispatchEvent(clonedEvent);
|
|
648
|
+
}, 500);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
*
|
|
654
|
+
* @param {HTMLElement} element The element which needs to set the Attributes
|
|
655
|
+
*/
|
|
656
|
+
function setAttributesToMenuItem(element) {
|
|
657
|
+
const body = document.querySelector("body");
|
|
658
|
+
const localLang = body.dataset.sitelanguage;
|
|
659
|
+
const locale = localLang.replace(/_/g, "-");
|
|
660
|
+
element.setAttribute("data-ctbuy-lang", locale);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
*
|
|
665
|
+
* @param {HTMLElement} block The element CTA which triggers
|
|
666
|
+
*/
|
|
667
|
+
export function ctbCTAClickHandler(block = document) {
|
|
668
|
+
const elements = block.querySelectorAll("[data-ctbuy]");
|
|
669
|
+
elements.forEach((element) => {
|
|
670
|
+
setAttributesToMenuItem(element);
|
|
671
|
+
element.addEventListener("click", handleCTBClick);
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const api = {
|
|
676
|
+
copyToClipboard,
|
|
677
|
+
checkDevice,
|
|
678
|
+
detectSwipe,
|
|
679
|
+
eventListenerDebouncer,
|
|
680
|
+
footerIntersection,
|
|
681
|
+
getPosition,
|
|
682
|
+
getGsap,
|
|
683
|
+
getJquery,
|
|
684
|
+
inCriticalCssConfig,
|
|
685
|
+
injectYouTubeIframeScript,
|
|
686
|
+
isEmailValid,
|
|
687
|
+
isInViewport,
|
|
688
|
+
isTablet,
|
|
689
|
+
loadCss,
|
|
690
|
+
mobileCheck,
|
|
691
|
+
resizeDebouncer,
|
|
692
|
+
supportsWebp,
|
|
693
|
+
toggleClassByValidity,
|
|
694
|
+
waitForLoad,
|
|
695
|
+
getElementStyles,
|
|
696
|
+
getPercent,
|
|
697
|
+
checkIosVersion,
|
|
698
|
+
appendPreconnect,
|
|
699
|
+
};
|
|
700
|
+
export default api;
|