diff --git a/aemedge/blocks/accordion/accordion.js b/aemedge/blocks/accordion/accordion.js index 7d6bfc1a..b29d5972 100644 --- a/aemedge/blocks/accordion/accordion.js +++ b/aemedge/blocks/accordion/accordion.js @@ -80,3 +80,23 @@ export default function decorate(block) { }); }); } + +export function rebindEvents(block) { + const items = block.querySelectorAll('.details.accordion-items'); + items.forEach((item) => { + const summary = item.querySelector('.summary.accordion-item-label'); + const answer = summary ? summary.nextSibling : null; + if (summary && answer) { + summary.onclick = null; + summary.addEventListener('click', () => { + const faqitem = item; + faqitem.classList.toggle('open'); + if (answer.style.maxHeight) { + answer.style.maxHeight = null; + } else { + answer.style.maxHeight = `${answer.scrollHeight}px`; + } + }); + } + }); +} diff --git a/aemedge/blocks/carousel/carousel.js b/aemedge/blocks/carousel/carousel.js index 2ad070b9..2c3ffe01 100644 --- a/aemedge/blocks/carousel/carousel.js +++ b/aemedge/blocks/carousel/carousel.js @@ -177,3 +177,56 @@ export default async function decorate(block) { setInterval(autoScroll, 3000); } } +/** + * Re-establishes event bindings for a carousel block + * @param {HTMLElement} block The carousel block element + */ +export function rebindEvents(block) { + // Get the carousel buttons - using the correct class names + const prevButton = block.querySelector('.slide-prev'); + const nextButton = block.querySelector('.slide-next'); + + // Get the carousel variant + const variant = block.dataset.variant || 'default'; + + // Re-establish event bindings + if (prevButton && nextButton) { + // Remove any existing event listeners + prevButton.replaceWith(prevButton.cloneNode(true)); + nextButton.replaceWith(nextButton.cloneNode(true)); + + // Get the fresh references after replacement + const newPrevButton = block.querySelector('.slide-prev'); + const newNextButton = block.querySelector('.slide-next'); + + // Add event listeners + newPrevButton.addEventListener('click', () => { + const currentSlide = parseInt(block.dataset.activeSlide, 10) || 0; + showSlide(block, currentSlide - 1); + }); + + newNextButton.addEventListener('click', () => { + const currentSlide = parseInt(block.dataset.activeSlide, 10) || 0; + showSlide(block, currentSlide + 1); + }); + + // Re-initialize auto-scrolling if applicable + if (variant.includes('autoscroll')) { + const autoScrollInterval = block.dataset.autoScrollInterval || 3000; + + // Clear any existing interval + if (block.autoScrollInterval) { + clearInterval(block.autoScrollInterval); + } + + // Set up new interval + const autoScroll = () => { + const currentSlide = parseInt(block.dataset.activeSlide, 10) || 0; + showSlide(block, currentSlide + 1); + }; + + // Store the timer ID + block.autoScrollInterval = setInterval(autoScroll, autoScrollInterval); + } + } +} diff --git a/aemedge/blocks/tabs/tabs.js b/aemedge/blocks/tabs/tabs.js index d41269c6..05d21016 100644 --- a/aemedge/blocks/tabs/tabs.js +++ b/aemedge/blocks/tabs/tabs.js @@ -197,3 +197,24 @@ export default async function decorate(block) { await loadBlocks(block); } } + +export function rebindEvents(block) { + const tablist = block.querySelector('.tabs-list'); + if (!tablist) return; + const tabButtons = tablist.querySelectorAll('button'); + const tabpanels = block.querySelectorAll('[role=tabpanel]'); + tabButtons.forEach((button, i) => { + const tabpanel = tabpanels[i]; + button.onclick = null; + button.addEventListener('click', () => { + tabpanels.forEach((panel) => { + panel.setAttribute('aria-hidden', true); + }); + tabButtons.forEach((btn) => { + btn.setAttribute('aria-selected', false); + }); + tabpanel.setAttribute('aria-hidden', false); + button.setAttribute('aria-selected', true); + }); + }); +} diff --git a/aemedge/blocks/zipcode/zipcode.js b/aemedge/blocks/zipcode/zipcode.js index 736ac34c..706f1616 100644 --- a/aemedge/blocks/zipcode/zipcode.js +++ b/aemedge/blocks/zipcode/zipcode.js @@ -15,6 +15,7 @@ const updateZip = (e, block) => { // create custom event and dispatch it const options = { bubbles: true, detail: { zipcode: zipinput } }; const zipUpdateEvent = new CustomEvent('zipupdate', options); + console.log('[DEBUG]dispatching zipUpdateEvent', zipUpdateEvent); document.dispatchEvent(zipUpdateEvent); }; diff --git a/aemedge/plugins/martech/.eslintignore b/aemedge/plugins/martech/.eslintignore new file mode 100644 index 00000000..8ff5461b --- /dev/null +++ b/aemedge/plugins/martech/.eslintignore @@ -0,0 +1,2 @@ +src/acdl.min.js +src/alloy.min.js \ No newline at end of file diff --git a/aemedge/plugins/martech/.eslintrc.js b/aemedge/plugins/martech/.eslintrc.js new file mode 100644 index 00000000..f1b51983 --- /dev/null +++ b/aemedge/plugins/martech/.eslintrc.js @@ -0,0 +1,17 @@ +module.exports = { + root: true, + extends: 'airbnb-base', + env: { + browser: true, + }, + parser: '@babel/eslint-parser', + parserOptions: { + allowImportExportEverywhere: true, + sourceType: 'module', + requireConfigFile: false, + }, + rules: { + 'no-param-reassign': [2, { props: false }], + 'import/extensions': ['error', { js: 'always' }], + }, +}; diff --git a/aemedge/plugins/martech/LICENSE b/aemedge/plugins/martech/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/aemedge/plugins/martech/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/aemedge/plugins/martech/README.md b/aemedge/plugins/martech/README.md new file mode 100644 index 00000000..6c43d9b9 --- /dev/null +++ b/aemedge/plugins/martech/README.md @@ -0,0 +1,397 @@ +:construction: This is an early access technology and is still heavily in development. Reach out to us over Slack before using it. + +# AEM Edge Delivery Services Marketing Technology + +The AEM Marketing Technology plugin helps you quickly set up a complete MarTech stack for your AEM project. It is currently available to customers in collaboration with AEM Engineering via co-innovation VIP Projects. To implement your use cases, please reach out to the AEM Engineering team in the Slack channel dedicated to your project. + + +## Features + +The AEM MarTech plugin is essentially a wrapper around the Adobe Experience Platform WebSDK (v2.19.2) and the Adobe Client Data Layer (v2.0.2), and that can seamlessly integrate your website with: + +- 🎯 Adobe Target or Adobe Journey Optimizer: to personalize your pages +- πŸ“Š Adobe Analytics: to track customer journey data +- 🚩 Adobe Experience Platform Tags (a.k.a. Launch): to track your custom events + +It's key differentiator are: +- 🌍 Experience Platform enabled: the library fully integrates with our main Adobe Experience Platform and all the services of our ecosystem +- πŸš€ extremely fast: the library is optimized to reduce load delay, TBT and CLS, and has minimal impact on your Core Web Vitals +- πŸ‘€ privacy-first: the library does not track end users by default, and can easily be integrated with your preferred consent management system to open up more advanced use cases +- πŸ”¬ speculative prerender aware: the library the library supports [speculative prerendering](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API) and won't fire Analytics events (and artificially inflate your page views) until the page is actually viewed + + +## Prerequisites + +You need to have access to: +- Adobe Experience Platform (AEP) +- Adobe Analytics +- Adobe Target or Adobe Journey Optimizer + +And you need to have preconfigured: +- a datastream in AEP with Adobe Analytics, and Adobe Target or Adobe Journey Optimizer configured +- an Adobe Experience Platform Tag (Launch) container with the Adobe Client Data Layer extensions at a minimum + +We also recommend using a proper consent management system. If not, make sure to default the consent to `in` so you don't block out personalization use cases. + +## Installation + +We have a comprehensive [tutorial on Experience League](https://experienceleague.adobe.com/en/docs/platform-learn/tutorial-one-adobe/assetmgmt/assetm1/ex6), or you can just follow the steps below. + +Add the plugin to your AEM project by running: +```sh +git subtree add --squash --prefix plugins/martech git@github.com:adobe-rnd/aem-martech.git main +``` + +If you later want to pull the latest changes and update your local copy of the plugin +```sh +git subtree pull --squash --prefix plugins/martech git@github.com:adobe-rnd/aem-martech.git main +``` + +If you prefer using `https` links you'd replace `git@github.com:adobe-rnd/aem-martech.git` in the above commands by `https://github.com/adobe-rnd/aem-martech.git`. + +If the `subtree pull` command is failing with an error like: +``` +fatal: can't squash-merge: 'plugins/martech' was never added +``` +you can just delete the folder and re-add the plugin via the `git subtree add` command above. + +If you use some ELint at the project level (or equivalent), make sure to update ignore minified files in your `.eslintignore`: +``` +*.min.js +``` + +## Project instrumentation + +To properly connect and configure the plugin for your project, you'll need to edit both the `head.html` and `scripts.js` in your AEM project and add the following: + +1. Add preload hints for the dependencies we need to speed up the page load at the end of your `head.html`: + ```html + + + + + ``` +2. Import the various plugin methods at the top of your `scripts.js` file: + ```js + import { + initMartech, + updateUserConsent, + martechEager, + martechLazy, + martechDelayed, + } from '../plugins/martech/src/index.js'; + ``` +3. Configure the plugin at the top of the `loadEager` method: + ```js + /** + * loads everything needed to get to LCP. + */ + async function loadEager(doc) { + const isConsentGiven = /* hook in your consent check here to make sure you can run personalization use cases. */; + const martechLoadedPromise = initMartech( + // The WebSDK config + // Documentation: https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/commands/configure/overview#configure-js + { + datastreamId: /* your datastream id here, formally edgeConfigId */, + orgId: /* your ims org id here */, + onBeforeEventSend: (payload) => { + // set custom Target params + // see doc at https://experienceleague.adobe.com/en/docs/platform-learn/migrate-target-to-websdk/send-parameters#parameter-mapping-summary + payload.data.__adobe.target ||= {}; + + // set custom Analytics params + // see doc at https://experienceleague.adobe.com/en/docs/analytics/implementation/aep-edge/data-var-mapping + payload.data.__adobe.analytics ||= {}; + }, + + // set custom datastream overrides + // see doc at: + // - https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/commands/datastream-overrides + // - https://experienceleague.adobe.com/en/docs/experience-platform/datastreams/overrides + edgeConfigOverrides: { + // Override the datastream id + // datastreamId: '...' + + // Override AEP event datasets + // com_adobe_experience_platform: { + // datasets: { + // event: { + // datasetId: '...' + // } + // } + // }, + + // Override the Analytics report suites + // com_adobe_analytics: { + // reportSuites: ['...'] + // }, + + // Override the Target property token + // com_adobe_target: { + // propertyToken: '...' + // } + }, + }, + // The library config + { + launchUrls: [/* your Launch script URLs here */], + personalization: !!getMetadata('target') && isConsentGiven, + }, + ); + … + } + ``` + Note that: + - the WebSDK [`context`](https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/commands/configure/context) flag will, by default, track the `web`, `device` and `environment` details + - the WebSDK [`debugEnabled`](https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/commands/configure/debugenabled) flag will, by default, be set to `true` on localhost and any `.page` URL + - the WebSDK [`defaultConsent`](https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/commands/configure/defaultconsent) is set to `pending` to avoid tracking any sensitive information by default. This will also prevent personalization to properly run unless consent is explicitly given + - we recommend enabling `personalization` only if needed to limit the performance impact, and only if consent has been given by the user to be compliant with privacy laws. We typically recommend using a page metadata flag for the former, and integrating with your preferred consent management system APIs for the latter. +4. Adjust your `loadEager` method so it waits for the martech to load and personalize the page: + ```js + /** + * loads everything needed to get to LCP. + */ + async function loadEager(doc) { + … + if (main) { + decorateMain(main); + await Promise.all([ + martechLoadedPromise.then(martechEager), + waitForLCP(LCP_BLOCKS), + ]); + } + } + ``` +5. Add a reference to the lazy logic just above the `sampleRUM('lazy');` call in your `loadLazy` method: + ```js + async function loadLazy(doc) { + … + await martechLazy(); + sampleRUM('lazy'); + … + } + ``` +6. Add a reference to the delayed logic in the `loadDelayed` method: + ```js + function loadDelayed() { + // eslint-disable-next-line import/no-cycle + window.setTimeout(() => { + martechDelayed(); + return import('./delayed.js'); + }, 3000); + } + ``` +7. Connect your consent management system to track when user consent is explicitly given. Typically call the `updateUserConsent` with a set of [categories](https://experienceleague.adobe.com/en/docs/experience-platform/xdm/data-types/consents#choices) & booleans pairs once your consent management sends the event. The marketing option supports granular control as in the official documentation: + ```js + updateUserConsent({ + collect: true, + marketing: { + preferred: 'email', + any: false, + email: true, + push: false, + sms: true, + }, + personalize: true, + share: true, + }) + ``` +:warning: Note that the integration will be specific to the vendor you chose. See some examples below. + +### Integrating with consent management solutions + +#### AEM Consent Banner Block + +Here is an example for the [consent banner block](https://github.com/adobe/aem-block-collection/pull/50) in AEM Block Collection: +```js +function consentEventHandler(ev) { + const collect = ev.detail.categories.includes('CC_ANALYTICS'); + const marketing = ev.detail.categories.includes('CC_MARKETING'); + const personalize = ev.detail.categories.includes('CC_TARGETING'); + const share = ev.detail.categories.includes('CC_SHARING'); + updateUserConsent({ collect, marketing, personalize, share }); +} +window.addEventListener('consent', consentEventHandler); +window.addEventListener('consent-updated', consentEventHandler); +``` + +#### Integrating with OneTrust +Here is an example for [OneTrust](https://www.onetrust.com): +```js +function consentEventHandler(ev) { + const groups = ev.detail; + const collect = groups.includes('C0002'); // Performance Cookies + const personalize = groups.includes('C0003'); // Functional Cookies + const share = groups.includes('C0008'); // Targeted Advertising and Selling/Sharing of Personal Information + updateUserConsent({ collect, personalize, share }); +} +window.addEventListener('consent.onetrust', consentEventHandler); +``` + +### Automatic forwarding of events + +The library is built to automatically foward all events that are pushed to the datalayer directly to Analyics/CJS. +So all you have to do is: +```js +window.adobeDatalayer.push({ + xdm: { ... }, // the XDM schema to push + data: { ... }, // The data mappings to use + configOverrides: { ... }, // The possible edge config overrides, like datastreamId overrides +}) +``` + +you can also just use our helper methods for this, that simplifies the input a bit: +```js +pushEventToDataLayer('my-event', xdm, data, configOverrides); +``` +or directly leverage: +```js +pushToDataLayer({ + xdm: { ... }, // the XDM schema to push + data: { ... }, // The data mappings to use + configOverrides: { ... }, // The possible edge config overrides, like datastreamId overrides +}) +``` +which is just a proxy for `window.adobeDatalayer.push`. + +:warning: When working with overrides, the `onBeforeEventSend` hook does not directly support those, so you have to handle those in the outer call to one of the methods above. + +### Working with SPA and dynamic content + +If your page is built dynamically with content that renders either asynchronously or updates based on user input, +it is likely that the default personalization applied will not be enough and you can end up with 2 possible edge cases: +1. The personalization is not applied at all +2. The personalization is incorrectly applied + +This is typical of a concurrency issue between personalization being applied and the content of the page not being ready yet +and still updating. Some examples are: +- blocks with lose promises that resolve after block decoration is finished +- frontend frameworks like (p)react that update the DOM based on internal state changes to a virtual DOM + +For those cases, we typically recommend: +1. Set up personalization following the SPA approach and [leverage views](https://experienceleague.adobe.com/en/docs/target/using/experiences/spa-visual-experience-composer) for the dynamic parts +2. Import the 2 helper methods from our plugin in the blocks that represent these views: + ```js + import { + isPersonalizationEnabled, + getPersonalizationForView, + applyPersonalization, + } from '../plugins/martech/src/index.js'; + ``` +3. Fetch the personalization once for the view when it is initially rendered, or whenever you change the page in a paginated component: + ```js + await getPersonalizationForView('my-view'); + ``` + We recommend directly including this in the `loadEager` logic so the default view immediately get the Target propositions: + ```js + /** + * loads everything needed to get to LCP. + */ + async function loadEager(doc) { + … + if (main) { + decorateMain(main); + await Promise.all([ + martechLoadedPromise.then(martechEager), + waitForLCP(LCP_BLOCKS), + ]); + if (isPersonalizationEnabled()) { + getPersonalizationForView('my-view'); + } + } + } + ``` + You can for instance map the page template to a given view name, or have the view name directly set in the page metadata. +4. Apply the personalization every time there is a meaningful DOM update done by your block or component: + ```js + applyPersonalization('my-view'); + ``` + +### Custom plugin options + +There are various aspects of the plugin that you can configure via options you can pass to the `initMartech` method above. +Here is the full list we support: + +```js +initMartech( + // Documentation: https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/commands/configure/overview#configure-js + { + datastreamId: '...', // the Datastream ID you want to report to + orgId: '...', // your IMS organisation ID, + // See https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/commands/configure/overview for other options + } + // The library config + { + analytics: true, // whether to track data in Adobe Analytics (AA) + alloyInstanceName: 'alloy', // the name of the global WebSDK instance + dataLayer: true, // whether to use the Adobe Client Data Layer (ACDL) + dataLayerInstanceName: 'adobeDataLayer', // the name of the global ACDL instance + includeDataLayerState: true, // whether to include the whole data layer state on every event sent + launchUrls: [], // the list of Launch scripts to load + personalization: true, // whether to apply page personalization from Adobe Target (AT) or Adobe Journey Optimizer (AJO) + performanceOptimized: true, // whether to use the agressive performance optimized approach or more traditional + personalizationTimeout: 1000, // the amount of time to wait (in ms) before bailing out and continuing page rendering + shouldProcessEvent: (payload) => true, // whether to process the datalayer event and forward it to the backend + }, +); +``` + +#### Selectively passing datalayer events to the backend + +By default, all datalayer events are processed and forwarded to the backend. You can filter which events should be processed by providing a function that returns `true` for events that should be processed, and `false` for events that should be ignored. + +For instance, if you want to only process events that have a specific event type: + +```js +// Example of configuring the martech library with a custom event filter +initMartech({ + datastreamId: 'abc123', + orgId: 'ABC@AdobeOrg' +}, { + // Only process page view events and add-to-cart events + shouldProcessEvent: (payload) => { + return [ + 'web.webpagedetails.pageViews', + 'commerce.productListAdds' + ].includes(payload.event); + } +}); +``` + +## FAQ + +### Why shouldn't I use the default Adobe Tag/Launch approach to do all of this? + +Typical instrumentations based on a centralized approach using Adobe Tag/Launch that is loaded early in the page life-cycle essentially impacts the user experience negatively for the benefit of marketing metrics. Core Web Vitals are noticeably impacted, and Google PageSpeed reports typically show a drop of 20~40 points in the performance category. + +### But can't I just defer the launch script to solve this? + +This can indeed solve the issue in some cases, but comes with some drawbacks: +1. personalization use cases will be delayed as well, so you'll introduce content flickering when the personalization kicks in +2. analytics metrics gathering will be delayed as well, so if your website has a large percentage of early bounces, your analytics reports won't be able to capture those + +### What guarantee do I have that your approach will not break …some feature name…? + +Adobe Tags/Launch will typically wrap the [Adobe Experience Platform SDK](https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/) and [Adobe Client Data Layer](https://github.com/adobe/adobe-client-data-layer), and configure your data streams to connect to Adobe Target and Adobe Analytics. + +Our approach just extracts those key elements from the Launch container so we can instrument those selectively at the right time in the page load for optimal performance, but we still leverage the official documented APIs and configurations as Adobe Launch would. + +We are basically building on top of: +- [Top and bottom of page events](https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/use-cases/top-bottom-page-events) so we can enable personalization early in the page load, and wait for the page to fully render to report metrics +- [Data object variable mapping](https://experienceleague.adobe.com/en/docs/analytics/implementation/aep-edge/data-var-mapping) so we can gather key page metadata for your page in Adobe Analytics +- Adobe Launch to trigger additional rules based on data elements in a delayed manner so we still support marketing use cases you'd expect to cover via Adobe Launch alone + +On top of this, we also fine-tuned the code to: +- avoid content flicker as the DOM is dynamically rendered to support AEM EDS and/or SPA use cases +- dynamically load personalization and data layer dependencies only when needed + +### So what's the catch? + +Since we essentially just split up the Adobe Launch container to execute it in a more controlled way, not all features can be controlled from the Launch UI, and some of the logic moves to the project code. + +Also, some default Adobe Launch extensions won't work directly with such a setup. +We recommend the following baseline: +- Core Extension +- Adobe Client Data Layer: so you can react to data layer events +- AA via AEP Web SDK: so you can have rules setting variables, product strings and send beacons +- (optionally) Mapping Table: so you can remap selected values in your data elements diff --git a/aemedge/plugins/martech/package.json b/aemedge/plugins/martech/package.json new file mode 100644 index 00000000..3de3d873 --- /dev/null +++ b/aemedge/plugins/martech/package.json @@ -0,0 +1,35 @@ +{ + "name": "@adobe/aem-martech", + "version": "1.0.0", + "main": "src/index.js", + "scripts": { + "lint": "eslint src" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/adobe/aem-martech.git" + }, + "author": "Adobe Inc.", + "license": "Apache-2.0", + "keywords": [ + "aem", + "martech", + "experience", + "plugin", + "campaigns", + "audiences", + "datalayer", + "analytics", + "launch" + ], + "bugs": { + "url": "https://github.com/adobe/aem-martech/issues" + }, + "homepage": "https://github.com/adobe/aem-martech#readme", + "devDependencies": { + "@babel/eslint-parser": "7.22.15", + "eslint": "8.48.0", + "eslint-config-airbnb-base": "15.0.0", + "eslint-plugin-import": "2.28.1" + } +} diff --git a/aemedge/plugins/martech/src/acdl.min.js b/aemedge/plugins/martech/src/acdl.min.js new file mode 100644 index 00000000..948094c8 --- /dev/null +++ b/aemedge/plugins/martech/src/acdl.min.js @@ -0,0 +1 @@ +(()=>{var e={},t={};t={get:function(e,t,n){let r=Array.isArray(t)?t:t.split("."),a=e;for(let e of r)if(void 0===(a=a[e]))return n;return a},has:function(e,t){let n=Array.isArray(t)?t:t.split("."),r=e;for(let e of n){if(!r?.hasOwnProperty(e))return!1;r=r[e]}return!0}};var n=JSON.parse('{"version":"2.0.2"}').version,r={},a={};a=function(e,t){let n=Object.keys(t).find(n=>{let r=t[n].type,a=n&&t[n].values,i=!t[n].optional,o=e[n],l=typeof o,c=r&&l!==r,f=a&&!a.includes(o);return i?!o||c||f:o&&(c||f)});return void 0===n};var i={};i={event:{event:{type:"string"},eventInfo:{optional:!0}},listenerOn:{on:{type:"string"},handler:{type:"function"},scope:{type:"string",values:["past","future","all"],optional:!0},path:{type:"string",optional:!0}},listenerOff:{off:{type:"string"},handler:{type:"function",optional:!0},scope:{type:"string",values:["past","future","all"],optional:!0},path:{type:"string",optional:!0}}};var o={};o={itemType:{DATA:"data",FCTN:"fctn",EVENT:"event",LISTENER_ON:"listenerOn",LISTENER_OFF:"listenerOff"},dataLayerEvent:{CHANGE:"adobeDataLayer:change",EVENT:"adobeDataLayer:event"},listenerScope:{PAST:"past",FUTURE:"future",ALL:"all"}};let l=e=>[Object,Array].includes((e||{}).constructor)&&!Object.entries(e||{}).length;r=function(e,t){let n=Object.keys(i).find(t=>a(e,i[t]))||"function"==typeof e&&o.itemType.FCTN||function(e){if("object"!=typeof e||null===e)return!1;let t=e;for(;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}(e)&&o.itemType.DATA,r=function(){let t=Object.keys(e).filter(e=>!Object.keys(i.event).includes(e)).reduce((t,n)=>(t[n]=e[n],t),{});if(!l(t))return t}();return{config:e,type:n,data:r,valid:!!n,index:t}};var c={};c=function(e){let t=e.config.on||e.config.off,n=e.config.handler||null,r=e.config.scope||e.config.on&&o.listenerScope.ALL||null,a=e.config.path||null;return{event:t,handler:n,scope:r,path:a}};var f={},s={};s={mergeWith:function e(t,n,r){if(n&&t)return Object.keys(n).forEach(a=>{let i=r?r(t[a],n[a],a,t):void 0;void 0===i&&(i=n[a]===Object(n[a])&&a in t&&!Array.isArray(n[a])?e(t[a],n[a],r):n[a]),t[a]=i}),t},cloneDeepWith:function e(t,n){let r=n?n(t):void 0;if(void 0===r){if(t===Object(t)&&!Array.isArray(t)){r={};let a=Object.keys(t);for(let i=0;i-1&&t[r].splice(a,1)}else t[r]=[]}},triggerListeners:function(e){let n=function(e){let t=[];switch(e.type){case o.itemType.DATA:t.push(o.dataLayerEvent.CHANGE);break;case o.itemType.EVENT:t.push(o.dataLayerEvent.EVENT),e.data&&t.push(o.dataLayerEvent.CHANGE),e.config.event!==o.dataLayerEvent.CHANGE&&t.push(e.config.event)}return t}(e);n.forEach(function(n){if(Object.prototype.hasOwnProperty.call(t,n))for(let a of t[n])r(a,e)})},triggerListener:function(e,t){r(e,t)}}};var g={},h=s.cloneDeepWith,v=s.mergeWith;g=function(e,t){return v(e,t,function(e,t){if(null==t)return null}),e=function(e,t=e=>!e){return h(e,function e(n){if(n===Object(n)){if(Array.isArray(n))return n.filter(e=>!t(e)).map(t=>h(t,e));let r={};for(let a of Object.keys(n))t(n[a])||(r[a]=h(n[a],e));return r}})}(e,e=>null==e)},e=function(e){let a;let i=e||{},l=[],s=[],u={},p={getState:function(){return u},getDataLayer:function(){return l}};function y(e){u=g(u,e.data)}function d(e){if(!e.valid){h(e);return}function t(e){return 0===l.length||e.index>l.length-1?[]:l.slice(0,e.index).map(e=>r(e))}({data:function(e){y(e),a.triggerListeners(e)},fctn:function(e){e.config.call(l,l)},event:function(e){e.data&&y(e),a.triggerListeners(e)},listenerOn:function(e){let n=c(e);switch(n.scope){case o.listenerScope.PAST:for(let r of t(e))a.triggerListener(n,r);break;case o.listenerScope.FUTURE:a.register(n);break;case o.listenerScope.ALL:{let r=a.register(n);if(r)for(let r of t(e))a.triggerListener(n,r)}}},listenerOff:function(e){a.unregister(c(e))}})[e.type](e)}function h(e){let t="The following item cannot be handled by the data layer because it does not have a valid format: "+JSON.stringify(e.config);console.error(t)}return Array.isArray(i.dataLayer)||(i.dataLayer=[]),s=i.dataLayer.splice(0,i.dataLayer.length),(l=i.dataLayer).version=n,u={},a=f(p),l.push=function(...e){if(Object.keys(e).forEach(function(t){let n=e[t],a=r(n);switch(a.valid||(h(a),delete e[t]),a.type){case o.itemType.DATA:case o.itemType.EVENT:d(a);break;case o.itemType.FCTN:delete e[t],d(a);break;case o.itemType.LISTENER_ON:case o.itemType.LISTENER_OFF:delete e[t]}}),e[0])return Array.prototype.push.apply(this,e)},l.getState=function(e){return e?(0,t.get)(structuredClone(u),e):structuredClone(u)},l.addEventListener=function(e,t,n){let a=r({on:e,handler:t,scope:n&&n.scope,path:n&&n.path});d(a)},l.removeEventListener=function(e,t){let n=r({off:e,handler:t});d(n)},function(){for(let e=0;enull==e,t=t=>!e(t)&&!Array.isArray(t)&&"object"==typeof t; + /*! js-cookie v3.0.5 | MIT */ + function n(e){for(var t=1;t{Object.keys(n).forEach((r=>{t(e[r])&&t(n[r])?i(e[r],n[r]):e[r]=n[r]}))};var a=(t,...n)=>{if(e(t))throw new TypeError('deepAssign "target" cannot be null or undefined');const r=Object(t);return n.forEach((e=>i(r,Object(e)))),r},s=(e,t)=>n=>{const r=t.split(".").reduce(((e,t)=>(e[t]=e[t]||{},e[t])),e);a(r,n)},c=()=>{const e=[];return{add(t){e.push(t)},call:(...t)=>Promise.all(e.map((e=>e(...t))))}},d=({logger:e,cookieJar:t})=>({...t,set(n,r,o){e.info("Setting cookie",{name:n,value:r,...o}),t.set(n,r,o)}});const l=(()=>{const e=[];for(let t=0;t<256;t++){let n=t;for(let e=0;e<8;e++)n=1&n?3988292384^n>>>1:n>>>1;e.push(n)}return function(t,n){t=unescape(encodeURIComponent(t)),n||(n=0),n=~n;for(let r=0;r>>8^e[o]}return(n=~n)>>>0}})();var u=()=>{const e={};return e.promise=new Promise(((t,n)=>{e.resolve=t,e.reject=n})),e};const p=(e,t)=>e===t;var g=(e,t)=>e.appendChild(t);var m=(e,n={},r={},o=[],i=document)=>{const a=i.createElement(e);return Object.keys(n).forEach((e=>{a.setAttribute(e,n[e])})),((e,n)=>{Object.keys(n).forEach((r=>{if("style"===r&&t(n[r])){const t=n[r];Object.keys(t).forEach((n=>{e.style[n]=t[n]}))}else e[r]=n[r]}))})(a,r),o.forEach((e=>g(a,e))),a};const f="IMG",h="STYLE",y="SCRIPT";var v=({src:e,currentDocument:t=document})=>new Promise(((n,r)=>{m(f,{src:e},{onload:n,onerror:r,onabort:r},[],t)})),w=e=>"function"==typeof e,b=e=>Array.isArray(e)&&e.length>0,E=e=>Array.isArray(e)?e:null==e?[]:[].slice.call(e);const C=/^\s*>/;var k=(e,t)=>C.test(t)?E(e.querySelectorAll(":scope "+t)):E(e.querySelectorAll(t)),I=":shadow";const S=(e,t)=>{const n=t;if(!n.startsWith(">"))return n;return(e instanceof Element||e instanceof Document?":scope":":host")+" "+n};var D=(e,t)=>{const n=(e=>e.split(I))(t);if(n.length<2)return k(e,t);let r=e;for(let e=0;e-1===e.indexOf(I)?k(t,e):D(t,e);const T="MutationObserver",R={childList:!0,subtree:!0},O=e=>new Error("Could not find: "+e),N=e=>new Promise(e);var M=(e,t=P,n=5e3,r=window,o=document)=>{const i=t(e);return b(i)?Promise.resolve(i):(e=>w(e[T]))(r)?((e,t,n,r,o)=>N(((i,a)=>{let s;const c=new e[T]((()=>{const e=o(n);b(e)&&(c.disconnect(),s&&clearTimeout(s),i(e))}));s=setTimeout((()=>{c.disconnect(),a(O(n))}),r),c.observe(t,R)})))(r,o,e,n,t):(e=>"visible"===e.visibilityState)(o)?((e,t,n,r)=>N(((o,i)=>{const a=()=>{const n=r(t);b(n)?o(n):e.requestAnimationFrame(a)};a(),setTimeout((()=>{i(O(t))}),n)})))(r,e,n,t):((e,t,n)=>N(((r,o)=>{const i=()=>{const t=n(e);b(t)?r(t):setTimeout(i,100)};i(),setTimeout((()=>{o(O(e))}),t)})))(e,n,t)},A=e=>{const t=e.parentNode;return t?t.removeChild(e):null};const x={name:"Adobe Alloy"},q={style:{display:"none",width:0,height:0}};var L=e=>t(e)&&0===Object.keys(e).length;const U=(n,r)=>e(n)||!t(n)?n:Object.keys(n).reduce(((e,o)=>{const i=n[o];if(t(i)){const t=U(i,r);return L(t)?e:{...e,[o]:t}}return r(i)?{...e,[o]:i}:e}),{});var j="com.adobe.alloy.";const B=j+"getTld";var F="kndctr",_=e=>e.replace("@","_"),V=(e,t)=>F+"_"+_(e)+"_"+t,z=(e,t)=>{const n={};return e.forEach((e=>{const r=t(e);n[r]||(n[r]=[]),n[r].push(e)})),n};const H="Chrome",J="Edge",G="EdgeChromium",Q="Firefox",X="IE",W="Safari",Y="Unknown";var K=e=>{let t,n=!1;return()=>(n||(n=!0,t=e()),t)};const $=[H,J,G,"IE",Y];var Z=({orgId:e})=>{const t=V(e,"identity");return()=>Boolean(o.get(t))};const ee=(e,t,n)=>({getItem(r){try{return e[t].getItem(n+r)}catch{return null}},setItem(r,o){try{return e[t].setItem(n+r,o),!0}catch{return!1}},clear(){try{return Object.keys(e[t]).forEach((r=>{r.startsWith(n)&&e[t].removeItem(r)})),!0}catch{return!1}}});var te=e=>t=>{const n=j+t;return{session:ee(e,"sessionStorage",n),persistent:ee(e,"localStorage",n)}},ne=e=>"boolean"==typeof e,re=e=>"number"==typeof e&&!Number.isNaN(e),oe=e=>{const t=parseInt(e,10);return re(t)&&e===t},ie=e=>"string"==typeof e,ae=e=>ie(e)&&e.length>0,se=()=>{};const ce=e=>{const t={},n=e.split(".");switch(n.length){case 1:t.subdomain="",t.domain=e,t.topLevelDomain="";break;case 2:t.subdomain="",t.domain=e,t.topLevelDomain=n[1];break;case 3:t.subdomain="www"===n[0]?"":n[0],t.domain=e,t.topLevelDomain=n[2];break;case 4:t.subdomain="www"===n[0]?"":n[0],t.domain=e,t.topLevelDomain=n[2]+"."+n[3]}return t},de=(e,t=ce)=>{ie(e)||(e="");const n=(e=>{try{const t=new URL(e);let n=t.pathname;return e.endsWith("/")||"/"!==n||(n=""),{host:t.hostname,path:n,query:t.search.replace(/^\?/,""),anchor:t.hash.replace(/^#/,"")}}catch{return{}}})(e)||{},{host:r="",path:o="",query:i="",anchor:a=""}=n;return{path:o,query:i,fragment:a,...t(r)}};var le,ue;function pe(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ge=(ue||(ue=1,le={parse:function(e){return function(e){var t={};if(!e||"string"!=typeof e)return t;var n=e.trim().replace(/^[?#&]/,""),r=new URLSearchParams(n),o=r.keys();do{var i=o.next(),a=i.value;if(a){var s=r.getAll(a);1===s.length?t[a]=s[0]:t[a]=s}}while(!1===i.done);return t}(e)},stringify:function(e){return function(e){var t="{{space}}",n=new URLSearchParams;return Object.keys(e).forEach((function(r){var o=e[r];"string"==typeof e[r]?o=o.replace(/ /g,t):["object","undefined"].includes(typeof o)&&!Array.isArray(o)&&(o=""),Array.isArray(o)?o.forEach((function(e){n.append(r,e)})):n.append(r,o)})),n.toString().replace(new RegExp(encodeURIComponent(t),"g"),"%20")}(e)}}),le),me=pe(ge),fe=e=>e instanceof Error?e:new Error(e),he=({error:e,message:t})=>{try{e.message=t}catch{}},ye=({error:e,message:t})=>{const n=fe(e),r=t+"\nCaused by: "+n.message;return he({error:n,message:r}),n},ve=(e,t)=>{if(re(e)||ie(e)){const t=Math.round(Number(e));if(!Number.isNaN(t))return t}return t};const we=(e,t,n)=>(""+e).padStart(t,n);const be=[];for(let e=0;e<256;++e)be.push((e+256).toString(16).slice(1));let Ee;const Ce=new Uint8Array(16);var ke={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function Ie(e,t,n){if(ke.randomUUID&&!t&&!e)return ke.randomUUID();const r=(e=e||{}).random??e.rng?.()??function(){if(!Ee){if("undefined"==typeof crypto||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");Ee=crypto.getRandomValues.bind(crypto)}return Ee(Ce)}();if(r.length<16)throw new Error("Random bytes length must be >= 16");if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){if((n=n||0)<0||n+16>t.length)throw new RangeError("UUID byte range "+n+":"+(n+15)+" is out of buffer bounds");for(let e=0;e<16;++e)t[n+e]=r[e];return t}return function(e,t=0){return(be[e[t+0]]+be[e[t+1]]+be[e[t+2]]+be[e[t+3]]+"-"+be[e[t+4]]+be[e[t+5]]+"-"+be[e[t+6]]+be[e[t+7]]+"-"+be[e[t+8]]+be[e[t+9]]+"-"+be[e[t+10]]+be[e[t+11]]+be[e[t+12]]+be[e[t+13]]+be[e[t+14]]+be[e[t+15]]).toLowerCase()}(r)}const Se=e=>function(t,n){return null==t?t:e.call(this,t,n)},De=(e,t)=>function(n,r){return t.call(this,e.call(this,n,r),r)},Pe=(e,t)=>function(n,r){const o=[],i=[e,t].reduce(((e,t)=>{try{return t.call(this,e,r)}catch(t){return o.push(t),e}}),n);if(o.length)throw new Error(o.join("\n"));return i},Te=(e,t,n)=>Object.assign(De(e,t),e,n),Re=(e,t,n)=>Object.assign(De(e,Se(t)),e,n),Oe=(e,t,n,r)=>{if(!e)throw new Error("'"+n+"': Expected "+r+", but got "+JSON.stringify(t)+".")};var Ne=(e,t)=>(Oe(ne(e),e,t,"true or false"),e),Me=(e,t)=>(Oe(w(e),e,t,"a function"),e),Ae=(e,t)=>function(n,r){let o;const i=e.find((e=>{try{return o=e.call(this,n,r),!0}catch{return!1}}));return Oe(i,n,r,t),o},xe=e=>function(t,n){Oe(Array.isArray(t),t,n,"an array");const r=[],o=t.map(((o,i)=>{try{return e.call(this,o,n+"["+i+"]",t)}catch(e){return void r.push(e.message)}}));if(r.length)throw new Error(r.join("\n"));return o},qe=(e="This field has been deprecated")=>function(t,n){let r=e;return void 0!==t&&(n&&(r="'"+n+"': "+r),this&&this.logger&&this.logger.warn(r)),t},Le=e=>function(n,r){Oe(t(n),n,r,"an object");const o=[],i={};if(Object.keys(n).forEach((t=>{const a=n[t],s=r?r+"."+t:t;try{const n=e.call(this,a,s);void 0!==n&&(i[t]=n)}catch(e){o.push(e.message)}})),o.length)throw new Error(o.join("\n"));return i},Ue=(e,t)=>(n,r)=>(Oe(n>=t,n,r,e+" greater than or equal to "+t),n),je=e=>(n,r)=>(t(n)?Oe(!L(n),n,r,e):Oe(n.length>0,n,r,e),n),Be=e=>function(n,r){Oe(t(n),n,r,"an object");const o=[],i={};if(Object.keys(e).forEach((t=>{const a=n[t],s=e[t],c=r?r+"."+t:t;try{const e=s.call(this,a,c);void 0!==e&&(i[t]=e)}catch(e){o.push(e.message)}})),Object.keys(n).forEach((e=>{Object.prototype.hasOwnProperty.call(i,e)||(i[e]=n[e])})),o.length)throw new Error(o.join("\n"));return i},Fe=(e,n,r)=>function(o,i){Oe(t(o),o,i,"an object");const{[e]:a,[r]:s,...c}=o,d=n(a,i);if(void 0!==d){let t="The field '"+e+"' is deprecated. Use '"+r+"' instead.";if(i&&(t="'"+i+"': "+t),void 0!==s&&s!==d)throw new Error(t);this&&this.logger&&this.logger.warn(t)}return{[r]:s||d,...c}},_e=()=>(e,t)=>(Oe((e=>{const t=Object.create(null);for(let n=0;n(Oe(Ve.test(e),e,t,"a valid domain"),e),He=(e,t)=>(Oe(oe(e),e,t,"an integer"),e),Je=(e,t)=>(Oe(re(e),e,t,"a number"),e),Ge=(e,t)=>(Oe((e=>{try{return null!==RegExp(e)}catch{return!1}})(e),e,t,"a regular expression"),e),Qe=(e,t)=>{if(null==e)throw new Error("'"+t+"' is a required option");return e},Xe=(e,t)=>(Oe(ie(e),e,t,"a string"),e);const We=e=>e;We.default=function(e){return Te(this,(e=>t=>null==t?e:t)(e))},We.required=function(){return Te(this,Qe)},We.deprecated=function(e){return Te(this,qe(e))};const Ye=function(){return Re(this,ze)},Ke=function(e){return Re(this,Ue("an integer",e))},$e=function(e){return Re(this,Ue("a number",e))},Ze=function(e){return Re(this,(t="a number",n=e,(e,r)=>(Oe(e<=n,e,r,t+" less than or equal to "+n),e)));var t,n},et=function(){return Re(this,He,{minimum:Ke})},tt=function(){return Re(this,je("a non-empty string"))},nt=function(){return Re(this,je("a non-empty array"))},rt=function(){return Re(this,je("a non-empty object"))},ot=function(){return Re(this,Ge)},it=function(e){return Re(this,(e=>(t,n)=>(Oe(e.test(t),t,n,"does not match the "+e.toString()),t))(e))},at=function(){return Re(this,(()=>{const e=[];return(t,n)=>(Oe(-1===e.indexOf(t),t,n,"a unique value across instances"),e.push(t),t)})())},st=function(){return Re(this,_e())},ct=e=>({noUnknownFields:function(){return Re(this,(e=>(t,n)=>{const r=[];if(Object.keys(t).forEach((t=>{if(!e[t]){const e=n?n+"."+t:t;r.push("'"+e+"': Unknown field.")}})),r.length)throw new Error(r.join("\n"));return t})(e))},nonEmpty:rt,concat:function(t){const n={...e,...t.schema};return Re(this,t,ct(n))},renamed:function(e,t,n){return r=this,o=Fe(e,t,n),Object.assign(Pe(Se(o),r),r,i);var r,o,i},schema:e}),dt=function(e,t){return Te(this,Ae(e,t))}.bind(We),lt=function(){return this}.bind(We),ut=function(e){return Re(this,xe(e),{nonEmpty:nt,uniqueItems:st})}.bind(We),pt=function(){return Re(this,Ne)}.bind(We),gt=function(){return Re(this,Me)}.bind(We),mt=function(e){return Re(this,(e=>(t,n)=>(Oe(t===e,t,n,""+e),t))(e))}.bind(We),ft=function(){return Re(this,Je,{minimum:$e,maximum:Ze,integer:et,unique:at})}.bind(We),ht=function(e){return Re(this,Le(e),{nonEmpty:rt})}.bind(We),yt=function(e){return Re(this,Be(e),ct(e))}.bind(We),vt=function(){return Re(this,Xe,{regexp:ot,domain:Ye,nonEmpty:tt,unique:at,matches:it})}.bind(We),wt=function(...e){return dt(e.map(mt),"one of these values: "+JSON.stringify(e))};var bt=ht(ut(yt({authenticatedState:wt("ambiguous","authenticated","loggedOut"),id:vt(),namespace:yt({code:vt()}).noUnknownFields(),primary:pt(),xid:vt()}).noUnknownFields()).required()),Et=yt({}),Ct="alloy_debug",kt=({console:e,locationSearch:t,createLogger:n,instanceName:r,createNamespacedStorage:o,getMonitors:i})=>{const a=me.parse(t),s=o("instance."+r+"."),c=s.session.getItem("debug");let d="true"===c,l=null===c;const u=()=>d,p=(e,{fromConfig:t})=>{t&&!l||(d=e),t||(s.session.setItem("debug",e.toString()),l=!1)};var g;return void 0!==a[Ct]&&p((g=a[Ct],ie(g)&&"true"===g.toLowerCase()),{fromConfig:!1}),{setDebugEnabled:p,logger:n({getDebugEnabled:u,context:{instanceName:r},getMonitors:i,console:e}),createComponentLogger:t=>n({getDebugEnabled:u,context:{instanceName:r,componentName:t},getMonitors:i,console:e})}};const It=["onComponentsRegistered","onBeforeEvent","onBeforeRequest","onResponse","onRequestFailure","onClick","onDecision"];var St=e=>It.reduce(((t,n)=>{var r;return t[n]=(r=((e,t)=>(...n)=>Promise.all(e.getLifecycleCallbacks(t).map((e=>new Promise((t=>{t(e(...n))}))))))(e,n),(...e)=>Promise.resolve().then((()=>r(...e)))),t}),{});const Dt=(e,t)=>(...n)=>{let r;try{r=e(...n)}catch(e){throw ye({error:e,message:t})}return r instanceof Promise&&(r=r.catch((e=>{throw ye({error:e,message:t})}))),r};var Pt=()=>{const e={},t={},n={};return{register(r,o){const{commands:i,lifecycle:a}=o;((e,n={})=>{const r=(o=Object.keys(t),i=Object.keys(n),o.filter((e=>i.includes(e))));var o,i;if(r.length)throw new Error("[ComponentRegistry] Could not register "+e+" because it has existing command(s): "+r.join(","));Object.keys(n).forEach((r=>{const o=n[r];o.commandName=r,o.run=Dt(o.run,"["+e+"] An error occurred while executing the "+r+" command."),t[r]=o}))})(r,i),((e,t={})=>{Object.keys(t).forEach((r=>{n[r]=n[r]||[],n[r].push(Dt(t[r],"["+e+"] An error occurred while executing the "+r+" lifecycle hook."))}))})(r,a),e[r]=o},getCommand:e=>t[e],getCommandNames:()=>Object.keys(t),getLifecycleCallbacks:e=>n[e]||[],getComponentNames:()=>Object.keys(e)}};const Tt="in",Rt="out",Ot="pending",Nt="general",Mt="declinedConsent",At="default",xt="initial",qt="new",Lt=e=>{const t=new Error(e);return t.code=Mt,t.message=e,t};var Ut=({logger:e})=>{const t=[],n=()=>Promise.resolve(),r=()=>Promise.resolve(),o=()=>Promise.reject(Lt("No consent preferences have been set.")),i=()=>Promise.reject(Lt("The user declined consent.")),a=e=>{if(e)return Promise.reject(new Error("Consent is pending."));const n=u();return t.push(n),n.promise};return{in(o){o===At?this.awaitConsent=n:(o===xt?e.info("Loaded user consent preferences. The user previously consented."):o===qt&&this.awaitConsent!==r&&e.info("User consented."),(()=>{for(;t.length;)t.shift().resolve()})(),this.awaitConsent=r)},out(n){n===At?(e.warn("User consent preferences not found. Default consent of out will be used."),this.awaitConsent=o):(n===xt?e.warn("Loaded user consent preferences. The user previously declined consent."):n===qt&&this.awaitConsent!==i&&e.warn("User declined consent."),(()=>{for(;t.length;)t.shift().reject(Lt("The user declined consent."))})(),this.awaitConsent=i)},pending(t){t===At&&e.info("User consent preferences not found. Default consent of pending will be used. Some commands may be delayed."),this.awaitConsent=a},awaitConsent:()=>Promise.resolve(),withConsent(){return this.awaitConsent(!0)},current(){switch(this.awaitConsent){case n:return{state:"in",wasSet:!1};case r:return{state:"in",wasSet:!0};case o:return{state:"out",wasSet:!1};case i:return{state:"out",wasSet:!0};case a:return{state:"pending",wasSet:!1};default:return{state:"in",wasSet:!1}}}}};const jt=e=>e&&e._experience&&e._experience.decisioning&&b(e._experience.decisioning.propositions)?e._experience.decisioning.propositions:[];var Bt=()=>{const e={};let t,n,r=!1,o=!1,i=!0;const s=e=>{if(o)throw new Error(e+" cannot be called after event is finalized.")},c={hasQuery(){return Object.prototype.hasOwnProperty.call(this.getContent(),"query")},getContent(){const r=JSON.parse(JSON.stringify(e));return t&&a(r,{xdm:t}),n&&a(r,{data:n}),r},setUserXdm(e){s("setUserXdm"),t=e},setUserData(e){s("setUserData"),n=e},mergeXdm(t){s("mergeXdm"),t&&a(e,{xdm:t})},mergeData(t){s("mergeData"),t&&a(e,{data:t})},mergeMeta(t){s("mergeMeta"),t&&a(e,{meta:t})},mergeQuery(t){s("mergeQuery"),t&&a(e,{query:t})},documentMayUnload(){r=!0},finalize(r){if(o)return;const a=((e,t=p)=>e.filter(((n,r)=>((e,t,n)=>{for(let r=0;re===t||e.id&&t.id&&e.id===t.id&&e.scope&&t.scope&&e.scope===t.scope));if(t&&this.mergeXdm(t),a.length>0&&(e.xdm._experience.decisioning.propositions=a),n&&c.mergeData(n),o=!0,r){i=!1;const t={xdm:e.xdm||{},data:e.data||{}},n=r(t);i=!1!==n,e.xdm=t.xdm||{},e.data=t.data||{},L(e.xdm)&&delete e.xdm,L(e.data)&&delete e.data}},getDocumentMayUnload:()=>r,isEmpty:()=>L(e)&&(!t||L(t))&&(!n||L(n)),shouldSend:()=>i,getViewName(){if(t&&t.web&&t.web.webPageDetails)return t.web.webPageDetails.viewName},toJSON(){if(!o)throw new Error("toJSON called before finalize");return e}};return c};const Ft="configure",_t="setDebug";var Vt=({logger:e,configureCommand:n,setDebugCommand:r,handleError:o,validateCommandOptions:i})=>{let a;return(s,c={})=>new Promise((t=>{const o=((t,o)=>{let s;if(t===Ft){if(a)throw new Error("The library has already been configured and may only be configured once.");s=()=>(a=n(o),a.then((()=>{})))}else{if(!a)throw new Error("The library must be configured first. Please do so by executing the configure command.");s=t===_t?()=>{const e=yt({enabled:pt().required()}).noUnknownFields(),t=i({command:{commandName:_t,optionsValidator:e},options:o});r(t)}:()=>a.then((e=>{const n=e.getCommand(t);if(!n||!w(n.run)){const n=[Ft,_t].concat(e.getCommandNames()).join(", ");throw new Error("The "+t+" command does not exist. List of available commands: "+n+".")}const r=i({command:n,options:o});return n.run(r)}),(()=>(e.warn("An error during configuration is preventing the "+t+" command from executing."),new Promise((()=>{})))))}return s})(s,c);e.logOnBeforeCommand({commandName:s,options:c}),t(o())})).catch((e=>o(e,s+" command"))).catch((t=>{throw e.logOnCommandRejected({commandName:s,options:c,error:t}),t})).then((n=>{const r=t(n)?n:{};return e.logOnCommandResolved({commandName:s,options:c,result:r}),r}))};const zt="https://adobe.ly/3sHgQHb";var Ht=({command:e,options:t})=>{const{commandName:n,documentationUri:r=zt,optionsValidator:o}=e;let i=t;if(o)try{i=o(t)}catch(e){throw new Error("Invalid "+n+" command options:\n\t - "+e+" For command documentation see: "+r)}return i};var Jt=({options:e,componentCreators:t,coreConfigValidators:n,createConfig:r,logger:o,setDebugEnabled:i})=>{const a=(e=>{const t=[],n={get enabled(){return e.enabled},flush(){t.forEach((({method:t,args:n})=>e[t](...n)))}};return Object.keys(e).filter((t=>"function"==typeof e[t])).forEach((e=>{n[e]=(...n)=>{t.push({method:e,args:n})}})),n})(o),s=t.map((({configValidators:e})=>e)).filter((e=>e)).reduce(((e,t)=>e.concat(t)),n),c=r((({combinedConfigValidator:e,options:t,logger:n})=>{try{return e.noUnknownFields().required().call({logger:n},t)}catch(e){throw new Error("Resolve these configuration problems:\n\t - "+e.message.split("\n").join("\n\t - ")+"\nFor configuration documentation see: https://adobe.ly/3sHh553")}})({combinedConfigValidator:s,options:e,logger:a}));i(c.debugEnabled,{fromConfig:!0}),a.flush();const d=((e,t,n)=>n.reduce(((n,{buildOnInstanceConfiguredExtraParams:r})=>(r&&(n={...n,...r({config:e,logger:t})}),n)),{}))(c,o,t);return o.logOnInstanceConfigured({...d,config:c}),c};const Gt=e=>({...e});var Qt=({errorPrefix:e,logger:t})=>(n,r)=>{const o=fe(n);if(o.code===Mt)return t.warn("The "+r+" could not fully complete. "+o.message),{};throw he({error:o,message:e+" "+o.message}),o},Xt=({getDebugEnabled:e,console:t,getMonitors:n,context:r})=>{let o="["+r.instanceName+"]";r.componentName&&(o+=" ["+r.componentName+"]");const i=(e,t)=>{const o=n();if(o.length>0){const n={...r,...t};o.forEach((t=>{t[e]&&t[e](n)}))}},a=(n,...r)=>{i("onBeforeLog",{level:n,arguments:r}),e()&&t[n](o,...r)};return{get enabled(){return n().length>0||e()},logOnInstanceCreated(e){i("onInstanceCreated",e),a("info","Instance initialized.")},logOnInstanceConfigured(e){i("onInstanceConfigured",e),a("info","Instance configured. Computed configuration:",e.config)},logOnBeforeCommand(e){i("onBeforeCommand",e),a("info","Executing "+e.commandName+" command. Options:",e.options)},logOnCommandResolved(e){i("onCommandResolved",e),a("info",e.commandName+" command resolved. Result:",e.result)},logOnCommandRejected(e){i("onCommandRejected",e),a("error",e.commandName+" command was rejected. Error:",e.error)},logOnBeforeNetworkRequest(e){i("onBeforeNetworkRequest",e),a("info","Request "+e.requestId+": Sending request.",e.payload)},logOnNetworkResponse(e){i("onNetworkResponse",e);const t=e.parsedBody||e.body?"response body:":"no response body.";a("info","Request "+e.requestId+": Received response with status code "+e.statusCode+" and "+t,e.parsedBody||e.body)},logOnNetworkError(e){i("onNetworkError",e),a("error","Request "+e.requestId+": Network request failed.",e.error)},logOnContentHiding(e){i("onContentHiding",{status:e.status}),a(e.logLevel,e.message)},logOnContentRendering(e){i("onContentRendering",{status:e.status,payload:e.detail}),a(e.logLevel,e.message)},info:a.bind(null,"info"),warn:a.bind(null,"warn"),error:a.bind(null,"error")}},Wt="__view__",Yt=e=>(t,n)=>{e.xdm=e.xdm||{},e.xdm.identityMap=e.xdm.identityMap||{},e.xdm.identityMap[t]=e.xdm.identityMap[t]||[],e.xdm.identityMap[t].push(n)},Kt=e=>{const{payload:t,getAction:n,getUseSendBeacon:r,datastreamIdOverride:o,edgeSubPath:i}=e,a=Ie();let s=!1,c=!1;return{getId:()=>a,getPayload:()=>t,getAction:()=>n({isIdentityEstablished:c}),getDatastreamIdOverride:()=>o,getUseSendBeacon:()=>r({isIdentityEstablished:c}),getEdgeSubPath:()=>i||"",getUseIdThirdPartyDomain:()=>s,setUseIdThirdPartyDomain(){s=!0},setIsIdentityEstablished(){c=!0}}},$t=({payload:e,datastreamIdOverride:t})=>{const n=({isIdentityEstablished:t})=>e.getDocumentMayUnload()&&t;return Kt({payload:e,getAction:({isIdentityEstablished:e})=>n({isIdentityEstablished:e})?"collect":"interact",getUseSendBeacon:n,datastreamIdOverride:t})},Zt=t=>{const{content:n,addIdentity:r,hasIdentity:o}=t,i=s(n,"meta.configOverrides");return{mergeMeta:s(n,"meta"),mergeState:s(n,"meta.state"),mergeQuery:s(n,"query"),mergeConfigOverride:t=>i((t=>{if(e(t)||"object"!=typeof t)return null;const n=U(t,(t=>!(e(t)||!ne(t)&&!re(t)&&!ae(t)&&!b(t))));return L(n)?null:n})(t)),addIdentity:r,hasIdentity:o,toJSON:()=>n}},en=e=>t=>void 0!==(e.xdm&&e.xdm.identityMap&&e.xdm.identityMap[t]),tn=()=>{const e={},t=Zt({content:e,addIdentity:Yt(e),hasIdentity:en(e)});return t.addEvent=t=>{e.events=e.events||[],e.events.push(t)},t.getDocumentMayUnload=()=>(e.events||[]).some((e=>e.getDocumentMayUnload())),t},nn=({localConfigOverrides:e,globalConfigOverrides:t,payload:n})=>{const r={payload:n},{datastreamId:o,...i}=e||{};return o&&(r.datastreamIdOverride=o),t&&!L(t)&&n.mergeConfigOverride(t),i&&!L(i)&&n.mergeConfigOverride(i),r};const rn="clientId";const on="Event was canceled because the onBeforeEventSend callback returned false.";var an=({orgId:e,targetMigrationEnabled:t})=>n=>((e,t)=>0===t.indexOf(F+"_"+_(e)+"_"))(e,n)||"at_qa_mode"===n||t&&"mbox"===n;var sn=e=>((...e)=>e.length<2?Object.assign(...e):e.reduce(((e,n)=>(t(n)&&Object.keys(n).forEach((t=>{Array.isArray(n[t])?Array.isArray(e[t])?e[t].push(...n[t]):e[t]=[...n[t]]:e[t]=n[t]})),e))))({},...e.shift()||[],...e.shift()||[],...e),cn=e=>t=>{const n=()=>{throw t};return e.call({error:t}).then(n,n)};const dn="The server responded with a";var ln=({orgId:e,cookieJar:t})=>{const n=V(e,"cluster");return()=>t.get(n)||(()=>{const e=t.get("mboxEdgeCluster");if(e)return"t"+e})()};const un=[429,503,502,504];var pn=({response:e,retriesAttempted:t})=>t<3&&un.includes(e.statusCode);var gn=({response:e,retriesAttempted:t})=>{let n=(e=>{const t=e.getHeader("Retry-After");let n;if(t){const e=parseInt(t,10);n=oe(e)?1e3*e:Math.max(0,new Date(t).getTime()-(new Date).getTime())}return n})(e);return void 0===n&&(n=(e=>{const t=1e3+1e3*e,n=.3*t,r=t-n,o=t+n;return Math.round(r+Math.random()*(o-r))})(t)),n};var mn=e=>t=>{const n=e().toISOString();a(t,{timestamp:n})},fn="2.26.0",hn=[["architecture","string"],["bitness","string"],["model","string"],["platformVersion","string"],["wow64","boolean"]];const yn=(e=>t=>{const n={webPageDetails:{URL:e.location.href||e.location},webReferrer:{URL:e.document.referrer}};a(t,{web:n})})(window),vn=(e=>t=>{const{screen:{width:n,height:r}}=e,o={},i=ve(r);i>=0&&(o.screenHeight=i);const s=ve(n);s>=0&&(o.screenWidth=s);const c=(e=>{const{screen:{orientation:t}}=e;if(null==t||null==t.type)return null;const n=t.type.split("-");return 0===n.length||"portrait"!==n[0]&&"landscape"!==n[0]?null:n[0]})(e)||(e=>{if(w(e.matchMedia)){if(e.matchMedia("(orientation: portrait)").matches)return"portrait";if(e.matchMedia("(orientation: landscape)").matches)return"landscape"}return null})(e);c&&(o.screenOrientation=c),Object.keys(o).length>0&&a(t,{device:o})})(window),wn=(e=>t=>{const{document:{documentElement:{clientWidth:n,clientHeight:r}={}}}=e,o={type:"browser"},i=ve(n);i>=0&&(o.browserDetails={viewportWidth:i});const s=ve(r);s>=0&&(o.browserDetails=o.browserDetails||{},o.browserDetails.viewportHeight=s),a(t,{environment:o})})(window),bn=(En=()=>new Date,e=>{const t=En(),n={},r=ve(t.getTimezoneOffset());void 0!==r&&(n.localTimezoneOffset=r),(void 0===r||Math.abs(r)<6e3)&&(n.localTime=(e=>{const t=e.getFullYear(),n=we(e.getMonth()+1,2,"0"),r=we(e.getDate(),2,"0"),o=we(e.getHours(),2,"0"),i=we(e.getMinutes(),2,"0"),a=we(e.getSeconds(),2,"0"),s=we(e.getMilliseconds(),3,"0"),c=ve(e.getTimezoneOffset(),0);return t+"-"+n+"-"+r+"T"+o+":"+i+":"+a+"."+s+(c>0?"-":"+")+we(Math.floor(Math.abs(c)/60),2,"0")+":"+we(Math.abs(c)%60,2,"0")})(t)),a(e,{placeContext:n})});var En;const Cn=mn((()=>new Date)),kn={web:yn,device:vn,environment:wn,placeContext:bn},In={highEntropyUserAgentHints:(e=>(e=>void 0!==e.userAgentData)(e)?(t,n)=>{try{return e.userAgentData.getHighEntropyValues(hn.map((e=>e[0]))).then((e=>{const n={};hn.forEach((([t,r])=>{Object.prototype.hasOwnProperty.call(e,t)&&typeof e[t]===r&&(n[t]=e[t])})),a(t,{environment:{browserDetails:{userAgentClientHints:n}}})}))}catch(e){return n.warn("Unable to collect user-agent client hints. "+e.message),se}}:se)(navigator)},Sn={...kn,...In},Dn=[Cn,e=>{a(e,{implementationDetails:{name:"https://ns.adobe.com/experience/alloy",version:fn,environment:"browser"}})}],Pn=({config:e,logger:t})=>((e,t,n,r)=>{const o=e.context.flatMap(((e,r)=>n[e]?[n[e]]:(t.warn("Invalid context["+r+"]: '"+e+"' is not available."),[]))).concat(r);return{namespace:"Context",lifecycle:{onBeforeEvent({event:e}){const n={};return Promise.all(o.map((e=>Promise.resolve(e(n,t))))).then((()=>e.mergeXdm(n)))}}}})(e,t,Sn,Dn);Pn.namespace="Context",Pn.configValidators=yt({context:ut(vt()).default(Object.keys(kn))});const Tn=({eventManager:e,logger:t})=>({commands:{sendEvent:{documentationUri:"https://adobe.ly/3GQ3Q7t",optionsValidator:e=>(({options:e})=>yt({type:vt(),xdm:yt({eventType:vt(),identityMap:bt}),data:yt({}),documentUnloading:pt(),renderDecisions:pt(),decisionScopes:ut(vt()).uniqueItems(),personalization:yt({decisionScopes:ut(vt()).uniqueItems(),surfaces:ut(vt()).uniqueItems(),sendDisplayEvent:pt().default(!0),includeRenderedPropositions:pt().default(!1),defaultPersonalizationEnabled:pt(),decisionContext:yt({})}).default({sendDisplayEvent:!0}),datasetId:vt(),mergeId:vt(),edgeConfigOverrides:Et,initializePersonalization:pt()}).required().noUnknownFields()(e))({options:e}),run:n=>{const{xdm:r,data:o,documentUnloading:i,type:s,mergeId:c,datasetId:d,edgeConfigOverrides:l,...u}=n,p=e.createEvent();return i&&p.documentMayUnload(),p.setUserXdm(r),p.setUserData(o),s&&p.mergeXdm({eventType:s}),c&&p.mergeXdm({eventMergeId:c}),l&&(u.edgeConfigOverrides=l),d&&(t.warn("The 'datasetId' option has been deprecated. Please use 'edgeConfigOverrides.com_adobe_experience_platform.datasets.event.datasetId' instead."),u.edgeConfigOverrides=l||{},a(u.edgeConfigOverrides,{com_adobe_experience_platform:{datasets:{event:{datasetId:d}}}})),e.sendEvent(p,u)}},applyResponse:{documentationUri:"",optionsValidator:e=>(({options:e})=>yt({renderDecisions:pt(),responseHeaders:ht(vt().required()),responseBody:yt({handle:ut(yt({type:vt().required(),payload:lt().required()})).required()}).required(),personalization:yt({sendDisplayEvent:pt().default(!0),decisionContext:yt({})}).default({sendDisplayEvent:!0})}).noUnknownFields()(e))({options:e}),run:t=>{const{renderDecisions:n=!1,decisionContext:r={},responseHeaders:o={},responseBody:i={handle:[]},personalization:a}=t,s=e.createEvent();return e.applyResponse(s,{renderDecisions:n,decisionContext:r,responseHeaders:o,responseBody:i,personalization:a})}}}});Tn.namespace="DataCollector";const Rn=(e,t)=>"ID sync "+(t?"succeeded":"failed")+": "+e.spec.url;const On=yt({thirdPartyCookiesEnabled:pt().default(!0),idMigrationEnabled:pt().default(!0)});var Nn=yt({url:vt().required().nonEmpty(),edgeConfigOverrides:Et}).required().noUnknownFields(),Mn="ECID",An=({logger:e})=>new Promise(((n,r)=>{if(t(window.adobe)&&t(window.adobe.optIn)){const t=window.adobe.optIn;e.info("Delaying request while waiting for legacy opt-in to let Visitor retrieve ECID from server."),t.fetchPermissions((()=>{t.isApproved([t.Categories.ECID])?(e.info("Received legacy opt-in approval to let Visitor retrieve ECID from server."),n()):r(new Error("Legacy opt-in was declined."))}),!0)}else n()})),xn=({logger:e,orgId:t,awaitVisitorOptIn:n})=>()=>{const r=(e=>{const t=e.Visitor;return w(t)&&w(t.getInstance)&&t})(window);return r?n({logger:e}).then((()=>(e.info("Delaying request while using Visitor to retrieve ECID from server."),new Promise((n=>{r.getInstance(t,{}).getMarketingCloudVisitorID((t=>{e.info("Resuming previously delayed request that was waiting for ECID from Visitor."),n(t)}),!0)}))))).catch((t=>{t?e.info(t.message+", retrieving ECID from experience edge"):e.info("An error occurred while obtaining the ECID from Visitor.")})):Promise.resolve()},qn="CORE",Ln=e=>{try{return decodeURIComponent(e)}catch{return""}};var Un=(e,t)=>{e.addIdentity(Mn,{id:t})},jn=e=>e.getPayloadsByType("identity:result").reduce(((e,t)=>(t.namespace&&t.namespace.code&&(e[t.namespace.code]=t.id),e)),{}),Bn=({payload:e,datastreamIdOverride:t})=>Kt({payload:e,datastreamIdOverride:t,getAction:()=>"identity/acquire",getUseSendBeacon:()=>!1}),Fn=e=>{const t={query:{identity:{fetch:e}}};return Zt({content:t,addIdentity:Yt(t),hasIdentity:en(t)})};const _n=/^([^?#]*)(\??[^#]*)(#?.*)$/;const Vn=yt({namespaces:ut(wt(Mn,qn)).nonEmpty().uniqueItems().default([Mn]),edgeConfigOverrides:Et}).noUnknownFields().default({namespaces:[Mn]});const zn=(e,t)=>{let n,r=0,o=0;do{if(t<0||t+o>=e.length)throw new Error("Invalid varint: buffer ended unexpectedly");if(n=e[t+o],r|=(127&n)<<7*o,o+=1,o>10)throw new Error("Invalid varint: too long")}while(128&n);return{value:r,length:o}},Hn=Object.freeze({VARINT:0,I64:1,LEN:2,SGROUP:3,EGROUP:4,I32:5});var Jn=({orgId:e,cookieJar:t,logger:n})=>{const r=V(e,"identity");return()=>{const e=t.get(r);if(!e)return null;try{const t=decodeURIComponent(e).replace(/_/g,"/").replace(/-/g,"+");return(e=>{let t=0,n=null;for(;t>3){if(i===Hn.LEN){const r=zn(e,t);return t+=r.length,n=(new TextDecoder).decode(e.slice(t,t+r.value)),t+=r.value,n}}else switch(i){case Hn.VARINT:t+=zn(e,t).length;break;case Hn.I64:t+=8;break;case Hn.LEN:{const n=zn(e,t);t+=n.length+n.value;break}case Hn.SGROUP:case Hn.EGROUP:break;case Hn.I32:t+=4;break;default:throw new Error("Malformed kndctr cookie. Unknown wire type: "+i)}}throw new Error("No ECID found in cookie.")})((e=>{const t=atob(e);return Uint8Array.from(t,(e=>e.codePointAt(0)))})(t))}catch(e){return n.warn("Unable to decode ECID from "+r+" cookie",e),null}}};const Gn=({config:e,logger:t,consent:n,fireReferrerHideableImage:r,sendEdgeNetworkRequest:i,apexDomain:a,getBrowser:s})=>{const{orgId:c,thirdPartyCookiesEnabled:l,edgeConfigOverrides:u}=e,p=xn({logger:t,orgId:c,awaitVisitorOptIn:An}),g=d({logger:t,cookieJar:o}),m=(({config:e,getEcidFromVisitor:t,apexDomain:n,isPageSsl:r,cookieJar:o})=>{const{idMigrationEnabled:i,orgId:a}=e,s="AMCV_"+a,c=()=>{let e=null;const t=o.get("s_ecid")||o.get(s);if(t){const n=/(^|\|)MCMID\|(\d+)($|\|)/,r=t.match(n);r&&(e=r[2])}return e};return{getEcid(){if(i){const e=c();return e?Promise.resolve(e):t()}return Promise.resolve()},setEcid(e){if(i&&c()!==e){const t=r?{sameSite:"none",secure:!0}:{};o.set(s,"MCMID|"+e,{domain:n,expires:390,...t})}}}})({config:e,getEcidFromVisitor:p,apexDomain:a,cookieJar:g,isPageSsl:"https:"===window.location.protocol}),f=Z({orgId:c}),h=(({sendEdgeNetworkRequest:e,createIdentityRequestPayload:t,createIdentityRequest:n,globalConfigOverrides:r})=>({namespaces:o,edgeConfigOverrides:i}={})=>{const a=nn({payload:t(o),globalConfigOverrides:r,localConfigOverrides:i}),s=n(a);return e({request:s})})({sendEdgeNetworkRequest:i,createIdentityRequestPayload:Fn,createIdentityRequest:Bn,globalConfigOverrides:u}),y=(({getBrowser:e})=>K((()=>$.includes(e()))))({getBrowser:s}),v=(({thirdPartyCookiesEnabled:e,areThirdPartyCookiesSupportedByDefault:t})=>n=>{e&&t()&&n.setUseIdThirdPartyDomain()})({thirdPartyCookiesEnabled:l,areThirdPartyCookiesSupportedByDefault:y}),w=(({getLegacyEcid:e,addEcidToPayload:t})=>n=>n.hasIdentity(Mn)?Promise.resolve():e().then((e=>{e&&t(n,e)})))({getLegacyEcid:m.getEcid,addEcidToPayload:Un}),b=(({locationSearch:e,dateProvider:t,orgId:n,logger:r})=>o=>{if(o.hasIdentity(Mn))return;let i=me.parse(e).adobe_mc;if(void 0===i)return;Array.isArray(i)&&(r.warn("Found multiple adobe_mc query string paramters, only using the last one."),i=i[i.length-1]);const a=i.split("|").reduce(((e,t)=>{const[n,r]=t.split("=");return e[n]=Ln(r),e[n]=e[n].replace(/[^a-zA-Z0-9@.]/g,""),e}),{}),s=parseInt(a.TS,10),c=a.MCMID,d=Ln(a.MCORGID);t().getTime()/1e3<=s+300&&d===n&&c?(r.info("Found valid ECID identity "+c+" from the adobe_mc query string parameter."),o.addIdentity(Mn,{id:c})):r.info("Detected invalid or expired adobe_mc query string parameter.")})({locationSearch:window.document.location.search,dateProvider:()=>new Date,orgId:c,logger:t}),E=(({doesIdentityCookieExist:e,orgId:t,logger:n})=>({onResponse:r,onRequestFailure:o})=>new Promise(((i,a)=>{r((()=>{e()?i():(n.warn("Identity cookie not found. This could be caused by any of the following issues:\n\t* The org ID "+t+" configured in Alloy doesn't match the org ID specified in the edge configuration.\n\t* Experience edge was not able to set the identity cookie due to domain or cookie restrictions.\n\t* The request was canceled by the browser and not fully processed."),a(new Error("Identity cookie not found.")))})),o((()=>{e()?i():a(new Error("Identity cookie not found."))}))})))({doesIdentityCookieExist:f,orgId:c,logger:t}),C=(({doesIdentityCookieExist:e,setDomainForInitialIdentityPayload:t,addLegacyEcidToPayload:n,awaitIdentityCookie:r,logger:o})=>{let i;const a=e=>(t(e),n(e.getPayload()));return({request:t,onResponse:n,onRequestFailure:s})=>{if(e())return t.setIsIdentityEstablished(),Promise.resolve();if(i){o.info("Delaying request while retrieving ECID from server.");const e=i;return i=e.catch((()=>r({onResponse:n,onRequestFailure:s}))),e.then((()=>{o.info("Resuming previously delayed request."),t.setIsIdentityEstablished()})).catch((()=>a(t)))}return i=r({onResponse:n,onRequestFailure:s}),i.catch((()=>{})),a(t)}})({doesIdentityCookieExist:f,setDomainForInitialIdentityPayload:v,addLegacyEcidToPayload:w,awaitIdentityCookie:E,logger:t}),k=(({fireReferrerHideableImage:e,logger:t})=>n=>{const r=n.filter((e=>"url"===e.type));return r.length?Promise.all(r.map((n=>e(n.spec).then((()=>{t.info(Rn(n,!0))})).catch((()=>{t.error(Rn(n,!1))}))))).then(se):Promise.resolve()})({fireReferrerHideableImage:r,logger:t}),I=(({processIdSyncs:e})=>t=>e(t.getPayloadsByType("identity:exchange")))({processIdSyncs:k}),S=(({dateProvider:e,orgId:t})=>(n,r)=>{const o=Math.round(e().getTime()/1e3),i=encodeURIComponent("TS="+o+"|MCMID="+n+"|MCORGID="+encodeURIComponent(t)),[,a,s,c]=r.match(_n),d=(e=>""===e?"?":"?"===e?"":"&")(s);return""+a+s+d+"adobe_mc="+i+c})({dateProvider:()=>new Date,orgId:c}),D=(({thirdPartyCookiesEnabled:e})=>t=>{const n=Vn(t);if(!e&&n.namespaces.includes(qn))throw new Error("namespaces: The CORE namespace cannot be requested when third-party cookies are disabled.");return n})({thirdPartyCookiesEnabled:l}),P=(({thirdPartyCookiesEnabled:e,areThirdPartyCookiesSupportedByDefault:t})=>{const n={identity:{fetch:[Mn]}};return e&&t()&&n.identity.fetch.push(qn),e=>{e.mergeQuery(n)}})({thirdPartyCookiesEnabled:l,areThirdPartyCookiesSupportedByDefault:y}),T=Jn({orgId:c,cookieJar:g,logger:t});return(({addEcidQueryToPayload:e,addQueryStringIdentityToPayload:t,ensureSingleIdentity:n,setLegacyEcid:r,handleResponseForIdSyncs:o,getNamespacesFromResponse:i,getIdentity:a,consent:s,appendIdentityToUrl:c,logger:d,getIdentityOptionsValidator:l,decodeKndctrCookie:u})=>{let p,g={};return{lifecycle:{onBeforeRequest:({request:r,onResponse:o,onRequestFailure:i})=>(e(r.getPayload()),t(r.getPayload()),n({request:r,onResponse:o,onRequestFailure:i})),onResponse({response:e}){const t=i(e);return p&&p[Mn]||!t||!t[Mn]||r(t[Mn]),t&&Object.keys(t).length>0&&(p={...p,...t}),g={...g,...e.getEdge()},o(e)}},commands:{getIdentity:{optionsValidator:l,run:e=>{const{namespaces:t}=e;return s.awaitConsent().then((()=>{if(p)return;const n=u();return n&&t.includes(Mn)&&(p||(p={}),p[Mn]=n,1===t.length)?void 0:a(e)})).then((()=>({identity:t.reduce(((e,t)=>(e[t]=p[t]||null,e)),{}),edge:g})))}},appendIdentityToUrl:{optionsValidator:Nn,run:e=>s.withConsent().then((()=>{if(p)return;const t=u();return t?(p||(p={}),void(p[Mn]=t)):a(e)})).then((()=>({url:c(p[Mn],e.url)}))).catch((t=>(d.warn("Unable to append identity to url. "+t.message),e)))}}}})({addEcidQueryToPayload:P,addQueryStringIdentityToPayload:b,ensureSingleIdentity:C,setLegacyEcid:m.setEcid,handleResponseForIdSyncs:I,getNamespacesFromResponse:jn,getIdentity:h,consent:n,appendIdentityToUrl:S,logger:t,getIdentityOptionsValidator:D,decodeKndctrCookie:T})};Gn.namespace="Identity",Gn.configValidators=On;const Qn=({config:e,componentRegistry:t})=>{const n=[...t.getCommandNames(),Ft,_t].sort(),r={...e};Object.keys(e).forEach((t=>{const n=e[t];"function"==typeof n&&(r[t]=n.toString())}));const o=t.getComponentNames();return{version:fn,configs:r,commands:n,components:o}},Xn=({config:e,componentRegistry:t})=>({commands:{getLibraryInfo:{run:()=>({libraryInfo:Qn({config:e,componentRegistry:t})})}}});Xn.namespace="LibraryInfo";var Wn=Object.freeze({__proto__:null,context:Pn,dataCollector:Tn,identity:Gn,libraryInfo:Xn});const Yn=te(window),{console:Kn,fetch:$n,navigator:Zn}=window,er=()=>window.__alloyMonitors||[],tr=yt({debugEnabled:pt().default(!1),datastreamId:vt().unique().required(),edgeDomain:vt().domain().default("edge.adobedc.net"),edgeBasePath:vt().nonEmpty().default("ee"),orgId:vt().unique().required(),onBeforeEventSend:gt().default(se),edgeConfigOverrides:Et}).renamed("edgeConfigId",vt().unique(),"datastreamId"),nr=((e,t)=>{let n="";const r=e.location.hostname.toLowerCase().split(".");let o=1;for(;o(t,n)=>e(t,{method:"POST",cache:"no-cache",credentials:"include",headers:{"Content-Type":"text/plain; charset=UTF-8"},referrerPolicy:"no-referrer-when-downgrade",body:n}).then((e=>e.text().then((t=>({statusCode:e.status,getHeader:t=>e.headers.get(t),body:t}))))))({fetch:$n}),or=(({appendNode:e=g,awaitSelector:t=M,createNode:n=m,fireImage:r=v}={})=>{const o=r;let i;const a=({src:o})=>t("BODY").then((([t])=>i||(i=n("IFRAME",x,q),e(t,i)))).then((e=>{const t=e.contentWindow.document;return r({src:o,currentDocument:t})}));return e=>{const{hideReferrer:t,url:n}=e;return t?a({src:n}):o({src:n})}})(),ir=(({window:e,createNamespacedStorage:t})=>{const n=t("validation.");return()=>{const t=me.parse(e.location.search).adb_validation_sessionid;if(!t)return"";const r=(e=>{let t=e.persistent.getItem(rn);return t||(t=Ie(),e.persistent.setItem(rn,t)),t})(n),o=t+"|"+r;return"&"+me.stringify({adobeAepValidationToken:o})}})({window:window,createNamespacedStorage:Yn}),ar=(({userAgent:e})=>K((()=>((e,t)=>{const n=Object.keys(e);for(let r=0;r{const a=Pt(),s=St(a),l=i.concat(Object.values(Wn)),u=d({logger:n,cookieJar:o}),p=Qt({errorPrefix:"["+e+"]",logger:n}),g=Vt({logger:n,configureCommand:i=>{const d=Jt({options:i,componentCreators:l,coreConfigValidators:tr,createConfig:Gt,logger:n,setDebugEnabled:t}),{orgId:p,targetMigrationEnabled:g}=d,m=an({orgId:p,targetMigrationEnabled:g}),f=(({cookieJar:e,shouldTransferCookie:t,apexDomain:n,dateProvider:r})=>({cookiesToPayload(r,o){const i=""!==n&&o.endsWith(n),a={domain:n,cookiesEnabled:!0};if(!i){const n=e.get(),r=Object.keys(n).filter(t).map((e=>({key:e,value:n[e]})));r.length&&(a.entries=r)}r.mergeState(a)},responseToCookies(t){t.getPayloadsByType("state:store").forEach((t=>{const o={domain:n},i=t.attrs&&t.attrs.SameSite&&t.attrs.SameSite.toLowerCase();void 0!==t.maxAge&&(o.expires=new Date(r().getTime()+1e3*t.maxAge)),void 0!==i&&(o.sameSite=i),"none"===i&&(o.secure=!0),e.set(t.key,t.value,o)}))}}))({cookieJar:u,shouldTransferCookie:m,apexDomain:nr,dateProvider:()=>new Date}),h=w(Zn.sendBeacon)?(({sendBeacon:e,sendFetchRequest:t,logger:n})=>(r,o)=>{const i=new Blob([o],{type:"text/plain; charset=UTF-8"});return e(r,i)?Promise.resolve({statusCode:204,getHeader:()=>null,body:""}):(n.info("Unable to use `sendBeacon`; falling back to `fetch`."),t(r,o))})({sendBeacon:Zn.sendBeacon.bind(Zn),sendFetchRequest:rr,logger:n}):rr,y=(({logger:e,sendFetchRequest:t,sendBeaconRequest:n,isRequestRetryable:r,getRequestRetryDelay:o})=>({requestId:i,url:a,payload:s,useSendBeacon:c})=>{const d=JSON.stringify(s),l=JSON.parse(d);e.logOnBeforeNetworkRequest({url:a,requestId:i,payload:l});const u=(s=0)=>(c?n:t)(a,d).then((t=>{if(r({response:t,retriesAttempted:s})){const e=o({response:t,retriesAttempted:s});return new Promise((t=>{setTimeout((()=>{t(u(s+1))}),e)}))}let n;try{n=JSON.parse(t.body)}catch{}return e.logOnNetworkResponse({requestId:i,url:a,payload:l,...t,parsedBody:n,retriesAttempted:s}),{statusCode:t.statusCode,body:t.body,parsedBody:n,getHeader:t.getHeader}}));return u().catch((t=>{throw e.logOnNetworkError({requestId:i,url:a,payload:l,error:t}),ye({error:t,message:"Network request failed."})}))})({logger:n,sendFetchRequest:rr,sendBeaconRequest:h,isRequestRetryable:pn,getRequestRetryDelay:gn}),v=(({logger:e})=>t=>{const{statusCode:n,body:r,parsedBody:o}=t;if(n<200||n>=300||!o&&204!==n||o&&!Array.isArray(o.handle)){const e=o?JSON.stringify(o,null,2):r;throw new Error(dn+" status code "+n+" and "+(e?"response body:\n"+e:"no response body."))}if(o){const{warnings:t=[],errors:n=[]}=o;t.forEach((t=>{e.warn(dn+" warning:",t)})),n.forEach((t=>{e.error(dn+" non-fatal error:",t)}))}})({logger:n}),b=(({logger:e})=>t=>{if(t){const n=t.split(";");if(n.length>=2&&n[1].length>0)try{const e=parseInt(n[1],10);if(!Number.isNaN(e))return{regionId:e}}catch{}e.warn('Invalid adobe edge: "'+t+'"')}return{}})({logger:n}),E=(({extractEdgeInfo:e})=>({content:t={},getHeader:n})=>{const{handle:r=[],errors:o=[],warnings:i=[]}=t;return{getPayloadsByType:e=>r.filter((t=>t.type===e)).flatMap((e=>e.payload)),getErrors:()=>o,getWarnings:()=>i,getEdge:()=>e(n("x-adobe-edge")),toJSON:()=>t}})({extractEdgeInfo:b}),C=ln({orgId:p,cookieJar:o}),k=(({config:e,lifecycle:t,cookieTransfer:n,sendNetworkRequest:r,createResponse:o,processWarningsAndErrors:i,getLocationHint:a,getAssuranceValidationTokenParams:s})=>{const{edgeDomain:d,edgeBasePath:l,datastreamId:u}=e;let p=!1;const g=(e,t)=>{const n=a(),r=n?l+"/"+n+t.getEdgeSubPath():""+l+t.getEdgeSubPath(),o=t.getDatastreamIdOverride()||u;return o!==u&&t.getPayload().mergeMeta({sdkConfig:{datastream:{original:u}}}),"https://"+e+"/"+r+"/v1/"+t.getAction()+"?configId="+o+"&requestId="+t.getId()+s()};return({request:e,runOnResponseCallbacks:a=se,runOnRequestFailureCallbacks:s=se})=>{const l=c();l.add(t.onResponse),l.add(a);const u=c();return u.add(t.onRequestFailure),u.add(s),t.onBeforeRequest({request:e,onResponse:l.add,onRequestFailure:u.add}).then((()=>{const t=p||!e.getUseIdThirdPartyDomain()?d:"adobedc.demdex.net",o=g(t,e),i=e.getPayload();return n.cookiesToPayload(i,t),r({requestId:e.getId(),url:o,payload:i,useSendBeacon:e.getUseSendBeacon()})})).then((e=>(i(e),e))).catch((t=>{if(((e,t)=>t.getUseIdThirdPartyDomain()&&(e=>"TypeError"===e.name||"NetworkError"===e.name||0===e.status)(e))(t,e)){p=!0,e.setUseIdThirdPartyDomain(!1);const t=g(d,e),o=e.getPayload();return n.cookiesToPayload(o,d),r({requestId:e.getId(),url:t,payload:o,useSendBeacon:e.getUseSendBeacon()})}return cn(u)(t)})).then((({parsedBody:e,getHeader:t})=>{const r=o({content:e,getHeader:t});return n.responseToCookies(r),l.call({response:r}).then(sn)}))}})({config:d,lifecycle:s,cookieTransfer:f,sendNetworkRequest:y,createResponse:E,processWarningsAndErrors:v,getLocationHint:C,getAssuranceValidationTokenParams:ir}),I=(({cookieTransfer:e,lifecycle:t,createResponse:n,processWarningsAndErrors:r})=>({request:o,responseHeaders:i,responseBody:a,runOnResponseCallbacks:s=se,runOnRequestFailureCallbacks:d=se})=>{const l=c();l.add(t.onResponse),l.add(s);const u=c();u.add(t.onRequestFailure),u.add(d);const p=e=>i[e];return t.onBeforeRequest({request:o,onResponse:l.add,onRequestFailure:u.add}).then((()=>r({statusCode:200,getHeader:p,body:JSON.stringify(a),parsedBody:a}))).catch(cn(u)).then((()=>{const t=n({content:a,getHeader:p});return e.responseToCookies(t),l.call({response:t}).then(sn)}))})({lifecycle:s,cookieTransfer:f,createResponse:E,processWarningsAndErrors:v}),S=(({generalConsentState:e,logger:t})=>{const n=(n,r)=>{switch(n[Nt]){case Tt:e.in(r);break;case Rt:e.out(r);break;case Ot:e.pending(r);break;default:t.warn("Unknown consent value: "+n[Nt])}};return{initializeConsent(e,t){t[Nt]?n(t,xt):n(e,At)},setConsent(e){n(e,qt)},suspend(){e.pending()},awaitConsent:()=>e.awaitConsent(),withConsent:()=>e.withConsent(),current:()=>e.current()}})({generalConsentState:Ut({logger:n}),logger:n}),D=(({config:e,logger:t,lifecycle:n,consent:r,createEvent:o,createDataCollectionRequestPayload:i,createDataCollectionRequest:a,sendEdgeNetworkRequest:s,applyResponse:d})=>{const{onBeforeEventSend:l,edgeConfigOverrides:u}=e;return{createEvent:o,sendEvent(e,o={}){const{edgeConfigOverrides:d,...p}=o,g=nn({payload:i(),localConfigOverrides:d,globalConfigOverrides:u}),m=a(g),f=c(),h=c();return n.onBeforeEvent({...p,event:e,onResponse:f.add,onRequestFailure:h.add}).then((()=>(g.payload.addEvent(e),r.awaitConsent()))).then((()=>{try{e.finalize(l)}catch(e){const t=()=>{throw e};return h.add(n.onRequestFailure),h.call({error:e}).then(t,t)}if(!e.shouldSend()){h.add(n.onRequestFailure),t.info(on);const e=new Error(on);return h.call({error:e}).then((()=>{}))}return s({request:m,runOnResponseCallbacks:f.call,runOnRequestFailureCallbacks:h.call})}))},applyResponse(e,t={}){const{renderDecisions:r=!1,decisionContext:o={},responseHeaders:s={},responseBody:l={handle:[]},personalization:u}=t,p=i(),g=a({payload:p}),m=c();return n.onBeforeEvent({event:e,renderDecisions:r,decisionContext:o,decisionScopes:[Wt],personalization:u,onResponse:m.add,onRequestFailure:se}).then((()=>(p.addEvent(e),d({request:g,responseHeaders:s,responseBody:l,runOnResponseCallbacks:m.call}))))}}})({config:d,logger:n,lifecycle:s,consent:S,createEvent:Bt,createDataCollectionRequestPayload:tn,createDataCollectionRequest:$t,sendEdgeNetworkRequest:k,applyResponse:I});return(({componentCreators:e,lifecycle:t,componentRegistry:n,getImmediatelyAvailableTools:r})=>(e.forEach((e=>{const{namespace:t}=e,o=r(t);let i;try{i=e(o)}catch(e){throw ye({error:e,message:"["+t+"] An error occurred during component creation."})}n.register(t,i)})),t.onComponentsRegistered({lifecycle:t}).then((()=>n))))({componentCreators:l,lifecycle:s,componentRegistry:a,getImmediatelyAvailableTools(t){const n=r(t);return{config:d,componentRegistry:a,consent:S,eventManager:D,fireReferrerHideableImage:or,logger:n,lifecycle:s,sendEdgeNetworkRequest:k,handleError:Qt({errorPrefix:"["+e+"] ["+t+"]",logger:n}),createNamespacedStorage:Yn,apexDomain:nr,getBrowser:ar}}})},setDebugCommand:e=>{t(e.enabled,{fromConfig:!1})},handleError:p,validateCommandOptions:Ht});return g};var cr=({eventManager:e,lifecycle:t,handleError:n})=>{const r=(({eventManager:e,lifecycle:t,handleError:n})=>r=>{if(r.s_fe)return Promise.resolve();const o=r.target,i=e.createEvent();return i.documentMayUnload(),t.onClick({event:i,clickedElement:o}).then((()=>i.isEmpty()?Promise.resolve():e.sendEvent(i))).then(se).catch((e=>{n(e,"click collection")}))})({eventManager:e,lifecycle:t,handleError:n});document.addEventListener("click",r,!0)};const dr="\\.(exe|zip|wav|mp3|mov|mpg|avi|wmv|pdf|doc|docx|xls|xlsx|ppt|pptx)$",lr=vt().regexp().default(dr),ur=yt({clickCollectionEnabled:pt().default(!0),clickCollection:yt({internalLinkEnabled:pt().default(!0),externalLinkEnabled:pt().default(!0),downloadLinkEnabled:pt().default(!0),sessionStorageEnabled:pt().default(!1),eventGroupingEnabled:pt().default(!1),filterClickProperties:gt()}).default({internalLinkEnabled:!0,externalLinkEnabled:!0,downloadLinkEnabled:!0,sessionStorageEnabled:!1,eventGroupingEnabled:!1}),downloadLinkQualifier:lr,onBeforeLinkClickSend:gt().deprecated('The field "onBeforeLinkClickSend" has been deprecated. Use "clickCollection.filterClickDetails" instead.')});var pr=(e=document)=>null!==e.getElementById("cppXYctnr"),gr=e=>{let t=e;/^https?:\/\//i.test(t)||(t=window.location.protocol+"//"+e);return new URL(t).hostname};var mr=({config:e,logger:t,getClickedElementProperties:n,clickActivityStorage:r})=>{const{clickCollectionEnabled:o,clickCollection:i}=e;return o?({event:o,clickedElement:a})=>{const s=n({clickActivityStorage:r,clickedElement:a,config:e,logger:t}),c=s.linkType;var d,l;pr()||(s.isValidLink()&&((e,t)=>t&&("download"===t&&!e.downloadLinkEnabled||"exit"===t&&!e.externalLinkEnabled||"other"===t&&!e.internalLinkEnabled))(i,c)?t.info("Cancelling link click event due to clickCollection."+c+"LinkEnabled = false."):s.isInternalLink()&&i.eventGroupingEnabled&&(!e.onBeforeLinkClickSend||i.filterClickDetails)&&(d=window.location.hostname,l=s.linkUrl,gr(d)===gr(l))?r.save(s.properties):s.isValidLink()?(o.mergeXdm(s.xdm),o.mergeData(s.data),r.save({pageName:s.pageName,pageIDType:s.pageIDType})):s.isValidActivityMapData()&&r.save(s.properties))}:()=>{}};var fr=({properties:e,logger:t}={})=>{let n=e||{};return{get pageName(){return n.pageName},set pageName(e){n.pageName=e},get linkName(){return n.linkName},set linkName(e){n.linkName=e},get linkRegion(){return n.linkRegion},set linkRegion(e){n.linkRegion=e},get linkType(){return n.linkType},set linkType(e){n.linkType=e},get linkUrl(){return n.linkUrl},set linkUrl(e){n.linkUrl=e},get pageIDType(){return n.pageIDType},set pageIDType(e){n.pageIDType=e},get clickedElement(){return n.clickedElement},set clickedElement(e){n.clickedElement=e},get properties(){return{pageName:n.pageName,linkName:n.linkName,linkRegion:n.linkRegion,linkType:n.linkType,linkUrl:n.linkUrl,pageIDType:n.pageIDType}},isValidLink:()=>!!(n.linkUrl&&n.linkType&&n.linkName&&n.linkRegion),isInternalLink(){return this.isValidLink()&&"other"===n.linkType},isValidActivityMapData:()=>!!n.pageName&&!!n.linkName&&!!n.linkRegion&&void 0!==n.pageIDType,get xdm(){return n.filteredXdm?n.filteredXdm:(e=>({eventType:"web.webinteraction.linkClicks",web:{webInteraction:{name:e.linkName,region:e.linkRegion,type:e.linkType,URL:e.linkUrl,linkClicks:{value:1}}}}))(this)},get data(){return n.filteredData?n.filteredData:(e=>({__adobe:{analytics:{contextData:{a:{activitymap:{page:e.pageName,link:e.linkName,region:e.linkRegion,pageIDType:e.pageIDType}}}}}}))(this)},applyPropertyFilter(e){e&&!1===e(n)&&(t&&t.info("Clicked element properties were rejected by filter function: "+JSON.stringify(this.properties,null,2)),n={})},applyOptionsFilter(e){const r=this.options;if(r&&r.clickedElement&&(r.xdm||r.data)){if(e&&!1===e(r))return t&&t.info("Clicked element properties were rejected by filter function: "+JSON.stringify(this.properties,null,2)),void(this.options=void 0);this.options=r,n.filteredXdm=r.xdm,n.filteredData=r.data}},get options(){const e={};if(this.isValidLink()&&(e.xdm=this.xdm),this.isValidActivityMapData()&&(e.data=this.data),this.clickedElement&&(e.clickedElement=this.clickedElement),e.xdm||e.data)return e},set options(e){n={},e&&((e,t)=>{const{xdm:n,data:r,clickedElement:o}=e;if(t.clickedElement=o,n&&n.web&&n.web.webInteraction){const{name:e,region:r,type:o,URL:i}=n.web.webInteraction;t.linkName=e,t.linkRegion=r,t.linkType=o,t.linkUrl=i}if(r&&r.__adobe&&r.__adobe.analytics){const{contextData:e}=r.__adobe.analytics;if(e&&e.a&&e.a.activitymap){const{page:n,link:r,region:o,pageIDType:i}=e.a.activitymap;t.pageName=n||t.pageName,t.linkName=r||t.linkName,t.linkRegion=o||t.linkRegion,void 0!==i&&(t.pageIDType=i)}}})(e,n)}}};const hr="clickData";var yr=e=>e&&e.replace(/\s+/g," ").trim();const vr=/^(SCRIPT|STYLE|LINK|CANVAS|NOSCRIPT|#COMMENT)$/i;const wr=e=>{let t=[],n=!1;if((e=>!(e&&e.nodeName&&e.nodeName.match(vr)))(e)){if(t.push(e),e.childNodes){Array.prototype.slice.call(e.childNodes).forEach((e=>{const r=wr(e);t=t.concat(r.supportedNodes),n=n||r.includesUnsupportedNodes}))}}else n=!0;return{supportedNodes:t,includesUnsupportedNodes:n}},br=(e,t,n)=>{let r;return n&&n!==e.nodeName.toUpperCase()||(r=e.getAttribute(t)),r};const Er=/^(HEADER|MAIN|FOOTER|NAV)$/i,Cr=e=>{let t;return"region"===e.role&&ae(e["aria-label"])&&(t=e["aria-label"]),t},kr=e=>{let t;return e&&e.nodeName&&e.nodeName.match(Er)&&(t=e.nodeName),t};var Ir=e=>!(!e.href||"A"!==e.tagName&&"AREA"!==e.tagName||e.onclick&&e.protocol&&!(e.protocol.toLowerCase().indexOf("javascript")<0)),Sr=e=>!!e&&!!e.onclick,Dr=e=>{if("INPUT"===e.tagName){const t=e.getAttribute("type");if("submit"===t)return!0;if("image"===t&&e.src)return!0}return!1},Pr=e=>"BUTTON"===e.tagName&&"submit"===e.type,Tr=e=>{const t=e.indexOf("?"),n=e.indexOf("#");return t>=0&&(t=0?e.substring(0,n):e};const Rr=(({window:e,getLinkName:t,getLinkRegion:n,getAbsoluteUrlFromAnchorElement:r,findClickableElement:o,determineLinkType:i})=>({clickedElement:a,config:s,logger:c,clickActivityStorage:d})=>{const{onBeforeLinkClickSend:l,clickCollection:u}=s,{filterClickDetails:p}=u,g=fr({logger:c});if(a){const c=o(a);if(c){g.clickedElement=a,g.linkUrl=r(e,c),g.linkType=i(e,s,g.linkUrl,c),g.linkRegion=n(c),g.linkName=t(c),g.pageIDType=0,g.pageName=e.location.href;const o=d.load();o&&o.pageName&&(g.pageName=o.pageName,g.pageIDType=1),p?g.applyPropertyFilter(p):l&&g.applyOptionsFilter(l)}}return g})({window:window,getLinkName:e=>{let t=yr(e.innerText||e.textContent);const n=wr(e);if(!t||n.includesUnsupportedNodes){const e=(e=>{const t={texts:[]};return e.supportedNodes.forEach((e=>{e.getAttribute&&(t.alt||(t.alt=yr(e.getAttribute("alt"))),t.title||(t.title=yr(e.getAttribute("title"))),t.inputValue||(t.inputValue=yr(br(e,"value","INPUT"))),t.imgSrc||(t.imgSrc=yr(br(e,"src","IMG")))),e.nodeValue&&t.texts.push(e.nodeValue)})),t})(n);t=yr(e.texts.join("")),t||(t=e.alt||e.title||e.inputValue||e.imgSrc)}return t||""},getLinkRegion:e=>{let t,n=e.parentNode;for(;n;){if(t=yr(n.id||Cr(n)||kr(n)),t)return t;n=n.parentNode}return"BODY"},getAbsoluteUrlFromAnchorElement:(e,t)=>{const n=e.location;let r=t.href||"";"string"!=typeof r&&(r="");let{protocol:o,host:i}=t;if(r&&(!(a=r)||!/^[a-z0-9]+:\/\//i.test(a))){o||(o=n.protocol?n.protocol:""),o=o?o+"//":"",i||(i=n.host?n.host:"");let e="";if("/"!==r.substring(0,1)){let t=n.pathname.lastIndexOf("/");t=t<0?0:t,e=n.pathname.substring(0,t)}r=""+o+i+e+"/"+r}var a;return r},findClickableElement:e=>{let t=e;for(;t;){if(Ir(t)||Sr(t)||Dr(t)||Pr(t))return t;t=t.parentNode}return null},determineLinkType:(e,t,n,r)=>{let o="other";return ae(n)&&(((e,t,n)=>{let r=!1;if(t)if(n&&n.download)r=!0;else if(e){const n=new RegExp(e),o=Tr(t).toLowerCase();r=n.test(o)}return r})(t.downloadLinkQualifier,n,r)?o="download":((e,t)=>{let n=!1;if(t&&e.location.hostname){const r=e.location.hostname.toLowerCase();n=Tr(t).toLowerCase().indexOf(r)<0}return n})(e,n)&&(o="exit")),o}});let Or;const Nr=e=>{if(!Or){const t=te(window)(e.orgId||""),n=(()=>{const e={};return{getItem:t=>e[t],setItem:(t,n)=>{e[t]=n},removeItem:t=>{delete e[t]}}})(),r=e.clickCollection.sessionStorageEnabled?t.session:n;Or=(({storage:e})=>({save:t=>{const n=JSON.stringify(t);e.setItem(hr,n)},load:()=>{let t=null;const n=e.getItem(hr);return n&&(t=JSON.parse(n)),t},remove:()=>{e.removeItem(hr)}}))({storage:r})}},Mr=({config:e,eventManager:t,handleError:n,logger:r})=>{((e,t)=>{const{clickCollectionEnabled:n,onBeforeLinkClickSend:r,downloadLinkQualifier:o}=e;!1===n&&(r&&t.warn("The 'onBeforeLinkClickSend' configuration was provided but will be ignored because clickCollectionEnabled is false."),o&&o!==dr&&t.warn("The 'downloadLinkQualifier' configuration was provided but will be ignored because clickCollectionEnabled is false."))})(e,r);const o=e.clickCollection;Or||Nr(e);const i=mr({config:e,logger:r,clickActivityStorage:Or,getClickedElementProperties:Rr}),a=(({clickActivityStorage:e})=>t=>{if(pr())return;const n=e.load(),r=fr({properties:n});if(r.isValidLink()||r.isValidActivityMapData()){if(r.isValidLink()){const e=r.xdm;delete e.eventType,t.mergeXdm(e)}r.isValidActivityMapData()&&t.mergeData(r.data),e.save({pageName:r.pageName,pageIDType:r.pageIDType})}})({clickActivityStorage:Or}),s=(({clickActivityStorage:e})=>t=>{e.save({pageName:t.getContent().xdm.web.webPageDetails.name,pageIDType:1})})({clickActivityStorage:Or});return{lifecycle:{onComponentsRegistered(e){const{lifecycle:r}=e;cr({eventManager:t,lifecycle:r,handleError:n})},onClick({event:e,clickedElement:t}){i({event:e,clickedElement:t})},onBeforeEvent({event:e}){(e=>{const t=e.getContent();return void 0!==t.xdm&&void 0!==t.xdm.web&&void 0!==t.xdm.web.webPageDetails&&void 0!==t.xdm.web.webPageDetails.name})(e)&&(o.eventGroupingEnabled&&a(e),s(e,r,Or))}}}};Mr.namespace="ActivityCollector",Mr.configValidators=ur,Mr.buildOnInstanceConfiguredExtraParams=({config:e,logger:t})=>(Or||Nr(e),{getLinkDetails:n=>Rr({clickActivityStorage:Or,clickedElement:n,config:e,logger:t}).properties});var Ar=({fireReferrerHideableImage:e,logger:t,cookieJar:n,isPageSsl:r})=>{const o=r?{sameSite:"none",secure:!0}:{};return r=>((e=>{e.filter((e=>"cookie"===e.type)).forEach((e=>{const{name:t,value:r,domain:i,ttlDays:a}=e.spec;n.set(t,r||"",{domain:i||"",expires:a||10,...o})}))})(r),(n=>{const r=n.filter((e=>"url"===e.type));return Promise.all(r.map((n=>e(n.spec).then((()=>{t.info((e=>"URL destination succeeded: "+e.spec.url)(n))})).catch((()=>{}))))).then(se)})(r))},xr=({processDestinations:e})=>({response:t})=>(({response:t})=>{const n=t.getPayloadsByType("activation:push");return e(n)})({response:t}).then((()=>(({response:e})=>({destinations:e.getPayloadsByType("activation:pull")}))({response:t})));const qr=({logger:e,fireReferrerHideableImage:t})=>{const n=o.withConverter({write:e=>encodeURIComponent(e)}),r=d({logger:e,cookieJar:n}),i=Ar({fireReferrerHideableImage:t,logger:e,cookieJar:r,isPageSsl:"https:"===window.location.protocol});return{lifecycle:{onResponse:xr({processDestinations:i})},commands:{}}};qr.namespace="Audiences";const Lr=e=>Array.isArray(e)?e.map((e=>Lr(e))):"object"==typeof e&&null!==e?Object.keys(e).sort().reduce(((t,n)=>(t[n]=Lr(e[n]),t)),{}):e;const Ur=({standard:e,version:t})=>e+"."+t;var jr=({storage:e})=>({clear(){e.clear()},lookup(t){const n={},r=e=>{const t=Ur(e),{standard:r,version:o,...i}=e;var a;return n[t]||(n[t]=(a=i,l(JSON.stringify(Lr(a)))).toString()),n[t]};return{isNew:()=>t.some((t=>{const n=Ur(t),o=e.getItem(n);return null===o||o!==r(t)})),save(){t.forEach((t=>{const n=Ur(t);e.setItem(n,r(t))}))}}}}),Br=()=>{const e={},t=Zt({content:e,addIdentity:(t,n)=>{e.identityMap=e.identityMap||{},e.identityMap[t]=e.identityMap[t]||[],e.identityMap[t].push(n)},hasIdentity:t=>void 0!==(e.identityMap&&e.identityMap[t])});return t.setConsent=t=>{e.consent=t},t},Fr=({payload:e,datastreamIdOverride:t})=>Kt({payload:e,datastreamIdOverride:t,getAction:()=>"privacy/set-consent",getUseSendBeacon:()=>!1}),_r=e=>e.split(";").reduce(((e,t)=>{const[n,r]=t.split("=");return e[n]=r,e}),{}),Vr=yt({consent:ut(lt()).required().nonEmpty(),identityMap:bt,edgeConfigOverrides:Et}).noUnknownFields().required(),zr=yt({defaultConsent:wt(Tt,Rt,Ot).default(Tt)});const Hr=({config:e,consent:n,sendEdgeNetworkRequest:r,createNamespacedStorage:i})=>{const{orgId:a,defaultConsent:s}=e,c=(({parseConsentCookie:e,orgId:t,cookieJar:n})=>{const r=V(t,"consent");return{read(){const t=n.get(r);return t?e(t):{}},clear(){n.remove(r)}}})({parseConsentCookie:_r,orgId:a,cookieJar:o}),d=(()=>{let e=0,t=Promise.resolve();return{addTask(n){e+=1;const r=()=>n().finally((()=>{e-=1}));return t=t.then(r,r),t},get length(){return e}}})(),l=(({createConsentRequestPayload:e,createConsentRequest:n,sendEdgeNetworkRequest:r,edgeConfigOverrides:o})=>({consentOptions:i,identityMap:a,edgeConfigOverrides:s})=>{const c=nn({payload:e(),globalConfigOverrides:o,localConfigOverrides:s});c.payload.setConsent(i),t(a)&&Object.keys(a).forEach((e=>{a[e].forEach((t=>{c.payload.addIdentity(e,t)}))}));const d=n(c);return r({request:d}).then((()=>{}))})({createConsentRequestPayload:Br,createConsentRequest:Fr,sendEdgeNetworkRequest:r,edgeConfigOverrides:e.edgeConfigOverrides}),u=i(_(a)+".consentHashes."),p=jr({storage:u.persistent}),g=Z({orgId:a});return(({storedConsent:e,taskQueue:t,defaultConsent:n,consent:r,sendSetConsentRequest:o,validateSetConsentOptions:i,consentHashStore:a,doesIdentityCookieExist:s})=>{const c={[Nt]:n};let d=e.read();const l=s(),u=void 0!==d[Nt];l&&u||a.clear(),l||(e.clear(),d={}),r.initializeConsent(c,d);const p=()=>{if(0===t.length){const t=e.read();void 0!==t[Nt]&&r.setConsent(t)}};return{commands:{setConsent:{optionsValidator:i,run:({consent:e,identityMap:n,edgeConfigOverrides:i})=>{r.suspend();const s=a.lookup(e);return t.addTask((()=>s.isNew()?o({consentOptions:e,identityMap:n,edgeConfigOverrides:i}):Promise.resolve())).then((()=>s.save())).finally(p)}}},lifecycle:{onResponse:p,onRequestFailure:p}}})({storedConsent:c,taskQueue:d,defaultConsent:s,consent:n,sendSetConsentRequest:l,validateSetConsentOptions:Vr,consentHashStore:p,doesIdentityCookieExist:g})};Hr.namespace="Consent",Hr.configValidators=zr;var Jr=()=>({eventMergeId:Ie()});const Gr=()=>(({createEventMergeId:e})=>({commands:{createEventMergeId:{run:e}}}))({createEventMergeId:Jr});Gr.namespace="EventMerge";var Qr={PAUSE:"media.pauseStart",PLAY:"media.play",BUFFER_START:"media.bufferStart",AD_START:"media.adStart",Ad_BREAK_START:"media.adBreakStart",SESSION_END:"media.sessionEnd",SESSION_START:"media.sessionStart",SESSION_COMPLETE:"media.sessionComplete",PING:"media.ping",AD_BREAK_COMPLETE:"media.adBreakComplete",AD_COMPLETE:"media.adComplete",AD_SKIP:"media.adSkip",BITRATE_CHANGE:"media.bitrateChange",CHAPTER_COMPLETE:"media.chapterComplete",CHAPTER_SKIP:"media.chapterSkip",CHAPTER_START:"media.chapterStart",ERROR:"media.error",STATES_UPDATE:"media.statesUpdate"},Xr=({config:e,eventManager:t,consent:n,sendEdgeNetworkRequest:r,setTimestamp:o})=>({createMediaEvent({options:n}){const r=t.createEvent(),{xdm:i}=n;if(o(i),r.setUserXdm(i),i.eventType===Qr.AD_START){const{advertisingDetails:t}=n.xdm.mediaCollection;r.mergeXdm({mediaCollection:{advertisingDetails:{playerName:t.playerName||e.streamingMedia.playerName}}})}return r},createMediaSession(n){const{playerName:r,channel:o,appVersion:i}=e.streamingMedia,a=t.createEvent(),{sessionDetails:s}=n.xdm.mediaCollection;return a.setUserXdm(n.xdm),a.mergeXdm({eventType:Qr.SESSION_START,mediaCollection:{sessionDetails:{playerName:s.playerName||r,channel:s.channel||o,appVersion:s.appVersion||i}}}),a},augmentMediaEvent({event:e,playerId:t,getPlayerDetails:n,sessionID:r}){if(!t||!n)return e;const{playhead:o,qoeDataDetails:i}=n({playerId:t});return e.mergeXdm({mediaCollection:{playhead:ve(o),qoeDataDetails:i,sessionID:r}}),e},trackMediaSession({event:e,mediaOptions:n,edgeConfigOverrides:r}){const o={mediaOptions:n,edgeConfigOverrides:r};return t.sendEvent(e,o)},trackMediaEvent({event:e,action:t}){const o=tn(),i=(({mediaRequestPayload:e,action:t})=>Kt({payload:e,edgeSubPath:"/va",getAction:()=>t,getUseSendBeacon:()=>!1}))({mediaRequestPayload:o,action:t});return o.addEvent(e),e.finalize(),n.awaitConsent().then((()=>r({request:i}).then((()=>({})))))}}),Wr="main",Yr="completed",Kr=()=>{let e;return{getSession:t=>e[t]||{},storeSession:({playerId:t,sessionDetails:n})=>{void 0===e&&(e={}),e[t]=n},stopPing:({playerId:t})=>{const n=e[t];n&&(clearTimeout(n.pingId),n.pingId=null,n.playbackState=Yr)},savePing:({playerId:t,pingId:n,playbackState:r})=>{e[t]&&(e[t].pingId&&clearTimeout(e[t].pingId),e[t].pingId=n,e[t].playbackState=r)}}};var $r=({mediaEventManager:e,mediaSessionCacheManager:t,config:n})=>{const r=o=>{const i=e.createMediaEvent({options:o}),{playerId:a,xdm:s}=o,{eventType:c}=s,d=c.split(".")[1],{getPlayerDetails:l,sessionPromise:u,playbackState:p}=t.getSession(a);return u.then((o=>o.sessionId?(e.augmentMediaEvent({event:i,eventType:c,playerId:a,getPlayerDetails:l,sessionID:o.sessionId}),e.trackMediaEvent({event:i,action:d}).then((()=>{if(a)if(c===Qr.SESSION_COMPLETE||c===Qr.SESSION_END)t.stopPing({playerId:a});else{const e=((e,t)=>e===Qr.AD_START||e===Qr.Ad_BREAK_START||e===Qr.AD_SKIP||e===Qr.AD_COMPLETE?"ad":e===Qr.AD_BREAK_COMPLETE||e===Qr.CHAPTER_COMPLETE||e===Qr.CHAPTER_START||e===Qr.CHAPTER_SKIP||e===Qr.SESSION_START?"main":e===Qr.SESSION_END||e===Qr.SESSION_COMPLETE?"completed":t)(c,p);if("completed"===e)return;const o="ad"===e?n.streamingMedia.adPingInterval:n.streamingMedia.mainPingInterval,i=setTimeout((()=>{r({playerId:a,xdm:{eventType:Qr.PING}})}),1e3*o);t.savePing({playerId:a,pingId:i,playbackState:e})}}))):Promise.reject(new Error("Failed to trigger media event: "+c+". Session ID is not available for playerId: "+a+"."))))};return e=>r(e)},Zr=({config:e,mediaEventManager:t,mediaSessionCacheManager:n,legacy:r=!1})=>o=>{if(!e.streamingMedia)return Promise.reject(new Error("Streaming media is not configured."));const{playerId:i,getPlayerDetails:a,edgeConfigOverrides:s}=o,c=t.createMediaSession(o);t.augmentMediaEvent({event:c,playerId:i,getPlayerDetails:a});const d=t.trackMediaSession({event:c,mediaOptions:{playerId:i,getPlayerDetails:a,legacy:r},edgeConfigOverrides:s});return n.storeSession({playerId:i,sessionDetails:{sessionPromise:d,getPlayerDetails:a,playbackState:Wr}}),d},eo=e=>!ie(e)||!e.trim(),to=({mediaSessionCacheManager:e,config:t,trackMediaEvent:n})=>({response:r,playerId:o,getPlayerDetails:i})=>{const a=r.getPayloadsByType("media-analytics:new-session");if(b(a)){const{sessionId:r}=a[0];if(eo(r))return{};if(!o||!i)return{sessionId:r};const s=setTimeout((()=>{n({playerId:o,xdm:{eventType:Qr.PING}})}),1e3*t.streamingMedia.mainPingInterval);return e.savePing({playerId:o,pingId:s,playbackState:Wr}),{sessionId:r}}return{}};const no={Video:"video",Audio:"audio"},ro={VOD:"vod",Live:"live",Linear:"linear",Podcast:"podcast",Audiobook:"audiobook",AOD:"aod"},oo={FullScreen:"fullScreen",ClosedCaption:"closedCaptioning",Mute:"mute",PictureInPicture:"pictureInPicture",InFocus:"inFocus"},io={AdBreakStart:"adBreakStart",AdBreakComplete:"adBreakComplete",AdStart:"adStart",AdComplete:"adComplete",AdSkip:"adSkip",ChapterStart:"chapterStart",ChapterComplete:"chapterComplete",ChapterSkip:"chapterSkip",SeekStart:"seekStart",SeekComplete:"seekComplete",BufferStart:"bufferStart",BufferComplete:"bufferComplete",BitrateChange:"bitrateChange",StateStart:"stateStart",StateEnd:"stateEnd"},ao="sessionStart",so="sessionEnd",co="sessionComplete",lo="play",uo="pauseStart",po="error",go="statesUpdate",mo={MediaResumed:"media.resumed",GranularAdTracking:"media.granularadtracking"},fo={Show:"a.media.show",Season:"a.media.season",Episode:"a.media.episode",AssetId:"a.media.asset",Genre:"a.media.genre",FirstAirDate:"a.media.airDate",FirstDigitalDate:"a.media.digitalDate",Rating:"a.media.rating",Originator:"a.media.originator",Network:"a.media.network",ShowType:"a.media.type",AdLoad:"a.media.adLoad",MVPD:"a.media.pass.mvpd",Authorized:"a.media.pass.auth",DayPart:"a.media.dayPart",Feed:"a.media.feed",StreamFormat:"a.media.format"},ho={Artist:"a.media.artist",Album:"a.media.album",Label:"a.media.label",Author:"a.media.author",Station:"a.media.station",Publisher:"a.media.publisher"},yo={Advertiser:"a.media.ad.advertiser",CampaignId:"a.media.ad.campaign",CreativeId:"a.media.ad.creative",PlacementId:"a.media.ad.placement",SiteId:"a.media.ad.site",CreativeUrl:"a.media.ad.creativeURL"};var vo=({logger:e})=>({createMediaObject:(t,n,r,o,i)=>{const a={friendlyName:t,name:n,length:r,streamType:i,contentType:o},s=yt({friendlyName:vt().nonEmpty(),name:vt().nonEmpty(),length:ft().required(),streamType:vt().nonEmpty(),contentType:vt().nonEmpty()});try{const e=s(a);return{sessionDetails:{name:e.name,friendlyName:e.friendlyName,length:e.length,streamType:e.streamType,contentType:e.contentType}}}catch(t){return e.warn("An error occurred while creating the Media Object.",t),{}}},createAdBreakObject:(t,n,r)=>{const o={friendlyName:t,offset:n,index:r},i=yt({friendlyName:vt().nonEmpty(),offset:ft(),index:ft()});try{const e=i(o);return{advertisingPodDetails:{friendlyName:e.friendlyName,offset:e.offset,index:e.index}}}catch(t){return e.warn("An error occurred while creating the Ad Break Object.",t),{}}},createAdObject:(t,n,r,o)=>{const i={friendlyName:t,name:n,podPosition:r,length:o},a=yt({friendlyName:vt().nonEmpty(),name:vt().nonEmpty(),podPosition:ft(),length:ft()});try{const e=a(i);return{advertisingDetails:{friendlyName:e.friendlyName,name:e.name,podPosition:e.podPosition,length:e.length}}}catch(t){return e.warn("An error occurred while creating the Advertising Object.",t),{}}},createChapterObject:(t,n,r,o)=>{const i={friendlyName:t,offset:n,length:r,index:o},a=yt({friendlyName:vt().nonEmpty(),offset:ft(),length:ft(),index:ft()});try{const e=a(i);return{chapterDetails:{friendlyName:e.friendlyName,offset:e.offset,index:e.index,length:e.length}}}catch(t){return e.warn("An error occurred while creating the Chapter Object.",t),{}}},createStateObject:t=>{const n=vt().matches(/^[a-zA-Z0-9_]{1,64}$/,"This is not a valid state name.");try{return{name:n(t)}}catch(t){return e.warn("An error occurred while creating the State Object.",t),{}}},createQoEObject:(t,n,r,o)=>{const i={bitrate:t,droppedFrames:n,fps:r,startupTime:o},a=yt({bitrate:ft(),droppedFrames:ft(),fps:ft(),startupTime:ft()});try{const e=a(i);return{bitrate:e.bitrate,droppedFrames:e.droppedFrames,framesPerSecond:e.fps,timeToStart:e.startupTime}}catch(t){return e.warn("An error occurred while creating the QOE Object.",t),{}}}});const wo={"a.media.show":"show","a.media.season":"season","a.media.episode":"episode","a.media.asset":"assetID","a.media.genre":"genre","a.media.airDate":"firstAirDate","a.media.digitalDate":"firstDigitalDate","a.media.rating":"rating","a.media.originator":"originator","a.media.network":"network","a.media.type":"showType","a.media.adLoad":"adLoad","a.media.pass.mvpd":"mvpd","a.media.pass.auth":"authorized","a.media.dayPart":"dayPart","a.media.feed":"feed","a.media.format":"streamFormat","a.media.artist":"artist","a.media.album":"album","a.media.label":"label","a.media.author":"author","a.media.station":"station","a.media.publisher":"publisher","media.resumed":"hasResume"},bo={"a.media.ad.advertiser":"advertiser","a.media.ad.campaign":"campaignID","a.media.ad.creative":"creativeID","a.media.ad.placement":"placementID","a.media.ad.site":"siteID","a.media.ad.creativeURL":"creativeURL"};var Eo=({logger:t,trackMediaSession:n,trackMediaEvent:r,uuid:o})=>{let i=null;const a=({eventType:e,mediaDetails:t={},contextData:n=[]})=>{const r=(({eventType:e})=>e===io.BufferComplete||e===io.SeekComplete?lo:e===io.StateStart||e===io.StateEnd?go:e===io.SeekStart?uo:e)({eventType:e});if(e===io.StateStart){return{eventType:"media."+r,mediaCollection:{statesStart:[t]}}}if(e===io.StateEnd){return{eventType:"media."+r,mediaCollection:{statesEnd:[t]}}}const o={eventType:"media."+r,mediaCollection:{...t}},i=[];return Object.keys(n).forEach((e=>{wo[e]?o.mediaCollection.sessionDetails[wo[e]]=n[e]:bo[e]?o.mediaCollection.advertisingDetails[bo[e]]=n[e]:i.push({name:e,value:n[e]})})),b(i)&&(o.mediaCollection.customMetadata=i),o};return{trackSessionStart:(r,s={})=>{if(e(r)||L(r))return t.warn("Invalid media object"),{};null===i&&(t.warn("The Media Session was completed. Restarting a new session."),i={qoe:null,lastPlayhead:0,playerId:o()});const c=a({eventType:ao,mediaDetails:r,contextData:s});return n({playerId:i.playerId,getPlayerDetails:()=>({playhead:i.lastPlayhead,qoeDataDetails:i.qoe}),xdm:c})},trackPlay:()=>{if(null===i)return t.warn("The Media Session was completed."),{};const e=a({eventType:lo});return r({playerId:i.playerId,xdm:e})},trackPause:()=>{if(null===i)return t.warn("The Media Session was completed."),{};const e=a({eventType:uo});return r({playerId:i.playerId,xdm:e})},trackSessionEnd:()=>{if(null===i)return t.warn("The Media Session was completed."),{};const e=a({eventType:so});return r({playerId:i.playerId,xdm:e})},trackComplete:()=>{if(null===i)return t.warn("The Media Session was completed."),{};const e=a({eventType:co});return r({playerId:i.playerId,xdm:e})},trackError:e=>{if(t.warn("trackError("+e+")"),null===i)return t.warn("The Media Session was completed."),{};const n=a({eventType:po,mediaDetails:{errorDetails:{name:e,source:"player"}}});return r({playerId:i.playerId,xdm:n})},trackEvent:(e,n,o)=>{if(L(n))return t.warn("Invalid media object."),{};if(null===i)return t.warn("The Media Session was completed."),{};if(!Object.values(io).includes(e))return t.warn("Invalid event type"),{};const s=a({eventType:e,mediaDetails:n,contextData:o});return r({playerId:i.playerId,xdm:s})},updatePlayhead:e=>{null!==i?re(e)&&(i.lastPlayhead=parseInt(e,10)):t.warn("The Media Session was completed.")},updateQoEObject:e=>{null!==i?e&&(i.qoe=e):t.warn("The Media Session was completed.")},destroy:()=>{t.warn("Destroy called, destroying the tracker."),i=null}}};const Co=({eventManager:e,sendEdgeNetworkRequest:t,config:n,logger:r,consent:o})=>{const i=Kr(),a=Xr({sendEdgeNetworkRequest:t,config:n,consent:o,eventManager:e,setTimestamp:mn((()=>new Date))}),s=$r({mediaSessionCacheManager:i,mediaEventManager:a,config:n}),c=Zr({config:n,mediaEventManager:a,mediaSessionCacheManager:i,legacy:!0});return(({trackMediaEvent:e,trackMediaSession:t,mediaResponseHandler:n,logger:r,createMediaHelper:o,createGetInstance:i,config:a})=>({lifecycle:{onBeforeEvent({mediaOptions:e,onResponse:t=se}){if(!e)return;const{legacy:r,playerId:o,getPlayerDetails:i}=e;r&&t((({response:e})=>n({playerId:o,getPlayerDetails:i,response:e})))}},commands:{getMediaAnalyticsTracker:{run:()=>{if(!a.streamingMedia)return Promise.reject(new Error("Streaming media is not configured."));r.info("Streaming media is configured in legacy mode.");const n=o({logger:r});return Promise.resolve({getInstance:()=>i({logger:r,trackMediaEvent:e,trackMediaSession:t,uuid:Ie}),Event:io,MediaType:no,PlayerState:oo,StreamType:ro,MediaObjectKey:mo,VideoMetadataKeys:fo,AudioMetadataKeys:ho,AdMetadataKeys:yo,...n})}}}}))({mediaResponseHandler:to({mediaSessionCacheManager:i,config:n,trackMediaEvent:s}),trackMediaSession:c,trackMediaEvent:s,createMediaHelper:vo,createGetInstance:Eo,logger:r,config:n})};Co.namespace="Legacy Media Analytics";const ko="web",Io="webapp",So="://",Do=/^(\w+):\/\/([^/#]+)(\/[^#]*)?(#.*)?$/,Po=/^(?:.*@)?(?:[a-z\d\u00a1-\uffff.-]+|\[[a-f\d:]+])(?::\d+)?$/,To=/^\/(?:[/\w\u00a1-\uffff-.~]|%[a-fA-F\d]{2})*$/,Ro=/^#(?:[/\w\u00a1-\uffff-.~]|%[a-fA-F\d]{2})+$/,Oo=(e="/")=>{let t=e.length;for(;t>0&&-1!=="/".indexOf(e.charAt(t-1));)t-=1;return e.substring(0,t)||"/"},No=e=>""+e.surfaceType+So+e.authority+(e.path||"")+(e.fragment||""),Mo=e=>{const t=e(),n=t.host.toLowerCase(),r=t.pathname;return ko+So+n+Oo(r)},Ao=(e,t,n)=>{const r=e=>(n.warn(e),null);if(!ae(e))return r("Invalid surface: "+e);const o=((e,t)=>e.startsWith("#")?Mo(t)+e:e)(e,t),i=(e=>{const t=e.match(Do);return t?{surfaceType:(o=t[1],ae(o)?o.toLowerCase():""),authority:(r=t[2],ae(r)?r.toLowerCase():""),path:(n=t[3],ae(n)?Oo(n):"/"),fragment:t[4]}:null;var n,r,o})(o);return null===i?r("Invalid surface: "+e):[ko,Io].includes(i.surfaceType)?i.authority&&Po.test(i.authority)?i.path&&!To.test(i.path)?r("Invalid path "+i.path+" in surface: "+e):i.fragment&&!Ro.test(i.fragment)?r("Invalid fragment "+i.fragment+" in surface: "+e):i:r("Invalid authority "+i.authority+" in surface: "+e):r("Unsupported surface type "+i.surfaceType+" in surface: "+e)},xo=e=>!!e&&0===e.indexOf(ko+So)&&-1===e.indexOf("#"),qo="https://ns.adobe.com/personalization/default-content-item",Lo="https://ns.adobe.com/personalization/dom-action",Uo="https://ns.adobe.com/personalization/html-content-item",jo="https://ns.adobe.com/personalization/json-content-item",Bo="https://ns.adobe.com/personalization/ruleset-item",Fo="https://ns.adobe.com/personalization/redirect-item",_o="https://ns.adobe.com/personalization/message/in-app",Vo=e=>e.filter(((t,n)=>e.indexOf(t)===n));var zo=({getPageLocation:t,renderDecisions:n,decisionScopes:r,personalization:o,event:i,isCacheInitialized:a,logger:s})=>{const c=i.getViewName();return{isRenderDecisions:()=>n,isSendDisplayEvent:()=>!!o.sendDisplayEvent,shouldIncludeRenderedPropositions:()=>!!o.includeRenderedPropositions,getViewName:()=>c,hasScopes:()=>r.length>0||b(o.decisionScopes),hasSurfaces:()=>b(o.surfaces),hasViewName:()=>ae(c),createQueryDetails(){const n=[...r];b(o.decisionScopes)&&n.push(...o.decisionScopes);const i=((t=[],n,r)=>t.map((e=>Ao(e,n,r))).filter((t=>!e(t))).map(No))(o.surfaces,t,s);this.shouldRequestDefaultPersonalization()&&((e=>{e.includes(Wt)||e.push(Wt)})(n),((e,t)=>{const n=Mo(t);e.includes(n)||e.push(n)})(i,t));const a=[qo,Uo,jo,Fo,Bo,_o,"https://ns.adobe.com/personalization/message/content-card"];return n.includes(Wt)&&a.push(Lo),{schemas:a,decisionScopes:Vo(n),surfaces:Vo(i)}},isCacheInitialized:()=>a,shouldFetchData(){return this.hasScopes()||this.hasSurfaces()||this.shouldRequestDefaultPersonalization()},shouldUseCachedData(){return this.hasViewName()&&!this.shouldFetchData()},shouldRequestDefaultPersonalization(){return o.defaultPersonalizationEnabled||!this.isCacheInitialized()&&!1!==o.defaultPersonalizationEnabled}}};const Ho="decisioning.propositionDisplay",Jo="decisioning.propositionInteract",Go="decisioning.propositionTrigger",Qo="decisioning.propositionDismiss",Xo="decisioning.propositionSuppressDisplay",Wo={DISPLAY:"display",INTERACT:"interact",TRIGGER:"trigger",DISMISS:"dismiss",SUPPRESS:"suppressDisplay"},Yo={[Ho]:Wo.DISPLAY,[Jo]:Wo.INTERACT,[Go]:Wo.TRIGGER,[Qo]:Wo.DISMISS,[Xo]:Wo.SUPPRESS},Ko={[Wo.DISPLAY]:Ho,[Wo.INTERACT]:Jo,[Wo.TRIGGER]:Go,[Wo.DISMISS]:Qo,[Wo.SUPPRESS]:Xo},$o=e=>Yo[e],Zo=e=>Ko[e],ei={propositions:[]};var ti=({getPageLocation:e,logger:t,fetchDataHandler:n,viewChangeHandler:r,onClickHandler:o,isAuthoringModeEnabled:i,mergeQuery:a,viewCache:s,showContainers:c,applyPropositions:d,setTargetMigration:l,mergeDecisionsMeta:u,renderedPropositions:p,onDecisionHandler:g,handleConsentFlicker:m})=>({lifecycle:{onComponentsRegistered(){m()},onDecision:g,onBeforeRequest:({request:e})=>(l(e),Promise.resolve()),onBeforeEvent({event:o,renderDecisions:d,decisionScopes:l=[],personalization:g={},onResponse:m=se,onRequestFailure:f=se}){if(m((()=>({propositions:[]}))),f((()=>c())),i())return t.warn("Rendering is disabled for authoring mode."),a(o,{enabled:!1}),Promise.resolve();const h=zo({getPageLocation:e,renderDecisions:d,decisionScopes:l,personalization:g,event:o,isCacheInitialized:s.isInitialized(),logger:t}),y=[];if(h.shouldIncludeRenderedPropositions()&&y.push(p.clear()),h.shouldFetchData()){const e=s.createCacheUpdate(h.getViewName());f((()=>e.cancel())),n({cacheUpdate:e,personalizationDetails:h,event:o,onResponse:m})}else h.shouldUseCachedData()&&y.push(r({personalizationDetails:h,event:o,onResponse:m,onRequestFailure:f}));return Promise.all(y).then((e=>{const t=e.flatMap((e=>e));b(t)&&u(o,t,[Wo.DISPLAY])}))},onClick({event:e,clickedElement:t}){o({event:e,clickedElement:t})}},commands:{applyPropositions:{optionsValidator:e=>(({logger:e,options:t})=>{const n=yt({propositions:ut(yt({id:vt().required(),scope:vt().required(),scopeDetails:yt({decisionProvider:vt().required()}).required(),items:ut(yt({id:vt().required(),schema:vt().required(),data:yt(lt())})).nonEmpty().required()}).required()).nonEmpty().required(),metadata:yt(lt()),viewName:vt()}).required();try{return n(t)}catch(t){return e.warn("Invalid options for applyPropositions. No propositions will be applied.",t),ei}})({logger:t,options:e}),run:d}}}),ni=(e="undefined")=>m("DIV",{},{innerHTML:e});const ri=/:eq\((\d+)\)/g,oi=e=>-1===e.indexOf(":eq("),ii=/(#|\.)(-?\w+)/g,ai=(e,t,n)=>""+t+CSS.escape(n),si=e=>{const t=[],n=(e=>e.split(ri).filter(ae))((e=>e.replace(ii,ai))(e.trim())),{length:r}=n;let o=0;for(;o{const t=document;if(oi(e))return P(e,t);const n=si(e),{length:r}=n;let o=[],i=t,a=0;for(;ac-1)break;at.getElementById(e),li=(e,t,n)=>{e.setAttribute(t,n)},ui=(e,t)=>e.getAttribute(t),pi=(e,t,n,r)=>{let o;o=r?t+":"+n+" !"+r+";":t+":"+n+";",e.style.cssText+=";"+o},gi=e=>e.parentNode,mi=(e,t)=>{if(!e)return;const n=gi(e);n&&n.insertBefore(t,(e=>e.nextElementSibling)(e))},fi=(e,t)=>{if(!e)return;const n=gi(e);n&&n.insertBefore(t,e)},hi=e=>{const{childNodes:t}=e;return t?E(t):[]},yi=e=>e.firstElementChild;let vi;var wi=(e=document)=>{if(void 0===vi){const t=e.querySelector("[nonce]");vi=t&&(t.nonce||t.getAttribute("nonce"))}return vi};const bi="src",Ei=e=>m(f,{src:e}),Ci=e=>{P(f,e).forEach((e=>{const t=ui(e,bi);t&&Ei(t)}))},ki=e=>((e,t)=>e.tagName===t)(e,h)&&!ui(e,bi);var Ii=e=>{const t=P(h,e),{length:n}=t,r=wi();if(r)for(let e=0;e{const t=document.createElement("script");t.src=e,t.async=!0;const n=((e,t)=>new Promise(((n,r)=>{t.onload=()=>{n(t)},t.onerror=()=>{r(new Error("Failed to load script: "+e))}})))(e,t);return document.head.appendChild(t),n},Di=(e,t)=>!!e&&e.tagName===t,Pi=e=>Di(e,y)&&!ui(e,bi),Ti=e=>Di(e,y)&&ui(e,bi),Ri=e=>{const t=P(y,e),n=[],{length:r}=t,o=wi(),i={...o&&{nonce:o}};for(let e=0;e{const t=P(y,e),n=[],{length:r}=t;for(let e=0;e{t.forEach((t=>{e.appendChild(t),e.removeChild(t)}))},Mi=e=>Promise.all(e.map(Si));var Ai=(e,t,n)=>{const r=ni(t);Ii(r);const o=hi(r),i=Ri(r),a=Oi(r);return Ci(r),o.forEach((t=>{g(e,t)})),n(e),Ni(e,i),Mi(a)};var xi=(e,t,n)=>((e=>{hi(e).forEach(A)})(e),Ai(e,t,n)),qi=(e,t,n)=>{const r=ni(t);Ii(r);const o=hi(r),i=Ri(r),a=Oi(r),{length:s}=o;let c=s-1;for(Ci(r);c>=0;){const t=o[c];n(t);const r=yi(e);r?fi(r,t):g(e,t),c-=1}return Ni(e,i),Mi(a)};const Li="alloy-prehiding",Ui={},ji=e=>{if(Ui[e])return;const t=wi(),n={...t&&{nonce:t}},r=m(h,n,{textContent:e+" { visibility: hidden }"});g(document.head,r),Ui[e]=r},Bi=e=>{const t=Ui[e];t&&(A(t),delete Ui[e])};var Fi=(e,t,n)=>{n(e),e.textContent=t},_i=(e,t,n)=>{const r=ni(t);Ii(r);const o=hi(r),i=Ri(r),a=Oi(r);return Ci(r),o.forEach((t=>{n(t),fi(e,t)})),Ni(e,i),Mi(a)},Vi=(e,t,n)=>_i(e,t,n).then((()=>{A(e)})),zi=(e,t,n)=>{const r=ni(t);Ii(r);const o=hi(r),i=Ri(r),a=Oi(r);Ci(r);let s=e;return o.forEach((e=>{n(e),mi(s,e),s=e})),Ni(e,i),Mi(a)},Hi=(e,t,n)=>{const{priority:r,...o}=t;Object.keys(o).forEach((t=>{pi(e,t,o[t],r)})),n(e)},Ji=(e,t,n)=>{Object.keys(t).forEach((n=>{li(e,n,t[n])})),n(e)},Gi=(e,t,n)=>{e.tagName===f&&(Ei(t),n(e),((e,t)=>{e.removeAttribute(t)})(e,bi),li(e,bi,t))},Qi=(e,{from:t,to:n},r)=>{const o=(e=>{const{children:t}=e;return t?E(t):[]})(e),i=o[t],a=o[n];i&&a&&(t(t,n)=>{const{selector:r,prehidingSelector:o,content:i}=t;return ji(o),M(r,ci).then((t=>((e,t,n,r)=>{const o=e.map((e=>r(e,t,n)));return Promise.all(o)})(t,i,n,e))).then((()=>{Bi(o)}),(e=>{throw Bi(o),e}))},Wi=e=>(""+e).endsWith("px")?e:e+"px";var Yi=(e,t,n)=>{const{priority:r,...o}=t;Object.keys(o).forEach((t=>{let n=o[t];"left"!==t&&"top"!==t||(n=Wi(n)),pi(e,t,n,r)})),n(e)},Ki=(e,t,n)=>{n(e)},$i=(e,t,n)=>{const{priority:r,...o}=t;Object.keys(o).forEach((t=>{let n=o[t];"width"!==t&&"height"!==t||(n=Wi(n)),pi(e,t,n,r)})),n(e)};const Zi="setHtml",ea="customCode",ta="setText",na="setAttribute",ra="setImageSource",oa="setStyle",ia="move",aa="resize",sa="rearrange",ca="remove",da="insertAfter",la="insertBefore",ua="replaceHtml",pa="prependHtml",ga="appendHtml",ma="click",fa="collectInteractions";var ha=({eventManager:e,mergeDecisionsMeta:t})=>({decisionsMeta:n=[],propositionAction:r,documentMayUnload:o=!1,eventType:i=Ho,propositionEventTypes:a=[$o(i)],viewName:s})=>{const c=e.createEvent(),d={eventType:i};return s&&(d.web={webPageDetails:{viewName:s}}),b(n)&&t(c,n,a,r),c.mergeXdm(d),o&&c.documentMayUnload(),e.sendEvent(c)};var ya=(e,t)=>{if(oi(e))return((e,t)=>t.matches?t.matches(e):t.msMatchesSelector(e))(e,t);const n=ci(e);let r=!1;for(let e=0;ee.map((e=>{const{trackingLabel:t,scopeType:n,...r}=e;return r})),ba=(e,t,n)=>{const{documentElement:r}=document;let o=e,i=0;for(;o&&o!==r;){if(ya(t,o)){const e=n(t),r={metas:e},o=e.find((e=>e.trackingLabel));o&&(r.label=o.trackingLabel,r.weight=i);const a=e.find((e=>e.scopeType===va));return a&&(r.viewName=a.scope,r.weight=i),r}o=o.parentNode,i+=1}return{metas:null}};var Ea=(e,t,n)=>{const r=[];let o,i="",a=Number.MAX_SAFE_INTEGER,s=Number.MAX_SAFE_INTEGER;for(let c=0;c{const n=JSON.stringify(e);return t===c.findIndex((e=>JSON.stringify(e)===n))}))),propositionActionLabel:i,propositionActionToken:void 0,viewName:o};var c},Ca=(e=document)=>-1!==e.location.href.indexOf("adobe_authoring_enabled");const ka=(e,t,n,r)=>{if(0===t.length)return;const o={};n.forEach((e=>{o[e]=1}));const i={_experience:{decisioning:{propositions:t,propositionEventType:o}}};r&&(i._experience.decisioning.propositionAction=r),e.mergeXdm(i)},Ia=(e,t)=>{e.mergeQuery({personalization:{...t}})};var Sa=()=>{const e={};return{storeClickMeta:({selector:t,meta:{id:n,scope:r,scopeDetails:o,trackingLabel:i,scopeType:a}})=>{e[t]||(e[t]={}),e[t][n]={scope:r,scopeDetails:o,trackingLabel:i,scopeType:a}},getClickSelectors:()=>Object.keys(e),getClickMetas:t=>e[t]?(e=>Object.keys(e).map((t=>({id:t,...e[t]}))))(e[t]):{}}};const Da=(e,t)=>e===jo&&t===fa,Pa={[Lo]:()=>!0,[Uo]:()=>!0,[jo]:Da,[_o]:()=>!0,[qo]:()=>!0};var Ta=({processPropositions:e,createProposition:t,renderedPropositions:n,viewCache:r})=>{const o=({items:e,metadataForScope:t={}})=>{const{actionType:n,selector:r}=t;return e.filter((e=>((e,t)=>"function"==typeof Pa[e]&&Pa[e](e,t))(e.schema,n))).map((e=>{const{schema:o}=e;return o===Uo||Da(o,n)?L(t)?void 0:{...e,schema:Da(o,n)?Lo:o,data:{...e.data,selector:r,type:n}}:{...e}})).filter((e=>e))},i=e=>!(e.scope===Wt&&e.renderAttempted);return({propositions:a=[],metadata:s={},viewName:c})=>{const d=u();n.concat(d.promise);const l=(({propositions:e,metadata:t})=>e.filter(i).map((e=>{if(b(e.items)){const{id:n,scope:r,scopeDetails:i}=e;return{id:n,scope:r,scopeDetails:i,items:o({items:e.items,metadataForScope:t[e.scope]})}}return e})).filter((e=>b(e.items))))({propositions:a,metadata:s}).map((e=>t(e)));return Promise.resolve().then((()=>c?r.getView(c):[])).then((t=>{const{render:n,returnedPropositions:r}=e([...l,...t]);return n().then(d.resolve),{propositions:r}}))}};var Ra=e=>{const{selector:t,type:n}=e;return n!==ea||"BODY > *:eq(0)"!==t?e:{...e,selector:"BODY"}};var Oa=e=>{const t={...e},{content:n,selector:r}=t;if(eo(n))return t;if(null==r)return t;const o=ci(r);return Di(o[0],"HEAD")?(t.type=ga,t.content=(e=>{const t=ni(e);return P("SCRIPT,LINK,STYLE",t).map((e=>e.outerHTML)).join("")})(n),t):t},Na=({preprocess:e,isPageWideSurface:t})=>(n,r=!0,o=!1)=>{const{id:i,scope:a,scopeDetails:s,items:c=[]}=n,{characteristics:{scopeType:d}={}}=s||{};return{getScope:()=>a,getScopeType:()=>a===Wt||t(a)?"page":d===va?va:"proposition",getItems(){return c.map((t=>((t,n)=>{const{id:r,schema:o,data:i,characteristics:{trackingLabel:a}={}}=t,s=i?i.type:void 0,c=e(i);return{getId:()=>r,getSchema:()=>o,getSchemaType:()=>s,getData:()=>c,getProposition:()=>n,getTrackingLabel:()=>a,getOriginalItem:()=>t,toString:()=>JSON.stringify(t),toJSON:()=>t}})(t,this)))},getNotification:()=>({id:i,scope:a,scopeDetails:s}),getId:()=>i,toJSON:()=>n,shouldSuppressDisplay:()=>o,addToReturnValues(e,t,o,i){r&&(e.push({...n,items:o.map((e=>e.getOriginalItem())),renderAttempted:i}),i||t.push({...n,items:o.map((e=>e.getOriginalItem()))}))}}},Ma=()=>({render:se,setRenderAttempted:!0,includeInNotification:!0});const Aa="always",xa="never",qa="decoratedElementsOnly",La=[Aa,xa,qa],Ua="data-aep-interact-id",ja="data-aep-click-label";let Ba=0;const Fa=(e,t,n,r,o,i,a,s)=>{const{scopeDetails:c={}}=a,{decisionProvider:d}=c;return((e,t)=>!!e&&!!e[t]&&[Aa,qa].includes(e[t]))(e,d)||t===ma?e=>{if(!e.tagName)return;const t=(c=ui(e,Ua))?parseInt(c,10):++Ba;var c;s(n,r,i,a,t),li(e,Ua,t),o&&!ui(e,ja)&&li(e,ja,o)}:se};var _a=({modules:e,logger:t,storeInteractionMeta:n,storeClickMeta:r,autoCollectPropositionInteractions:o})=>i=>{const{type:a,selector:s}=i.getData()||{};if(!a)return t.warn("Invalid DOM action data: missing type.",i.getData()),{setRenderAttempted:!1,includeInNotification:!1};if(a===ma)return s?(r({selector:s,meta:{...i.getProposition().getNotification(),trackingLabel:i.getTrackingLabel(),scopeType:i.getProposition().getScopeType()}}),{setRenderAttempted:!0,includeInNotification:!1}):(t.warn("Invalid DOM action data: missing selector.",i.getData()),{setRenderAttempted:!1,includeInNotification:!1});if(!e[a])return t.warn("Invalid DOM action data: unknown type.",i.getData()),{setRenderAttempted:!1,includeInNotification:!1};const c=Fa(o,a,i.getProposition().getId(),i.getId(),i.getTrackingLabel(),i.getProposition().getScopeType(),i.getProposition().getNotification(),n);return{render:()=>e[a](i.getData(),c),setRenderAttempted:!0,includeInNotification:!0}},Va=({modules:e,logger:t,storeInteractionMeta:n,autoCollectPropositionInteractions:r})=>o=>{const{type:i,selector:a}=o.getData()||{};if(!a||!i)return{setRenderAttempted:!1,includeInNotification:!1};if(!e[i])return t.warn("Invalid HTML content data",o.getData()),{setRenderAttempted:!1,includeInNotification:!1};const s=Fa(r,i,o.getProposition().getId(),o.getId(),o.getTrackingLabel(),o.getProposition().getScopeType(),o.getProposition().getNotification(),n);return{render:()=>e[i](o.getData(),s),setRenderAttempted:!0,includeInNotification:!0}};const za="BODY";var Ha=({logger:e,executeRedirect:t,collect:n})=>r=>{const{content:o}=r.getData()||{};if(!o)return e.warn("Invalid Redirect data",r.getData()),{};return{render:()=>(ji(za),n({decisionsMeta:[r.getProposition().getNotification()],documentMayUnload:!0}).then((()=>(e.logOnContentRendering({status:"rendering-redirect",detail:{propositionDetails:r.getProposition().getNotification(),redirect:o},message:"Redirect action "+r.toString()+" executed.",logLevel:"info"}),t(o)))).catch((e=>{throw Bi(za),e}))),setRenderAttempted:!0,onlyRenderThis:!0}},Ja=({schemaProcessors:e,logger:t})=>{const n=(e,n)=>()=>Promise.resolve().then(e).then((()=>(t.enabled&&t.info("Action "+n.toString()+" executed."),n.toJSON()))).catch((e=>{const{message:r,stack:o}=e,i="Failed to execute action "+n.toString()+". "+r+" "+o;t.logOnContentRendering({status:"rendering-failed",detail:{propositionDetails:n.getProposition().getNotification(),item:n.toJSON()},error:e,message:i,logLevel:"warn"})})),r=t=>{const n=e[t.getSchema()];return n?n(t):{}},o=({renderers:e,returnedPropositions:t,returnedDecisions:o,items:i,proposition:a})=>{let s,c,d,l,u=[...e],p=[...t],g=[...o],m=[],f=[],h=[],y=!1,v=!1,w=0;for(;i.length>w;){if(l=i[w],({render:s,setRenderAttempted:c,includeInNotification:d,onlyRenderThis:v}=r(l)),v){p=[],g=[],c?(m=[l],f=[]):(m=[],f=[l]),u=[],h=[s],y=d;break}s&&h.push(n(s,l)),d&&(y=!0),c?m.push(l):f.push(l),w+=1}if(h.length>0){const e=y?a.getNotification():void 0;u.push((()=>(async(e,t)=>{const n=(await Promise.allSettled(e.map((e=>e())))).filter((e=>"fulfilled"===e.status)).map((e=>e.value));if(t&&b(n))return{...t,items:n}})(h,e)))}else y&&u.push((()=>Promise.resolve(a.getNotification())));return m.length>0&&a.addToReturnValues(p,g,m,!0),f.length>0&&a.addToReturnValues(p,g,f,!1),{renderers:u,returnedPropositions:p,returnedDecisions:g,onlyRenderThis:v}};return(e,n=[])=>{let r,i,a,s=[],c=[],d=[],l=0;for(;e.length>l&&(i=e[l],a=i.getItems(),({renderers:s,returnedPropositions:c,returnedDecisions:d,onlyRenderThis:r}=o({renderers:s,returnedPropositions:c,returnedDecisions:d,items:a,proposition:i})),!r);)l+=1;r&&e.forEach(((e,t)=>{t!==l&&e.addToReturnValues(c,d,e.getItems(),!1)})),n.forEach((e=>{e.addToReturnValues(c,d,e.getItems(),!1)}));return{returnedPropositions:c,returnedDecisions:d,render:()=>Promise.all(s.map((e=>e()))).then((e=>{const n=e.filter((e=>e)),r=n.map((e=>{const{id:t,scope:n,scopeDetails:r}=e;return{id:t,scope:n,scopeDetails:r}}));if(b(n)){const e=z(n,(e=>e.scope));t.logOnContentRendering({status:"rendering-succeeded",detail:{...e},message:"Scopes: "+JSON.stringify(e)+" successfully executed.",logLevel:"info"})}return r}))}}};var Ga=({processPropositions:e,createProposition:t,notificationHandler:n})=>({renderDecisions:r,propositions:o,event:i,personalization:a={}})=>{if(!r)return Promise.resolve();const{sendDisplayEvent:s=!0}=a,c=i?i.getViewName():void 0,d=(()=>{let e=0;return t=>{const{items:n=[]}=t;return!!n.some((e=>e.schema===_o))&&(e+=1,e>1)}})(),l=o.map((e=>t(e,!0,d(e)))),{render:u,returnedPropositions:p}=e(l),g=n(r,s,c),m=l.reduce(((e,t)=>(e[t.getId()]=t,e)),{});return u().then((e=>{const t=e.filter((e=>!m[e.id].shouldSuppressDisplay())),n=e.filter((e=>m[e.id].shouldSuppressDisplay()));g(t,n)})),Promise.resolve({propositions:p})};const Qa="text/html",Xa="defaultContent",Wa=["content","contentType"],Ya=["mobileParameters","webParameters","html"];var Ka=({modules:e,logger:t})=>n=>{const r=n.getData(),o=n.getProposition(),i={...o.getNotification()},a=o.shouldSuppressDisplay();if(!r)return t.warn("Invalid in-app message data: undefined.",r),{};const{type:s=Xa}=r;return e[s]?((e,t)=>{for(let n=0;na?null:e[s]({...r,meta:i}),setRenderAttempted:!0,includeInNotification:!0}:(t.warn("Invalid in-app message meta: undefined.",i),{}):{}:(t.warn("Invalid in-app message data: unknown type.",r),{})};const $a=e=>{const t=P("#"+e,document);t&&t.length>0&&A(t[0])};var Za=e=>(t,n=!1)=>(n?e.location.href=t:e.location.replace(t),new Promise((()=>{})));const es="alloy-messaging-container",ts="alloy-overlay-container",ns="alloy-content-iframe",rs=()=>[es,ts].forEach($a),os=(e,t=Za(window))=>n=>{n.preventDefault(),n.stopImmediatePropagation();const{target:r}=n,o="a"===r.tagName.toLowerCase()?r:r.closest("a");if(!o)return;const{action:i,interaction:a,link:s,label:c,uuid:d}=(e=>{const t={};if(!e||"a"!==e.tagName.toLowerCase())return t;const{href:n}=e;if(!n||!n.startsWith("adbinapp://"))return t;const r=n.split("?"),o=r[0].split("://")[1],i=e.innerText,a=e.getAttribute("data-uuid")||"";let s,c;if(b(r)){const e=me.parse(r[1]);s=e.interaction||"",c=Ln(e.link||"")}return{action:o,interaction:s,link:c,label:i,uuid:a}})(o);e(i,{label:c,id:a,uuid:d,link:s}),"dismiss"===i&&rs(),ae(s)&&s.length>0&&t(s,!0)},is=e=>{const{verticalAlign:t,width:n,horizontalAlign:r,backdropColor:o,height:i,cornerRadius:a,horizontalInset:s,verticalInset:c,uiTakeover:d=!1}=e,l={width:n?n+"%":"100%",backgroundColor:o||"rgba(0, 0, 0, 0.5)",borderRadius:a?a+"px":"0px",border:"none",position:d?"fixed":"relative",overflow:"hidden"};return"left"===r?l.left=s?s+"%":"0":"right"===r?l.right=s?s+"%":"0":"center"===r&&(l.left="50%",l.transform="translateX(-50%)"),"top"===t?l.top=c?c+"%":"0":"bottom"===t?(l.position="fixed",l.bottom=c?c+"%":"0"):"center"===t&&(l.top="50%",l.transform=("center"===r?l.transform+" ":"")+"translateY(-50%)",l.display="flex",l.alignItems="center",l.justifyContent="center"),l.height=i?i+"vh":"100%",l},as=e=>{const{backdropOpacity:t,backdropColor:n}=e;return{position:"fixed",top:"0",left:"0",width:"100%",height:"100%",background:"transparent",opacity:t||.5,backgroundColor:n||"#FFFFFF"}},ss=["enabled","parentElement","insertionMethod"],cs=(e={},t)=>{rs();const{content:n,contentType:r,mobileParameters:o}=e;let{webParameters:i}=e;if(r!==Qa)return;const a=m("div",{id:es}),s=((e,t)=>{const n=(new DOMParser).parseFromString(e,Qa),r=n.querySelector("script");r&&r.setAttribute("nonce",wi());const o=m("iframe",{src:URL.createObjectURL(new Blob([n.documentElement.outerHTML],{type:"text/html"})),id:ns});return o.addEventListener("load",(()=>{const{addEventListener:e}=o.contentDocument||o.contentWindow.document;e("click",t)})),o})(n,os(t)),c=m("div",{id:ts});(e=>{if(!e)return!1;const t=Object.keys(e);if(!t.includes(es))return!1;if(!t.includes(ts))return!1;const n=Object.values(e);for(let e=0;e{if(!e)return;const{uiTakeover:t=!1}=e;return{[ns]:{style:{border:"none",width:"100%",height:"100%"},params:{enabled:!0,parentElement:"#alloy-messaging-container",insertionMethod:"appendChild"}},[es]:{style:is(e),params:{enabled:!0,parentElement:"body",insertionMethod:"appendChild"}},[ts]:{style:as(e),params:{enabled:!0===t,parentElement:"body",insertionMethod:"appendChild"}}}})(o)),i&&((e,t,n,r)=>{[{id:ts,element:r},{id:es,element:n},{id:ns,element:e}].forEach((({id:e,element:n})=>{const{style:r={},params:o={}}=t[e];n.style={...n.style,...r};const{parentElement:i="body",insertionMethod:a="appendChild",enabled:s=!0}=o,c=document.querySelector(i);s&&c&&"function"==typeof c[a]&&c[a](n)}))})(s,i,a,c)};var ds=e=>({defaultContent:t=>((e,t)=>new Promise((n=>{const{meta:r}=e;cs(e,((e,n)=>{const o={};o[Wo.INTERACT]=1,-1!==Object.values(Wo).indexOf(e)&&(o[e]=1),t({decisionsMeta:[r],propositionAction:n,eventType:Jo,propositionEventTypes:Object.keys(o)})})),n({meta:r})})))(t,e)});const ls=e=>{const t=e.find((e=>e.scopeType===va));return t?t.scope:void 0};var us=(e,t,n)=>{const{interactIds:r,clickLabel:o="",clickToken:i}=(e=>{const{documentElement:t}=document;let n=e;const r=new Set;let o,i;for(;n&&n!==t;){const e=ui(n,Ua);e&&r.add(e),o=o||ui(n,ja),i=i||ui(n,"data-aep-click-token"),n=n.parentNode}return{interactIds:[...r],clickLabel:o,clickToken:i}})(e),a=((e,t,n)=>r=>{const{scopeDetails:o={}}=r,{decisionProvider:i}=o;return e[i]===Aa||e[i]===qa&&(t||n)})(n,o,i);if(0===r.length)return{};const s=t(r).filter(a);return{decisionsMeta:wa(s),propositionActionLabel:o,propositionActionToken:i,viewName:ls(s)}};const ps="AJO",gs="TGT",ms=({config:e,logger:t,eventManager:n,consent:r})=>{const{targetMigrationEnabled:o,prehidingStyle:i,autoCollectPropositionInteractions:a}=e,s=ha({eventManager:n,mergeDecisionsMeta:ka}),c=(e=>()=>{const t=di(Li);t&&(e.logOnContentHiding({status:"show-containers",message:"Prehiding style removed to show containers.",logLevel:"info"}),A(t))})(t),d=(e=>t=>{if(!t)return;if(di(Li))return;const n=wi(),r={id:Li,...n&&{nonce:n}},o=m(h,r,{textContent:t});e.logOnContentHiding({status:"hide-containers",message:"Prehiding style applied to hide containers.",logLevel:"info"}),g(document.head,o)})(t),{storeInteractionMeta:l,getInteractionMetas:p}=(()=>{const e={},t={};return{storeInteractionMeta:(n,r,o,i,a)=>{a=parseInt(a,10),e[a]||(e[a]={},t[a]={}),t[a][n]||(t[a][n]=new Set),t[a][n].add(r),e[a][n]={...i,scopeType:o}},getInteractionMetas:n=>Array.isArray(n)&&0!==n.length?Object.values(n.map((e=>parseInt(e,10))).reduce(((n,r)=>(Object.keys(e[r]||{}).forEach((o=>{n[o]||(n[o]={proposition:e[r][o],items:new Set}),n[o].items=new Set([...n[o].items,...t[r][o]])})),n)),{})).map((({proposition:e,items:t})=>({...e,items:Array.from(t).map((e=>({id:e})))}))):[]}})(),{storeClickMeta:f,getClickSelectors:y,getClickMetas:v}=Sa(),w=(({window:e})=>()=>e.location)({window:window}),E={[Zi]:Xi(xi),[ea]:Xi(qi),[ta]:Xi(Fi),[na]:Xi(Ji),[ra]:Xi(Gi),[oa]:Xi(Hi),[ia]:Xi(Yi),[aa]:Xi($i),[sa]:Xi(Qi),[ca]:Xi(A),[da]:Xi(zi),[la]:Xi(_i),[ua]:Xi(Vi),[pa]:Xi(qi),[ga]:Xi(Ai),[fa]:Xi(Ki)},C=(k=[Oa,Ra],e=>e?k.reduce(((e,t)=>({...e,...t(e)})),e):e);var k;const I=Na({preprocess:C,isPageWideSurface:xo}),S=(({createProposition:e})=>{let t=!1,n=Promise.resolve({});const r=(t,n)=>{const r=t[n.toLowerCase()];return r&&r.length>0?r:[e({scope:n,scopeDetails:{characteristics:{scopeType:va}},items:[{schema:qo}]},!1)]};return{createCacheUpdate:e=>{const o=u();return t=!0,n=n.then((e=>o.promise.then((t=>({...e,...t}))).catch((()=>e)))),{update(t){const n=t.filter((e=>e.getScope())),i=z(n,(e=>e.getScope().toLowerCase()));return o.resolve(i),e?r(i,e):[]},cancel(){o.reject()}}},getView:e=>n.then((t=>r(t,e))),isInitialized:()=>t}})({createProposition:I}),D=Za(window),P={[qo]:Ma,[Lo]:_a({modules:E,logger:t,storeInteractionMeta:l,storeClickMeta:f,autoCollectPropositionInteractions:a}),[Uo]:Va({modules:E,logger:t,storeInteractionMeta:l,autoCollectPropositionInteractions:a}),[Fo]:Ha({logger:t,executeRedirect:D,collect:s}),[_o]:Ka({modules:ds(s),logger:t})},T=Ja({schemaProcessors:P,logger:t}),R=(()=>{let e=Promise.resolve([]);return{concat(t){e=e.then((e=>t.then((t=>e.concat(t))).catch((()=>e))))},clear(){const t=e;return e=Promise.resolve([]),t}}})(),O=((e,t)=>(n,r,o)=>{if(!n)return()=>{};if(!r){const e=u();return t.concat(e.promise),e.resolve}return(t=[],n=[])=>{b(t)&&e({decisionsMeta:t,viewName:o}),b(n)&&e({decisionsMeta:n,eventType:Xo,propositionAction:{reason:"Conflict"},viewName:o})}})(s,R),N=(({logger:e,prehidingStyle:t,showContainers:n,hideContainers:r,mergeQuery:o,processPropositions:i,createProposition:a,notificationHandler:s,consent:c})=>({cacheUpdate:d,personalizationDetails:l,event:u,onResponse:p})=>{const{state:g,wasSet:m}=c.current();"out"===g&&m||(l.isRenderDecisions()?r(t):n()),o(u,l.createQueryDetails());const f=s(l.isRenderDecisions(),l.isSendDisplayEvent(),l.getViewName());p((({response:t})=>{const r=t.getPayloadsByType("personalization:decisions");b(r)||e.logOnContentRendering({status:"no-offers",message:"No offers were returned.",logLevel:"info",detail:{query:l.createQueryDetails()}});const o=r.map((e=>a(e))),{page:s=[],view:c=[],proposition:u=[]}=z(o,(e=>e.getScopeType())),p=d.update(c);let g,m,h;return l.isRenderDecisions()?(({render:g,returnedPropositions:m,returnedDecisions:h}=i([...s,...p],u)),b(s)&&e.logOnContentRendering({status:"rendering-started",message:"Started rendering propositions for page-wide scope.",logLevel:"info",detail:{scope:Wt,propositions:s.map((e=>e.toJSON()))}}),b(p)&&e.logOnContentRendering({status:"rendering-started",message:"Rendering propositions started for view scope - "+l.getViewName()+".",logLevel:"info",detail:{scope:l.getViewName(),propositions:p.map((e=>e.toJSON()))}}),g().then(f),n()):({returnedPropositions:m,returnedDecisions:h}=i([],[...s,...p,...u])),{propositions:m,decisions:h}}))})({prehidingStyle:i,showContainers:c,hideContainers:d,mergeQuery:Ia,processPropositions:T,createProposition:I,notificationHandler:O,consent:r,logger:t}),M=(({mergeDecisionsMeta:e,collectInteractions:t,collectClicks:n,getInteractionMetas:r,getClickMetas:o,getClickSelectors:i,autoCollectPropositionInteractions:a})=>({event:s,clickedElement:c})=>{const d=[];let l,u,p;if([t(c,r,a),n(c,i(),o)].forEach((({decisionsMeta:e,propositionActionLabel:t,propositionActionToken:n,viewName:r})=>{Array.prototype.push.apply(d,e),!l&&t&&(l=t),!u&&n&&(u=n),!p&&r&&(p=r)})),b(d)){const t={eventType:Jo};p&&(t.web={webPageDetails:{viewName:p}}),s.mergeXdm(t),e(s,d,[Wo.INTERACT],((e,t)=>{if(!t&&!e)return;const n={};return e&&(n.label=e),t&&(n.tokens=[t]),n})(l,u))}})({mergeDecisionsMeta:ka,collectInteractions:us,collectClicks:Ea,getInteractionMetas:p,getClickMetas:v,getClickSelectors:y,autoCollectPropositionInteractions:a}),x=(({processPropositions:e,viewCache:t,logger:n})=>({personalizationDetails:r,onResponse:o})=>{let i,a;const s=r.getViewName();return o((()=>({propositions:i,decisions:a}))),t.getView(s).then((t=>{let o;return r.isRenderDecisions()?(({render:o,returnedPropositions:i,returnedDecisions:a}=e(t)),n.logOnContentRendering({status:"rendering-started",message:"Started rendering propositions for view scope - "+s+".",logLevel:"info",detail:{scope:s,propositions:t.map((e=>e.toJSON()))}}),o()):(({returnedPropositions:i,returnedDecisions:a}=e([],t)),[])}))})({processPropositions:T,viewCache:S,logger:t}),q=Ta({processPropositions:T,createProposition:I,renderedPropositions:R,viewCache:S}),L=(({targetMigrationEnabled:e})=>e?e=>{e.getPayload().mergeMeta({target:{migration:!0}})}:se)({targetMigrationEnabled:o}),U=Ga({processPropositions:T,createProposition:I,notificationHandler:O}),j=(({showContainers:e,consent:t})=>()=>{const{state:n,wasSet:r}=t.current();n===Rt&&r?e():t.awaitConsent().catch(e)})({showContainers:c,consent:r});return ti({getPageLocation:w,logger:t,fetchDataHandler:N,viewChangeHandler:x,onClickHandler:M,isAuthoringModeEnabled:Ca,mergeQuery:Ia,viewCache:S,showContainers:c,applyPropositions:q,setTargetMigration:L,mergeDecisionsMeta:ka,renderedPropositions:R,onDecisionHandler:U,handleConsentFlicker:j})};ms.namespace="Personalization";const fs=La.map((e=>mt(e)));ms.configValidators=yt({prehidingStyle:vt().nonEmpty(),targetMigrationEnabled:pt().default(!1),autoCollectPropositionInteractions:yt({[ps]:dt(fs).default(Aa),[gs]:dt(fs).default(xa)}).default({[ps]:Aa,[gs]:xa}).noUnknownFields()});const hs=e=>null!==e&&"object"==typeof e&&Object.getPrototypeOf(e)===Object.prototype,ys=(e,t={},n=[])=>(Object.keys(e).forEach((r=>{hs(e[r])||Array.isArray(e[r])?ys(e[r],t,[...n,r]):t[[...n,r].join(".")]=e[r]})),t);var vs=e=>hs(e)?ys(e):e;const ws="matcher",bs="group",Es="historical",Cs="eq",ks="ne",Is="ex",Ss="nx",Ds="gt",Ps="ge",Ts="lt",Rs="le",Os="co",Ns="nc",Ms="sw",As="ew",xs="and",qs="or",Ls="ordered";function Us(e){return"object"==typeof e||void 0===e}function js(e){return"number"==typeof e}const Bs={[Cs]:{matches:(e,t,n=[])=>{if(Us(e[t]))return!1;const r=String(e[t]).toLowerCase();for(let e=0;e{if(Us(e[t]))return!1;const r=String(e[t]).toLowerCase();for(let e=0;evoid 0!==e[t]&&null!==e[t]},[Ss]:{matches:(e,t)=>void 0===e[t]||null===e[t]},[Ds]:{matches:(e,t,n=[])=>{const r=e[t];if(!js(r))return!1;for(let e=0;en[e])return!0;return!1}},[Ps]:{matches:(e,t,n=[])=>{const r=e[t];if(!js(r))return!1;for(let e=0;e=n[e])return!0;return!1}},[Ts]:{matches:(e,t,n=[])=>{const r=e[t];if(!js(r))return!1;for(let e=0;e{const r=e[t];if(!js(r))return!1;for(let e=0;e{if(Us(e[t]))return!1;const r=String(e[t]).toLowerCase();for(let e=0;e{if(Us(e[t]))return!1;const r=String(e[t]).toLowerCase();for(let e=0;e{if(Us(e[t]))return!1;const r=String(e[t]).toLowerCase();for(let e=0;e{if(Us(e[t]))return!1;const r=String(e[t]).toLowerCase();for(let e=0;et.evaluate(e),toString:()=>"Condition{type="+e+", definition="+t+"}"}}function Qs(e,t,n){return{evaluate:r=>{const o=function(e){return Bs[e]}(t);return!!o&&o.matches(r,e,n)}}}function Xs(e,t,n,r,o,i){return{evaluate:a=>{let s;return s=Ls===i?function(e,t,n,r){let o=n;return e.every((e=>{const n=Hs(e,Vs);if(!n)return!1;const i=t.events[n];if(!i)return!1;const a=Hs(e,zs);if(!a)return!1;const s=i[a];if(!Js(e,s))return!1;if(null===s||Fs(s)||0===s.count)return!1;const c=(Fs(o)||s.timestamp>=o)&&(Fs(r)||s.timestamp<=r);return o=s.timestamp,c}))?1:0}(e,a,r,o):function(e,t,n,r){return e.reduce(((e,o)=>{const i=Hs(o,Vs);if(!i)return e;const a=t.events[i];if(!a)return e;const s=Hs(o,zs);if(!s)return e;const c=a[s];if(!c)return e;if(!Js(o,c))return e;const{count:d=1}=c;return Fs(n)||Fs(r)||c.timestamp>=n&&c.timestamp<=r?e+d:e}),0)}(e,a,r,o),function(e,t,n){switch(t){case Ds:return e>n;case Ps:return e>=n;case Ts:return exs===e?function(e,t){let n=!0;for(let r=0;re.evaluate(n)?t:[],toString:()=>"Rule{condition="+e+", consequences="+t+"}"}}(Ys(t),n.map(Ks),r)}function Zs(e){return{provider:"DEFAULT",execute:t=>e.map((e=>e.execute(t))).filter((e=>e.length>0))}}function ec(e,t,n){const{providerData:r}=n,{identityTemplate:o}=r;return o.replace("",t).replace("",e)}function tc(e,t=e=>e[0]){const n={};return function(...r){const o=t(r);return Fs(n[o])&&(n[o]=e(...r)),n[o]}}function nc(e,t){const n=65535&t;return((t-n)*e|0)+(n*e|0)|0}const rc=tc((function(e,t=0){let n;const r=e.length,o=3432918353,i=461845907;let a=t;const s=-2&r;for(let t=0;t>>17,n=nc(n,i),a^=n,a=(524287&a)<<13|a>>>19,a=5*a+3864292196|0;return r%2==1&&(n=e.charCodeAt(s),n=nc(n,o),n=(131071&n)<<15|n>>>17,n=nc(n,i),a^=n),a^=r<<1,a^=a>>>16,a=nc(a,2246822507),a^=a>>>13,a=nc(a,3266489909),a^=a>>>16,a}),(e=>e.join("-")));const oc=tc((function(e,t){const n=rc(e),r=Math.abs(n)%t/t*100;return Math.round(100*r)/100}));function ic(e,t,n){return{allocation:oc(e,t),...n}}function ac(e,t){return t.map((t=>t.execute(e))).filter((e=>e.length>0))}function sc(e,t){!function(e){const{providerData:t}=e;if(!t)throw new Error("Provider data is missing in metadata");const{identityTemplate:n,buckets:r}=t;if(!n)throw new Error("Identity template is missing in provider data");if(!r)throw new Error("Buckets is missing in provider data")}(t);const n=e.filter((e=>!e.key)),r=function(e){const t={};for(let n=0;n{const i=function(e){const{xdm:t}=e;if(!t)throw new Error("XDM object is missing in the context");const{identityMap:n}=t;if(!n)throw new Error("Identity map is missing in the XDM object");const r=n.ECID;if(!r)throw new Error("ECID identity namespace is missing in the identity map");if(!Array.isArray(r)||0===r.length)throw new Error("ECID identities array is empty or not an array");const o=r[0].id;if(!o)throw new Error("ECID identity is missing in the identities array");return o}(e),a=ac(e,n),s=_s(r),c=[];for(let n=0;n{const{scopeDetails:t={}}=e,{activity:n={}}=t,{id:r}=n;return r},lc=()=>{const e={};return{getItem:t=>t in e?e[t]:null,setItem:(t,n)=>{e[t]=n}}},uc=e=>{e.clear()},pc=(e=[])=>{const t=[];return Array.isArray(e)?(e.forEach((e=>{Array.isArray(e)?t.push(...pc(e)):t.push(e)})),t):e};const gc="cjmiam",mc="schema",fc={[gc]:(e,t,n)=>{const{html:r,mobileParameters:o}=n;return{schema:_o,data:{mobileParameters:o,webParameters:{},content:r,contentType:Qa},id:e}},[mc]:(e,t,n)=>{const{schema:r,data:o,id:i}=n;return{schema:r,data:o,id:i||e}}};const hc=e=>{const{schema:t,data:n}=e;if(t===Bo)return!0;if(t!==jo)return!1;try{const e="string"==typeof n.content?JSON.parse(n.content):n.content;return e&&Object.prototype.hasOwnProperty.call(e,"version")&&Object.prototype.hasOwnProperty.call(e,"rules")}catch{return!1}};var yc=(e,t,n)=>{const r=e=>{const{id:t,type:n,detail:r}=e;return"function"==typeof fc[n]?fc[n](t,n,r):r},o=dc(e),i=[],a=e=>{const{data:t={},schema:n}=e,r=n===Bo?t:t.content;r&&i.push(cc("string"==typeof r?JSON.parse(r):r))};return Array.isArray(e.items)&&e.items.filter(hc).forEach(a),{rank:e?.scopeDetails?.rank||1/0,evaluate:a=>{const s=t.getEvent(Ho,o),c=s?s.firstTimestamp:void 0,d=pc(i.map((e=>e.execute(a)))).map(r).map((e=>{const{firstTimestamp:t}=n.recordQualified(o)||{};return{...e,data:{...e.data,qualifiedDate:t,displayedDate:c}}}));return{...e,items:d}},isEvaluable:i.length>0}},vc=({eventRegistry:e})=>{const t={},n=(({eventRegistry:e})=>({recordQualified:t=>{if(t)return e.addEvent({},Wo.TRIGGER,t)}}))({eventRegistry:e}),r=r=>{const o=dc(r);if(!o)return;const i=yc(r,e,n);i.isEvaluable&&(t[o]=i)};return{addPayload:r,addPayloads:e=>{e.forEach(r)},evaluate:(e={})=>Object.values(t).sort((({rank:e},{rank:t})=>e-t)).map((t=>t.evaluate(e))).filter((e=>e.items.length>0))}};const wc="events",bc=e=>"iam."+e,Ec=(e=1e3,t=30)=>n=>{const r={};return Object.keys(n).forEach((o=>{r[o]={},Object.values(n[o]).filter((e=>new Date(e.firstTimestamp)>=(e=>{const t=new Date;return t.setDate(t.getDate()-e),t})(t))).sort(((e,t)=>e.firstTimestamp-t.firstTimestamp)).slice(-1*e).forEach((e=>{r[o][e.event[bc("id")]]=e}))})),r};var Cc=({storage:e})=>{let t,n,r,o=e;const i=e=>{o=e,t=((e,t)=>n=>{const r=e.getItem(t);if(!r)return n;try{return JSON.parse(r)}catch{return n}})(o,wc),n=((e,t,n=e=>e)=>r=>{e.setItem(t,JSON.stringify(n(r)))})(o,wc,Ec(1e3,30)),r=t({})};i(e);const a=(e,t,o,i)=>{if(!t||!o)return;r[t]||(r[t]={});const a=r[t][o],s=a?a.count:0,c=(new Date).getTime(),d=a?a.firstTimestamp||a.timestamp:c;return r[t][o]={event:{...e,[bc("id")]:o,[bc("eventType")]:t,[bc("action")]:i},firstTimestamp:d,timestamp:c,count:s+1},n(r),r[t][o]};return{addExperienceEdgeEvent:e=>{const{xdm:t={}}=e.getContent(),{_experience:n}=t;if(!((e={})=>{const{_experience:t}=e;return!(!t||"object"!=typeof t)})(t))return;const{decisioning:r={}}=n,{propositionEventType:o={},propositionAction:i={},propositions:s=[]}=r,c=Object.keys(o);if(0===c.length)return;const{id:d}=i;c.filter((e=>1===o[e])).forEach((e=>{s.forEach((t=>{(e=>{const{scopeDetails:t={}}=e,{decisionProvider:n}=t;return n})(t)===ps&&a({},e,dc(t),d)}))}))},addEvent:a,getEvent:(e,t)=>{if(r[e])return r[e][t]},toJSON:()=>r,setStorage:i}};const kc=(e,...t)=>t,Ic=(e,...t)=>!0;var Sc=({collect:e})=>{let t=()=>{};const n=new Set,r=(e,t,r)=>{const o=[e,t].join("-"),i=!r.has(o)&&((e=>[Wo.INTERACT,Wo.DISMISS].includes(e))(e)||!n.has(o));return r.add(o),n.add(o),i},o=(t,n=[])=>{if(!(n instanceof Array))return Promise.resolve();if(!Object.values(Wo).includes(t))return Promise.resolve();const o=[],i=new Set;return n.forEach((e=>{const n=(e=>{const{id:t,scope:n,scopeDetails:r}=e;return{id:t,scope:n,scopeDetails:r}})(e);r(t,n.id,i)&&o.push(n)})),o.length>0?e({decisionsMeta:o,eventType:Zo(t),documentMayUnload:!0}):Promise.resolve()},i=(()=>{let e=kc,t=Ic,n=0;const r={};return{add:(e,t=void 0)=>{return"function"!=typeof e?()=>{}:(n+=1,r[n]={callback:e,params:t},{id:n,unsubscribe:(o=n,()=>{delete r[o]})});var o},emit:(...n)=>{Object.values(r).forEach((({callback:r,params:o})=>{const i=e(o,...n);t(o,...i)&&r(...i)}))},emitOne:(n,...o)=>{if(!n||!r[n])return;const{callback:i,params:a}=r[n],s=e(a,...o);t(a,...s)&&i(...s)},hasSubscriptions:()=>Object.keys(r).length>0,setEmissionPreprocessor:t=>{"function"==typeof t&&(e=t)},setEmissionCondition:e=>{"function"==typeof e&&(t=e)}}})();i.setEmissionPreprocessor(((e,t)=>{const{surfacesFilter:n,schemasFilter:r}=e;return[{propositions:t.filter((e=>!n||n.includes(e.scope))).map((e=>{const{items:t=[]}=e;return{...e,items:t.filter((e=>!r||r.includes(e.schema)))}})).filter((e=>e.items.length>0))},o]}));return{refresh:e=>{t=t=>{t?i.emitOne(t,e):i.emit(e)},t()},command:{optionsValidator:e=>(({options:e})=>yt({surfaces:ut(vt()).uniqueItems(),schemas:ut(vt()).uniqueItems(),callback:gt().required()}).noUnknownFields()(e))({options:e}),run:({surfaces:e,schemas:n,callback:r})=>{const{id:o,unsubscribe:a}=i.add(r,{surfacesFilter:e instanceof Array?e:void 0,schemasFilter:n instanceof Array?n:void 0});return t(o),Promise.resolve({unsubscribe:a})}}}};const Dc="~type",Pc="~source",Tc="com.adobe.eventType.edge",Rc="com.adobe.eventType.rulesEngine",Oc="com.adobe.eventSource.requestContent";var Nc=({contextProvider:e,decisionProvider:t})=>({optionsValidator:e=>(({options:e})=>yt({renderDecisions:pt(),personalization:yt({decisionContext:yt({})})}).noUnknownFields()(e))({options:e}),run:({renderDecisions:n,decisionContext:r,applyResponse:o})=>o({renderDecisions:n,propositions:t.evaluate(e.getContext(r))})});const Mc=({config:e,eventManager:t,createNamespacedStorage:n,consent:r,getBrowser:o})=>{const{orgId:i,personalizationStorageEnabled:a}=e,s=ha({eventManager:t,mergeDecisionsMeta:ka}),c=n(_(i)+".decisioning.");a||uc(c.persistent);const d=Cc({storage:lc()}),l=vc({eventRegistry:d}),u=(({eventRegistry:e,window:t,getBrowser:n})=>{const r=(new Date).getTime(),o=()=>{const e=new Date,t=e.getTime();return{pageLoadTimestamp:r,currentTimestamp:t,currentDate:e.getDate(),"~state.com.adobe.module.lifecycle/lifecyclecontextdata.dayofweek":e.getDay()+1,"~state.com.adobe.module.lifecycle/lifecyclecontextdata.hourofday":e.getHours(),currentMinute:e.getMinutes(),currentMonth:e.getMonth(),currentYear:e.getFullYear(),pageVisitDuration:t-r,"~timestampu":t/1e3,"~timestampz":e.toISOString()}},i={browser:{name:n()},page:{title:t.title,url:t.url,...de(t.url)},referringPage:{url:t.referrer,...de(t.referrer)}};return{getContext:(n={})=>{const r={...{...i,...o(),window:{height:t.height,width:t.width,scrollY:t.scrollY,scrollX:t.scrollX},"~sdkver":fn},...n};return{...vs(r),events:e.toJSON()}}}})({eventRegistry:d,window:window,getBrowser:o}),p=Nc({contextProvider:u,decisionProvider:l}),g=Sc({collect:s});let m;return{lifecycle:{onDecision({propositions:e}){g.refresh(e)},onComponentsRegistered(e){var t;t=e.lifecycle,m=({renderDecisions:e=!1,propositions:n=[],event:r,personalization:o})=>(t&&t.onDecision({renderDecisions:e,propositions:n,event:r,personalization:o}),{propositions:n}),a&&r.awaitConsent().then((()=>{d.setStorage(c.persistent)})).catch((()=>{c&&uc(c.persistent)}))},onBeforeEvent({event:e,renderDecisions:t,personalization:n={},onResponse:r=se}){const{decisionContext:o={}}=n;r((({renderDecisions:e,decisionProvider:t,applyResponse:n,event:r,personalization:o,decisionContext:i})=>{const a={...vs(r.getContent()),...i};return({response:i})=>{if(t.addPayloads(i.getPayloadsByType("personalization:decisions")),!r.hasQuery())return{propositions:[]};const s=t.evaluate(a);return n({renderDecisions:e,propositions:s,event:r,personalization:o})}})({renderDecisions:t,decisionProvider:l,applyResponse:m,event:e,personalization:n,decisionContext:u.getContext({[Dc]:Tc,[Pc]:Oc,...o})}))},onBeforeRequest({request:e}){const t=e.getPayload().toJSON(),{events:n=[]}=t;0!==n.length&&n.forEach((e=>d.addExperienceEdgeEvent(e)))}},commands:{evaluateRulesets:{run:({renderDecisions:e,personalization:t={}})=>{const{decisionContext:n={}}=t;return p.run({renderDecisions:e,decisionContext:{[Dc]:Rc,[Pc]:Oc,...n},applyResponse:m})},optionsValidator:p.optionsValidator},subscribeRulesetItems:g.command}}};Mc.namespace="RulesEngine",Mc.configValidators=yt({personalizationStorageEnabled:pt().default(!1)});var Ac=yt({streamingMedia:yt({channel:vt().nonEmpty().required(),playerName:vt().nonEmpty().required(),appVersion:vt(),mainPingInterval:ft().minimum(10).maximum(50).default(10),adPingInterval:ft().minimum(1).maximum(10).default(10)}).noUnknownFields()}),xc=({config:e,trackMediaEvent:t,trackMediaSession:n,mediaResponseHandler:r})=>({lifecycle:{onBeforeEvent({mediaOptions:e,onResponse:t=se}){if(!e)return;const{legacy:n,playerId:o,getPlayerDetails:i}=e;n||t((({response:e})=>r({playerId:o,getPlayerDetails:i,response:e})))}},commands:{createMediaSession:{optionsValidator:e=>(({options:e})=>dt([yt({playerId:vt().required(),getPlayerDetails:gt().required(),xdm:yt({mediaCollection:yt({sessionDetails:yt(lt()).required()})}),edgeConfigOverrides:yt({})}).required(),yt({xdm:yt({mediaCollection:yt({playhead:ft().required(),sessionDetails:yt(lt()).required()})}),edgeConfigOverrides:yt({})}).required()],"Error validating the createMediaSession command options.")(e))({options:e}),run:n},sendMediaEvent:{optionsValidator:e=>(({options:e})=>dt([yt({playerId:vt().required(),xdm:yt({eventType:wt(...Object.values(Qr)).required(),mediaCollection:yt(lt())}).required()}).required(),yt({xdm:yt({eventType:wt(...Object.values(Qr)).required(),mediaCollection:yt({playhead:ft().integer().required(),sessionID:vt().required()}).required()}).required()}).required()],"Error validating the sendMediaEvent command options.")(e))({options:e}),run:n=>e.streamingMedia?t(n):Promise.reject(new Error("Streaming media is not configured."))}}});const qc=({config:e,logger:t,eventManager:n,sendEdgeNetworkRequest:r,consent:o})=>{const i=Kr(),a=Xr({config:e,eventManager:n,consent:o,sendEdgeNetworkRequest:r,setTimestamp:mn((()=>new Date))}),s=$r({mediaSessionCacheManager:i,mediaEventManager:a,config:e}),c=Zr({config:e,mediaEventManager:a,mediaSessionCacheManager:i}),d=to({mediaSessionCacheManager:i,config:e,trackMediaEvent:s});return xc({config:e,trackMediaEvent:s,mediaResponseHandler:d,trackMediaSession:c})};qc.namespace="Streaming media",qc.configValidators=Ac;var Lc=Object.freeze({__proto__:null,activityCollector:Mr,audiences:qr,consent:Hr,eventMerge:Gr,mediaAnalyticsBridge:Co,personalization:ms,rulesEngine:Mc,streamingMedia:qc});(({components:e})=>{const t=window.__alloyNS;t&&t.forEach((t=>{const n=kt({console:Kn,locationSearch:window.location.search,createLogger:Xt,instanceName:t,createNamespacedStorage:Yn,getMonitors:er}),r=(e=>([t,n,[r,o]])=>{e(r,o).then(t,n)})(sr({instanceName:t,logController:n,components:e})),o=window[t].q;o.push=r,n.logger.logOnInstanceCreated({instance:r}),o.forEach(r)}))})({components:Object.values(Lc)})}(); \ No newline at end of file diff --git a/aemedge/plugins/martech/src/index.js b/aemedge/plugins/martech/src/index.js new file mode 100644 index 00000000..7c04abd9 --- /dev/null +++ b/aemedge/plugins/martech/src/index.js @@ -0,0 +1,644 @@ +/* eslint-disable no-underscore-dangle */ +/** + * Default configuration for the library. + * @typedef {Object} MartechConfig + * @property {Boolean} analytics Indicates whether analytics tracking should be enabled + * (defaults to true) + * @property {String} alloyInstanceName The name of the alloy instance in the global scope + * (defaults to "alloy") + * @property {Boolean} dataLayer Indicates whether the data layer should be used + * (defaults to true) + * @property {String} dataLayerInstanceName The name of the data ayer instance in the global scope + * (defaults to "adobeDataLayer") + * @property {Boolean} includeDataLayerState Whether to include the datalayer state on every + * event that is sent by alloy (defaults to true) + * @property {String[]} launchUrls A list of launch container URLs to load (defults to empty list) + * @property {Boolean} personalization Indicates whether Adobe Target should be enabled + * (defaults to true) + * @property {Boolean} performanceOptimized Whether to use the agressive performance optimized + * instrumentation, or the more traditional alloy approach + * (defaults to true) + * @property {Number} personalizationTimeout Indicates the amount of time to wait before bailing + * out on the personalization and continue rendering the + * page (defaults to 1s) + * @property {Function} shouldProcessEvent Optional function to filter which events are sent to + * analytics. It gets the datalayer event as a parameter + * and returns a boolean. Return true to process the event, + * false to ignore it. The default is a function that + * always returns true. + */ +export const DEFAULT_CONFIG = { + analytics: true, + alloyInstanceName: 'alloy', + dataLayer: true, + dataLayerInstanceName: 'adobeDataLayer', + includeDataLayerState: true, + launchUrls: [], + personalization: true, + performanceOptimized: true, + personalizationTimeout: 1000, + shouldProcessEvent: () => true, +}; + +let config; +let alloyConfig; +let isAlloyConfigured = false; +const pendingAlloyCommands = []; +const pendingDatalayerEvents = []; + +/** + * Triggers the callback when the page is actually activated, + * This is to properly handle speculative page prerendering and marketing events. + * @param {Function} cb The callback to run + */ +async function onPageActivation(cb) { + // Speculative prerender-aware execution. + // See: https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#unsafe_prerendering + if (document.prerendering) { + document.addEventListener('prerenderingchange', cb, { once: true }); + } else { + cb(); + } +} + +/** + * Runs a promise with a timeout that rejects it if the time has passed. + * @param {Promise} promise The base promise to use + * @param {Number} [timeout=1000] The timeout to use in ms + * @returns the promise result, or a rejected promise if it did not resolve in time + */ +function promiseWithTimeout(promise, timeout = 1000) { + let timer; + return Promise.race([ + promise, + new Promise((_, reject) => { timer = setTimeout(reject, timeout); }), + ]).finally(() => clearTimeout(timer)); +} + +/** + * Error handler for rejected promises. + * @param {Error} error The base error + * @throws a decorated error that can be intercepted by RUM handlers. + */ +function handleRejectedPromise(error) { + const [, file, line] = error.stack.split('\n')[1].trim().split(' ')[1].match(/(.*):(\d+):(\d+)/); + error.sourceURL = file; + error.line = line; + throw error; +} + +/** + * Initializes a queue for the alloy instance in order to be ready to receive events before the + * alloy library is fully loaded. + * Documentation: + * https://experienceleague.adobe.com/docs/experience-platform/edge/fundamentals/installing-the-sdk.html?lang=en#adding-the-code + * @param {String} instanceName The name of the instance in the blobal scope + */ +function initAlloyQueue(instanceName) { + if (window[instanceName]) { + return; + } + // eslint-disable-next-line no-underscore-dangle + (window.__alloyNS ||= []).push(instanceName); + window[instanceName] = (...args) => new Promise((resolve, reject) => { + window.setTimeout(() => { + window[instanceName].q.push([resolve, reject, args]); + }); + }); + window[instanceName].q = []; +} + +/** + * Initializes a queue for the datalayer in order to be ready to receive events before the + * ACDL library is fully loaded. + * Documentation: + * https://github.com/adobe/adobe-client-data-layer/wiki#setup + * @param {String} instanceName The name of the instance in the blobal scope + */ +function initDatalayer(instanceName) { + window[instanceName] ||= []; + if (instanceName !== 'adobeDataLayer') { + window[instanceName] ||= []; + } +} + +/** + * Returns the default alloy configuration + * Documentation: + * https://experienceleague.adobe.com/docs/experience-platform/edge/fundamentals/configuring-the-sdk.html + */ +function getDefaultAlloyConfiguration() { + const { hostname } = window.location; + + return { + context: ['web', 'device', 'environment'], + // enable while debugging + debugEnabled: hostname === 'localhost' || hostname.endsWith('.hlx.page') || hostname.endsWith('.aem.page'), + // wait for exlicit consent before tracking anything + defaultConsent: 'in', + }; +} + +/** + * Just a proxy method for the `alloy('sendEvent', …)` method + * @param {Object} payload the payload to send + * @returns {Promise<*>} a promise that the event was sent + */ +export async function sendEvent(payload) { + // eslint-disable-next-line no-console + console.assert(config.alloyInstanceName && window[config.alloyInstanceName], 'Martech needs to be initialized before the `sendEvent` method is called'); + return window[config.alloyInstanceName]('sendEvent', payload); +} + +/** + * Sends an analytics event to alloy + * @param {Object} xdmData the xdm data object to send + * @param {Object} [dataMapping] additional data mapping for the event + * @param {Object} [configOverrides] optional config overrides + * @returns {Promise<*>} a promise that the event was sent + */ +export async function sendAnalyticsEvent(xdmData, dataMapping = {}, configOverrides = {}) { + // eslint-disable-next-line no-console + console.assert(config.alloyInstanceName && window[config.alloyInstanceName], 'Martech needs to be initialized before the `sendAnalyticsEvent` method is called'); + // eslint-disable-next-line no-console + console.assert(config.analytics, 'Analytics tracking is disabled in the martech config'); + try { + return sendEvent({ + documentUnloading: true, + xdm: xdmData, + data: dataMapping, + edgeConfigOverrides: configOverrides, + }); + } catch (err) { + handleRejectedPromise(new Error(err)); + return Promise.reject(new Error(err)); + } +} + +/** + * Loads the alloy library and configures it. + * Documentation: + * https://experienceleague.adobe.com/docs/experience-platform/edge/fundamentals/configuring-the-sdk.html + * @param {String} instanceName The name of the instance in the blobal scope + * @param {Object} webSDKConfig The configuration to use + * @returns a promise that the library was loaded and configured + */ +async function loadAndConfigureAlloy(instanceName, webSDKConfig) { + await import('./alloy.min.js'); + try { + await window[instanceName]('configure', webSDKConfig); + isAlloyConfigured = true; + pendingAlloyCommands.forEach((fn) => fn()); + pendingDatalayerEvents.forEach((args) => sendAnalyticsEvent(...args)); + } catch (err) { + handleRejectedPromise(new Error(err)); + } +} + +/** + * Runs the specified function on every decorated block/section + * @param {Function} fn The function to call + */ +function onDecoratedElement(fn) { + // Apply propositions to all already decorated blocks/sections + if (document.querySelector('[data-block-status="loaded"],[data-section-status="loaded"]')) { + fn(); + } + + const observer = new MutationObserver((mutations) => { + if (mutations.some((m) => m.target.tagName === 'BODY' + || m.target.dataset.sectionStatus === 'loaded' + || m.target.dataset.blockStatus === 'loaded')) { + fn(); + } + }); + // Watch sections and blocks being decorated async + observer.observe(document.querySelector('main'), { + subtree: true, + attributes: true, + attributeFilter: ['data-block-status', 'data-section-status'], + }); + // Watch anything else added to the body + document.querySelectorAll('body').forEach((el) => { + observer.observe(el, { childList: true }); + }); +} + +/** + * Pushes data to the data layer + * @param {Object} payload the data to push + */ +export function pushToDataLayer(payload) { + // eslint-disable-next-line no-console + console.assert(config.dataLayerInstanceName && window[config.dataLayerInstanceName], 'Martech needs to be initialized before the `pushToDataLayer` method is called'); + window[config.dataLayerInstanceName].push(payload); +} + +/** + * Pushes an event to the data layer + * @param {String} event the name of the event to push + * @param {Object} xdm the xdm data object to send + * @param {Object} [data] additional data mapping for the event + * @param {Object} [configOverrides] optional configuration overrides + */ +export function pushEventToDataLayer(event, xdm, data, configOverrides) { + pushToDataLayer({ + event, xdm, data, configOverrides, + }); +} + +/** + * Loads the ACDL library. + * @returns the ACDL instance + */ +async function loadAndConfigureDataLayer() { + await import('./acdl.min.js'); + if (config.analytics) { + if (config.dataLayerInstanceName !== 'adobeDataLayer') { + window.adobeDataLayer.push((dl) => { + window[config.dataLayerInstanceName] = dl; + }); + } + window[config.dataLayerInstanceName].push((dl) => { + dl.addEventListener('adobeDataLayer:event', (payload) => { + const eventType = payload.event; + delete payload.event; + const args = [ + { eventType, ...payload.xdm }, + payload.data, + payload.configOverrides, + ]; + + // Check whether the event should be processed or not + if (!config.shouldProcessEvent(payload)) { + return; + } + + if (!isAlloyConfigured) { + pendingDatalayerEvents.push(args); + } else { + sendAnalyticsEvent(...args); + } + }); + }); + } + [...document.querySelectorAll('[data-block-data-layer]')].forEach((el) => { + let data; + try { + data = JSON.parse(el.dataset.blockDataLayer); + } catch (err) { + data = {}; + } + if (!el.id) { + const index = [...document.querySelectorAll(`.${el.classList[0]}`)].indexOf(el); + el.id = `${data.parentId ? `${data.parentId}-` : ''}${index + 1}`; + } + window[config.dataLayerInstanceName].push({ + blocks: { [el.id]: data }, + }); + }); +} + +/** + * Sets Adobe standard v2.0 consent for alloy based on the input + * Documentation: + * https://experienceleague.adobe.com/en/docs/experience-platform/landing/governance-privacy-security/consent/adobe/dataset#structure + * https://experienceleague.adobe.com/en/docs/experience-platform/xdm/data-types/consents + * @param {Object} config The consent config to use + * @param {Boolean} [config.collect] Whether data collection is allowed + * @param {Boolean|Object} [config.marketing] Whether data can be used for marketing purposes + * @param {String} [config.marketing.preferred] The preferred medium for marketing communication + * @param {Boolean} [config.marketing.any] Whether any marketing channels are consented to or not + * @param {Boolean} [config.marketing.email] Whether marketing emails are consented to or not + * @param {Boolean} [config.marketing.push] Whether marketing push notifications are consented to + * @param {Boolean} [config.marketing.sms] Whether marketing messages are consented to or not + * @param {Boolean} [config.personalize] Whether data can be used for personalization purposes + * @param {Boolean} [config.share] Whether data can be shared/sold to 3rd parties + * @returns {Promise<*>} a promise that the consent setting shave been updated + */ +export async function updateUserConsent(consent) { + // eslint-disable-next-line no-console + console.assert(config.alloyInstanceName, 'Martech needs to be initialized before the `updateUserConsent` method is called'); + + let marketingConfig; + if (typeof consent.marketing === 'boolean') { + marketingConfig = { + any: { val: consent.marketing ? 'y' : 'n' }, + preferred: 'email', + }; + } else if (typeof consent.marketing === 'object') { + marketingConfig = { + preferred: consent.marketing.preferred || 'email', + any: { + val: consent.marketing.email ? 'y' : 'n', + }, + email: { + val: consent.marketing.email ? 'y' : 'n', + }, + push: { + val: consent.marketing.push ? 'y' : 'n', + }, + sms: { + val: consent.marketing.sms ? 'y' : 'n', + }, + }; + } + const fn = () => window[config.alloyInstanceName]('setConsent', { + consent: [{ + standard: 'Adobe', + version: '2.0', + value: { + collect: { val: consent.collect ? 'y' : 'n' }, + marketing: marketingConfig, + personalize: { + content: { val: consent.personalize ? 'y' : 'n' }, + }, + share: { val: consent.share ? 'y' : 'n' }, + }, + }], + }); + if (isAlloyConfigured) { + return fn(); + } + pendingAlloyCommands.push(fn); + return Promise.resolve(); +} + +let response; + +/** + * Fetching propositions from the backend and applying the propositions as the AEM EDS page loads + * its content async. + * Documentation: + * https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/personalization/rendering-personalization-content#manual + * @param {String} instanceName The name of the instance in the blobal scope + * @returns a promise that the propositions were retrieved and will be applied as the page renders + */ +async function applyPropositions(instanceName) { + // Get the decisions, but don't render them automatically + // so we can hook up into the AEM EDS page load sequence + const renderDecisionResponse = await sendEvent({ + type: 'decisioning.propositionFetch', + renderDecisions: false, + personalization: { + sendDisplayEvent: false, + }, + }); + response = renderDecisionResponse; + if (!renderDecisionResponse?.propositions) { + return []; + } + let propositions = window.structuredClone(renderDecisionResponse.propositions) + .filter((p) => p.items.some( + (i) => i.schema === 'https://ns.adobe.com/personalization/dom-action', + )); + onDecoratedElement(async () => { + if (!propositions.length) { + return; + } + const appliedPropositions = await window[instanceName]( + 'applyPropositions', + { propositions }, + ); + appliedPropositions.propositions.forEach((item) => { + if (item.renderAttempted) { + propositions = propositions.filter((p) => p.id !== item.id); + } + }); + }); + return renderDecisionResponse; +} + +/** + * Initializes the martech library. + * Documentation: + * https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/commands/configure/overview + * @param {Object} webSDKConfig the WebSDK config + * @param {Object} [martechConfig] the martech config + * @param {String} [martechConfig.alloyInstanceName="alloy"] the WebSDK instance name in the global + * scope (defaults to `alloy`) + * @param {String} [martechConfig.dataLayerInstanceName="adobeDataLayer"] the ACDL instance name in + * the global scope (defaults to `adobeDataLayer`) + * @param {String[]} [martechConfig.launchUrls] a list of Launch configurations to load + * @returns a promise that the library was loaded and configured + */ +export async function initMartech(webSDKConfig, martechConfig = {}) { + // eslint-disable-next-line no-console + console.assert(!config, 'Martech already initialized.'); + // eslint-disable-next-line no-console + console.assert(webSDKConfig?.datastreamId || webSDKConfig?.edgeConfigId, 'Please set your "datastreamId" for the WebSDK config.'); + // eslint-disable-next-line no-console + console.assert(webSDKConfig?.orgId, 'Please set your "orgId" for the WebSDK config.'); + + config = { + ...DEFAULT_CONFIG, + ...martechConfig, + }; + + initAlloyQueue(config.alloyInstanceName); + if (config.dataLayer) { + initDatalayer(config.dataLayerInstanceName); + } + + alloyConfig = { + ...getDefaultAlloyConfiguration(), + ...webSDKConfig, + onBeforeEventSend: (payload) => { + // ACDL is initialized in the lazy phase, so fetching from the JS array as a fallback during + // the eager phase + if (config.includeDataLayerState) { + const dlState = window.adobeDataLayer.getState + ? window.adobeDataLayer.getState() + : window.adobeDataLayer[0]; + payload.xdm = { + ...payload.xdm, + ...dlState, + }; + } + + payload.data ||= {}; + payload.data.__adobe ||= {}; + // Documentation: https://experienceleague.adobe.com/en/docs/analytics/implementation/aep-edge/data-var-mapping + payload.data.__adobe.analytics ||= {}; + // Documentation: https://experienceleague.adobe.com/en/docs/platform-learn/migrate-target-to-websdk/send-parameters + payload.data.__adobe.target ||= {}; + + // Let project override the data if needed + if (webSDKConfig?.onBeforeEventSend) { + try { + const shouldSend = webSDKConfig?.onBeforeEventSend(payload); + if (shouldSend === false) { + return false; + } + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error in "onBeforeEventSend" handler:', err); + return false; + } + } + if (!Object.keys(payload.data.__adobe.target).length) { + delete payload.data.__adobe.target; + } + if (!Object.keys(payload.data.__adobe.analytics).length) { + delete payload.data.__adobe.analytics; + } + if (!Object.keys(payload.data.__adobe).length) { + delete payload.data.__adobe; + } + if (!Object.keys(payload.data).length) { + delete payload.data; + } + return true; + }, + }; + if (config.personalization) { + await loadAndConfigureAlloy(config.alloyInstanceName, alloyConfig); + } + return Promise.resolve(); +} + +const debug = (label = 'martech', ...args) => { + if (alloyConfig.debugEnabled) { + // eslint-disable-next-line no-console + console.debug.call(null, `[${label}]`, ...args); + } +}; + +export function initRumTracking(sampleRUM, options = {}) { + // Load the RUM enhancer so we can map all RUM events even on non-sampled pages + if (options.withRumEnhancer) { + const script = document.createElement('script'); + script.src = new URL('.rum/@adobe/helix-rum-enhancer@^1/src/index.js', sampleRUM.baseURL).href; + document.head.appendChild(script); + } + + // Define RUM tracking function + let track; + if (sampleRUM.always) { + track = (ev, cb) => sampleRUM.always.on(ev, (data) => { + debug('rum', ev, data); + cb(data); + }); + } else { + track = (ev, cb) => document.addEventListener('rum', (data) => { + debug('rum', ev, data); + cb(data); + }); + } + return track; +} + +/** + * Checks whether personalization is enabled or not. + * @returns a `true` if personalization is enabled, or `false` otherwise + */ +export function isPersonalizationEnabled() { + return config.personalization; +} + +/** + * Retrieves the list of propositions to personalize the specified view. + * @param {String} viewName The view name, or defaults to the page context + * @returns a promise that resolves to an array of propositions to be used with + * `applyPersonalization`. + */ +export async function getPersonalizationForView(viewName) { + // eslint-disable-next-line no-console + console.assert(viewName, 'The `viewName` parameter needs to be defined'); + return sendEvent({ + renderDecisions: true, + xdm: { + web: { + webPageDetails: { viewName }, + }, + }, + }); +} + +/** + * Applies the specified propositions to personalize the current page. + * @param {String} viewName The view name the personalization applies to + * @returns a promise that the propositions were applied + */ +export async function applyPersonalization(viewName) { + // eslint-disable-next-line no-console + console.assert(viewName, 'The `viewName` parameter needs to be defined'); + return window[config.alloyInstanceName]('applyPropositions', { viewName }); +} + +/** + * Martech logic to be executed in the eager phase. + * @returns a promise that the eager logic was executed + */ +export async function martechEager() { + console.log(config); + if (config.personalization && config.performanceOptimized) { + // eslint-disable-next-line no-console + console.assert(window.alloy, 'Martech needs to be initialized before the `martechEager` method is called'); + return promiseWithTimeout( + applyPropositions(config.alloyInstanceName), + config.personalizationTimeout, + ).then(() => { + onPageActivation(() => { + // Automatically report displayed propositions + sendAnalyticsEvent({ + eventType: 'web.webpagedetails.pageViews', + _experience: { + decisioning: { + propositions: response.propositions + .map((p) => ({ id: p.id, scope: p.scope, scopeDetails: p.scopeDetails })), + propositionEventType: { display: 1 }, + }, + }, + }); + }); + }).catch(() => { + if (alloyConfig.debugEnabled) { + // eslint-disable-next-line no-console + console.warn('Could not apply personalization in time. Either backend is taking too long, or user did not give consent in time.'); + } + }); + } + if (config.personalization) { + document.body.style.visibility = 'hidden'; + } + return Promise.resolve(); +} + +/** + * Martech logic to be executed in the lazy phase. + * @returns a promise that the lazy logic was executed + */ +export async function martechLazy() { + if (config.dataLayer) { + await loadAndConfigureDataLayer({}); + } + + if (!config.personalization && config.performanceOptimized) { + await loadAndConfigureAlloy(config.alloyInstanceName, alloyConfig); + onPageActivation(() => { + sendAnalyticsEvent({ eventType: 'web.webpagedetails.pageViews' }); + }); + } else if (!config.performanceOptimized) { + const renderDecisionResponse = await sendEvent({ renderDecisions: true, decisionScopes: ['__view__'] }); + response = renderDecisionResponse; + document.body.style.visibility = null; + // Automatically report displayed propositions + onPageActivation(() => { + sendAnalyticsEvent({ eventType: 'web.webpagedetails.pageViews' }); + }); + } +} + +/** + * Martech logic to be executed in the delayed phase. + * @returns a promise that the delayed logic was executed + */ +export async function martechDelayed() { + // eslint-disable-next-line no-console + console.assert(window.alloy, 'Martech needs to be initialized before the `martechDelayed` method is called'); + + const { launchUrls } = config; + return Promise.all(launchUrls.map((url) => import(url))) + .catch((err) => handleRejectedPromise(new Error(err))); +} diff --git a/aemedge/scripts/delayed.js b/aemedge/scripts/delayed.js index 5f346f6e..d0c29083 100644 --- a/aemedge/scripts/delayed.js +++ b/aemedge/scripts/delayed.js @@ -1,15 +1,16 @@ // eslint-disable-next-line import/no-cycle -import { loadScript, getMetadata } from './aem.js'; +// import { loadScript, getMetadata } from './aem.js'; -const isTarget = getMetadata('target'); -if (!isTarget) { - await loadScript('/aemedge/scripts/sling-martech/analytics-lib.js'); - if (window.location.host.startsWith('localhost')) { - await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-b69ac51c7dcd-development.min.js'); - } else if (window.location.host.startsWith('www.sling.com') || window.location.host.endsWith('.live')) { - await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-c846c0e0cbc6.min.js'); - } else if (window.location.host.endsWith('.page')) { - await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-6367a8aeb307-staging.min.js'); - } -} +// const isTarget = getMetadata('target'); +// if (!isTarget) { +// await loadScript('/aemedge/scripts/sling-martech/analytics-lib.js'); +// if (window.location.host.startsWith('localhost')) { +// await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-b69ac51c7dcd-development.min.js'); +// } else if (window.location.host.startsWith('www.sling.com') +// || window.location.host.endsWith('.live')) { +// await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-c846c0e0cbc6.min.js'); +// } else if (window.location.host.endsWith('.page')) { +// await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-6367a8aeb307-staging.min.js'); +// } +// } diff --git a/aemedge/scripts/martech-utils.js b/aemedge/scripts/martech-utils.js new file mode 100644 index 00000000..5e411f7c --- /dev/null +++ b/aemedge/scripts/martech-utils.js @@ -0,0 +1,352 @@ +/* eslint-disable import/no-relative-packages */ +/* eslint-disable import/no-unresolved */ +/* + * martech-personalization.js + * Unified utility for martech initialization, consent, personalization lifecycle, + * and DOM/block observer/rebinding for personalization blocks. + * Exports everything scripts.js needs for martech and personalization. + */ + +import { + initMartech, + updateUserConsent, + martechEager, + martechLazy, + martechDelayed, + isPersonalizationEnabled, + getPersonalizationForView, + applyPersonalization, + pushToDataLayer, + pushEventToDataLayer, +} from '../plugins/martech/src/index.js'; + +// --- Martech Config --- +const DEFAULT_ALLOY_CONFIG = { + orgId: 'C8F3055362AB2C450A495E69@AdobeOrg', // ACS Sandbox + datastreamId: 'cce7e9e9-6e47-4b10-b74d-0e16cb8d3f01', + defaultConsent: 'in', + onBeforeEventSend: (payload) => { + const dlState = window.adobeDataLayer?.getState + ? window.adobeDataLayer.getState() + : window.adobeDataLayer?.[0]; + payload.xdm = { + ...payload.xdm, + ...dlState, + }; + }, + edgeConfigOverrides: {}, +}; + +const DEFAULT_MARTECH_CONFIG = { + analytics: true, + alloyInstanceName: 'alloy', + dataLayer: true, + dataLayerInstanceName: 'adobeDataLayer', + includeDataLayerState: true, + launchUrls: ['https://assets.adobedtm.com/b571b7f9ddbe/d2cb1fb5f7cb/launch-9faa83378e20-development.min.js'], + personalization: true, + performanceOptimized: true, + personalizationTimeout: 1000, +}; + +export const martechLoadedPromise = initMartech( + DEFAULT_ALLOY_CONFIG, + DEFAULT_MARTECH_CONFIG, +); + +// Consent event handler +function consentEventHandler(ev) { + const collect = ev.detail.categories.includes('CC_ANALYTICS') || true; + const marketing = ev.detail.categories.includes('CC_MARKETING') || true; + const personalize = ev.detail.categories.includes('CC_TARGETING') || true; + const share = ev.detail.categories.includes('CC_SHARING') || true; + updateUserConsent({ + collect, marketing, personalize, share, + }); +} +window.addEventListener('consent', consentEventHandler); + +// --- Block Observer & Personalization DOM Utilities --- +export const blocksToObserve = [ + 'carousel', + 'accordion', + 'tabs', + 'modal', + 'image-slider', + 'game-finder', + 'channel-lookup', + 'chat', + 'marquee', + 'offer-cards', + 'channel-shopper', + 'category', +]; + +export const blocksNeedingRebind = new Set(); + +export function isHeaderOrFooter(el) { + let parent = el.parentElement; + while (parent) { + if (parent.tagName === 'HEADER' || parent.tagName === 'FOOTER') return true; + parent = parent.parentElement; + } + return false; +} + +export function rebindFlaggedBlocks() { + blocksNeedingRebind.forEach((el) => { + const blockType = el.getAttribute('data-block-name') + || blocksToObserve.find((blockName) => el.classList.contains(blockName) + || (el.classList.contains('block') && el.classList.contains(blockName))); + if (blockType) { + const importPath = window.hlx?.codeBasePath + ? `${window.hlx.codeBasePath}/blocks/${blockType}/${blockType}.js` + : `/aemedge/blocks/${blockType}/${blockType}.js`; + import(importPath) + .then((module) => { + if (module.rebindEvents) { + module.rebindEvents(el); + el.setAttribute('data-bound', 'true'); + if (el.hasAttribute('data-rebind')) { + el.removeAttribute('data-rebind'); + } + } + }) + .catch(() => { + const altImportPath = `../blocks/${blockType}/${blockType}.js`; + import(altImportPath) + .then((module) => { + if (module.rebindEvents) { + module.rebindEvents(el); + el.setAttribute('data-bound', 'true'); + if (el.hasAttribute('data-rebind')) { + el.removeAttribute('data-rebind'); + } + } + }) + .catch(() => { + // Handle error silently + }); + }); + } + }); + blocksNeedingRebind.clear(); +} + +export function setupBlockObserver() { + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { + mutation.addedNodes.forEach((node) => { + if (node.nodeType !== Node.ELEMENT_NODE + || (node.parentElement + && (node.parentElement.tagName === 'HEADER' + || node.parentElement.tagName === 'FOOTER'))) { + return; + } + const nodesToCheck = [node]; + if (node.querySelectorAll) { + node.querySelectorAll('.block').forEach((descendant) => { + nodesToCheck.push(descendant); + }); + } + nodesToCheck.forEach((el) => { + if (isHeaderOrFooter(el)) return; + const isObservedBlock = el.classList + && blocksToObserve.some((blockName) => el.classList.contains(blockName) + || (el.classList.contains('block') && el.classList.contains(blockName))); + if (isObservedBlock) { + blocksNeedingRebind.add(el); + } + }); + }); + } + }); + }); + observer.observe(document.body, { + childList: true, + subtree: true, + }); + setTimeout(() => { + observer.disconnect(); + }, 10000); +} + +export function handleTargetSections(doc) { + const main = doc.querySelector('main'); + main.querySelectorAll(':scope > div.section').forEach((section) => { + const childSection = section.querySelector('div.section'); + if (childSection) { + const parentFragmentId = section.getAttribute('data-fragment-id'); + const childFragmentId = childSection.getAttribute('data-fragment-id'); + if (parentFragmentId && childFragmentId && parentFragmentId === childFragmentId) { + section.replaceWith(childSection); + } + } + }); +} + +// Add XDM mapping utility for page load +function mapPageLoadToXDM(params) { + return { + eventType: params.eventType || 'web.webpagedetails.pageViews', + web: { + webPageDetails: { + url: params.url, + name: params.pageName, + domain: params.server, + siteSection: params.siteSection, + type: params.siteSubSection, + language: params.language, + pName: params.pName, + pURL: params.pURL, + }, + user: { + ecid: params.ecid, + guid: params.guid, + dma: params.dma, + accountStatus: params.accountStatus, + authState: params.authenticatedState, + }, + platform: params.platform, + currentChannel: params.currentChannel, + _sling: { + appName: 'aem-marketing-site', + analyticsVersion: '7.0.38', + }, + }, + zipcode: params.zipcode, + selectedLanguage: params.selectedLanguage, + screenLoadFired: true, + }; +} + +// Utility to gather all page/user/environment info for analytics +function getPageLoadParams() { + // Helper to get cookie value + const getCookieValue = (name) => { + const match = document.cookie.match(new RegExp(`(^| )${name}=([^;]+)`)); + return match ? match[2] : ''; + }; + // Helper to get localStorage value + const getLocalStorage = (key) => { + try { + return localStorage.getItem(key) || ''; + } catch { + return ''; + } + }; + // Determine siteSection and siteSubSection from URL or other logic + let siteSection = ''; + let siteSubSection = ''; + const url = window.location.href; + if (url.includes('/whatson')) { + siteSection = 'domestic'; + siteSubSection = 'blog'; + } else if (url.includes('/help')) { + siteSection = 'domestic'; + siteSubSection = 'help'; + } else if (url === `${window.location.origin}/`) { + siteSection = 'domestic'; + siteSubSection = 'home'; + } else { + siteSection = 'domestic'; + siteSubSection = 'generic'; + } + return { + ecid: getCookieValue('AMCV_9425401053CD40810A490D4C@AdobeOrg'), + url, + pageName: document.title, + server: window.location.hostname, + siteSection, + siteSubSection, + language: document.documentElement.lang || 'en', + pName: getCookieValue('pPage'), + pURL: getCookieValue('pURL'), + guid: getLocalStorage('sling_user_guid'), + dma: getLocalStorage('user_dma'), + accountStatus: getLocalStorage('account_status'), + authenticatedState: 'logged_out', // Update if you have auth logic + platform: 'web', // or 'mobile' if you have logic for this + currentChannel: getCookieValue('aaMC'), + zipcode: getLocalStorage('user_zip'), + selectedLanguage: document.documentElement.lang || 'en', + }; +} + +// --- Personalization (Target) Event Rules --- +export function setupPersonalizationEventRules() { + // Listen for zipcode updates and trigger Target event + document.addEventListener('zipupdate', (e) => { + console.log('[DEBUG]setupPersonalizationEventRules: zipupdate received', e.detail); + const { zipcode } = e.detail; + pushEventToDataLayer( + 'zipcode-update', + { + web: { + user: { + zipcode, + }, + }, + }, + { + __adobe: { + target: { + zipcode, + }, + }, + }, + ); + console.log('pushEventToDataLayer called for zipcode', zipcode); + }); + + // Listen for local channel availability (after API call) + document.addEventListener('localchannels-available', (e) => { + console.log('setupPersonalizationEventRules: localchannels-available received', e.detail); + const { zipcode, channels } = e.detail; + pushEventToDataLayer( + 'localchannels-available', + { + web: { + user: { + zipcode, + }, + }, + localChannels: channels, // XDM extension for local channel info + }, + { + __adobe: { + target: { + zipcode, + localChannels: channels, + }, + }, + }, + ); + console.log('pushEventToDataLayer called for localchannels', zipcode, channels); + }); +} + +// --- Analytics Event Rules (scaffold for future expansion) --- +export function setupAnalyticsEventRules() { + // Listen for a custom analytics event: pageview + document.addEventListener('pageview', () => { + const params = getPageLoadParams(); + const xdm = mapPageLoadToXDM(params); + pushToDataLayer({ xdm }); + }); +} + +export { + martechEager, + martechLazy, + martechDelayed, + isPersonalizationEnabled, + getPersonalizationForView, + applyPersonalization, + updateUserConsent, + pushToDataLayer, + getPageLoadParams, + mapPageLoadToXDM, + pushEventToDataLayer, +}; \ No newline at end of file diff --git a/aemedge/scripts/scripts.js b/aemedge/scripts/scripts.js index e2823c13..8027286b 100644 --- a/aemedge/scripts/scripts.js +++ b/aemedge/scripts/scripts.js @@ -1,3 +1,19 @@ +/* eslint-disable import/no-relative-packages */ +/* eslint-disable no-underscore-dangle */ + +/* eslint-disable no-underscore-dangle */ +import { + martechEager, + martechLazy, + martechDelayed, + setupBlockObserver, + rebindFlaggedBlocks, + handleTargetSections, + martechLoadedPromise, + setupPersonalizationEventRules, + setupAnalyticsEventRules, +} from './martech-utils.js'; + import { buildBlock, loadFooter, @@ -12,7 +28,6 @@ import { decorateBlock, loadBlock, toClassName, - loadScript, } from './aem.js'; import { @@ -443,8 +458,6 @@ export function makeLastButtonSticky() { } } -/* LOOKING FOR CURLY BRACES */ - /** * Extracts color + number information from text content in curly braces. * @returns {Object|null} - An object containing the extracted color @@ -617,26 +630,11 @@ function decorateLinkedImages() { }); } -async function loadLaunchEager() { - const isTarget = getMetadata('target'); - if (isTarget && isTarget.toLowerCase() === 'true') { - await loadScript('/aemedge/scripts/sling-martech/analytics-lib.js'); - if (window.location.host.startsWith('localhost')) { - await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-b69ac51c7dcd-development.min.js'); - } else if (window.location.host.startsWith('www.sling.com') || window.location.host.endsWith('.live')) { - await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-c846c0e0cbc6.min.js'); - } else if (window.location.host.endsWith('.page')) { - await loadScript('https://assets.adobedtm.com/f4211b096882/26f71ad376c4/launch-6367a8aeb307-staging.min.js'); - } - } -} /** * Decorates the main element. * @param {Element} main The main element */ -// eslint-disable-next-line import/prefer-default-export export function decorateMain(main) { - // hopefully forward compatible button decoration centerHeadlines(); decorateIcons(main); buildAutoBlocks(main); @@ -666,8 +664,11 @@ async function loadEager(doc) { } decorateMain(main); await loadTemplate(main); + await Promise.all([ + martechLoadedPromise.then(martechEager), + waitForLCP(LCP_BLOCKS), + ]); document.body.classList.add('appear'); - await waitForLCP(LCP_BLOCKS); } try { @@ -681,10 +682,10 @@ async function loadEager(doc) { } /** - * Loads a block named 'header' into header - * @param {Element} header header element - * @returns {Promise} - */ + * Loads a block named 'header' into header + * @param {Element} header header element + * @returns {Promise} + */ async function loadHeader(header) { let block = 'header'; const template = getMetadata('template'); @@ -734,6 +735,12 @@ async function loadLazy(doc) { buildGlobalBanner(main); loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles.css`); loadFonts(); + await martechLazy(); + + // --- Trigger pageview analytics event after all lazy content and personalization is done --- + // Register analytics event rules (if any) + setupAnalyticsEventRules(); + document.dispatchEvent(new Event('pageview')); } /** @@ -741,23 +748,22 @@ async function loadLazy(doc) { * without impacting the user experience. */ function loadDelayed() { - // eslint-disable-next-line import/no-cycle - window.setTimeout(() => import('./delayed.js'), 3000); - // load anything that can be postponed to the latest here + window.setTimeout(async () => { + await martechDelayed(); + return import('./delayed.js'); + }, 3000); } async function loadPage() { - // load everything that needs to be loaded eagerly + window.adobeDataLayer = window.adobeDataLayer || []; + setupPersonalizationEventRules(); + setupBlockObserver(); await loadEager(document); - - // load everything that can be postponed to the latest here await loadLazy(document); + rebindFlaggedBlocks(); + handleTargetSections(document); configSideKick(); - // load launch eagerly when target metadata is set to true - await loadLaunchEager(); - // load everything that needs to be loaded later loadDelayed(); - // make the last button sticky on blog pages makeLastButtonSticky(); } loadPage(); diff --git a/head.html b/head.html index 7d1a07da..b5c0b2ad 100644 --- a/head.html +++ b/head.html @@ -2,3 +2,6 @@ + + +