diff --git a/events/blocks/adobe-connect/adobe-connect.css b/events/blocks/adobe-connect/adobe-connect.css new file mode 100644 index 00000000..73b469d4 --- /dev/null +++ b/events/blocks/adobe-connect/adobe-connect.css @@ -0,0 +1,147 @@ +.adobe-connect { + width: 100%; + padding: 0; + margin: 0; +} + +.adobe-connect .fullwidth { + width: 100%; + height: 900px; +} + +.adobe-connect button { + font-size: var(--type-body-s-size); + background-color: var(--link-color); + min-width: 114px; + height: 40px; + line-height: 20px; + padding: 9px 16px; + border: 0; + border-radius: 24px; + color: #fff; + font-weight: 700; + box-shadow: none; + margin: 0 auto; + cursor: pointer; + transition: background-color 0.13s, color 0.13s; +} + +.adobe-connect .hidden { + visibility: hidden !important; + opacity: 0 !important; +} + +/* Overlay background */ +.adobe-connect .iframe-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 85%); + color: #fff; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 9999; + font-family: inherit; + pointer-events: auto; + padding: 10px; + gap: 16px; + flex-shrink: 0; + margin: 0; +} + +.adobe-connect .iframe-overlay .overlay-close-btn { + display: flex; + width: 22px; + height: 22px; + align-items: flex-start; + position: absolute; + right: 32px; + top: 33px; +} + +.adobe-connect .iframe-overlay .overlay-heading, +.adobe-connect .iframe-overlay .overlay-heading-yrs { + font-weight: 700; + text-align: center; + line-height: 125%; + font-style: normal; + color: #fff; + font-size: 24px; + align-self: stretch; + margin: 0; +} + +.adobe-connect .iframe-overlay .overlay-subheading { + width: 245px; + color: #FFF; + text-align: center; + font-size: 16px; + font-weight: 700; + line-height: 125%; +} + +.adobe-connect .iframe-overlay .overlay-illustration { + margin: 40px 0 40px 0; + display: flex; + justify-content: center; +} + +.adobe-connect .iframe-overlay .overlay-landscape { + height: 22px; + align-self: stretch; + color: #FFF; + text-align: center; + font-size: 18px; + font-style: normal; + font-weight: 700; + line-height: 125%; + /* 22.5px */ +} + +.adobe-connect .iframe-overlay .overlay-rotate-msg { + align-self: stretch; + color: #FFF; + text-align: center; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 150%; + /* 18px */ +} + +.adobe-connect .iframe-overlay .overlay-continue-btn { + height: 50px; + background: #3B63FB; + color: #FFF; + text-align: center; + font-size: 19px; + font-style: normal; + font-weight: 700; + padding: 12px 24px 14px 24px; + border-radius: 100px; + line-height: 24px; + /* 126.316% */ + margin-top: 40px; +} + +@media only screen and (orientation: portrait) { + .adobe-connect .iframe-overlay .overlay-heading-yrs { + display: none; + } +} + +@media only screen and (orientation: landscape) { + .adobe-connect .iframe-overlay .overlay-heading { + display: none; + } + .adobe-connect .iframe-overlay .overlay-subheading { + display: none; + } + .adobe-connect .iframe-overlay .overlay-landscape { + display: none; + } + .adobe-connect .iframe-overlay .overlay-rotate-msg { + display: none; + } +} diff --git a/events/blocks/adobe-connect/adobe-connect.js b/events/blocks/adobe-connect/adobe-connect.js new file mode 100644 index 00000000..8130808e --- /dev/null +++ b/events/blocks/adobe-connect/adobe-connect.js @@ -0,0 +1,150 @@ +import { LIBS, getMetadata } from '../../scripts/utils.js'; + +const { createTag } = await import(`${LIBS}/utils/utils.js`); + +// TODO: remove post validation with marketo integration. +function addParams(searchParams, params, key, value) { + if (params.has(key)) { + searchParams[key] = params.get(key); + } else { + searchParams[key] = value; + } +} + +// TODO: remove post validation with marketo integration. +function addSearchParams(url, searchParams) { + const urlObj = new URL(url); + + Object.entries(searchParams).forEach(([key, value]) => { + urlObj.searchParams.append(key, value); + }); + + return urlObj.toString(); +} + +// TODO: remove post validation with marketo integration. +function getSearchParamsFromCurrentUrl() { + const params = new URL(window.location.href).searchParams; + const searchParams = {}; + addParams(searchParams, params, 'mkto_trk', 'marketo_tracker'); + addParams(searchParams, params, 'mkt_tok', 'marketo_token'); + addParams(searchParams, params, 'ecid', 'experience_cloud_id'); + addParams(searchParams, params, 'mkto_event_id', 'marketo_event_id'); + return searchParams; +} + +function createOverlay(rowBlock) { + // Show the overlay div if it exists and is hidden + if (rowBlock.classList.contains('hidden')) { + rowBlock.classList.remove('hidden'); + } + + // Overlay container + const elements = { + overlay: rowBlock.querySelector(':scope > div:nth-of-type(1)'), + heading: rowBlock.querySelector(':scope > div:nth-of-type(1) > h2:nth-of-type(1)'), + subheading: rowBlock.querySelector(':scope > div:nth-of-type(1) > p:nth-of-type(1)'), + bestViewedInLandscape: rowBlock.querySelector(':scope > div:nth-of-type(1) > p:nth-of-type(2)'), + rotateYourPhone: rowBlock.querySelector(':scope > div:nth-of-type(1) > p:nth-of-type(3)'), + youAreAllSet: rowBlock.querySelector(':scope > div:nth-of-type(1) > h2:nth-of-type(2)'), + continueBtn: rowBlock.querySelector(':scope > div:nth-of-type(1) > p:nth-of-type(4) > a'), + }; + + const overlay = rowBlock.querySelector(':scope > div:nth-of-type(1)'); + overlay.classList.add('iframe-overlay'); + + // Close button (top right) + const closeBtn = createTag( + 'img', + { + class: 'overlay-close-btn', + 'aria-label': 'Close overlay', + src: '/events/blocks/adobe-connect/asset/Cross.svg', + alt: 'Close overlay', + }, + ); + overlay.insertAdjacentElement('beforeend', closeBtn); + closeBtn.addEventListener('click', () => overlay.remove()); + + // Heading + elements.heading.classList.add('overlay-heading'); + + // subheading + elements.subheading.classList.add('overlay-subheading'); + + // Illustration (SVG) + const rotateImg = createTag('img', { + class: 'overlay-illustration', + src: '/events/blocks/adobe-connect/asset/Rotate.svg', + alt: 'Phone rotation illustration', + }); + elements.subheading.insertAdjacentElement('afterend', rotateImg); + + elements.bestViewedInLandscape.classList.add('overlay-landscape'); + elements.rotateYourPhone.classList.add('overlay-rotate-msg'); + // Message container + const messageContainer = createTag('div', { class: 'overlay-message-container' }, [elements.bestViewedInLandscape, elements.rotateYourPhone]); + rotateImg.insertAdjacentElement('afterend', messageContainer); + + elements.continueBtn.classList.add('overlay-continue-btn'); + elements.continueBtn.addEventListener('click', (event) => { + event.preventDefault(); + overlay.remove(); + }); + + // Message for landscape mode + elements.youAreAllSet.classList.add('overlay-heading-yrs'); + + return overlay; +} + +function isMobileDevice() { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); +} + +function isPortraitMode() { + return window.innerHeight > window.innerWidth; +} + +function shouldShowOverlay() { + return isMobileDevice() && isPortraitMode(); +} + +export default async function init(el) { + const h2 = el.querySelector('h2'); + let url = h2?.textContent; + + h2.remove(); + + // Hide the overlay div if it exists + const overlayElem = el.querySelector(':scope > div:nth-of-type(2)'); + if (overlayElem) { + overlayElem.classList.add('hidden'); + } + + if (getMetadata('adobe-connect-url')) { + url = getMetadata('adobe-connect-url'); + } else { + console.log('No adobe-connect-url found'); + return; + } + + // TODO: remove post validation with marketo integration. + const searchParams = getSearchParamsFromCurrentUrl(); + url = addSearchParams(url, searchParams); + + if (overlayElem && shouldShowOverlay()) { + createOverlay(overlayElem); + } else if (overlayElem) { + overlayElem.remove(); + } + + // Create iframe + createTag('iframe', { + src: url, + frameborder: '0', + allowfullscreen: 'true', + class: 'fullwidth', + style: 'position: relative; z-index: 1;', + }, '', { parent: el }); +} diff --git a/events/blocks/adobe-connect/asset/Cross.svg b/events/blocks/adobe-connect/asset/Cross.svg new file mode 100644 index 00000000..cd2b4a5e --- /dev/null +++ b/events/blocks/adobe-connect/asset/Cross.svg @@ -0,0 +1,3 @@ + + + diff --git a/events/blocks/adobe-connect/asset/Rotate.svg b/events/blocks/adobe-connect/asset/Rotate.svg new file mode 100644 index 00000000..9234467a --- /dev/null +++ b/events/blocks/adobe-connect/asset/Rotate.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/events/blocks/anchor-point/anchor-point.css b/events/blocks/anchor-point/anchor-point.css new file mode 100644 index 00000000..e69de29b diff --git a/events/blocks/anchor-point/anchor-point.js b/events/blocks/anchor-point/anchor-point.js new file mode 100644 index 00000000..6da98c11 --- /dev/null +++ b/events/blocks/anchor-point/anchor-point.js @@ -0,0 +1,7 @@ +export default async function init(el) { + const rows = Array.from(el.children); + const idName = `${rows[0].textContent.trim().toLowerCase()}`; + + el.innerHTML = ''; + el.setAttribute('id', idName); +} diff --git a/events/blocks/mcz-handler/mcz-handler.css b/events/blocks/mcz-handler/mcz-handler.css new file mode 100644 index 00000000..e69de29b diff --git a/events/blocks/mcz-handler/mcz-handler.js b/events/blocks/mcz-handler/mcz-handler.js new file mode 100644 index 00000000..9f7dca1e --- /dev/null +++ b/events/blocks/mcz-handler/mcz-handler.js @@ -0,0 +1,73 @@ +import { CheckResourceLocation } from '../../../rs/360-KCI-804/images/mktoTestFormConfig.js'; +import { setMetadata, getMetadata } from '../../scripts/utils.js'; + +export default async function init(el) { + const rows = Array.from(el.children); + const resourceLocation = `#${rows[0].textContent.trim().toLowerCase()}`; + // suggestion to use getElementbyId + const key = rows[1].textContent.trim().toLowerCase(); + + const resourceWatch = 'main .section .chrono-box'; + + el.innerHTML = ''; + + el.setAttribute('data-mcz-dl-status', 'loading'); + + let mczId = null; + if (getMetadata('eventExternalId')) { + const eventExternalId = getMetadata('eventExternalId'); + // split the eventExternalId by - and get the last part + mczId = eventExternalId.replace('-', '').toLowerCase(); + } + + await CheckResourceLocation(el, resourceWatch, resourceLocation, mczId); + + async function mczMarketoFormAdobeConnectEvent() { + if (window.mcz_marketoForm_pref?.form?.success?.type === 'adobe_connect') { + const { metadataStore } = await import('../../features/timing-framework/plugins/metadata/plugin.js'); + + const eventUrl = window.mcz_marketoForm_pref?.form?.success?.content; + setMetadata('adobe-connect-url', eventUrl); + metadataStore.set(key, 'adobe-connect'); + + console.log('Marketo form completed - Adobe Connect URL:', eventUrl); + } + } + + // Debounce function to limit rapid calls + function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + } + + // Debounced callback for observer + const debouncedCallback = debounce(() => { + const status = el.getAttribute('data-mcz-dl-status'); + + // TODO: remove this console.log post validation with marketo integration. + console.log('Attribute "data-mcz-dl-status" changed to', status); + if (status === 'active') { + mczMarketoFormAdobeConnectEvent(); + } + }, 300); // 300ms debounce delay + + const observer = new MutationObserver((mutationsList) => { + mutationsList.forEach((mutation) => { + if (mutation.type === 'attributes') { + debouncedCallback(); + } + }); + }); + + observer.observe(el, { + attributes: true, // Observe attribute changes + attributeFilter: ['data-mcz-dl-status'], // Optional: filter specific attributes + }); +} diff --git a/rs/360-KCI-804/images/mktoTestFormConfig.js b/rs/360-KCI-804/images/mktoTestFormConfig.js new file mode 100644 index 00000000..525366a8 --- /dev/null +++ b/rs/360-KCI-804/images/mktoTestFormConfig.js @@ -0,0 +1,1019 @@ +// ## +// ## Test Module - Just a rough test module to set the config of the form.11 +// ## + +if ( + typeof mczFrm_mkto_testing_loader !== 'function' + && typeof mczFrm_mkto_testing_loader === 'undefined' +) { + //* + //* + async function getMktToken() { + const storageKey = 'mkt_tok'; + let mktToken = ''; + + if (window.__mktTokVal && window.__mktTokVal?.trim()?.length > 0) { + return window.__mktTokVal; + } + + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has(storageKey)) { + mktToken = urlParams.get(storageKey); + try { + sessionStorage.setItem(storageKey, mktToken); + localStorage.setItem(storageKey, mktToken); + } catch (e) { + console.warn(`Could not save ${storageKey} to storage`, e); + } + } else { + mktToken = sessionStorage.getItem(storageKey) || localStorage.getItem(storageKey) || ''; + } + + window.__mktTokVal = mktToken; + return mktToken; + } + + window.getMktToken = await getMktToken(); + + //* + //* + //* + //* + //* + //* + //* + //* + //* + + // hide Join button + + const BASE_URL = 'https://engage.marketo.com'; + const MUNCHKIN_ID = '360-KCI-804'; + var mczFrm_mkto_testing_loader = (el, resourceLocation, mczId = null) => { + const cssFast = ` + button[daa-ll="Join the event-1--"] { + visibility: hidden !important; + opacity: 0 !important; + } + `; + const cssLinkFast = document.createElement('style'); + cssLinkFast.innerHTML = cssFast; + document.head.appendChild(cssLinkFast); + + const formID = 3131; + const resourceFormHTML = ` +
+
+ + + +
+
+ `; + + const marketoCSSresource = 'https://business.adobe.com/libs/blocks/marketo/marketo.css'; + + const marketoConfiguratorLink = 'eyJmb3JtLnRlbXBsYXRlIjoicmVxdWVzdF9mb3JfaW5mb3JtYXRpb24iLCJmb3JtLnN1YnR5cGUiOiJyZXF1ZXN0X2Zvcl9pbmZvcm1hdGlvbiIsInByb2dyYW0uY2FtcGFpZ25pZHMuc2ZkYyI6IjcwMTE0MDAwMDAyWFl2SUFBVyIsInByb2dyYW0ucG9pIjoiTUFSS0VUT0VOR0FHRU1FTlRQTEFURk9STSIsImZvcm0uc3VjY2Vzcy5jb250ZW50IjoiaHR0cHM6Ly9idXNpbmVzcy5hZG9iZS5jb20vcmVzb3VyY2VzL2Vib29rcy9wcm92aW5nLXRoZS1pbXBhY3Qtb2YtbWFya2V0aW5nLW9uLXJldmVudWUvdGhhbmsteW91Lmh0bWwiLCJmb3JtLnN1Y2Nlc3MudHlwZSI6IiIsInByb2dyYW0uY29udGVudC50eXBlIjoiIiwicHJvZ3JhbS5jb250ZW50LmlkIjoiIiwiZmllbGRfdmlzaWJpbGl0eS5uYW1lIjoicmVxdWlyZWQiLCJmaWVsZF92aXNpYmlsaXR5LnBob25lIjoicmVxdWlyZWQiLCJmaWVsZF92aXNpYmlsaXR5LmNvbXBhbnkiOiJyZXF1aXJlZCIsImZpZWxkX3Zpc2liaWxpdHkud2Vic2l0ZSI6InJlcXVpcmVkIiwiZmllbGRfZmlsdGVycy5mdW5jdGlvbmFsX2FyZWEiOiJGdW5jdGlvbmFsIEFyZWEtRFgiLCJmaWVsZF92aXNpYmlsaXR5LnN0YXRlIjoicmVxdWlyZWQiLCJmaWVsZF92aXNpYmlsaXR5LnBvc3Rjb2RlIjoicmVxdWlyZWQiLCJmaWVsZF92aXNpYmlsaXR5LmNvbXBhbnlfc2l6ZSI6InJlcXVpcmVkIiwiZmllbGRfZmlsdGVycy5wcm9kdWN0cyI6ImhpZGRlbiIsImZpZWxkX2ZpbHRlcnMuaW5kdXN0cnkiOiJoaWRkZW4iLCJmaWVsZF9maWx0ZXJzLmpvYl9yb2xlIjoiYWxsIiwiZmllbGRfdmlzaWJpbGl0eS5jb21tZW50cyI6ImhpZGRlbiIsImZpZWxkX3Zpc2liaWxpdHkuZGVtbyI6ImhpZGRlbiIsInByb2dyYW0uY29wYXJ0bmVybmFtZXMiOiIiLCJwcm9ncmFtLmNhbXBhaWduaWRzLmV4dGVybmFsIjoiIiwicHJvZ3JhbS5jYW1wYWlnbmlkcy5yZXRvdWNoIjoiIiwicHJvZ3JhbS5jYW1wYWlnbmlkcy5vbnNpdGUiOiIiLCJwcm9ncmFtLmFkZGl0aW9uYWxfZm9ybV9pZCI6IiIsImZvcm0gaWQiOiIxNzIzIiwibWFya2V0byBtdW5ja2luIjoiMzYwLUtDSS04MDQiLCJtYXJrZXRvIGhvc3QiOiJlbmdhZ2UuYWRvYmUuY29tIiwiZm9ybSB0eXBlIjoibWFya2V0b19mb3JtIn0'; + const marketoConfiguratorHTML = ` +
+
+
Marketo Configurator
+
+
+
Marketo Configurator
+
+
+ `; + + const cssLink = document.createElement('link'); + cssLink.rel = 'stylesheet'; + cssLink.href = marketoCSSresource; + document.head.appendChild(cssLink); + + let marketoConfiguratorJSON = {}; + try { + marketoConfiguratorJSON = JSON.parse( + decodeURIComponent(escape(window.atob(marketoConfiguratorLink))) + ); + } catch (e) { + console.error('Error parsing Marketo Configurator JSON', e); + } + + const use_marketoConfiguratorLink = false; + + const mcz_marketoForm_pref_local = { + sync_profiles: {}, + 'marketo munckin': '360-KCI-804', + 'marketo host': 'engage.adobe.com', + 'form id': '3131', + form: { + template: 'flex_event', + success: { + type: 'adobe_connect', + content: 'https://livekitqe.dev.adobeconnect.com/paxlkm0gwsj4', + delay: 5000, + confirm: false, + }, + baseSite: 'https://business.adobe.com', + id: 3131, + }, + program: { + campaignids: { + sfdc: '7015Y000004BWOnQAO', + external: '', + retouch: '', + onsite: '', + cgen: '', + cuid: '', + }, + poi: 'MARKETOENGAGEMENTPLATFORM', + additional_form_id: '', + copartnernames: '', + marketo_asset: { + name: '', + id: '', + }, + event: { + type: 'adobe_connect', // connect_recording or video + subtype: 'flex_event', + id: mczId ?? 'mcz114328', + status: { + viewport: { + width: 1024, + height: 768, + active: true, + audio: true, + }, + activity: { + start: '2025-06-07-10:00', + end: '2025-06-07-10:00', + duration_ticks: 1000, + active_ticks: 1000, + log: [ + { + type: 'adobe_connect', + subtype: 'flex_event', + id: 'ACTEST4242', + }, + ], + }, + activities: [], + }, + }, + status: { + label: 'invited', + milestone: 'responded', + is: '1001', + dateTime: '2025-06-07-10:00', + }, + status_previous: { + label: 'invited', + milestone: 'responded', + is: '1001', + dateTime: '2025-06-07-10:00', + }, + }, + field_visibility: { + name: 'required', + phone: 'hidden', + company: 'visible', + website: 'hidden', + state: 'hidden', + postcode: 'hidden', + }, + field_filters: { + functional_area: 'Functional Area-DX', + products: 'hidden', + industry: 'hidden', + job_role: 'hidden', + comments: 'hidden', + demo: 'hidden', + }, + }; + + if (use_marketoConfiguratorLink) { + for (const key in marketoConfiguratorJSON) { + const keyArray = key.split('.'); + let obj = mcz_marketoForm_pref_local; + for (let i = 0; i < keyArray.length - 1; i++) { + if (!obj[keyArray[i]]) { + obj[keyArray[i]] = {}; + } + obj = obj[keyArray[i]]; + } + obj[keyArray[keyArray.length - 1]] = marketoConfiguratorJSON[key]; + } + } + + if (!window.location.search.includes('preview=1')) { + history.pushState({}, '', `${window.location.pathname}?preview=1`); + } + + window.mcz_marketoForm_pref = JSON.parse(JSON.stringify(mcz_marketoForm_pref_local)); + + const loadScript = (url, type, { mode } = {}) => new Promise((resolve, reject) => { + let script = document.querySelector(`head > script[src="${url}"]`); + if (!script) { + const { head } = document; + script = document.createElement('script'); + script.setAttribute('src', url); + if (type) { + script.setAttribute('type', type); + } + + if (['async', 'defer'].includes(mode)) script.setAttribute(mode, true); + + head.append(script); + } + + if (script.dataset.loaded) { + resolve(script); + return; + } + + const onScript = (event) => { + script.removeEventListener('load', onScript); + script.removeEventListener('error', onScript); + + if (event.type === 'error') { + reject(new Error(`error loading script: ${script.src}`)); + } else if (event.type === 'load') { + script.dataset.loaded = true; + resolve(script); + } + }; + + script.addEventListener('load', onScript); + script.addEventListener('error', onScript); + }); + + const resourceForm = document.createElement('div'); + resourceForm.innerHTML = resourceFormHTML; + if (document.querySelector(resourceLocation)) { + document.querySelector(resourceLocation).appendChild(resourceForm); + } else { + console.log('resourceLocation not found', resourceLocation); + } + + loadScript(`${BASE_URL}/js/forms2/js/forms2.min.js`) + .then(() => { + const { MktoForms2 } = window; + if (!MktoForms2) throw new Error('Marketo forms not loaded'); + + MktoForms2.loadForm(`${BASE_URL}`, MUNCHKIN_ID, formID); + MktoForms2.whenReady((form) => { + console.log("Marketo Form Thinks it's Ready", form); + }); + }) + .catch(() => { + console.error('Error loading Marketo form'); + }); + + window.addMunchkin = async function ( + munchkinId = '360-KCI-804', + pageFromEventId = '', + mktToken = '', + ) { + loadScript('https://munchkin.marketo.net/munchkin.js') + .then(() => { + Munchkin.init(munchkinId, { + customName: `Testing BACOM Connect UX ${pageFromEventId}`, + mkt_tok: mktToken, + }); + }) + .catch(() => { + console.error('Error loading Munchkin.js'); + }); + }; + + mczFrm_createProgramSyncIframe(); + }; + + window.mcz_marketoForm_adobe_connect_event = (event_url = '') => { + let final_url = ''; + if (event_url != '') { + final_url = event_url.trim(); + } + + let adobe_connect_status = window.mcz_marketoForm_pref?.program?.event?.adobe_connect?.status || null; + if (adobe_connect_status == null) { + adobe_connect_status = {}; + } + adobe_connect_status.overall = 'active'; + + mczFrm_saveSnapshot(); + + if (document.querySelector('.marketo-form-wrapper')) { + document.querySelector('.marketo-form-wrapper').classList.add('hide'); + } + if (document.querySelector('.adobe-connect button[daa-ll*="Join"]')) { + document.querySelector('.adobe-connect button[daa-ll*="Join"]').click(); + } + console.log('adobe_connect_event', final_url); + + // To lookup a resource which has attribute 'data-mcz-dl-status'. + // Required for loading adobe connect player + const resource = document.querySelector('[data-mcz-dl-status]'); + if (resource) { + resource.setAttribute('data-mcz-dl-status', 'active'); + } else { + console.log('no resource with attribute data-mcz-dl-status found'); + } + + mczFrm_updateTimeUntil(); + }; + + // + // + // + // + // + // + + function mczFrm_getTimeUntil(targetDate, nowDate) { + const timeUntil = targetDate - nowDate; + + if (timeUntil <= 0) { + return { days: 0, hours: 0, minutes: 0, seconds: 0, timeUntil }; + } + + const days = Math.floor(timeUntil / (1000 * 60 * 60 * 24)); + const hours = Math.floor((timeUntil % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + const minutes = Math.floor((timeUntil % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = Math.floor((timeUntil % (1000 * 60)) / 1000); + + return { days, hours, minutes, seconds, timeUntil }; + } + + function mczFrm_currentMarketoTime() { + let serverTimeOffset = mcz_marketoForm_pref?.program?.marketo_asset?.time?.offset || null; + try { + if (serverTimeOffset == null) { + const serverTimeRaw = mcz_marketoForm_pref?.program?.marketo_asset?.time?.systemDateTime || null; + if (serverTimeRaw) { + const serverTimeInitial = new Date(serverTimeRaw).getTime(); + if (!isNaN(serverTimeInitial)) { + const clientTimeInitial = new Date().getTime() + serverTimeOffset; + serverTimeOffset = serverTimeInitial - clientTimeInitial; + mcz_marketoForm_pref.program.marketo_asset.time.offset = serverTimeOffset; + } + } else { + serverTimeOffset = 0; + } + } else { + serverTimeOffset = parseInt(serverTimeOffset) || 0; + } + } catch (e) { + console.warn('Error getting server time offset', e); + serverTimeOffset = 0; + } + if (typeof serverTimeOffset === 'undefined') { + return new Date(); + } + return new Date(new Date().getTime() + serverTimeOffset); + } + + let timeUntilInterval = null; + function mczFrm_updateTimeUntil() { + const base = window?.mcz_marketoForm_pref || {}; + const endDateTime = base?.program?.event?.dateTime?.pst?.dateTimeEnd || null; + const startDateTime = base?.program?.event?.dateTime?.pst?.dateTimeStart || null; + const nowDateTime = mczFrm_currentMarketoTime(); + let general_status = 'pending'; + if (endDateTime == null || startDateTime == null || nowDateTime == null) { + console.warn('No end or now date time found for this event'); + return null; + } + + let adobe_connect_status = base?.program?.event?.adobe_connect?.status || null; + if (adobe_connect_status == null) { + adobe_connect_status = {}; + } + + general_status = adobe_connect_status.overall || 'register'; + + const endDate = new Date(endDateTime); + const startDate = new Date(startDateTime); + const nowDate = new Date(nowDateTime); + const timeUntilEnds = mczFrm_getTimeUntil(endDate, nowDate); + const timeUntilStart = mczFrm_getTimeUntil(startDate, nowDate); + + let stillReview = true; + + // Reset status flags + adobe_connect_status.is_starting_soon = false; + adobe_connect_status.is_starting_in_5_minutes = false; + adobe_connect_status.is_starting_in_1_minute = false; + adobe_connect_status.is_halfway_through = false; + adobe_connect_status.about_to_end = false; + adobe_connect_status.has_ended = false; + adobe_connect_status.has_finished = false; + adobe_connect_status.can_enter = false; + adobe_connect_status.can_attend = false; + adobe_connect_status.can_register = false; + adobe_connect_status.has_started = false; + adobe_connect_status.is_reg_open = false; + adobe_connect_status.is_reg_closed = false; + + if (timeUntilInterval) { + clearInterval(timeUntilInterval); + } + + // Set timing status flags based on time until start + if (timeUntilStart.timeUntil > 0 && stillReview) { + if (timeUntilStart.minutes <= 10) { + adobe_connect_status.is_starting_soon = true; + } + if (timeUntilStart.minutes <= 5) { + adobe_connect_status.is_starting_in_5_minutes = true; + } + if (timeUntilStart.minutes <= 1) { + adobe_connect_status.is_starting_in_1_minute = true; + } + } + + const duration = mczFrm_getTimeUntil(endDate, startDate); + base.program.event.dateTime.duration = duration; + + const remaining = mczFrm_getTimeUntil(endDate, nowDate); + base.program.event.dateTime.remaining = remaining; + + // Calculate halfway point using total milliseconds for accuracy + if (duration?.timeUntil > 0 && stillReview) { + const half_duration_ms = duration.timeUntil / 2; + if (remaining?.timeUntil <= half_duration_ms && remaining?.timeUntil > 0) { + adobe_connect_status.is_halfway_through = true; + } + } + + // Check if event is about to end + if (remaining?.timeUntil > 0 && remaining.minutes <= 10 && stillReview) { + adobe_connect_status.about_to_end = true; + } + + // check if finished + if ( + adobe_connect_status.has_started + && remaining?.timeUntil > 0 + && remaining.minutes <= 2 + && stillReview + ) { + adobe_connect_status.has_ended = true; + adobe_connect_status.has_finished = true; + adobe_connect_status.is_reg_open = false; + adobe_connect_status.is_reg_closed = true; + adobe_connect_status.can_enter = false; + adobe_connect_status.can_attend = false; + adobe_connect_status.can_register = false; + adobe_connect_status.has_started = true; + general_status = 'finished'; + stillReview = false; + } + + // open_minutes and close_minutes + const open_minutes = base?.program?.event?.dateTime?.pst?.open_minutes || 10; // you can join 10 minutes before the event starts + const close_minutes = base?.program?.event?.dateTime?.pst?.close_minutes || 999; // you can join 999 minutes after the event starts + + // workout if the event is open or closed + let doorsOpen = false; + if (timeUntilStart?.minutes > 0 && stillReview) { + if (timeUntilStart?.minutes <= open_minutes) { + doorsOpen = true; + adobe_connect_status.can_enter = true; + adobe_connect_status.can_attend = true; + adobe_connect_status.can_register = true; + adobe_connect_status.has_started = true; + adobe_connect_status.has_ended = false; + adobe_connect_status.is_reg_open = true; + adobe_connect_status.is_reg_closed = false; + general_status = 'active'; + stillReview = false; + } + } + if (doorsOpen) { + if (duration?.minutes >= close_minutes && stillReview) { + doorsOpen = false; + adobe_connect_status.can_enter = false; + adobe_connect_status.can_attend = false; + adobe_connect_status.can_register = false; + adobe_connect_status.has_started = true; + adobe_connect_status.is_reg_open = false; + adobe_connect_status.is_reg_closed = true; + general_status = 'active'; + stillReview = false; + } + } + + base.program.event.dateTime.timeUntil = { + starts: timeUntilStart, + ends: timeUntilEnds, + }; + adobe_connect_status.overall = general_status; + base.program.event.status = general_status; + base.program.event.id = window?.programId || null; + + console.log('timeUntilStart', timeUntilStart); + + if (timeUntilStart.minutes <= 30) { + mczFrm_saveSnapshot("root.program.event"); + if (!timeUntilInterval) { + timeUntilInterval = setInterval(mczFrm_updateTimeUntil, 1000); + } + } else if (timeUntilInterval) { + clearInterval(timeUntilInterval); + timeUntilInterval = null; + } + } + + function mczFrm_ACE_confirmURL(url_name = '', url = '') { + try { + const confirmLocation = window.mcz_marketoForm_pref?.program?.event?.adobe_connect?.urls?.[url_name] || null; + if (confirmLocation == null) { + return false; + } + + const current_status = confirmLocation?.valid_url || false; + if (current_status) { + return true; + } + + if (url.trim() == '') { + // get the url from the config and we will validate it. + url = confirmLocation.url || ''; + if (url.trim() == '') { + return false; + } + } + + confirmLocation.url = url; + + try { + confirmLocation.url_obj = new URL(url); + confirmLocation.valid_url = true; + } catch (e) { + confirmLocation.valid_url = false; + confirmLocation.url_obj = {}; + confirmLocation.url = ''; + return false; + } + return true; + } catch (e) { + return false; + } + } + //* + //* + //* + //* Communication frunctions + //* + async function mczFrm_createProgramSyncIframe() { + const mktToken = window.getMktToken; + const pageFromEventId = mcz_marketoForm_pref.program.event.id || ''; + if (pageFromEventId == '') { + console.log('No event id found, skipping profile sync iframe.'); + return; + } + + const munchkinId = MUNCHKIN_ID || '360-KCI-804'; + addMunchkin(munchkinId, pageFromEventId, mktToken); + const programIDOnly = pageFromEventId.replace(/[^0-9]/g, ''); + const programfromStorage = await mczFrm_aquireFromStorage(`pr_${programIDOnly}_`); + if (programfromStorage) { + console.log('MCZ Form, Updating DL from storage:', programfromStorage); + mczFrm_updateDL(programfromStorage); + } + + let baseURL = `https://engage.adobe.com/${pageFromEventId}.html`; + if (mktToken) { + baseURL = `${baseURL}?mkt_tok=${mktToken}`; + } + + const iframe = document.createElement('iframe'); + iframe.sandbox = 'allow-scripts allow-same-origin'; + iframe.src = baseURL; + iframe.style.display = 'none'; + iframe.id = 'mcz-marketo-program-iframe'; + document.body.appendChild(iframe); + console.log('Profile sync iframe added with URL:', iframe.src); + } + + function mczFrm_aquireFromStorage(lookupKey = null, updateWith = null) { + const now = new Date().getTime(); + let locationWas = 'session'; + + let itemStr = null; + itemStr = sessionStorage.getItem(lookupKey); + if (!itemStr) { + itemStr = localStorage.getItem(lookupKey); + locationWas = 'local'; + } + if (!itemStr) { + return null; + } + + try { + const item = JSON.parse(itemStr); + const shelfLife = item?.shelf_life || 0; + const timestamp = item?.timestamp || 0; + const expires = item?.expires || 0; + + if (now - timestamp > shelfLife || now > expires) { + sessionStorage.removeItem(lookupKey); + localStorage.removeItem(lookupKey); + console.log(`MCZ RefData: Expired program removed from ${lookupKey}`); + return null; + } + if (updateWith) { + item.location = locationWas; + item.timestamp = now; + item.expires = now + shelfLife; + item.data = updateWith; + itemStr = JSON.stringify(item); + if (locationWas == 'session') { + sessionStorage.setItem(lookupKey, itemStr); + } else { + localStorage.setItem(lookupKey, itemStr); + } + console.log("MCZ RefData: Updated snapshot in", locationWas, lookupKey); + } + return item; + } catch (e) { + return null; + } + } + + async function mczFrm_saveRefs(baseAlias = null, refStorageKey = null) { + if (!refStorageKey) { + console.warn('No reference storage key found'); + return; + } + + let existingRefs = []; + const newRefs = []; + try { + existingRefs = JSON.parse(localStorage.getItem(refStorageKey) || '[]'); + } catch (e) { + console.warn('Error parsing refStorage', e); + existingRefs = []; + } + + const dataProfileKeys = existingRefs || []; + const now = new Date().getTime(); + + for (const key of dataProfileKeys) { + const lookupKey = key; + let itemStr = null; + itemStr = sessionStorage.getItem(lookupKey); + if (!itemStr) { + itemStr = localStorage.getItem(lookupKey); + } + if (!itemStr) { + continue; + } + + try { + const item = JSON.parse(itemStr); + const shelfLife = item?.shelf_life || 0; + const timestamp = item?.timestamp || 0; + const expires = item?.expires || 0; + + if (now - timestamp > shelfLife || now > expires) { + sessionStorage.removeItem(lookupKey); + localStorage.removeItem(lookupKey); + console.log(`MCZ RefData: Expired ref removed from ${lookupKey}`); + } else { + newRefs.push(lookupKey); + } + } catch (e) { + console.warn(`Error parsing dataProfile for key: ${lookupKey}`, e); + sessionStorage.removeItem(lookupKey); + localStorage.removeItem(lookupKey); + } + } + + if ( + baseAlias != null + && newRefs.indexOf(baseAlias) == -1 + && existingRefs.indexOf(baseAlias) == -1 + ) { + newRefs.push(baseAlias); + } + + localStorage.setItem(refStorageKey, JSON.stringify(newRefs)); + console.log('MCZ RefData: Updated refs in refStorage', newRefs); + } + + async function mczFrm_saveMsg(message) { + try { + const saveAlias = message?.alias || null; + const refStorageKey = message?.refStorage || null; + const location = message?.location || 'local'; + const dataStr = JSON.stringify(message); + const sizeInBytes = new Blob([dataStr]).size; + const sizeInMB = sizeInBytes / (1024 * 1024); + + if (sizeInMB > 2) { + console.warn(`MCZ RefData: Large data size (${sizeInMB.toFixed(2)}MB)`); + } + if (!saveAlias) { + console.warn('No save alias found'); + return; + } + + if (location == 'session') { + sessionStorage.setItem(saveAlias, dataStr); + } else { + localStorage.setItem(saveAlias, dataStr); + } + + console.log('MCZ RefData: Saved message:', message); + + mczFrm_saveRefs(saveAlias, refStorageKey); + } catch (storageError) { + console.warn('Storage error:', storageError); + } + } + + async function mczFrm_saveSnapshot(targetPath = 'root.program.event') { + const base = window?.mcz_marketoForm_pref || null; + const pageFromEventId = base?.program?.event?.id || ''; + const programIDOnly = pageFromEventId?.replace(/[^0-9]/g, '') || ''; + if (programIDOnly == '') { + console.log('No event id found, skipping snapshot.'); + return; + } + targetPath = targetPath.toLowerCase().trim(); + const syncActions = { + 'root.program': 'program', + 'root.program_profile': 'program_profile', + 'root.program.event': 'event', + 'root.profile': 'profile', + 'root.profile.acc': 'profile_acc', + 'root.form': 'form', + 'root.landingPage': 'landingPage', + }; + + if (syncActions[targetPath]) { + let snapshot = {}; + if (base == null) { + console.warn('mczFrm_saveSnapshot: No base found'); + return; + } + const targetPathRef = (targetPath?.split(".")?.pop() || '').toLowerCase().trim(); + const saveAlias = base?.sync_profiles?.data_profiles?.[targetPathRef]?.ref || null; + const host = base?.sync_profiles?.data_profiles?.[targetPathRef]?.host || null; + + if (host == null) { + console.warn('mczFrm_saveSnapshot: No host found for', targetPath); + return; + } + if (saveAlias == null) { + console.warn('mczFrm_saveSnapshot: No save alias found for', targetPath); + return; + } + const hostRef = (host?.split('.')?.pop() || '').toLowerCase().trim(); + snapshot = base[hostRef] || {}; + + if (Object.keys(snapshot).length == 0) { + console.warn('mczFrm_saveSnapshot: snapshot is empty for', targetPath); + return; + } + + const existingSnapshot = mczFrm_aquireFromStorage(saveAlias, snapshot); + if (existingSnapshot) { + console.log( + 'mczFrm_saveSnapshot: Existing snapshot found and updated with', + existingSnapshot + ); + return; + } + } else { + console.warn('mczFrm_saveSnapshot: targetPath not found', targetPath); + } + } + + window.addEventListener('message', (event) => { + const config = { allowedOrigins: ['https://engage.adobe.com', 'https://business.adobe.com'] }; + const eventOrigin = new URL(event.origin); + let allowedToPass = false; + for (let i = 0; i < config.allowedOrigins.length; i++) { + const allowedOriginURL = new URL(config.allowedOrigins[i]); + if ( + eventOrigin.host === allowedOriginURL.host + && eventOrigin.protocol === allowedOriginURL.protocol + && eventOrigin.port === allowedOriginURL.port + ) { + allowedToPass = true; + break; + } + } + if (event.data && event?.data?.type !== 'mcz_marketoForm_pref_sync') { + allowedToPass = false; + } + if (!allowedToPass) { + return; + } + console.log('MCZ RefData Received:', event.data); + if (event.data && event?.data?.target_path !== null && event?.data?.target_attribute !== null) { + const save = event?.data?.save || false; + mczFrm_updateDL(event?.data); + if (save) { + mczFrm_saveMsg(event?.data); + } + } + }); + + function crawlAndUpdateObject( + thisObject = null, + targetObject = null, + shouldOverwrite = true, + addToTarget = true + ) { + if (thisObject == null || targetObject == null) { + console.warn('No object to crawl or update'); + return; + } + + const isSourceMeaningful = (value) => { + if (value === null || value === undefined) return false; + if (value === "") return false; + if (value === 0 || value === false) return false; // preserve original loose `!= 0` semantics + return true; + }; + + const isTargetEmpty = (value) => { + return ( + value === null || + value === undefined || + value === "" || + value === 0 || + value === false || + value === "NULL" + ); + }; + + const isObjectLike = (value) => typeof value === 'object' && value !== null; + + for (const key of Object.keys(thisObject)) { + if (!Object.prototype.hasOwnProperty.call(targetObject, key) && !addToTarget) { + continue; + } + const sourceVal = thisObject[key]; + if (!isSourceMeaningful(sourceVal)) { + continue; + } + + const targetVal = targetObject[key]; + if (isObjectLike(sourceVal) && isObjectLike(targetVal)) { + crawlAndUpdateObject(sourceVal, targetVal, shouldOverwrite, addToTarget); + continue; + } + + if (isTargetEmpty(targetVal) || shouldOverwrite) { + targetObject[key] = sourceVal; + } + } + } + + function mczFrm_updateDL(data = null) { + if (data == null) { + return; + } + const targetPath = data?.target_path || null; + if (targetPath == null) { + return; + } + let program_type = data?.data?.program?.type || 'default'; + let program_status = 'default'; + + const this_data = JSON.parse(JSON.stringify(data?.data)); + if (targetPath == 'root.program_profile') { + window.mcz_marketoForm_pref.program_profile = this_data; + //crawlAndUpdateObject(this_data, window.mcz_marketoForm_pref.program_profile, true, true); + } else if (targetPath === 'root.profile') { + window.mcz_marketoForm_pref.profile = this_data; + //crawlAndUpdateObject(this_data, window.mcz_marketoForm_pref.profile, true, true); + } else if (targetPath === 'root.form') { + window.mcz_marketoForm_pref.form = this_data; + crawlAndUpdateObject(this_data, window.mcz_marketoForm_pref.form, true, true); + } else if (targetPath === 'root.profile.acc') { + window.mcz_marketoForm_pref.profile.acc = this_data; + //crawlAndUpdateObject(this_data, window.mcz_marketoForm_pref.profile.acc, true, true); + } else if (targetPath === 'root.program') { + window.mcz_marketoForm_pref.program = this_data; + //crawlAndUpdateObject(this_data, window.mcz_marketoForm_pref.program); + program_type = this_data?.type || 'default'; + } else if (targetPath === 'root.program.event') { + window.mcz_marketoForm_pref.program.event = this_data; + //crawlAndUpdateObject(this_data, window.mcz_marketoForm_pref.program.event); + program_type = this_data?.type || 'default'; + } + + if (window.mcz_marketoForm_pref?.program?.type == 'event') { + mczFrm_updateTimeUntil(); + if (window.mcz_marketoForm_pref?.program?.event?.type == 'adobe_connect') { + program_type = 'adobe_connect'; + program_status = + window.mcz_marketoForm_pref?.program?.event?.adobe_connect?.status?.overall || "pending"; + } + } + + function mczFrm_statusLbls(program_type = '', program_status = '') { + if (program_type == '' || program_status == '') { + console.log('No program type or status found'); + return; + } + + const dataLabel = 'data-mcz-dl-status'; + const elements = document.querySelectorAll(`[${dataLabel}]`); + const timeNow = new Date().getTime(); + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + element.setAttribute(`${dataLabel}-dt`, timeNow); + element.setAttribute(`${dataLabel}-type`, program_type); + element.setAttribute(`${dataLabel}`, program_status); + } + } + + console.log('Status Labels:', program_type, program_status); + + mczFrm_statusLbls(program_type, program_status); + } + + function mczFrm_sendMessage(targetPath = 'root.program', data = {}) { + const iframe = document.getElementById('mcz-marketo-program-iframe'); + if (iframe) { + if (JSON.stringify(data) == '{}') { + data = { + type: 'mcz_marketoForm_pref_sync', + target_path: targetPath, + }; + } + + iframe.contentWindow.postMessage(data, 'https://engage.adobe.com'); + } else { + console.warn('No iframe found'); + } + } + + window.mczFrm_sendMessage = mczFrm_sendMessage; + + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* + //* +} + +// ## +// ## +let maxTries = 1000; +export function CheckResourceLocation(el, resourceWatch, resourceLocation, mczId = null) { + if (maxTries <= 0) { + console.log('maxTries reached', maxTries); + return; + } + maxTries--; + if (document.querySelector(resourceWatch)) { + setTimeout(() => { + console.log('Resource found, loading...'); + mczFrm_mkto_testing_loader(el, resourceLocation, mczId); + }, 1000); + } else { + setTimeout(() => { + console.log('Resource not found, checking again...'); + CheckResourceLocation(el, resourceWatch, resourceLocation, mczId); + }, 20); + } +}