@mparticle/web-sdk 3.16.1 → 3.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/dist/mparticle.common.js +109 -39
- package/dist/mparticle.esm.js +109 -39
- package/dist/mparticle.js +853 -32
- package/dist/types/src/buttonTracking/buttonClickEvent.d.ts +5 -0
- package/dist/types/src/buttonTracking/buttonDescription.d.ts +14 -0
- package/dist/types/src/buttonTracking/buttonTracker.d.ts +36 -0
- package/dist/types/src/buttonTracking/checkoutIntent.d.ts +3 -0
- package/dist/types/src/buttonTracking/controls.d.ts +5 -0
- package/dist/types/src/buttonTracking/labelMasking.d.ts +1 -0
- package/dist/types/src/constants.d.ts +1 -0
- package/dist/types/src/internal-types.d.ts +1 -1
- package/dist/types/src/mp-instance.d.ts +2 -0
- package/dist/types/src/roktManager.d.ts +11 -0
- package/dist/types/src/store.d.ts +1 -0
- package/package.json +4 -3
- package/src/buttonTracking/buttonClickEvent.ts +49 -0
- package/src/buttonTracking/buttonDescription.ts +244 -0
- package/src/buttonTracking/buttonTracker.ts +376 -0
- package/src/buttonTracking/checkoutIntent.ts +200 -0
- package/src/buttonTracking/controls.ts +92 -0
- package/src/buttonTracking/labelMasking.ts +41 -0
- package/src/constants.ts +1 -0
- package/src/internal-types.ts +1 -0
- package/src/mp-instance.ts +33 -1
- package/src/roktManager.ts +76 -0
- package/src/store.ts +4 -1
package/dist/mparticle.js
CHANGED
|
@@ -204,7 +204,7 @@ var mParticle = (function () {
|
|
|
204
204
|
Base64: Base64$1
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
-
var version = "3.
|
|
207
|
+
var version = "3.18.0";
|
|
208
208
|
|
|
209
209
|
var Constants = {
|
|
210
210
|
sdkVersion: version,
|
|
@@ -370,16 +370,17 @@ var mParticle = (function () {
|
|
|
370
370
|
CacheIdentity: 'cacheIdentity',
|
|
371
371
|
AudienceAPI: 'audienceAPI',
|
|
372
372
|
// CaptureIntegrationSpecificIds (legacy): boolean flag from server/UI
|
|
373
|
-
// - 'True'
|
|
374
|
-
// - 'False'
|
|
373
|
+
// - 'True' \u2192 capture all integration-specific IDs
|
|
374
|
+
// - 'False' \u2192 capture none
|
|
375
375
|
CaptureIntegrationSpecificIds: 'captureIntegrationSpecificIds',
|
|
376
376
|
// CaptureIntegrationSpecificIdsV2 (new): string mode from server
|
|
377
|
-
// - 'all'
|
|
378
|
-
// - 'none'
|
|
379
|
-
// - 'roktonly'
|
|
377
|
+
// - 'all' \u2192 capture all IDs
|
|
378
|
+
// - 'none' \u2192 capture none
|
|
379
|
+
// - 'roktonly' \u2192 capture only Rokt-related IDs
|
|
380
380
|
CaptureIntegrationSpecificIdsV2: 'captureIntegrationSpecificIds.V2',
|
|
381
381
|
AstBackgroundEvents: 'astBackgroundEvents',
|
|
382
|
-
AutoLogPageView: 'autoLogPageView'
|
|
382
|
+
AutoLogPageView: 'autoLogPageView',
|
|
383
|
+
AutoButtonTracking: 'autoButtonTracking'
|
|
383
384
|
},
|
|
384
385
|
DefaultInstance: 'default_instance',
|
|
385
386
|
CCPAPurpose: 'data_sale_opt_out',
|
|
@@ -696,7 +697,7 @@ var mParticle = (function () {
|
|
|
696
697
|
return fullUrl;
|
|
697
698
|
};
|
|
698
699
|
// Google's cookie matching service requires `google_hm` values to be web-safe
|
|
699
|
-
// base64 (RFC 4648
|
|
700
|
+
// base64 (RFC 4648 \u00A75) without padding, otherwise the write to the hosted
|
|
700
701
|
// match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
|
|
701
702
|
// (String#replaceAll is not available in our ES5 browser targets.)
|
|
702
703
|
var WEB_SAFE_BASE64_REPLACEMENTS = {
|
|
@@ -3974,7 +3975,7 @@ var mParticle = (function () {
|
|
|
3974
3975
|
}
|
|
3975
3976
|
// The Search endpoint is colocated with /v1/identify under
|
|
3976
3977
|
// identityUrl, so we reuse the same service URL builder. We do
|
|
3977
|
-
// NOT append the apiKey to the URL
|
|
3978
|
+
// NOT append the apiKey to the URL \u2014 auth is done via x-mp-key.
|
|
3978
3979
|
var serviceUrl = _Helpers.createServiceUrl(identityUrl);
|
|
3979
3980
|
var searchUrl = serviceUrl + 'search?cb=1';
|
|
3980
3981
|
var environment = isDevelopmentMode ? 'development' : 'production';
|
|
@@ -4616,7 +4617,7 @@ var mParticle = (function () {
|
|
|
4616
4617
|
var domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
|
|
4617
4618
|
// Google Marketing Platform accepts a web-safe base64-encoded MPID via the
|
|
4618
4619
|
// `google_hm` query parameter, but only for accounts provisioned for Google
|
|
4619
|
-
// Hosted Matching
|
|
4620
|
+
// Hosted Matching \u2014 Google returns errors otherwise. It is therefore gated
|
|
4620
4621
|
// behind the opt-in `enableHmTag` setting (off by default), which the server
|
|
4621
4622
|
// delivers as the string 'True' when the UI toggle is enabled.
|
|
4622
4623
|
var enableHmTag = ((_c = settings === null || settings === void 0 ? void 0 : settings.enableHmTag) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === 'true';
|
|
@@ -5788,7 +5789,8 @@ var mParticle = (function () {
|
|
|
5788
5789
|
CaptureIntegrationSpecificIds = _a.CaptureIntegrationSpecificIds,
|
|
5789
5790
|
CaptureIntegrationSpecificIdsV2 = _a.CaptureIntegrationSpecificIdsV2,
|
|
5790
5791
|
AstBackgroundEvents = _a.AstBackgroundEvents,
|
|
5791
|
-
AutoLogPageView = _a.AutoLogPageView
|
|
5792
|
+
AutoLogPageView = _a.AutoLogPageView,
|
|
5793
|
+
AutoButtonTracking = _a.AutoButtonTracking;
|
|
5792
5794
|
if (!config.flags) {
|
|
5793
5795
|
return {};
|
|
5794
5796
|
}
|
|
@@ -5805,6 +5807,7 @@ var mParticle = (function () {
|
|
|
5805
5807
|
flags[CaptureIntegrationSpecificIdsV2] = config.flags[CaptureIntegrationSpecificIdsV2] || 'none';
|
|
5806
5808
|
flags[AstBackgroundEvents] = config.flags[AstBackgroundEvents] === 'True';
|
|
5807
5809
|
flags[AutoLogPageView] = config.flags[AutoLogPageView] === 'True';
|
|
5810
|
+
flags[AutoButtonTracking] = config.flags[AutoButtonTracking] === 'True';
|
|
5808
5811
|
return flags;
|
|
5809
5812
|
}
|
|
5810
5813
|
function processBaseUrls(config, flags, apiKey) {
|
|
@@ -6700,14 +6703,14 @@ var mParticle = (function () {
|
|
|
6700
6703
|
return !!win && win.history !== undefined && typeof win.history.pushState === 'function' && typeof win.addEventListener === 'function';
|
|
6701
6704
|
};
|
|
6702
6705
|
// Wraps pushState/replaceState so `onNavigate` runs after the real method, and
|
|
6703
|
-
// returns the single function that undoes it
|
|
6706
|
+
// returns the single function that undoes it \u2014 or null when nothing was
|
|
6704
6707
|
// installed (history is already wrapped, or a frozen/sealed History rejected the
|
|
6705
6708
|
// assignment). Handing back one undo closure keeps the wrapper and original
|
|
6706
6709
|
// references out of the tracker entirely: there is no half-patched state for a
|
|
6707
6710
|
// caller to inspect, and no way to restore the wrong pair.
|
|
6708
6711
|
var patchHistory = function patchHistory(onNavigate, log) {
|
|
6709
6712
|
if (window.history.pushState[WRAPPED_MARKER]) {
|
|
6710
|
-
log('[patch] history already wrapped, skipping to avoid double-wrap
|
|
6713
|
+
log('[patch] history already wrapped, skipping to avoid double-wrap \u2014 STACKED WRAPPER DETECTED');
|
|
6711
6714
|
return null;
|
|
6712
6715
|
}
|
|
6713
6716
|
var originals = {};
|
|
@@ -6854,14 +6857,14 @@ var mParticle = (function () {
|
|
|
6854
6857
|
// SECURITY: `code`, `state` and `nonce` are the OAuth 2.0 / OIDC authorization
|
|
6855
6858
|
// code and the CSRF/replay tokens. They are credentials until redeemed, and
|
|
6856
6859
|
// attaching them here persists them in the event store and forwards them to
|
|
6857
|
-
// every configured kit. They are on the list by explicit product decision
|
|
6860
|
+
// every configured kit. They are on the list by explicit product decision \u2014
|
|
6858
6861
|
// deleting that line is the whole of the fix if that decision is revisited.
|
|
6859
6862
|
var ALLOWED_QUERY_PARAMS = [
|
|
6860
6863
|
// Campaign attribution
|
|
6861
6864
|
'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_id',
|
|
6862
6865
|
// Ad-network click ids
|
|
6863
6866
|
'gclid', 'gbraid', 'wbraid', 'fbclid', 'msclkid', 'ttclid', 'twclid', 'li_fat_id', 'dclid',
|
|
6864
|
-
// OAuth / OIDC
|
|
6867
|
+
// OAuth / OIDC \u2014 see the SECURITY note above
|
|
6865
6868
|
'client_id', 'redirect_uri', 'response_type', 'scope', 'state', 'code', 'nonce',
|
|
6866
6869
|
// Pagination
|
|
6867
6870
|
'page', 'limit', 'offset', 'cursor', 'per_page',
|
|
@@ -6876,7 +6879,7 @@ var mParticle = (function () {
|
|
|
6876
6879
|
// Referral
|
|
6877
6880
|
'ref', 'referrer'];
|
|
6878
6881
|
// ---------------------------------------------------------------------------
|
|
6879
|
-
// Pure helpers. No `this`, no globals, no side effects
|
|
6882
|
+
// Pure helpers. No `this`, no globals, no side effects \u2014 callable and assertable
|
|
6880
6883
|
// on their own, which is where the interesting rules live.
|
|
6881
6884
|
// ---------------------------------------------------------------------------
|
|
6882
6885
|
// Pulls the allowlisted query params off a URL. Delegates to the SDK's own
|
|
@@ -6903,7 +6906,7 @@ var mParticle = (function () {
|
|
|
6903
6906
|
// Membership is an own-property check, not `in`. `in` walks the prototype
|
|
6904
6907
|
// chain, so a name matching an Object.prototype member reports as present on
|
|
6905
6908
|
// any plain object. ALLOWED_QUERY_PARAMS contains no such name, which was the
|
|
6906
|
-
// only thing making `in` safe here
|
|
6909
|
+
// only thing making `in` safe here \u2014 and it stops being a safe assumption the
|
|
6907
6910
|
// moment this list can be extended from configuration.
|
|
6908
6911
|
var capturedNames = function capturedNames(params) {
|
|
6909
6912
|
return ALLOWED_QUERY_PARAMS.filter(function (name) {
|
|
@@ -6912,12 +6915,12 @@ var mParticle = (function () {
|
|
|
6912
6915
|
};
|
|
6913
6916
|
// The dedup key: pathname plus the allowlisted params in a fixed order, so that
|
|
6914
6917
|
// reordering the query string is not a new page. Params outside the allowlist
|
|
6915
|
-
// never make it into `page.params` and so cannot key a view
|
|
6918
|
+
// never make it into `page.params` and so cannot key a view \u2014 nor can the hash.
|
|
6916
6919
|
//
|
|
6917
6920
|
// Values are re-encoded because queryStringParser hands them back DECODED. A
|
|
6918
6921
|
// value holding the pair delimiters would otherwise serialize exactly like two
|
|
6919
|
-
// separate params
|
|
6920
|
-
// `q=a&search=b`
|
|
6922
|
+
// separate params \u2014 `{q: 'a&search=b'}` and `{q: 'a', search: 'b'}` both becoming
|
|
6923
|
+
// `q=a&search=b` \u2014 and dedup would treat a real navigation between them as the
|
|
6921
6924
|
// same page and drop the view. `q`, `search` and `redirect_uri` carry `&` and `=`
|
|
6922
6925
|
// routinely, so this is reachable rather than theoretical.
|
|
6923
6926
|
var pageKey = function pageKey(page) {
|
|
@@ -7033,7 +7036,7 @@ var mParticle = (function () {
|
|
|
7033
7036
|
};
|
|
7034
7037
|
};
|
|
7035
7038
|
// Log-safe description of a page: the path, plus the NAMES of the captured
|
|
7036
|
-
// params. Values are deliberately omitted
|
|
7039
|
+
// params. Values are deliberately omitted \u2014 verbose logging goes to the console,
|
|
7037
7040
|
// and session-replay tooling ships console output off-domain, which is not a
|
|
7038
7041
|
// place for an OAuth code or a consumer's search terms.
|
|
7039
7042
|
// Only reachable before init() assigns lastPage, which is ahead of any navigation
|
|
@@ -7081,7 +7084,7 @@ var mParticle = (function () {
|
|
|
7081
7084
|
// Take over from whichever tracker is currently active: one left behind by
|
|
7082
7085
|
// a previous module load, or this same tracker on a repeat
|
|
7083
7086
|
// mParticle.init(). Their pending navigations are transferred rather than
|
|
7084
|
-
// dropped
|
|
7087
|
+
// dropped \u2014 a route change may have queued a deferred fire that has not
|
|
7085
7088
|
// flushed yet, and dropping it loses a page view entirely.
|
|
7086
7089
|
var active = getActiveTracker();
|
|
7087
7090
|
var inheritedPages = this.retire(active);
|
|
@@ -7121,7 +7124,7 @@ var mParticle = (function () {
|
|
|
7121
7124
|
if (!outgoing) {
|
|
7122
7125
|
return [];
|
|
7123
7126
|
}
|
|
7124
|
-
this.log(outgoing === this ? '[init] retiring this tracker for a repeat mParticle.init()
|
|
7127
|
+
this.log(outgoing === this ? '[init] retiring this tracker for a repeat mParticle.init() \u2014 transferring pending navigations and tearing down' : '[init] retiring a tracker from a previous module load \u2014 transferring pending navigations and tearing down');
|
|
7125
7128
|
var pages = outgoing.takePendingNavigations();
|
|
7126
7129
|
outgoing.teardown();
|
|
7127
7130
|
return pages;
|
|
@@ -7284,7 +7287,7 @@ var mParticle = (function () {
|
|
|
7284
7287
|
};
|
|
7285
7288
|
// The auto page view for the landing page. The SPA navigations that follow it
|
|
7286
7289
|
// come from PageViewTracker instead, so both emitters attach the same
|
|
7287
|
-
// allowlisted query params
|
|
7290
|
+
// allowlisted query params \u2014 and this is the one that matters for campaign
|
|
7288
7291
|
// attribution, since utm_*/gclid live on the entry URL.
|
|
7289
7292
|
this.logPageView = function (options) {
|
|
7290
7293
|
self.logEvent({
|
|
@@ -8429,8 +8432,8 @@ var mParticle = (function () {
|
|
|
8429
8432
|
if (isEmpty(readyQueue)) {
|
|
8430
8433
|
return [];
|
|
8431
8434
|
}
|
|
8432
|
-
// Without draining first, sync re-entry (cache-hit identify
|
|
8433
|
-
// parseIdentityResponse
|
|
8435
|
+
// Without draining first, sync re-entry (cache-hit identify \u2192
|
|
8436
|
+
// parseIdentityResponse \u2192 processReadyQueue) re-iterates the same array:
|
|
8434
8437
|
// callers only replace `_preInit.readyQueue` with [] after we return, so the
|
|
8435
8438
|
// nested call still sees every entry. That duplicates Identity/event calls
|
|
8436
8439
|
// and can nest "Unable to compute proper mParticle function" wraps.
|
|
@@ -8508,7 +8511,7 @@ var mParticle = (function () {
|
|
|
8508
8511
|
mpInstance.Logger.verbose(Messages$2.InformationMessages.StartingLogEvent + ': ' + method);
|
|
8509
8512
|
// First, remove any falsy identity values and warn about them
|
|
8510
8513
|
var removedFalsyIdentityData = mpInstance._Helpers.Validators.removeFalsyIdentityValues(identityApiData, mpInstance.Logger);
|
|
8511
|
-
// Normalize convenience aliases (email_sha256
|
|
8514
|
+
// Normalize convenience aliases (email_sha256 \u2192 other, mobile_sha256 \u2192 other2)
|
|
8512
8515
|
var cleanedIdentityApiData = (removedFalsyIdentityData === null || removedFalsyIdentityData === void 0 ? void 0 : removedFalsyIdentityData.userIdentities) ? __assign(__assign({}, removedFalsyIdentityData), {
|
|
8513
8516
|
userIdentities: normalizeUserIdentityKeys(removedFalsyIdentityData.userIdentities)
|
|
8514
8517
|
}) : removedFalsyIdentityData;
|
|
@@ -9691,7 +9694,7 @@ var mParticle = (function () {
|
|
|
9691
9694
|
* Add a GDPR Consent State to the consent state object
|
|
9692
9695
|
*
|
|
9693
9696
|
* @method addGDPRConsentState
|
|
9694
|
-
* @param purpose [String] Data processing purpose that describes the type of processing done on the data subject
|
|
9697
|
+
* @param purpose [String] Data processing purpose that describes the type of processing done on the data subject\u2019s data
|
|
9695
9698
|
* @param gdprConsent [Object] A GDPR consent object created via mParticle.Consent.createGDPRConsent(...)
|
|
9696
9699
|
*/
|
|
9697
9700
|
function addGDPRConsentState(purpose, gdprConsent) {
|
|
@@ -9727,7 +9730,7 @@ var mParticle = (function () {
|
|
|
9727
9730
|
* Remove a GDPR Consent State to the consent state object
|
|
9728
9731
|
*
|
|
9729
9732
|
* @method removeGDPRConsentState
|
|
9730
|
-
* @param purpose [String] Data processing purpose that describes the type of processing done on the data subject
|
|
9733
|
+
* @param purpose [String] Data processing purpose that describes the type of processing done on the data subject\u2019s data
|
|
9731
9734
|
*/
|
|
9732
9735
|
function removeGDPRConsentState(purpose) {
|
|
9733
9736
|
var normalizedPurpose = canonicalizeForDeduplication(purpose);
|
|
@@ -10624,6 +10627,8 @@ var mParticle = (function () {
|
|
|
10624
10627
|
}
|
|
10625
10628
|
|
|
10626
10629
|
var PASSBACK_CONVERSION_TRACKING_ID = 'passbackconversiontrackingid';
|
|
10630
|
+
var CHECKOUT_INTENT_BUFFER_LIMIT = 4;
|
|
10631
|
+
var CHECKOUT_INTENT_MAX_AGE_MS = 30000;
|
|
10627
10632
|
var ON_SHOPPABLE_ADS_READY_METHOD = 'onShoppableAdsReady';
|
|
10628
10633
|
// The purpose of this class is to create a link between the Core mParticle SDK and the
|
|
10629
10634
|
// Rokt Web SDK via a Web Kit.
|
|
@@ -10649,6 +10654,8 @@ var mParticle = (function () {
|
|
|
10649
10654
|
this.initialized = false;
|
|
10650
10655
|
this.isShoppableAdsLoaded = false;
|
|
10651
10656
|
this.stopRouteChangeWatch = null;
|
|
10657
|
+
// Checkout intent raised before the kit attached: latest per kind.
|
|
10658
|
+
this.pendingCheckoutIntents = [];
|
|
10652
10659
|
}
|
|
10653
10660
|
/**
|
|
10654
10661
|
* Sets a callback to be invoked when RoktManager becomes ready
|
|
@@ -10727,6 +10734,7 @@ var mParticle = (function () {
|
|
|
10727
10734
|
var _a, _b, _c, _d;
|
|
10728
10735
|
this.kit = kit;
|
|
10729
10736
|
this.watchRouteChanges();
|
|
10737
|
+
this.flushCheckoutIntents();
|
|
10730
10738
|
if ((_a = kit.settings) === null || _a === void 0 ? void 0 : _a.accountId) {
|
|
10731
10739
|
this.store.setRoktAccountId(kit.settings.accountId);
|
|
10732
10740
|
}
|
|
@@ -10772,6 +10780,70 @@ var mParticle = (function () {
|
|
|
10772
10780
|
(_c = this.logger) === null || _c === void 0 ? void 0 : _c.error("RoktManager: Error in onRouteChange: ".concat(getErrorMessage(e)));
|
|
10773
10781
|
}
|
|
10774
10782
|
};
|
|
10783
|
+
// Checkout intent is off unless the flag is on, and under opt-out, any Rokt
|
|
10784
|
+
// privacy launcher option, or Global Privacy Control.
|
|
10785
|
+
RoktManager.prototype.isCheckoutIntentSuppressed = function () {
|
|
10786
|
+
var _a, _b;
|
|
10787
|
+
var flags = (_b = (_a = this.store) === null || _a === void 0 ? void 0 : _a.SDKConfig) === null || _b === void 0 ? void 0 : _b.flags;
|
|
10788
|
+
if (!flags || flags[Constants.FeatureFlags.AutoButtonTracking] !== true || !this.store.isEnabled) {
|
|
10789
|
+
return true;
|
|
10790
|
+
}
|
|
10791
|
+
var options = this.launcherOptions || {};
|
|
10792
|
+
if (options.noFunctional || options.noTargeting || options.doNotShareOrSell) {
|
|
10793
|
+
return true;
|
|
10794
|
+
}
|
|
10795
|
+
var nav = typeof navigator === 'undefined' ? undefined : navigator;
|
|
10796
|
+
return !!nav && nav.globalPrivacyControl === true;
|
|
10797
|
+
};
|
|
10798
|
+
// Reads `this.kit` at call time; before a kit attaches the signal is buffered
|
|
10799
|
+
// with the path it was raised on.
|
|
10800
|
+
RoktManager.prototype.notifyCheckoutIntent = function (signal, pathname) {
|
|
10801
|
+
var _a;
|
|
10802
|
+
if (pathname === void 0) {
|
|
10803
|
+
pathname = window.location.pathname;
|
|
10804
|
+
}
|
|
10805
|
+
if (this.isCheckoutIntentSuppressed()) {
|
|
10806
|
+
return;
|
|
10807
|
+
}
|
|
10808
|
+
var kit = this.kit;
|
|
10809
|
+
if (!kit) {
|
|
10810
|
+
this.pendingCheckoutIntents = this.pendingCheckoutIntents.filter(function (pending) {
|
|
10811
|
+
return pending.signal.kind !== signal.kind;
|
|
10812
|
+
}).concat({
|
|
10813
|
+
signal: signal,
|
|
10814
|
+
pathname: pathname
|
|
10815
|
+
}).slice(-CHECKOUT_INTENT_BUFFER_LIMIT);
|
|
10816
|
+
return;
|
|
10817
|
+
}
|
|
10818
|
+
try {
|
|
10819
|
+
if (isFunction(kit.onCheckoutIntent)) {
|
|
10820
|
+
kit.onCheckoutIntent(signal);
|
|
10821
|
+
}
|
|
10822
|
+
} catch (e) {
|
|
10823
|
+
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error("RoktManager: Error in onCheckoutIntent: ".concat(getErrorMessage(e)));
|
|
10824
|
+
}
|
|
10825
|
+
};
|
|
10826
|
+
RoktManager.prototype.discardCheckoutIntents = function () {
|
|
10827
|
+
this.pendingCheckoutIntents = [];
|
|
10828
|
+
};
|
|
10829
|
+
// Replays recent buffered signals raised on the current page, oldest first.
|
|
10830
|
+
RoktManager.prototype.flushCheckoutIntents = function () {
|
|
10831
|
+
var _this = this;
|
|
10832
|
+
var pending = this.pendingCheckoutIntents;
|
|
10833
|
+
this.pendingCheckoutIntents = [];
|
|
10834
|
+
var now = Date.now();
|
|
10835
|
+
var pathname = window.location.pathname;
|
|
10836
|
+
pending.filter(function (_a) {
|
|
10837
|
+
var signal = _a.signal,
|
|
10838
|
+
signalPathname = _a.pathname;
|
|
10839
|
+
return now - signal.t < CHECKOUT_INTENT_MAX_AGE_MS && signalPathname === pathname;
|
|
10840
|
+
}).sort(function (a, b) {
|
|
10841
|
+
return a.signal.t - b.signal.t;
|
|
10842
|
+
}).forEach(function (_a) {
|
|
10843
|
+
var signal = _a.signal;
|
|
10844
|
+
return _this.notifyCheckoutIntent(signal);
|
|
10845
|
+
});
|
|
10846
|
+
};
|
|
10775
10847
|
RoktManager.prototype.isAutoLogPageViewEnabled = function () {
|
|
10776
10848
|
var _a, _b, _c;
|
|
10777
10849
|
return ((_c = (_b = (_a = this.store) === null || _a === void 0 ? void 0 : _a.SDKConfig) === null || _b === void 0 ? void 0 : _b.flags) === null || _c === void 0 ? void 0 : _c[Constants.FeatureFlags.AutoLogPageView]) === true;
|
|
@@ -10850,7 +10922,7 @@ var mParticle = (function () {
|
|
|
10850
10922
|
if (isEmpty(newIdentities_1)) {
|
|
10851
10923
|
this.setUserAttributes(this.currentUser, userAttributes_1);
|
|
10852
10924
|
} else {
|
|
10853
|
-
// Fire-and-forget identify
|
|
10925
|
+
// Fire-and-forget identify \u2014 best-effort, does not block selectPlacements
|
|
10854
10926
|
try {
|
|
10855
10927
|
this.identityService.identify({
|
|
10856
10928
|
userIdentities: __assign(__assign({}, currentUserIdentities), newIdentities_1)
|
|
@@ -11002,7 +11074,7 @@ var mParticle = (function () {
|
|
|
11002
11074
|
/**
|
|
11003
11075
|
* Tears down the current Rokt launcher and removes the placements it rendered.
|
|
11004
11076
|
*
|
|
11005
|
-
* Intended for hosts that cannot simply destroy the container element
|
|
11077
|
+
* Intended for hosts that cannot simply destroy the container element \u2014
|
|
11006
11078
|
* SPA route changes and confirmation pages, for example.
|
|
11007
11079
|
*
|
|
11008
11080
|
* Unlike the other proxied methods, a call made before the kit is ready is
|
|
@@ -11387,6 +11459,723 @@ var mParticle = (function () {
|
|
|
11387
11459
|
return LoggingDispatcher;
|
|
11388
11460
|
}();
|
|
11389
11461
|
|
|
11462
|
+
// Marks an automatically logged button click, like `is_auto_page_view`.
|
|
11463
|
+
var AUTO_BUTTON_ATTRIBUTE = 'is_auto_button_click';
|
|
11464
|
+
var BUTTON_CLICK_EVENT_NAME = 'Button Click';
|
|
11465
|
+
// One custom event per click, logged like logLink() with the auto marker and the
|
|
11466
|
+
// page fields that auto page views carry. No intent: classifying clicks is left
|
|
11467
|
+
// to whoever reads the data.
|
|
11468
|
+
var buildButtonClickEvent = function buildButtonClickEvent(click, pathname) {
|
|
11469
|
+
var _a;
|
|
11470
|
+
var data = (_a = {
|
|
11471
|
+
label_source: click.labelSource,
|
|
11472
|
+
element: click.element,
|
|
11473
|
+
type: click.type,
|
|
11474
|
+
path: pathname,
|
|
11475
|
+
hostname: window.location.hostname
|
|
11476
|
+
}, _a[AUTO_BUTTON_ATTRIBUTE] = true, _a);
|
|
11477
|
+
if (click.label) {
|
|
11478
|
+
data.label = click.label;
|
|
11479
|
+
}
|
|
11480
|
+
if (click.ariaLabel) {
|
|
11481
|
+
data.aria_label = click.ariaLabel;
|
|
11482
|
+
}
|
|
11483
|
+
if (click.icon) {
|
|
11484
|
+
data.icon = click.icon;
|
|
11485
|
+
}
|
|
11486
|
+
if (click.elementId) {
|
|
11487
|
+
data.element_id = click.elementId;
|
|
11488
|
+
}
|
|
11489
|
+
if (click.elementName) {
|
|
11490
|
+
data.element_name = click.elementName;
|
|
11491
|
+
}
|
|
11492
|
+
if (click.testId) {
|
|
11493
|
+
data.test_id = click.testId;
|
|
11494
|
+
}
|
|
11495
|
+
return {
|
|
11496
|
+
messageType: MessageType$1.PageEvent,
|
|
11497
|
+
name: BUTTON_CLICK_EVENT_NAME,
|
|
11498
|
+
data: data,
|
|
11499
|
+
eventType: EventType.Other
|
|
11500
|
+
};
|
|
11501
|
+
};
|
|
11502
|
+
|
|
11503
|
+
// Finds the control a click landed on, for both checkout intent and button
|
|
11504
|
+
// descriptions. Rokt's own elements, disabled controls and anything under
|
|
11505
|
+
// data-mp-no-track are never returned.
|
|
11506
|
+
var CONTROL_SELECTOR = 'button, input[type="submit"], input[type="button"], input[type="image"], [role="button"]';
|
|
11507
|
+
// closest() over the composed tree: slots, parents, then shadow hosts.
|
|
11508
|
+
function hasComposedAncestor(element, selector) {
|
|
11509
|
+
var node = element;
|
|
11510
|
+
while (node) {
|
|
11511
|
+
if (node.matches(selector)) {
|
|
11512
|
+
return true;
|
|
11513
|
+
}
|
|
11514
|
+
var root = isFunction(node.getRootNode) ? node.getRootNode() : null;
|
|
11515
|
+
node = node.assignedSlot || node.parentElement || root && root.host || null;
|
|
11516
|
+
}
|
|
11517
|
+
return false;
|
|
11518
|
+
}
|
|
11519
|
+
// Rokt's own placements and frames never count as partner checkout controls.
|
|
11520
|
+
function isRoktElement(element) {
|
|
11521
|
+
if (hasComposedAncestor(element, '[data-core-type], #rokt-controller-frame')) {
|
|
11522
|
+
return true;
|
|
11523
|
+
}
|
|
11524
|
+
var src = element.getAttribute('src');
|
|
11525
|
+
if (src) {
|
|
11526
|
+
try {
|
|
11527
|
+
if (new URL(src, 'https://example.test').pathname.indexOf('/wsdk/') !== -1) {
|
|
11528
|
+
return true;
|
|
11529
|
+
}
|
|
11530
|
+
} catch (e) {
|
|
11531
|
+
// An unparseable src is not a Rokt element.
|
|
11532
|
+
}
|
|
11533
|
+
}
|
|
11534
|
+
return false;
|
|
11535
|
+
}
|
|
11536
|
+
function isWalletElement(element) {
|
|
11537
|
+
return element.matches('apple-pay-button, shop-pay-button, .gpay-button, #AmazonPayButton, [data-amazon-pay-button]');
|
|
11538
|
+
}
|
|
11539
|
+
// Walks at most 12 entries of an event's composed path for the nearest control.
|
|
11540
|
+
// Rokt elements, disabled controls and anything under data-mp-no-track are skipped.
|
|
11541
|
+
function findControl(path) {
|
|
11542
|
+
var control;
|
|
11543
|
+
for (var _i = 0, _a = path.slice(0, 12); _i < _a.length; _i++) {
|
|
11544
|
+
var target = _a[_i];
|
|
11545
|
+
if (!(target instanceof Element)) {
|
|
11546
|
+
continue;
|
|
11547
|
+
}
|
|
11548
|
+
if (isRoktElement(target) || target.matches('[data-mp-no-track]')) {
|
|
11549
|
+
return undefined;
|
|
11550
|
+
}
|
|
11551
|
+
if (!control && (target.matches(CONTROL_SELECTOR + ', a[href]') || isWalletElement(target))) {
|
|
11552
|
+
control = target;
|
|
11553
|
+
}
|
|
11554
|
+
}
|
|
11555
|
+
if (!control || isRoktElement(control) || hasComposedAncestor(control, '[data-mp-no-track]') || control.matches(':disabled, [aria-disabled="true"]')) {
|
|
11556
|
+
return undefined;
|
|
11557
|
+
}
|
|
11558
|
+
return control;
|
|
11559
|
+
}
|
|
11560
|
+
|
|
11561
|
+
// Masks a button label before it is logged. Button text can carry personal data,
|
|
11562
|
+
// so this is the privacy boundary for every label and aria-label.
|
|
11563
|
+
// Masking needs \p{Nd} to see every script's digits. Browsers without Unicode
|
|
11564
|
+
// property escapes send no labels at all.
|
|
11565
|
+
function createDigitPatterns() {
|
|
11566
|
+
try {
|
|
11567
|
+
return {
|
|
11568
|
+
run: new RegExp('\\p{Nd}{6,}', 'u'),
|
|
11569
|
+
number: new RegExp('\\p{Nd}+(?:[.,\u060C\u066B\u066C]\\p{Nd}+)*', 'gu')
|
|
11570
|
+
};
|
|
11571
|
+
} catch (e) {
|
|
11572
|
+
return undefined;
|
|
11573
|
+
}
|
|
11574
|
+
}
|
|
11575
|
+
var digitPatterns = createDigitPatterns();
|
|
11576
|
+
// The label as logged: lower case, at most 64 characters, digits and amounts
|
|
11577
|
+
// masked. Dropped when it looks like an email, phone or card number.
|
|
11578
|
+
function maskLabel(raw) {
|
|
11579
|
+
if (!raw || raw.length > 128 || !digitPatterns) {
|
|
11580
|
+
return undefined;
|
|
11581
|
+
}
|
|
11582
|
+
var text = raw.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
11583
|
+
if (!text || text.indexOf('@') !== -1 || digitPatterns.run.test(text.replace(/[\s()\-\u2010-\u2015]/g, ''))) {
|
|
11584
|
+
return undefined;
|
|
11585
|
+
}
|
|
11586
|
+
var masked = text.replace(digitPatterns.number, '#').slice(0, 64).trim();
|
|
11587
|
+
return masked || undefined;
|
|
11588
|
+
}
|
|
11589
|
+
|
|
11590
|
+
function elementKind(control) {
|
|
11591
|
+
if (isWalletElement(control)) {
|
|
11592
|
+
return 'wallet';
|
|
11593
|
+
}
|
|
11594
|
+
var tag = control.tagName.toLowerCase();
|
|
11595
|
+
return tag === 'button' || tag === 'input' ? tag : 'role';
|
|
11596
|
+
}
|
|
11597
|
+
var EDITABLE = '[contenteditable]:not([contenteditable="false"])';
|
|
11598
|
+
// Text that may hold what a shopper typed or what the page opted out: form
|
|
11599
|
+
// fields, editable regions, and anything under data-mp-no-track.
|
|
11600
|
+
function isUnsafeTextSource(element) {
|
|
11601
|
+
return element.matches('input, select, textarea, option, optgroup, datalist, ' + EDITABLE) || !!element.querySelector('input, select, textarea, [data-mp-no-track], ' + EDITABLE) || hasComposedAncestor(element, 'select, datalist, textarea, [data-mp-no-track], ' + EDITABLE);
|
|
11602
|
+
}
|
|
11603
|
+
function textOf(element) {
|
|
11604
|
+
return element && !isUnsafeTextSource(element) && element.textContent ? element.textContent.trim() : '';
|
|
11605
|
+
}
|
|
11606
|
+
function nonBlank(value) {
|
|
11607
|
+
return value && value.trim() ? value : undefined;
|
|
11608
|
+
}
|
|
11609
|
+
// The button's name as a screen reader would read it, simplified: the first of
|
|
11610
|
+
// aria-labelledby, aria-label, own text (or an inner image's alt), title, and for
|
|
11611
|
+
// inputs their value. The text sources are skipped for controls that hold form
|
|
11612
|
+
// fields or more than four children, so a typed value is never read.
|
|
11613
|
+
function accessibleName(control) {
|
|
11614
|
+
var labelledBy = control.getAttribute('aria-labelledby');
|
|
11615
|
+
if (labelledBy) {
|
|
11616
|
+
var root_1 = isFunction(control.getRootNode) ? control.getRootNode() : document;
|
|
11617
|
+
var text = labelledBy.split(/\s+/).map(function (id) {
|
|
11618
|
+
return textOf(root_1.getElementById ? root_1.getElementById(id) : null);
|
|
11619
|
+
}).filter(function (part) {
|
|
11620
|
+
return !!part;
|
|
11621
|
+
}).join(' ');
|
|
11622
|
+
if (text) {
|
|
11623
|
+
return {
|
|
11624
|
+
raw: text,
|
|
11625
|
+
source: 'labelledby'
|
|
11626
|
+
};
|
|
11627
|
+
}
|
|
11628
|
+
}
|
|
11629
|
+
var aria = nonBlank(control.getAttribute('aria-label'));
|
|
11630
|
+
if (aria) {
|
|
11631
|
+
return {
|
|
11632
|
+
raw: aria,
|
|
11633
|
+
source: 'aria'
|
|
11634
|
+
};
|
|
11635
|
+
}
|
|
11636
|
+
var readable = control.children.length <= 4 && !isUnsafeTextSource(control);
|
|
11637
|
+
if (control instanceof HTMLInputElement) {
|
|
11638
|
+
// The browser's normalised type: an unknown value reads as 'text'.
|
|
11639
|
+
var type = control.type;
|
|
11640
|
+
if (type === 'image') {
|
|
11641
|
+
var alt = nonBlank(control.getAttribute('alt'));
|
|
11642
|
+
if (alt) {
|
|
11643
|
+
return {
|
|
11644
|
+
raw: alt,
|
|
11645
|
+
source: 'alt'
|
|
11646
|
+
};
|
|
11647
|
+
}
|
|
11648
|
+
} else if (type === 'submit' || type === 'button' || type === 'reset') {
|
|
11649
|
+
var value = nonBlank(control.value);
|
|
11650
|
+
if (value) {
|
|
11651
|
+
return {
|
|
11652
|
+
raw: value,
|
|
11653
|
+
source: 'value'
|
|
11654
|
+
};
|
|
11655
|
+
}
|
|
11656
|
+
if (type !== 'button') {
|
|
11657
|
+
// What the browser shows on an input without a value.
|
|
11658
|
+
return {
|
|
11659
|
+
raw: type,
|
|
11660
|
+
source: 'value'
|
|
11661
|
+
};
|
|
11662
|
+
}
|
|
11663
|
+
}
|
|
11664
|
+
} else if (readable) {
|
|
11665
|
+
var text = textOf(control);
|
|
11666
|
+
if (text) {
|
|
11667
|
+
return {
|
|
11668
|
+
raw: text,
|
|
11669
|
+
source: 'text'
|
|
11670
|
+
};
|
|
11671
|
+
}
|
|
11672
|
+
var image = control.querySelector('img[alt]');
|
|
11673
|
+
var alt = image ? nonBlank(image.getAttribute('alt')) : undefined;
|
|
11674
|
+
if (alt) {
|
|
11675
|
+
return {
|
|
11676
|
+
raw: alt,
|
|
11677
|
+
source: 'alt'
|
|
11678
|
+
};
|
|
11679
|
+
}
|
|
11680
|
+
}
|
|
11681
|
+
var title = nonBlank(control.getAttribute('title'));
|
|
11682
|
+
if (title) {
|
|
11683
|
+
return {
|
|
11684
|
+
raw: title,
|
|
11685
|
+
source: 'title'
|
|
11686
|
+
};
|
|
11687
|
+
}
|
|
11688
|
+
return {
|
|
11689
|
+
source: 'none'
|
|
11690
|
+
};
|
|
11691
|
+
}
|
|
11692
|
+
var ICON_CLASS = /^(?:fa|fas|far|fal|icon|bi|glyphicon|ti|mdi|ri|la|lucide|ion|feather|ph)-([a-z][a-z-]{1,31})$/;
|
|
11693
|
+
// Size, style and animation classes that share the icon prefixes.
|
|
11694
|
+
var ICON_MODIFIER = /^(?:solid|regular|light|thin|duotone|brands|sharp|fw|lg|xs|sm|xl|spin|pulse|border|inverse|stack|flip(?:-[a-z]+)?|rotate(?:-[a-z]+)?|pull-[a-z]+)$/;
|
|
11695
|
+
function iconToken(candidate) {
|
|
11696
|
+
if (!candidate) {
|
|
11697
|
+
return undefined;
|
|
11698
|
+
}
|
|
11699
|
+
var token = candidate.toLowerCase().replace(/^icon-/, '');
|
|
11700
|
+
return /^[a-z][a-z-]{1,31}$/.test(token) ? token : undefined;
|
|
11701
|
+
}
|
|
11702
|
+
// An icon name from an SVG sprite reference or an icon-font class, for buttons
|
|
11703
|
+
// whose only content is an icon. Letters and hyphens only.
|
|
11704
|
+
function iconHint(control) {
|
|
11705
|
+
var allowed = function allowed(element) {
|
|
11706
|
+
return !hasComposedAncestor(element, '[data-mp-no-track]');
|
|
11707
|
+
};
|
|
11708
|
+
var use = Array.from(control.querySelectorAll('use')).filter(allowed)[0];
|
|
11709
|
+
if (use) {
|
|
11710
|
+
var href = use.getAttribute('href') || use.getAttribute('xlink:href') || '';
|
|
11711
|
+
var hash = href.indexOf('#');
|
|
11712
|
+
var sprite = iconToken(hash === -1 ? null : href.slice(hash + 1));
|
|
11713
|
+
if (sprite) {
|
|
11714
|
+
return sprite;
|
|
11715
|
+
}
|
|
11716
|
+
}
|
|
11717
|
+
var candidates = [control].concat(Array.from(control.querySelectorAll('i, span, svg'))).filter(allowed).slice(0, 5);
|
|
11718
|
+
for (var _i = 0, candidates_1 = candidates; _i < candidates_1.length; _i++) {
|
|
11719
|
+
var element = candidates_1[_i];
|
|
11720
|
+
var classes = (element.getAttribute('class') || '').split(/\s+/);
|
|
11721
|
+
for (var _a = 0, classes_1 = classes; _a < classes_1.length; _a++) {
|
|
11722
|
+
var name_1 = classes_1[_a];
|
|
11723
|
+
var match = ICON_CLASS.exec(name_1.toLowerCase());
|
|
11724
|
+
if (match && !ICON_MODIFIER.test(match[1])) {
|
|
11725
|
+
return match[1];
|
|
11726
|
+
}
|
|
11727
|
+
}
|
|
11728
|
+
}
|
|
11729
|
+
return undefined;
|
|
11730
|
+
}
|
|
11731
|
+
// Words of letters, each optionally ending in up to three digits, joined by
|
|
11732
|
+
// hyphens or underscores: `add-to-cart`, `btnCheckout`, `step_2`. Generated ids
|
|
11733
|
+
// (`ember1234`, `a8f3e2c1`, `radix-:r1:`) and long numbers don't match.
|
|
11734
|
+
var HAND_WRITTEN_ID = /^[A-Za-z]+[0-9]{0,3}(?:[-_](?:[A-Za-z]+[0-9]{0,3}|[0-9]{1,3}))*$/;
|
|
11735
|
+
// A developer-written identifier, or nothing.
|
|
11736
|
+
function stableIdentifier(value) {
|
|
11737
|
+
if (!value) {
|
|
11738
|
+
return undefined;
|
|
11739
|
+
}
|
|
11740
|
+
var id = value.trim();
|
|
11741
|
+
// At most three digits in all, so numbers split into groups don't pass.
|
|
11742
|
+
var digits = id.replace(/[^0-9]/g, '').length;
|
|
11743
|
+
return id.length <= 64 && digits <= 3 && HAND_WRITTEN_ID.test(id) ? id : undefined;
|
|
11744
|
+
}
|
|
11745
|
+
var TEST_ID_ATTRIBUTES = ['data-testid', 'data-test-id', 'data-test', 'data-qa', 'data-cy'];
|
|
11746
|
+
function testIdentifier(control) {
|
|
11747
|
+
for (var _i = 0, TEST_ID_ATTRIBUTES_1 = TEST_ID_ATTRIBUTES; _i < TEST_ID_ATTRIBUTES_1.length; _i++) {
|
|
11748
|
+
var attribute = TEST_ID_ATTRIBUTES_1[_i];
|
|
11749
|
+
var id = stableIdentifier(control.getAttribute(attribute));
|
|
11750
|
+
if (id) {
|
|
11751
|
+
return id;
|
|
11752
|
+
}
|
|
11753
|
+
}
|
|
11754
|
+
return undefined;
|
|
11755
|
+
}
|
|
11756
|
+
// The browser's normalised type: a button defaults to 'submit' and an unknown
|
|
11757
|
+
// input type reads as 'text'. Wallet and role buttons are just 'button'.
|
|
11758
|
+
function controlType(control, element) {
|
|
11759
|
+
return element === 'button' || element === 'input' ? control.type : 'button';
|
|
11760
|
+
}
|
|
11761
|
+
// Describes a clicked button for auto button tracking. Links are left to page
|
|
11762
|
+
// views, so a link click is not described.
|
|
11763
|
+
function describeButtonClick(path) {
|
|
11764
|
+
var control = findControl(path);
|
|
11765
|
+
if (!control || control.matches('a[href]:not([role="button"])')) {
|
|
11766
|
+
return undefined;
|
|
11767
|
+
}
|
|
11768
|
+
var element = elementKind(control);
|
|
11769
|
+
var name = accessibleName(control);
|
|
11770
|
+
var label = maskLabel(name.raw);
|
|
11771
|
+
return {
|
|
11772
|
+
label: label,
|
|
11773
|
+
labelSource: label ? name.source : 'none',
|
|
11774
|
+
// Kept on its own as well: it often says more than the visible text,
|
|
11775
|
+
// such as which item a remove button removes.
|
|
11776
|
+
ariaLabel: maskLabel(control.getAttribute('aria-label')),
|
|
11777
|
+
icon: iconHint(control),
|
|
11778
|
+
elementId: stableIdentifier(control.getAttribute('id')),
|
|
11779
|
+
elementName: stableIdentifier(control.getAttribute('name')),
|
|
11780
|
+
testId: testIdentifier(control),
|
|
11781
|
+
element: element,
|
|
11782
|
+
type: controlType(control, element)
|
|
11783
|
+
};
|
|
11784
|
+
}
|
|
11785
|
+
|
|
11786
|
+
var checkoutPath = /(?:^|\/)(?:checkouts?|payment|order|kasse|paiement|commander?|pago)(?:\/|$)/i;
|
|
11787
|
+
var exclusions = new RegExp(['coupon|promo|newsletter|sign in|add to cart|gift card|pay later|pay in|payment plan|learn more', 'gutschein|anmelden|warenkorb|bon cadeau|connexion|panier|cup\u00F3n|iniciar sesi\u00F3n|carrito', 'carrello|codice sconto|accedi|buono regalo|winkelwagen|kortingscode|inloggen|cadeaubon|achteraf betalen', '\u30AB\u30FC\u30C8|\u30AF\u30FC\u30DD\u30F3|\u30ED\u30B0\u30A4\u30F3|\u4F1A\u54E1\u767B\u9332|\u30AE\u30D5\u30C8|\u5F8C\u6255\u3044|\u30E1\u30EB\u30DE\u30AC'].join('|'));
|
|
11788
|
+
var commits = new RegExp('^(?:' + ['(?:place|submit|complete|confirm|finali[sz]e)(?!(?: [^ ]+){0,2} (?:another|new|next)(?: |$))(?: [^ ]+){0,2} (?:order|purchase|booking|reservation|payment|checkout)', 'pay(?: securely)?(?: now)?|confirm and pay|order and pay|order with obligation to pay', '(?:jetzt )?(?:zahlungs|kosten)pflichtig (?:bestellen|buchen)|(?:jetzt )?bezahlen|kauf abschlie\u00DFen|bestellung abschlie\u00DFen', 'commander et payer|payer(?: maintenant)?|(?:confirmer|finaliser) (?:ma |la )?commande', "valider et payer|confirmer et payer|commande avec obligation de paiement|finaliser l'achat", '(?:realizar|confirmar|completar) (?:el |mi |la )?(?:pedido|compra|reserva)|finalizar (?:el |mi )?pedido|pagar(?: ahora)?', 'pedido con obligaci\u00F3n de pago', "(?:effettua|conferma|concludi|completa) (?:l'|il )?(?:ordine|acquisto)|ordine con obbligo di pagamento", 'paga(?: ora| adesso)?|ordina e paga', '(?:nu )?betalen|bestellen en betalen|bestelling plaatsen|plaats bestelling|bestelling afronden', 'bestelling met betalingsverplichting', '(?:\u3053\u306E\u5185\u5BB9\u3067|\u4E0A\u8A18\u306E?\u5185\u5BB9\u3067)?\u3054?(?:\u6CE8\u6587|\u8CFC\u5165|\u4E88\u7D04|\u6C7A\u6E08|\u304A?\u652F\u6255\u3044?)\u3092?(?:\u78BA\u5B9A|\u5B8C\u4E86)(?:\u3059\u308B)?', '(?:\u3053\u306E\u5185\u5BB9\u3067|\u4E0A\u8A18\u306E?\u5185\u5BB9\u3067)\u3054?(?:\u6CE8\u6587|\u8CFC\u5165|\u4E88\u7D04)\u3059\u308B|\u3054?\u6CE8\u6587\u3059\u308B|\u6C7A\u6E08\u3059\u308B|(?:\u4ECA\u3059\u3050|\u3092)?\u652F\u6255\u3046'].join('|') + ')$');
|
|
11789
|
+
var routeGatedCommits = new RegExp('^(?:buy now|purchase|jetzt kaufen|kaufen|commander|(?:passer|valider) (?:la |ma )?commande|finalizar (?:la |mi )?compra|' + 'comprar ahora|acquista ora|nu kopen|\u4ECA\u3059\u3050\u8CFC\u5165|\u8CFC\u5165\u3059\u308B|\u3053\u306E\u5185\u5BB9\u3067\u7533\u3057\u8FBC\u3080)$');
|
|
11790
|
+
var steps = new RegExp('^(?:' + ['checkout|check out|check-out|continue to payment|proceed to checkout', 'zur kasse|zur kasse gehen|auschecken|weiter zur zahlung', 'passer \u00E0 la caisse|continuer vers le paiement|proc\u00E9der au paiement', 'ir a caja|continuar al pago|tramitar pedido', "procedi al checkout|procedi all'acquisto|procedi al pagamento|vai alla cassa", 'afrekenen|verder naar betalen|doorgaan naar betalen', '\u30EC\u30B8\u306B\u9032\u3080|\u3054?(?:\u8CFC\u5165|\u6CE8\u6587)\u624B\u7D9A\u304D(?:\u3078|\u306B)(?:\u9032\u3080)?|\u304A?\u652F\u6255\u3044(?:\u3078|\u306B)\u9032\u3080|(?:\u3054\u6CE8\u6587\u5185\u5BB9\u306E)?\u78BA\u8A8D\u753B\u9762\u3078(?:\u9032\u3080)?'].join('|') + ')$');
|
|
11791
|
+
var wallets = /^(?:apple pay|google pay|paypal|venmo|amazon pay|shop pay|klarna|afterpay|affirm)$/;
|
|
11792
|
+
// Exact HTTPS hosts of hosted wallet and payment frames.
|
|
11793
|
+
var frameHosts = new Set(['www.paypal.com', 'www.sandbox.paypal.com', 'pay.google.com', 'applepay.cdn-apple.com', 'js.stripe.com', 'checkoutshopper-live.adyen.com', 'payments.amazon.com', 'pay.shopify.com']);
|
|
11794
|
+
// Keep these classes ASCII-only in source bytes to avoid mojibake parse failures
|
|
11795
|
+
// when a host page forces non-UTF-8 decoding before JavaScript parse.
|
|
11796
|
+
var TYPOGRAPHIC_APOSTROPHES = /[\u2019\u2018]/g;
|
|
11797
|
+
var CURRENCY_AND_NUMBER_CHARS = /[0-9\uFF10-\uFF19$\u20AC\u00A3\u00A5\uFFE5\u20B9\u5186.,\uFF0C]/g;
|
|
11798
|
+
function getIntentRouteClass(pathname) {
|
|
11799
|
+
return checkoutPath.test(pathname) ? 'checkout_path' : 'other';
|
|
11800
|
+
}
|
|
11801
|
+
function normalize(text) {
|
|
11802
|
+
if (!text || text.length > 128) {
|
|
11803
|
+
return undefined;
|
|
11804
|
+
}
|
|
11805
|
+
return text.toLowerCase().replace(TYPOGRAPHIC_APOSTROPHES, "'").replace(CURRENCY_AND_NUMBER_CHARS, '').replace(/\s+/g, ' ').trim();
|
|
11806
|
+
}
|
|
11807
|
+
// Labels come from aria-label then visible text. Only submit and button inputs
|
|
11808
|
+
// have their value read, so a typed field value is never touched.
|
|
11809
|
+
function labelSources(element) {
|
|
11810
|
+
if (element.children.length > 4 || element.querySelector('input, select, textarea, [data-mp-no-track]')) {
|
|
11811
|
+
return [];
|
|
11812
|
+
}
|
|
11813
|
+
var text = null;
|
|
11814
|
+
if (element instanceof HTMLInputElement) {
|
|
11815
|
+
var type = element.type;
|
|
11816
|
+
if (type === 'submit' || type === 'button') {
|
|
11817
|
+
text = element.value;
|
|
11818
|
+
} else if (type === 'image') {
|
|
11819
|
+
text = element.getAttribute('alt');
|
|
11820
|
+
}
|
|
11821
|
+
} else {
|
|
11822
|
+
text = element.textContent;
|
|
11823
|
+
}
|
|
11824
|
+
return [element.getAttribute('aria-label'), text];
|
|
11825
|
+
}
|
|
11826
|
+
function labels(element) {
|
|
11827
|
+
return labelSources(element).map(normalize).filter(function (label) {
|
|
11828
|
+
return !!label;
|
|
11829
|
+
});
|
|
11830
|
+
}
|
|
11831
|
+
function classifyLabel(text, button, pathname) {
|
|
11832
|
+
if (commits.test(text) || routeGatedCommits.test(text) && getIntentRouteClass(pathname) === 'checkout_path') {
|
|
11833
|
+
return 'commit';
|
|
11834
|
+
}
|
|
11835
|
+
if (button && wallets.test(text)) {
|
|
11836
|
+
return 'wallet';
|
|
11837
|
+
}
|
|
11838
|
+
if (steps.test(text)) {
|
|
11839
|
+
return 'checkout_step';
|
|
11840
|
+
}
|
|
11841
|
+
return undefined;
|
|
11842
|
+
}
|
|
11843
|
+
function classifyIntentControl(path, pathname) {
|
|
11844
|
+
var control = findControl(path);
|
|
11845
|
+
return control ? classifyControl(control, pathname) : undefined;
|
|
11846
|
+
}
|
|
11847
|
+
function classifyControl(control, pathname) {
|
|
11848
|
+
var texts = labels(control);
|
|
11849
|
+
if (texts.some(function (text) {
|
|
11850
|
+
return exclusions.test(text);
|
|
11851
|
+
})) {
|
|
11852
|
+
return undefined;
|
|
11853
|
+
}
|
|
11854
|
+
var button = false;
|
|
11855
|
+
if (!control.matches('a[href]')) {
|
|
11856
|
+
button = control.matches(CONTROL_SELECTOR) || isWalletElement(control);
|
|
11857
|
+
}
|
|
11858
|
+
if (button && isWalletElement(control)) {
|
|
11859
|
+
return 'wallet';
|
|
11860
|
+
}
|
|
11861
|
+
for (var _i = 0, texts_1 = texts; _i < texts_1.length; _i++) {
|
|
11862
|
+
var text = texts_1[_i];
|
|
11863
|
+
var kind = classifyLabel(text, button, pathname);
|
|
11864
|
+
if (kind) {
|
|
11865
|
+
return kind;
|
|
11866
|
+
}
|
|
11867
|
+
}
|
|
11868
|
+
return undefined;
|
|
11869
|
+
}
|
|
11870
|
+
// Focus moving into a visible hosted wallet or payment frame.
|
|
11871
|
+
function classifyIntentFrame(target, win) {
|
|
11872
|
+
if (win.document.visibilityState !== 'visible' || !(target instanceof HTMLIFrameElement)) {
|
|
11873
|
+
return undefined;
|
|
11874
|
+
}
|
|
11875
|
+
if (isRoktElement(target)) {
|
|
11876
|
+
return undefined;
|
|
11877
|
+
}
|
|
11878
|
+
try {
|
|
11879
|
+
var url = new URL(target.src);
|
|
11880
|
+
if (url.protocol === 'https:' && frameHosts.has(url.hostname)) {
|
|
11881
|
+
return 'frame';
|
|
11882
|
+
}
|
|
11883
|
+
} catch (e) {
|
|
11884
|
+
// An unparseable frame src is not a wallet frame.
|
|
11885
|
+
}
|
|
11886
|
+
return undefined;
|
|
11887
|
+
}
|
|
11888
|
+
|
|
11889
|
+
// State lives on `window`, like APV, so a re-executed bundle never doubles the
|
|
11890
|
+
// listeners. Inspect `window.__mpBtn__` in a console to see if one is live.
|
|
11891
|
+
var WIN_BTN_KEY = '__mpBtn__';
|
|
11892
|
+
var THROTTLE_MS = 2000;
|
|
11893
|
+
var SUBMIT_AFTER_COMMIT_CLICK_MS = 1000;
|
|
11894
|
+
var PATH_LIMIT = 12;
|
|
11895
|
+
var EVENT_DEDUP_MS = 1000;
|
|
11896
|
+
var PAGE_EVENT_LIMIT = 50;
|
|
11897
|
+
var btnWindow = function btnWindow() {
|
|
11898
|
+
return typeof window === 'undefined' ? null : window;
|
|
11899
|
+
};
|
|
11900
|
+
var getActiveButtonTracker = function getActiveButtonTracker() {
|
|
11901
|
+
var win = btnWindow();
|
|
11902
|
+
var state = win ? win[WIN_BTN_KEY] : undefined;
|
|
11903
|
+
return state ? state.tracker : undefined;
|
|
11904
|
+
};
|
|
11905
|
+
// Stops whichever tracker is active, including one from an earlier bundle load.
|
|
11906
|
+
var resetButtonTracking = function resetButtonTracking() {
|
|
11907
|
+
var win = btnWindow();
|
|
11908
|
+
if (!win) {
|
|
11909
|
+
return;
|
|
11910
|
+
}
|
|
11911
|
+
var active = getActiveButtonTracker();
|
|
11912
|
+
if (active) {
|
|
11913
|
+
active.teardown();
|
|
11914
|
+
}
|
|
11915
|
+
win[WIN_BTN_KEY] = {};
|
|
11916
|
+
};
|
|
11917
|
+
// Runs once the browser has painted the page's response to the interaction, so
|
|
11918
|
+
// describing and logging a click never delays that paint.
|
|
11919
|
+
var afterNextPaint = function afterNextPaint(fn) {
|
|
11920
|
+
if (isFunction(window.requestAnimationFrame) && document.visibilityState !== 'hidden') {
|
|
11921
|
+
requestAnimationFrame(function () {
|
|
11922
|
+
setTimeout(fn, 0);
|
|
11923
|
+
});
|
|
11924
|
+
} else {
|
|
11925
|
+
setTimeout(fn, 0);
|
|
11926
|
+
}
|
|
11927
|
+
};
|
|
11928
|
+
var CAPTURE_PASSIVE = {
|
|
11929
|
+
capture: true,
|
|
11930
|
+
passive: true
|
|
11931
|
+
};
|
|
11932
|
+
var PASSIVE = {
|
|
11933
|
+
passive: true
|
|
11934
|
+
};
|
|
11935
|
+
// Logs every button click as an mParticle event, and hands checkout intent to the
|
|
11936
|
+
// Rokt kit as a frozen {kind, t}. Submits and wallet frame focus feed the kit only.
|
|
11937
|
+
var ButtonTracker = /** @class */function () {
|
|
11938
|
+
function ButtonTracker(mpInstance) {
|
|
11939
|
+
var _this = this;
|
|
11940
|
+
this.active = false;
|
|
11941
|
+
this.hasRoktKit = false;
|
|
11942
|
+
// Bumped on reset so classification queued before it is dropped.
|
|
11943
|
+
this.generation = 0;
|
|
11944
|
+
this.lastCommitClick = -Infinity;
|
|
11945
|
+
this.lastSent = {};
|
|
11946
|
+
this.pagePath = null;
|
|
11947
|
+
this.pageEvents = 0;
|
|
11948
|
+
this.lastEvent = {};
|
|
11949
|
+
// Clicks read at dispatch and waiting for the next paint.
|
|
11950
|
+
this.pending = [];
|
|
11951
|
+
this.drain = function () {
|
|
11952
|
+
var queue = _this.pending;
|
|
11953
|
+
_this.pending = [];
|
|
11954
|
+
queue.forEach(function (delivery) {
|
|
11955
|
+
return delivery();
|
|
11956
|
+
});
|
|
11957
|
+
};
|
|
11958
|
+
// A click that navigates away can unload the page before the next paint, so
|
|
11959
|
+
// anything waiting is delivered as the page is hidden or left. The capture
|
|
11960
|
+
// listeners run before the SDK's own upload on the same events, including
|
|
11961
|
+
// beforeunload, the first of them.
|
|
11962
|
+
this.onHide = function (event) {
|
|
11963
|
+
if (event.type === 'beforeunload' || event.type === 'pagehide' || document.visibilityState === 'hidden') {
|
|
11964
|
+
_this.drain();
|
|
11965
|
+
}
|
|
11966
|
+
};
|
|
11967
|
+
this.onPageShow = function (event) {
|
|
11968
|
+
// A page restored from the back-forward cache starts fresh.
|
|
11969
|
+
if (event.persisted) {
|
|
11970
|
+
_this.reset();
|
|
11971
|
+
}
|
|
11972
|
+
};
|
|
11973
|
+
this.onEvent = function (event) {
|
|
11974
|
+
try {
|
|
11975
|
+
_this.observe(event);
|
|
11976
|
+
} catch (e) {
|
|
11977
|
+
_this.log('[error] event handler threw: ' + e);
|
|
11978
|
+
}
|
|
11979
|
+
};
|
|
11980
|
+
this.mpInstance = mpInstance;
|
|
11981
|
+
}
|
|
11982
|
+
Object.defineProperty(ButtonTracker.prototype, "isActive", {
|
|
11983
|
+
get: function get() {
|
|
11984
|
+
return this.active;
|
|
11985
|
+
},
|
|
11986
|
+
enumerable: false,
|
|
11987
|
+
configurable: true
|
|
11988
|
+
});
|
|
11989
|
+
ButtonTracker.prototype.init = function (options) {
|
|
11990
|
+
var win = btnWindow();
|
|
11991
|
+
if (!win) {
|
|
11992
|
+
return;
|
|
11993
|
+
}
|
|
11994
|
+
var previous = getActiveButtonTracker();
|
|
11995
|
+
if (previous) {
|
|
11996
|
+
previous.teardown();
|
|
11997
|
+
}
|
|
11998
|
+
if (this.active) {
|
|
11999
|
+
this.teardown();
|
|
12000
|
+
}
|
|
12001
|
+
this.hasRoktKit = options.hasRoktKit;
|
|
12002
|
+
if (!this.eventsEnabled() && this.isKitSuppressed()) {
|
|
12003
|
+
this.log('opted out, not starting');
|
|
12004
|
+
return;
|
|
12005
|
+
}
|
|
12006
|
+
win.document.addEventListener('click', this.onEvent, CAPTURE_PASSIVE);
|
|
12007
|
+
win.document.addEventListener('submit', this.onEvent, CAPTURE_PASSIVE);
|
|
12008
|
+
win.addEventListener('blur', this.onEvent, PASSIVE);
|
|
12009
|
+
win.addEventListener('pageshow', this.onPageShow, PASSIVE);
|
|
12010
|
+
win.addEventListener('beforeunload', this.onHide, CAPTURE_PASSIVE);
|
|
12011
|
+
win.addEventListener('pagehide', this.onHide, CAPTURE_PASSIVE);
|
|
12012
|
+
win.document.addEventListener('visibilitychange', this.onHide, CAPTURE_PASSIVE);
|
|
12013
|
+
this.active = true;
|
|
12014
|
+
if (!win[WIN_BTN_KEY]) {
|
|
12015
|
+
win[WIN_BTN_KEY] = {};
|
|
12016
|
+
}
|
|
12017
|
+
win[WIN_BTN_KEY].tracker = this;
|
|
12018
|
+
this.log('listening');
|
|
12019
|
+
};
|
|
12020
|
+
ButtonTracker.prototype.teardown = function () {
|
|
12021
|
+
var win = btnWindow();
|
|
12022
|
+
if (win) {
|
|
12023
|
+
win.document.removeEventListener('click', this.onEvent, true);
|
|
12024
|
+
win.document.removeEventListener('submit', this.onEvent, true);
|
|
12025
|
+
win.removeEventListener('blur', this.onEvent);
|
|
12026
|
+
win.removeEventListener('pageshow', this.onPageShow);
|
|
12027
|
+
win.removeEventListener('beforeunload', this.onHide, true);
|
|
12028
|
+
win.removeEventListener('pagehide', this.onHide, true);
|
|
12029
|
+
win.document.removeEventListener('visibilitychange', this.onHide, true);
|
|
12030
|
+
var state = win[WIN_BTN_KEY];
|
|
12031
|
+
if (state && state.tracker === this) {
|
|
12032
|
+
state.tracker = undefined;
|
|
12033
|
+
}
|
|
12034
|
+
}
|
|
12035
|
+
this.active = false;
|
|
12036
|
+
// Signals not yet handed to a kit go with the tracker.
|
|
12037
|
+
this.clearPending();
|
|
12038
|
+
};
|
|
12039
|
+
// Drops queued classification, commit timing and buffered signals, so
|
|
12040
|
+
// nothing captured before an opt-out is delivered after an opt-in.
|
|
12041
|
+
ButtonTracker.prototype.clearPending = function () {
|
|
12042
|
+
this.reset();
|
|
12043
|
+
var roktManager = this.mpInstance._RoktManager;
|
|
12044
|
+
if (roktManager) {
|
|
12045
|
+
roktManager.discardCheckoutIntents();
|
|
12046
|
+
}
|
|
12047
|
+
};
|
|
12048
|
+
ButtonTracker.prototype.reset = function () {
|
|
12049
|
+
this.generation++;
|
|
12050
|
+
this.pending = [];
|
|
12051
|
+
this.lastCommitClick = -Infinity;
|
|
12052
|
+
this.lastSent = {};
|
|
12053
|
+
this.pagePath = null;
|
|
12054
|
+
this.pageEvents = 0;
|
|
12055
|
+
this.lastEvent = {};
|
|
12056
|
+
};
|
|
12057
|
+
ButtonTracker.prototype.enqueue = function (delivery) {
|
|
12058
|
+
this.pending.push(delivery);
|
|
12059
|
+
if (this.pending.length === 1) {
|
|
12060
|
+
afterNextPaint(this.drain);
|
|
12061
|
+
}
|
|
12062
|
+
};
|
|
12063
|
+
ButtonTracker.prototype.observe = function (event) {
|
|
12064
|
+
var _this = this;
|
|
12065
|
+
if (!this.active || !event.isTrusted) {
|
|
12066
|
+
return;
|
|
12067
|
+
}
|
|
12068
|
+
var layer = event.type;
|
|
12069
|
+
var logsEvents = layer === 'click' && this.eventsEnabled();
|
|
12070
|
+
if (!logsEvents && this.isKitSuppressed()) {
|
|
12071
|
+
return;
|
|
12072
|
+
}
|
|
12073
|
+
var t = Date.now();
|
|
12074
|
+
var pathname = window.location.pathname;
|
|
12075
|
+
// The button is read now, while the click is still being dispatched:
|
|
12076
|
+
// this capture listener runs before the page's own handlers, so a
|
|
12077
|
+
// button that disables or relabels itself is seen as it was clicked.
|
|
12078
|
+
// Logging and the kit hand-off wait for the next paint.
|
|
12079
|
+
var click;
|
|
12080
|
+
var kind;
|
|
12081
|
+
var unconfirmedCommit = false;
|
|
12082
|
+
if (layer === 'click') {
|
|
12083
|
+
// The composed path is only populated during dispatch.
|
|
12084
|
+
var path = isFunction(event.composedPath) ? event.composedPath().slice(0, PATH_LIMIT) : [];
|
|
12085
|
+
click = logsEvents ? describeButtonClick(path) : undefined;
|
|
12086
|
+
kind = classifyIntentControl(path, pathname);
|
|
12087
|
+
} else if (layer === 'submit') {
|
|
12088
|
+
var submitter = event.submitter;
|
|
12089
|
+
// A form submitted without a submitter only counts right after a
|
|
12090
|
+
// commit click, which is known once that click is delivered.
|
|
12091
|
+
kind = submitter ? classifyIntentControl([submitter], pathname) : 'commit';
|
|
12092
|
+
unconfirmedCommit = !submitter;
|
|
12093
|
+
}
|
|
12094
|
+
var generation = this.generation;
|
|
12095
|
+
this.enqueue(function () {
|
|
12096
|
+
if (generation !== _this.generation) {
|
|
12097
|
+
return;
|
|
12098
|
+
}
|
|
12099
|
+
try {
|
|
12100
|
+
if (layer === 'blur') {
|
|
12101
|
+
// Read after the blur settles: some browsers have not moved
|
|
12102
|
+
// focus into the frame yet when the window blurs.
|
|
12103
|
+
kind = classifyIntentFrame(document.activeElement, window);
|
|
12104
|
+
} else if (unconfirmedCommit && t - _this.lastCommitClick > SUBMIT_AFTER_COMMIT_CLICK_MS) {
|
|
12105
|
+
kind = undefined;
|
|
12106
|
+
}
|
|
12107
|
+
if (click && logsEvents) {
|
|
12108
|
+
_this.logClick(click, t, pathname);
|
|
12109
|
+
}
|
|
12110
|
+
if (kind) {
|
|
12111
|
+
_this.emit(kind, layer, t, pathname);
|
|
12112
|
+
}
|
|
12113
|
+
} catch (e) {
|
|
12114
|
+
_this.log('[error] delivery threw: ' + e);
|
|
12115
|
+
}
|
|
12116
|
+
});
|
|
12117
|
+
};
|
|
12118
|
+
// One event per identical button every second, and at most 50 per page.
|
|
12119
|
+
ButtonTracker.prototype.logClick = function (click, t, pathname) {
|
|
12120
|
+
if (!this.eventsEnabled()) {
|
|
12121
|
+
return;
|
|
12122
|
+
}
|
|
12123
|
+
if (pathname !== this.pagePath) {
|
|
12124
|
+
this.pagePath = pathname;
|
|
12125
|
+
this.pageEvents = 0;
|
|
12126
|
+
this.lastEvent = {};
|
|
12127
|
+
}
|
|
12128
|
+
if (this.pageEvents >= PAGE_EVENT_LIMIT) {
|
|
12129
|
+
return;
|
|
12130
|
+
}
|
|
12131
|
+
var key = [click.label, click.ariaLabel, click.icon, click.elementId, click.elementName, click.testId, click.element, click.type].join('|');
|
|
12132
|
+
var last = this.lastEvent[key];
|
|
12133
|
+
if (last !== undefined && t - last < EVENT_DEDUP_MS) {
|
|
12134
|
+
return;
|
|
12135
|
+
}
|
|
12136
|
+
this.lastEvent[key] = t;
|
|
12137
|
+
this.pageEvents++;
|
|
12138
|
+
// Restarts an expired session, as public logEvent() and page views do.
|
|
12139
|
+
this.mpInstance._SessionManager.resetSessionTimer();
|
|
12140
|
+
this.mpInstance._Events.logEvent(buildButtonClickEvent(click, pathname));
|
|
12141
|
+
};
|
|
12142
|
+
ButtonTracker.prototype.emit = function (kind, layer, t, pathname) {
|
|
12143
|
+
if (this.isKitSuppressed()) {
|
|
12144
|
+
return;
|
|
12145
|
+
}
|
|
12146
|
+
if (kind === 'commit' && layer === 'click') {
|
|
12147
|
+
this.lastCommitClick = t;
|
|
12148
|
+
}
|
|
12149
|
+
var last = this.lastSent[kind];
|
|
12150
|
+
if (last && last.pathname === pathname && t - last.t < THROTTLE_MS) {
|
|
12151
|
+
return;
|
|
12152
|
+
}
|
|
12153
|
+
this.lastSent[kind] = {
|
|
12154
|
+
t: t,
|
|
12155
|
+
pathname: pathname
|
|
12156
|
+
};
|
|
12157
|
+
this.mpInstance._RoktManager.notifyCheckoutIntent(Object.freeze({
|
|
12158
|
+
kind: kind,
|
|
12159
|
+
t: t
|
|
12160
|
+
}), pathname);
|
|
12161
|
+
};
|
|
12162
|
+
// Events follow the SDK's opt-out like any other event.
|
|
12163
|
+
ButtonTracker.prototype.eventsEnabled = function () {
|
|
12164
|
+
var store = this.mpInstance._Store;
|
|
12165
|
+
return !!store && store.isEnabled;
|
|
12166
|
+
};
|
|
12167
|
+
// The Rokt manager owns the kit's privacy gates: flag, opt-out, launcher
|
|
12168
|
+
// options and Global Privacy Control.
|
|
12169
|
+
ButtonTracker.prototype.isKitSuppressed = function () {
|
|
12170
|
+
var roktManager = this.mpInstance._RoktManager;
|
|
12171
|
+
return !this.hasRoktKit || !roktManager || roktManager.isCheckoutIntentSuppressed();
|
|
12172
|
+
};
|
|
12173
|
+
ButtonTracker.prototype.log = function (message) {
|
|
12174
|
+
this.mpInstance.Logger.verbose('mParticle button tracking: ' + message);
|
|
12175
|
+
};
|
|
12176
|
+
return ButtonTracker;
|
|
12177
|
+
}();
|
|
12178
|
+
|
|
11390
12179
|
var Messages = Constants.Messages,
|
|
11391
12180
|
HTTPCodes = Constants.HTTPCodes,
|
|
11392
12181
|
FeatureFlags = Constants.FeatureFlags,
|
|
@@ -11520,6 +12309,10 @@ var mParticle = (function () {
|
|
|
11520
12309
|
this.reset = function (instance) {
|
|
11521
12310
|
try {
|
|
11522
12311
|
instance._Persistence.resetPersistence();
|
|
12312
|
+
// The Rokt manager keeps its own store reference, so stop button
|
|
12313
|
+
// tracking here rather than relying on the deleted store.
|
|
12314
|
+
resetButtonTracking();
|
|
12315
|
+
instance._ButtonTracker = undefined;
|
|
11523
12316
|
if (instance._Store) {
|
|
11524
12317
|
delete instance._Store;
|
|
11525
12318
|
}
|
|
@@ -11541,6 +12334,8 @@ var mParticle = (function () {
|
|
|
11541
12334
|
instance._Events.stopTracking();
|
|
11542
12335
|
resetPageViewTracking();
|
|
11543
12336
|
instance._PageViewTracker = undefined;
|
|
12337
|
+
resetButtonTracking();
|
|
12338
|
+
instance._ButtonTracker = undefined;
|
|
11544
12339
|
if (!keepPersistence) {
|
|
11545
12340
|
instance._Persistence.resetPersistence();
|
|
11546
12341
|
}
|
|
@@ -12139,6 +12934,12 @@ var mParticle = (function () {
|
|
|
12139
12934
|
if (queued) return;
|
|
12140
12935
|
self._SessionManager.resetSessionTimer();
|
|
12141
12936
|
self._Store.isEnabled = !isOptingOut;
|
|
12937
|
+
if (isOptingOut) {
|
|
12938
|
+
if (self._ButtonTracker) {
|
|
12939
|
+
self._ButtonTracker.clearPending();
|
|
12940
|
+
}
|
|
12941
|
+
self._RoktManager.discardCheckoutIntents();
|
|
12942
|
+
}
|
|
12142
12943
|
self._Events.logOptOut();
|
|
12143
12944
|
self._Persistence.update();
|
|
12144
12945
|
if (self._Store.activeForwarders.length) {
|
|
@@ -12305,7 +13106,9 @@ var mParticle = (function () {
|
|
|
12305
13106
|
var _a, _b, _c, _d;
|
|
12306
13107
|
var kitBlocker = createKitBlocker(config, mpInstance);
|
|
12307
13108
|
var getFeatureFlag = mpInstance._Helpers.getFeatureFlag;
|
|
12308
|
-
var
|
|
13109
|
+
var _e = Constants.FeatureFlags,
|
|
13110
|
+
AutoLogPageView = _e.AutoLogPageView,
|
|
13111
|
+
AutoButtonTracking = _e.AutoButtonTracking;
|
|
12309
13112
|
// Destroy previous batch uploader to prevent leaked timers and event listeners
|
|
12310
13113
|
if ((_a = mpInstance._APIClient) === null || _a === void 0 ? void 0 : _a.uploader) {
|
|
12311
13114
|
mpInstance._APIClient.uploader.destroy();
|
|
@@ -12321,6 +13124,8 @@ var mParticle = (function () {
|
|
|
12321
13124
|
// persistence or Identity directly.
|
|
12322
13125
|
if (mpInstance._Store.webviewBridgeEnabled) {
|
|
12323
13126
|
mpInstance._NativeSdkHelpers.initializeSessionAttributes(apiKey);
|
|
13127
|
+
// Button tracking never runs as a native passthrough.
|
|
13128
|
+
resetButtonTracking();
|
|
12324
13129
|
} else {
|
|
12325
13130
|
// Main SDK initialization flow
|
|
12326
13131
|
// Load any settings/identities/attributes from cookie or localStorage
|
|
@@ -12400,6 +13205,20 @@ var mParticle = (function () {
|
|
|
12400
13205
|
// so it stops listening. Re-created if the flag is re-enabled later.
|
|
12401
13206
|
mpInstance._PageViewTracker.teardown();
|
|
12402
13207
|
}
|
|
13208
|
+
// Button clicks are logged for any workspace that opts in. Checkout
|
|
13209
|
+
// intent also goes to the Rokt kit when this init configures one.
|
|
13210
|
+
if (getFeatureFlag(AutoButtonTracking)) {
|
|
13211
|
+
if (!mpInstance._ButtonTracker) {
|
|
13212
|
+
mpInstance._ButtonTracker = new ButtonTracker(mpInstance);
|
|
13213
|
+
}
|
|
13214
|
+
mpInstance._ButtonTracker.init({
|
|
13215
|
+
hasRoktKit: !!roktConfig
|
|
13216
|
+
});
|
|
13217
|
+
} else {
|
|
13218
|
+
// Also retires a tracker left by an earlier bundle load. Like APV,
|
|
13219
|
+
// this assumes a single active SDK instance.
|
|
13220
|
+
resetButtonTracking();
|
|
13221
|
+
}
|
|
12403
13222
|
processIdentityCallback(mpInstance, currentUser, currentUserMPID, currentUserIdentities);
|
|
12404
13223
|
}
|
|
12405
13224
|
// We will continue to clear out the ready queue as part of the initial init flow
|
|
@@ -12493,6 +13312,8 @@ var mParticle = (function () {
|
|
|
12493
13312
|
mpInstance._LoggingDispatcher.logger = mpInstance.Logger;
|
|
12494
13313
|
mpInstance._Store = new Store(config, mpInstance, apiKey);
|
|
12495
13314
|
window.mParticle.Store = mpInstance._Store;
|
|
13315
|
+
// Button tracking stops until this init's config decides whether it runs.
|
|
13316
|
+
resetButtonTracking();
|
|
12496
13317
|
mpInstance.Logger.verbose(StartingInitialization);
|
|
12497
13318
|
// Initialize CookieConsentManager with privacy flags from launcherOptions
|
|
12498
13319
|
var _a = normalizeRoktLauncherOptions(config === null || config === void 0 ? void 0 : config.launcherOptions),
|