@mparticle/web-sdk 3.23.0 → 3.24.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/dist/mparticle.common.js +1 -1688
- package/dist/mparticle.esm.js +1 -1688
- package/dist/mparticle.js +706 -197
- package/dist/types/src/buttonTracking/buttonClickEvent.d.ts +4 -0
- package/dist/types/src/buttonTracking/buttonDescription.d.ts +2 -2
- package/dist/types/src/buttonTracking/buttonTracker.d.ts +2 -0
- package/dist/types/src/buttonTracking/checkoutIntent.d.ts +2 -2
- package/dist/types/src/buttonTracking/controls.d.ts +6 -3
- package/dist/types/src/buttonTracking/labelMasking.d.ts +1 -0
- package/dist/types/src/configAPIClient.d.ts +1 -0
- package/dist/types/src/forwarder-utils.d.ts +3 -0
- package/dist/types/src/pageViewTracker.d.ts +19 -3
- package/dist/types/src/roktManager.d.ts +3 -1
- package/dist/types/src/sdkRuntimeModels.d.ts +1 -0
- package/dist/types/src/store.d.ts +2 -0
- package/package.json +21 -14
- package/src/buttonTracking/buttonClickEvent.ts +76 -1
- package/src/buttonTracking/buttonDescription.ts +236 -46
- package/src/buttonTracking/buttonTracker.ts +122 -7
- package/src/buttonTracking/checkoutIntent.ts +51 -9
- package/src/buttonTracking/controls.ts +160 -50
- package/src/buttonTracking/labelMasking.ts +4 -1
- package/src/configAPIClient.ts +1 -0
- package/src/events.ts +9 -3
- package/src/forwarder-utils.ts +38 -0
- package/src/forwarders.ts +2 -28
- package/src/mp-instance.ts +18 -2
- package/src/pageUrl.ts +43 -2
- package/src/pageViewTracker.ts +193 -111
- package/src/roktManager.ts +19 -4
- package/src/sdkRuntimeModels.ts +1 -0
- package/src/store.ts +14 -0
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.24.0";
|
|
208
208
|
|
|
209
209
|
var Constants = {
|
|
210
210
|
sdkVersion: version,
|
|
@@ -315,7 +315,6 @@ var mParticle = (function () {
|
|
|
315
315
|
aliasMaxWindow: 90,
|
|
316
316
|
uploadInterval: 0 // Maximum milliseconds in between batch uploads, below 500 will mean immediate upload. The server returns this as a string, but we are using it as a number internally
|
|
317
317
|
},
|
|
318
|
-
|
|
319
318
|
DefaultBaseUrls: {
|
|
320
319
|
v1SecureServiceUrl: 'jssdks.mparticle.com/v1/JS/',
|
|
321
320
|
v2SecureServiceUrl: 'jssdks.mparticle.com/v2/JS/',
|
|
@@ -601,14 +600,13 @@ var mParticle = (function () {
|
|
|
601
600
|
// @ts-ignore
|
|
602
601
|
randomValue = window.crypto.getRandomValues(new Uint8Array(1)); // eslint-disable-line no-undef
|
|
603
602
|
}
|
|
604
|
-
|
|
605
603
|
if (randomValue) {
|
|
606
604
|
// @ts-ignore
|
|
607
605
|
return (a ^ randomValue[0] % 16 >> a / 4).toString(16);
|
|
608
606
|
}
|
|
609
607
|
return (a ^ Math.random() * 16 >> a / 4).toString(16);
|
|
610
608
|
};
|
|
611
|
-
var
|
|
609
|
+
var _generateUniqueId = function generateUniqueId(a) {
|
|
612
610
|
// https://gist.github.com/jed/982883
|
|
613
611
|
// Added support for crypto for better random
|
|
614
612
|
if (a === void 0) {
|
|
@@ -624,7 +622,7 @@ var mParticle = (function () {
|
|
|
624
622
|
// -1e11 -> //-100000000000,
|
|
625
623
|
"".concat(1e7, "-").concat(1e3, "-").concat(4e3, "-").concat(8e3, "-").concat(1e11).replace(/[018]/g,
|
|
626
624
|
// zeroes, ones, and eights with
|
|
627
|
-
|
|
625
|
+
_generateUniqueId // random hex digits
|
|
628
626
|
);
|
|
629
627
|
};
|
|
630
628
|
/**
|
|
@@ -930,7 +928,7 @@ var mParticle = (function () {
|
|
|
930
928
|
* obfuscateData({ tags: ['premium', 'verified'] })
|
|
931
929
|
* // Returns: { tags: ['string', 'string'] }
|
|
932
930
|
*/
|
|
933
|
-
var
|
|
931
|
+
var _obfuscateData = function obfuscateData(value) {
|
|
934
932
|
// Preserve null and undefined
|
|
935
933
|
if (value === null || value === undefined) {
|
|
936
934
|
return value;
|
|
@@ -938,7 +936,7 @@ var mParticle = (function () {
|
|
|
938
936
|
// Handle arrays - recursively obfuscate each element
|
|
939
937
|
if (Array.isArray(value)) {
|
|
940
938
|
return value.map(function (item) {
|
|
941
|
-
return
|
|
939
|
+
return _obfuscateData(item);
|
|
942
940
|
});
|
|
943
941
|
}
|
|
944
942
|
// Handle objects - recursively obfuscate each property
|
|
@@ -946,7 +944,7 @@ var mParticle = (function () {
|
|
|
946
944
|
var obfuscated = {};
|
|
947
945
|
for (var key in value) {
|
|
948
946
|
if (value.hasOwnProperty(key)) {
|
|
949
|
-
obfuscated[key] =
|
|
947
|
+
obfuscated[key] = _obfuscateData(value[key]);
|
|
950
948
|
}
|
|
951
949
|
}
|
|
952
950
|
return obfuscated;
|
|
@@ -970,7 +968,7 @@ var mParticle = (function () {
|
|
|
970
968
|
* // Returns: { email: 'string' }
|
|
971
969
|
*/
|
|
972
970
|
var obfuscateDevData = function obfuscateDevData(data, isDevelopmentMode) {
|
|
973
|
-
return isDevelopmentMode ? data :
|
|
971
|
+
return isDevelopmentMode ? data : _obfuscateData(data);
|
|
974
972
|
};
|
|
975
973
|
// Standalone version of jQuery.extend, from https://github.com/dansdom/extend
|
|
976
974
|
// https://go.mparticle.com/work/SQDSDKS-6047
|
|
@@ -2183,7 +2181,7 @@ var mParticle = (function () {
|
|
|
2183
2181
|
application_info: {
|
|
2184
2182
|
application_version: lastEvent.AppVersion,
|
|
2185
2183
|
application_name: lastEvent.AppName,
|
|
2186
|
-
|
|
2184
|
+
package: lastEvent.Package,
|
|
2187
2185
|
sideloaded_kits_count: mpInstance._Store.sideloadedKitsCount
|
|
2188
2186
|
},
|
|
2189
2187
|
device_info: {
|
|
@@ -2523,7 +2521,6 @@ var mParticle = (function () {
|
|
|
2523
2521
|
//note: External Events DTO does not support the session mpids array as of this time.
|
|
2524
2522
|
//spanning_mpids: sdkEvent.SessionMpids
|
|
2525
2523
|
};
|
|
2526
|
-
|
|
2527
2524
|
sessionEndEventData = Object.assign(sessionEndEventData, commonEventData);
|
|
2528
2525
|
return {
|
|
2529
2526
|
event_type: dist.EventTypeEnum.sessionEnd,
|
|
@@ -2652,7 +2649,7 @@ var mParticle = (function () {
|
|
|
2652
2649
|
var commonEventData = convertBaseEventData(sdkEvent);
|
|
2653
2650
|
var userAttributeChangeEvent = {
|
|
2654
2651
|
user_attribute_name: sdkEvent.UserAttributeChanges.UserAttributeName,
|
|
2655
|
-
|
|
2652
|
+
new: sdkEvent.UserAttributeChanges.New,
|
|
2656
2653
|
old: sdkEvent.UserAttributeChanges.Old,
|
|
2657
2654
|
deleted: sdkEvent.UserAttributeChanges.Deleted,
|
|
2658
2655
|
is_new_attribute: sdkEvent.UserAttributeChanges.IsNewAttribute
|
|
@@ -2666,7 +2663,7 @@ var mParticle = (function () {
|
|
|
2666
2663
|
function convertUserIdentityChangeEvent(sdkEvent) {
|
|
2667
2664
|
var commonEventData = convertBaseEventData(sdkEvent);
|
|
2668
2665
|
var userIdentityChangeEvent = {
|
|
2669
|
-
|
|
2666
|
+
new: {
|
|
2670
2667
|
identity_type: convertUserIdentityTypeToServerIdentityType(sdkEvent.UserIdentityChanges.New.IdentityType),
|
|
2671
2668
|
identity: sdkEvent.UserIdentityChanges.New.Identity || null,
|
|
2672
2669
|
timestamp_unixtime_ms: sdkEvent.Timestamp,
|
|
@@ -2886,23 +2883,9 @@ var mParticle = (function () {
|
|
|
2886
2883
|
// Nothing has to be deleted, and there is no ordering in which half the state
|
|
2887
2884
|
// survives the reset.
|
|
2888
2885
|
var WIN_APV_KEY = '__mpApv__';
|
|
2889
|
-
//
|
|
2890
|
-
//
|
|
2891
|
-
// none of those should reach the event stream because someone forgot to exclude
|
|
2892
|
-
// them. Anything absent from this list is dropped.
|
|
2893
|
-
//
|
|
2894
|
-
// SECURITY: `code`, `state` and `nonce` are the OAuth 2.0 / OIDC authorization
|
|
2895
|
-
// code and the CSRF/replay tokens. They are credentials until redeemed, and
|
|
2896
|
-
// attaching them here persists them in the event store and forwards them to
|
|
2897
|
-
// every configured kit. They are on the list by explicit product decision \u2014
|
|
2898
|
-
// deleting that line is the whole of the fix if that decision is revisited.
|
|
2886
|
+
// Parameters that identify which page a view is of. Authorization-flow and campaign
|
|
2887
|
+
// attribution parameters are deliberately absent; an input can add them explicitly.
|
|
2899
2888
|
var ALLOWED_QUERY_PARAMS = [
|
|
2900
|
-
// Campaign attribution
|
|
2901
|
-
'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_id', 'utm_source_platform', 'utm_creative_format', 'utm_marketing_tactic', 'gad_source', 'gad_campaignid', 'mc_cid',
|
|
2902
|
-
// Ad-network click ids
|
|
2903
|
-
'gclid', 'gbraid', 'wbraid', 'fbclid', 'msclkid', 'ttclid', 'twclid', 'li_fat_id', 'dclid', 'srsltid', 'epik', 'ScCid', 'irclickid', 'rtid', 'rclid',
|
|
2904
|
-
// OAuth / OIDC \u2014 see the SECURITY note above
|
|
2905
|
-
'client_id', 'redirect_uri', 'response_type', 'scope', 'state', 'code', 'nonce',
|
|
2906
2889
|
// Pagination
|
|
2907
2890
|
'page', 'limit', 'offset', 'cursor', 'per_page',
|
|
2908
2891
|
// Site search term, as the common platforms spell it. Matching is
|
|
@@ -2916,6 +2899,22 @@ var mParticle = (function () {
|
|
|
2916
2899
|
'category',
|
|
2917
2900
|
// Referral
|
|
2918
2901
|
'ref', 'referrer'];
|
|
2902
|
+
var AUTO_PAGE_VIEW_ATTRIBUTE = 'is_auto_page_view';
|
|
2903
|
+
var CORE_EVENT_FIELDS = ['hostname', 'title', 'path', AUTO_PAGE_VIEW_ATTRIBUTE];
|
|
2904
|
+
var PROTOTYPE_MEMBER_NAMES = ['constructor', '__proto__', 'prototype'];
|
|
2905
|
+
// Server flag values are the strings 'True' and 'False', so these get typed here by habit.
|
|
2906
|
+
var FLAG_VALUE_NAMES = ['true', 'false'];
|
|
2907
|
+
var RESERVED_QUERY_PARAMS = CORE_EVENT_FIELDS.concat(PROTOTYPE_MEMBER_NAMES, FLAG_VALUE_NAMES);
|
|
2908
|
+
var BUILT_IN_NAMES = ALLOWED_QUERY_PARAMS.map(function (name) {
|
|
2909
|
+
return name.toLowerCase();
|
|
2910
|
+
});
|
|
2911
|
+
var isBuiltIn = function isBuiltIn(name) {
|
|
2912
|
+
return BUILT_IN_NAMES.indexOf(name.toLowerCase()) !== -1;
|
|
2913
|
+
};
|
|
2914
|
+
// Excludes `&`, `=`, `%` and whitespace, which would distort the dedup key.
|
|
2915
|
+
var QUERY_PARAM_NAME = /^[a-z0-9_][a-z0-9_.-]{0,63}$/;
|
|
2916
|
+
var MAX_CUSTOM_QUERY_PARAMS = 25;
|
|
2917
|
+
var MAX_CUSTOM_QUERY_PARAM_VALUE_LENGTH = 512;
|
|
2919
2918
|
// Params matched by name prefix, for platforms that put a per-product id in the
|
|
2920
2919
|
// name. Salesforce Commerce Cloud spells a variant choice
|
|
2921
2920
|
// `dwvar_<productId>_<attribute>`, e.g. `dwvar_12345_color=navy`.
|
|
@@ -2925,34 +2924,113 @@ var mParticle = (function () {
|
|
|
2925
2924
|
return name.indexOf(prefix) === 0;
|
|
2926
2925
|
});
|
|
2927
2926
|
};
|
|
2928
|
-
|
|
2929
|
-
return name.toLowerCase();
|
|
2930
|
-
});
|
|
2931
|
-
// Whether a query param name, in any case, is one an auto page view captures.
|
|
2927
|
+
// Whether a query param name, in any case, is one an auto page view captures by default.
|
|
2932
2928
|
var isAllowedQueryParam = function isAllowedQueryParam(name) {
|
|
2933
|
-
|
|
2934
|
-
return ALLOWED_QUERY_PARAMS_LOWER.indexOf(lower) !== -1 || hasAllowedPrefix(lower);
|
|
2929
|
+
return isBuiltIn(name) || hasAllowedPrefix(name.toLowerCase());
|
|
2935
2930
|
};
|
|
2936
2931
|
// ---------------------------------------------------------------------------
|
|
2937
2932
|
// Pure helpers. No `this`, no globals, no side effects \u2014 callable and assertable
|
|
2938
2933
|
// on their own, which is where the interesting rules live.
|
|
2939
2934
|
// ---------------------------------------------------------------------------
|
|
2940
|
-
//
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2935
|
+
// Validated here as well as in the dashboard, because remote config is untrusted input.
|
|
2936
|
+
var parseQueryParamAllowlist = function parseQueryParamAllowlist(configured) {
|
|
2937
|
+
var allowed = [];
|
|
2938
|
+
var rejectedPositions = [];
|
|
2939
|
+
var overLimit = 0;
|
|
2940
|
+
if (!configured) {
|
|
2941
|
+
return {
|
|
2942
|
+
allowed: allowed,
|
|
2943
|
+
rejectedPositions: rejectedPositions,
|
|
2944
|
+
overLimit: overLimit
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
var entries = Array.isArray(configured) ? configured : String(configured).split(',');
|
|
2948
|
+
entries.forEach(function (entry, index) {
|
|
2949
|
+
var position = index + 1;
|
|
2950
|
+
var name = String(entry).trim().toLowerCase();
|
|
2951
|
+
if (!name) {
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
if (!QUERY_PARAM_NAME.test(name) || RESERVED_QUERY_PARAMS.indexOf(name) !== -1) {
|
|
2955
|
+
rejectedPositions.push(position);
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
if (isBuiltIn(name) || allowed.indexOf(name) !== -1) {
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2961
|
+
if (allowed.length >= MAX_CUSTOM_QUERY_PARAMS) {
|
|
2962
|
+
overLimit++;
|
|
2963
|
+
return;
|
|
2964
|
+
}
|
|
2965
|
+
allowed.push(name);
|
|
2966
|
+
});
|
|
2967
|
+
return {
|
|
2968
|
+
allowed: allowed,
|
|
2969
|
+
rejectedPositions: rejectedPositions,
|
|
2970
|
+
overLimit: overLimit
|
|
2971
|
+
};
|
|
2972
|
+
};
|
|
2973
|
+
// Not localeCompare: this order feeds the dedup key, so it must be identical in every
|
|
2974
|
+
// browser, and localeCompare is locale-dependent.
|
|
2975
|
+
var byName = function byName(a, b) {
|
|
2976
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
2977
|
+
};
|
|
2978
|
+
// Built-ins keep their positions and additions sort after them, so a dedup key depends on
|
|
2979
|
+
// which params are configured, not the order they were typed.
|
|
2980
|
+
var effectiveAllowlist = function effectiveAllowlist(extras) {
|
|
2981
|
+
if (extras === void 0) {
|
|
2982
|
+
extras = [];
|
|
2983
|
+
}
|
|
2984
|
+
return ALLOWED_QUERY_PARAMS.concat(extras.filter(function (name) {
|
|
2985
|
+
return !isBuiltIn(name);
|
|
2986
|
+
}).sort(byName));
|
|
2987
|
+
};
|
|
2988
|
+
// Over-long custom values are dropped rather than truncated: two truncated values could
|
|
2989
|
+
// share a dedup key and swallow a real page view.
|
|
2990
|
+
var isWithinValueLimit = function isWithinValueLimit(name, value) {
|
|
2991
|
+
return isBuiltIn(name) || value.length <= MAX_CUSTOM_QUERY_PARAM_VALUE_LENGTH;
|
|
2992
|
+
};
|
|
2993
|
+
// A prefix match is a platform name, not a configured extra, so the custom
|
|
2994
|
+
// value limit does not apply. A name already on the allowlist keeps that
|
|
2995
|
+
// position instead of being appended again.
|
|
2996
|
+
var prefixMatchedParams = function prefixMatchedParams(href, allowlist) {
|
|
2947
2997
|
var allParams = queryStringParser(href);
|
|
2948
|
-
Object.keys(allParams).filter(function (name) {
|
|
2949
|
-
return hasAllowedPrefix(name) && allParams[name];
|
|
2950
|
-
}).
|
|
2951
|
-
|
|
2998
|
+
return Object.keys(allParams).filter(function (name) {
|
|
2999
|
+
return hasAllowedPrefix(name) && allParams[name] && allowlist.indexOf(name) === -1;
|
|
3000
|
+
}).sort(byName).map(function (name) {
|
|
3001
|
+
return {
|
|
3002
|
+
name: name,
|
|
3003
|
+
value: allParams[name]
|
|
3004
|
+
};
|
|
2952
3005
|
});
|
|
2953
|
-
return params;
|
|
2954
3006
|
};
|
|
2955
|
-
var
|
|
3007
|
+
var allowedQueryParams = function allowedQueryParams(href, extras) {
|
|
3008
|
+
if (extras === void 0) {
|
|
3009
|
+
extras = [];
|
|
3010
|
+
}
|
|
3011
|
+
var allowlist = effectiveAllowlist(extras);
|
|
3012
|
+
var found = queryStringParser(href, allowlist);
|
|
3013
|
+
var listed = allowlist.filter(function (name) {
|
|
3014
|
+
return hasOwnProp(found, name);
|
|
3015
|
+
}).filter(function (name) {
|
|
3016
|
+
return isWithinValueLimit(name, found[name]);
|
|
3017
|
+
}).map(function (name) {
|
|
3018
|
+
return {
|
|
3019
|
+
name: name,
|
|
3020
|
+
value: found[name]
|
|
3021
|
+
};
|
|
3022
|
+
});
|
|
3023
|
+
return listed.concat(prefixMatchedParams(href, allowlist));
|
|
3024
|
+
};
|
|
3025
|
+
var paramsToAttributes = function paramsToAttributes(params) {
|
|
3026
|
+
var attributes = {};
|
|
3027
|
+
params.forEach(function (_a) {
|
|
3028
|
+
var name = _a.name,
|
|
3029
|
+
value = _a.value;
|
|
3030
|
+
attributes[name] = value;
|
|
3031
|
+
});
|
|
3032
|
+
return attributes;
|
|
3033
|
+
};
|
|
2956
3034
|
// Absent rather than false on a manual page view, so the attribute is only ever
|
|
2957
3035
|
// added by the automatic emitters. Shared by both of them so the key and that
|
|
2958
3036
|
// rule have one definition instead of two that can drift.
|
|
@@ -2960,33 +3038,13 @@ var mParticle = (function () {
|
|
|
2960
3038
|
var _a;
|
|
2961
3039
|
return isAutoPageView ? (_a = {}, _a[AUTO_PAGE_VIEW_ATTRIBUTE] = true, _a) : {};
|
|
2962
3040
|
};
|
|
2963
|
-
//
|
|
2964
|
-
//
|
|
2965
|
-
// string cannot produce a different key.
|
|
2966
|
-
//
|
|
2967
|
-
// Membership is an own-property check, not `in`. `in` walks the prototype
|
|
2968
|
-
// chain, so a name matching an Object.prototype member reports as present on
|
|
2969
|
-
// any plain object. ALLOWED_QUERY_PARAMS contains no such name, which was the
|
|
2970
|
-
// only thing making `in` safe here \u2014 and it stops being a safe assumption the
|
|
2971
|
-
// moment this list can be extended from configuration.
|
|
2972
|
-
var capturedNames = function capturedNames(params) {
|
|
2973
|
-
return ALLOWED_QUERY_PARAMS.filter(function (name) {
|
|
2974
|
-
return hasOwnProp(params, name);
|
|
2975
|
-
}).concat(Object.keys(params).filter(hasAllowedPrefix).sort());
|
|
2976
|
-
};
|
|
2977
|
-
// The dedup key: pathname plus the allowlisted params in a fixed order, so that
|
|
2978
|
-
// reordering the query string is not a new page. Params outside the allowlist
|
|
2979
|
-
// never make it into `page.params` and so cannot key a view \u2014 nor can the hash.
|
|
2980
|
-
//
|
|
2981
|
-
// Values are re-encoded because queryStringParser hands them back DECODED. A
|
|
2982
|
-
// value holding the pair delimiters would otherwise serialize exactly like two
|
|
2983
|
-
// separate params \u2014 `{q: 'a&search=b'}` and `{q: 'a', search: 'b'}` both becoming
|
|
2984
|
-
// `q=a&search=b` \u2014 and dedup would treat a real navigation between them as the
|
|
2985
|
-
// same page and drop the view. `q`, `search` and `redirect_uri` carry `&` and `=`
|
|
2986
|
-
// routinely, so this is reachable rather than theoretical.
|
|
3041
|
+
// Values are re-encoded because they arrive decoded: `{q: 'a&search=b'}` would otherwise
|
|
3042
|
+
// key the same as `{q: 'a', search: 'b'}` and dedup a real navigation away.
|
|
2987
3043
|
var pageKey = function pageKey(page) {
|
|
2988
|
-
var query =
|
|
2989
|
-
|
|
3044
|
+
var query = page.params.map(function (_a) {
|
|
3045
|
+
var name = _a.name,
|
|
3046
|
+
value = _a.value;
|
|
3047
|
+
return "".concat(name, "=").concat(encodeURIComponent(value));
|
|
2990
3048
|
}).join('&');
|
|
2991
3049
|
return query ? "".concat(page.path, "?").concat(query) : page.path;
|
|
2992
3050
|
};
|
|
@@ -3011,7 +3069,7 @@ var mParticle = (function () {
|
|
|
3011
3069
|
// Params spread first, then the core fields by name, so a core field always
|
|
3012
3070
|
// wins. No allowlist entry collides with hostname/title/path today; naming
|
|
3013
3071
|
// them here is what keeps a later addition from silently overwriting one.
|
|
3014
|
-
data: __assign(__assign(__assign({}, params), {
|
|
3072
|
+
data: __assign(__assign(__assign({}, paramsToAttributes(params)), {
|
|
3015
3073
|
hostname: hostname,
|
|
3016
3074
|
title: title,
|
|
3017
3075
|
path: path
|
|
@@ -3090,10 +3148,10 @@ var mParticle = (function () {
|
|
|
3090
3148
|
state.tracker = undefined;
|
|
3091
3149
|
}
|
|
3092
3150
|
};
|
|
3093
|
-
var currentPage = function currentPage() {
|
|
3151
|
+
var currentPage = function currentPage(extras) {
|
|
3094
3152
|
return {
|
|
3095
3153
|
path: window.location.pathname,
|
|
3096
|
-
params: allowedQueryParams(getHref())
|
|
3154
|
+
params: allowedQueryParams(getHref(), extras)
|
|
3097
3155
|
};
|
|
3098
3156
|
};
|
|
3099
3157
|
// Log-safe description of a page: the path, plus the NAMES of the captured
|
|
@@ -3106,7 +3164,10 @@ var mParticle = (function () {
|
|
|
3106
3164
|
if (!page) {
|
|
3107
3165
|
return '';
|
|
3108
3166
|
}
|
|
3109
|
-
var names =
|
|
3167
|
+
var names = page.params.map(function (_a) {
|
|
3168
|
+
var name = _a.name;
|
|
3169
|
+
return name;
|
|
3170
|
+
});
|
|
3110
3171
|
return names.length ? "".concat(page.path, " (params: ").concat(names.join(), ")") : page.path;
|
|
3111
3172
|
};
|
|
3112
3173
|
// ---------------------------------------------------------------------------
|
|
@@ -3119,6 +3180,7 @@ var mParticle = (function () {
|
|
|
3119
3180
|
function PageViewTracker(mpInstance, options) {
|
|
3120
3181
|
this.lastPage = null;
|
|
3121
3182
|
this.active = false;
|
|
3183
|
+
this.customQueryParams = [];
|
|
3122
3184
|
this.pendingNavigations = [];
|
|
3123
3185
|
this.undoHistoryPatch = null;
|
|
3124
3186
|
this.mpInstance = mpInstance;
|
|
@@ -3153,7 +3215,8 @@ var mParticle = (function () {
|
|
|
3153
3215
|
inheritedPages = inheritedPages.concat(this.retire(this));
|
|
3154
3216
|
}
|
|
3155
3217
|
this.active = true;
|
|
3156
|
-
this.
|
|
3218
|
+
this.customQueryParams = this.readCustomQueryParams();
|
|
3219
|
+
this.lastPage = currentPage(this.customQueryParams);
|
|
3157
3220
|
this.log("[init] seeded lastPage: ".concat(describePage(this.lastPage)));
|
|
3158
3221
|
this.undoHistoryPatch = subscribeToRouteChange('pageView', function (source) {
|
|
3159
3222
|
return _this.safeHandleNavigation(source);
|
|
@@ -3178,6 +3241,25 @@ var mParticle = (function () {
|
|
|
3178
3241
|
this.active = false;
|
|
3179
3242
|
clearActiveTracker(this);
|
|
3180
3243
|
};
|
|
3244
|
+
PageViewTracker.prototype.readCustomQueryParams = function () {
|
|
3245
|
+
var _a = this.mpInstance._Store.SDKConfig.autoLogPageViewQueryParams || {},
|
|
3246
|
+
_b = _a.allowed,
|
|
3247
|
+
allowed = _b === void 0 ? [] : _b,
|
|
3248
|
+
_c = _a.rejectedPositions,
|
|
3249
|
+
rejectedPositions = _c === void 0 ? [] : _c,
|
|
3250
|
+
_d = _a.overLimit,
|
|
3251
|
+
overLimit = _d === void 0 ? 0 : _d;
|
|
3252
|
+
if (rejectedPositions.length) {
|
|
3253
|
+
this.mpInstance.Logger.warning('mParticle APV: ignoring invalid additional page view query ' + "parameters at positions ".concat(rejectedPositions.join(', ')));
|
|
3254
|
+
}
|
|
3255
|
+
if (overLimit) {
|
|
3256
|
+
this.mpInstance.Logger.warning("mParticle APV: ignoring ".concat(overLimit, " additional page view ") + "query parameters beyond the limit of " + "".concat(MAX_CUSTOM_QUERY_PARAMS));
|
|
3257
|
+
}
|
|
3258
|
+
if (allowed.length) {
|
|
3259
|
+
this.log("[init] additional query params: ".concat(allowed.join()));
|
|
3260
|
+
}
|
|
3261
|
+
return allowed;
|
|
3262
|
+
};
|
|
3181
3263
|
// Stops the outgoing tracker and returns the pages it had queued so this
|
|
3182
3264
|
// tracker can re-schedule them once it is active. Private, but reachable
|
|
3183
3265
|
// across instances: the handoff protocol stays internal to the class.
|
|
@@ -3219,7 +3301,7 @@ var mParticle = (function () {
|
|
|
3219
3301
|
}
|
|
3220
3302
|
};
|
|
3221
3303
|
PageViewTracker.prototype.handleNavigation = function (source) {
|
|
3222
|
-
var candidate = currentPage();
|
|
3304
|
+
var candidate = currentPage(this.customQueryParams);
|
|
3223
3305
|
var lastKey = this.lastPage ? pageKey(this.lastPage) : null;
|
|
3224
3306
|
this.log("[detect] navigation signal (source: ".concat(source, ", candidate: ").concat(describePage(candidate), ", last: ").concat(describePage(this.lastPage), ")"));
|
|
3225
3307
|
if (!isNewPage(lastKey, pageKey(candidate))) {
|
|
@@ -3271,11 +3353,21 @@ var mParticle = (function () {
|
|
|
3271
3353
|
|
|
3272
3354
|
// The one rule for a URL that leaves the SDK, shared with kits through
|
|
3273
3355
|
// `mParticle._cleanUrl`. `path` keeps scheme, host and path. `full` adds the
|
|
3274
|
-
// query params an auto page view
|
|
3356
|
+
// query params an auto page view captures by default and the campaign
|
|
3357
|
+
// attribution params, then the fragment.
|
|
3275
3358
|
var UrlMode = {
|
|
3276
3359
|
Path: 'path',
|
|
3277
3360
|
Full: 'full'
|
|
3278
3361
|
};
|
|
3362
|
+
// Campaign attribution and ad-network click ids. URLs keep them; an auto page view
|
|
3363
|
+
// carries one as an attribute only when an input configures it.
|
|
3364
|
+
var URL_ATTRIBUTION_QUERY_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_id', 'utm_source_platform', 'utm_creative_format', 'utm_marketing_tactic', 'gad_source', 'gad_campaignid', 'mc_cid', 'gclid', 'gbraid', 'wbraid', 'fbclid', 'msclkid', 'ttclid', 'twclid', 'li_fat_id', 'dclid', 'srsltid', 'epik', 'ScCid', 'irclickid', 'rtid', 'rclid'];
|
|
3365
|
+
var URL_ATTRIBUTION_QUERY_PARAMS_LOWER = URL_ATTRIBUTION_QUERY_PARAMS.map(function (name) {
|
|
3366
|
+
return name.toLowerCase();
|
|
3367
|
+
});
|
|
3368
|
+
var isUrlQueryParam = function isUrlQueryParam(name) {
|
|
3369
|
+
return isAllowedQueryParam(name) || URL_ATTRIBUTION_QUERY_PARAMS_LOWER.indexOf(name.toLowerCase()) !== -1;
|
|
3370
|
+
};
|
|
3279
3371
|
// OAuth 2.0 / OIDC authorization-flow params. They stay off URLs even where the
|
|
3280
3372
|
// page view allowlist admits them, because a URL travels on every event.
|
|
3281
3373
|
var EXCLUDED_URL_QUERY_PARAMS = ['client_id', 'redirect_uri', 'response_type', 'scope', 'state', 'code', 'nonce'];
|
|
@@ -3297,7 +3389,7 @@ var mParticle = (function () {
|
|
|
3297
3389
|
return null;
|
|
3298
3390
|
}
|
|
3299
3391
|
var lowerName = name.toLowerCase();
|
|
3300
|
-
if (!name || !
|
|
3392
|
+
if (!name || !isUrlQueryParam(name) || EXCLUDED_URL_QUERY_PARAMS.indexOf(lowerName) !== -1) {
|
|
3301
3393
|
return null;
|
|
3302
3394
|
}
|
|
3303
3395
|
if (separator === -1 || URL_VALUED_QUERY_PARAMS.indexOf(lowerName) === -1) {
|
|
@@ -3745,7 +3837,7 @@ var mParticle = (function () {
|
|
|
3745
3837
|
var event = {
|
|
3746
3838
|
AppName: SDKConfig.appName,
|
|
3747
3839
|
AppVersion: SDKConfig.appVersion,
|
|
3748
|
-
Package: SDKConfig
|
|
3840
|
+
Package: SDKConfig.package,
|
|
3749
3841
|
EventDataType: MessageType$1.AppStateTransition,
|
|
3750
3842
|
Timestamp: now,
|
|
3751
3843
|
SessionId: sessionId,
|
|
@@ -3976,7 +4068,7 @@ var mParticle = (function () {
|
|
|
3976
4068
|
});
|
|
3977
4069
|
batchesToUpload.forEach(function (batch) {
|
|
3978
4070
|
if (retriedIds.indexOf(batch.source_request_id) === -1) {
|
|
3979
|
-
_this_1.batchOrder
|
|
4071
|
+
_this_1.batchOrder.delete(batch.source_request_id);
|
|
3980
4072
|
}
|
|
3981
4073
|
});
|
|
3982
4074
|
// Batches that do not successfully upload are added back to the process queue
|
|
@@ -4003,7 +4095,6 @@ var mParticle = (function () {
|
|
|
4003
4095
|
});
|
|
4004
4096
|
});
|
|
4005
4097
|
};
|
|
4006
|
-
|
|
4007
4098
|
BatchUploader.prototype.storeBatchesQueuedForProcessing = function () {
|
|
4008
4099
|
var _this_1 = this;
|
|
4009
4100
|
var _a;
|
|
@@ -4271,7 +4362,6 @@ var mParticle = (function () {
|
|
|
4271
4362
|
logger.error('search called without a callback function; skipping request.');
|
|
4272
4363
|
return [2 /*return*/];
|
|
4273
4364
|
}
|
|
4274
|
-
|
|
4275
4365
|
safeInvoke = function safeInvoke(result) {
|
|
4276
4366
|
try {
|
|
4277
4367
|
callback(result);
|
|
@@ -4301,7 +4391,6 @@ var mParticle = (function () {
|
|
|
4301
4391
|
});
|
|
4302
4392
|
return [2 /*return*/];
|
|
4303
4393
|
}
|
|
4304
|
-
|
|
4305
4394
|
_a.label = 1;
|
|
4306
4395
|
case 1:
|
|
4307
4396
|
_a.trys.push([1, 9,, 10]);
|
|
@@ -4604,7 +4693,7 @@ var mParticle = (function () {
|
|
|
4604
4693
|
sdk_version: Constants.sdkVersion
|
|
4605
4694
|
},
|
|
4606
4695
|
environment: environment,
|
|
4607
|
-
request_id:
|
|
4696
|
+
request_id: _generateUniqueId(),
|
|
4608
4697
|
request_timestamp_ms: Date.now()
|
|
4609
4698
|
};
|
|
4610
4699
|
};
|
|
@@ -5058,7 +5147,7 @@ var mParticle = (function () {
|
|
|
5058
5147
|
this.decoded = decoded;
|
|
5059
5148
|
this.parseStringOrNumber = parseStringOrNumber;
|
|
5060
5149
|
this.generateHash = generateHash;
|
|
5061
|
-
this.generateUniqueId =
|
|
5150
|
+
this.generateUniqueId = _generateUniqueId;
|
|
5062
5151
|
// Imported Validators
|
|
5063
5152
|
this.Validators = Validators;
|
|
5064
5153
|
}
|
|
@@ -6144,6 +6233,7 @@ var mParticle = (function () {
|
|
|
6144
6233
|
this.SDKConfig.flags = {};
|
|
6145
6234
|
}
|
|
6146
6235
|
this.SDKConfig.flags = processFlags(config);
|
|
6236
|
+
this.SDKConfig.autoLogPageViewQueryParams = parseQueryParamAllowlist(config.autoLogPageViewQueryParams);
|
|
6147
6237
|
if (config.deviceId) {
|
|
6148
6238
|
this.deviceId = config.deviceId;
|
|
6149
6239
|
}
|
|
@@ -6178,7 +6268,7 @@ var mParticle = (function () {
|
|
|
6178
6268
|
this.SDKConfig.appName = config.appName;
|
|
6179
6269
|
}
|
|
6180
6270
|
if (config.hasOwnProperty('package')) {
|
|
6181
|
-
this.SDKConfig
|
|
6271
|
+
this.SDKConfig.package = config.package;
|
|
6182
6272
|
}
|
|
6183
6273
|
if (config.hasOwnProperty('integrationDelayTimeout')) {
|
|
6184
6274
|
this.SDKConfig.integrationDelayTimeout = config.integrationDelayTimeout;
|
|
@@ -6420,6 +6510,9 @@ var mParticle = (function () {
|
|
|
6420
6510
|
if (config.flags) {
|
|
6421
6511
|
_this.SDKConfig.flags = processFlags(config);
|
|
6422
6512
|
}
|
|
6513
|
+
// Stored whole: the tracker reports the rejections, and cannot recover them by
|
|
6514
|
+
// re-parsing a list that has already had them removed.
|
|
6515
|
+
_this.SDKConfig.autoLogPageViewQueryParams = parseQueryParamAllowlist(config.autoLogPageViewQueryParams);
|
|
6423
6516
|
var baseUrls = processBaseUrls(config, _this.SDKConfig.flags, apiKey);
|
|
6424
6517
|
for (var baseUrlKeys in baseUrls) {
|
|
6425
6518
|
_this.SDKConfig[baseUrlKeys] = baseUrls[baseUrlKeys];
|
|
@@ -7438,13 +7531,13 @@ var mParticle = (function () {
|
|
|
7438
7531
|
};
|
|
7439
7532
|
// The auto page view for the landing page. The SPA navigations that follow it
|
|
7440
7533
|
// come from PageViewTracker instead, so both emitters attach the same
|
|
7441
|
-
// allowlisted query params
|
|
7442
|
-
// attribution, since utm_*/gclid live on the entry URL.
|
|
7534
|
+
// allowlisted query params.
|
|
7443
7535
|
this.logPageView = function (options) {
|
|
7536
|
+
var _a;
|
|
7444
7537
|
self.logEvent({
|
|
7445
7538
|
messageType: Types.MessageType.PageView,
|
|
7446
7539
|
name: 'PageView',
|
|
7447
|
-
data: __assign(__assign(__assign({}, allowedQueryParams(getHref())), {
|
|
7540
|
+
data: __assign(__assign(__assign({}, paramsToAttributes(allowedQueryParams(getHref(), (_a = mpInstance._Store.SDKConfig.autoLogPageViewQueryParams) === null || _a === void 0 ? void 0 : _a.allowed))), {
|
|
7448
7541
|
hostname: window.location.hostname,
|
|
7449
7542
|
title: window.document.title
|
|
7450
7543
|
}), autoPageViewAttribute(options && options.isAutoPageView)),
|
|
@@ -7738,6 +7831,19 @@ var mParticle = (function () {
|
|
|
7738
7831
|
}
|
|
7739
7832
|
return false;
|
|
7740
7833
|
}
|
|
7834
|
+
// sendEventToForwarders passes no kitBlocker: apiClient has already applied
|
|
7835
|
+
// the data plan by then.
|
|
7836
|
+
function isEventForwarded(event, forwarder, kitBlocker) {
|
|
7837
|
+
var plannedEvent = kitBlocker && kitBlocker.kitBlockingEnabled ? kitBlocker.createBlockedEvent(event) : event;
|
|
7838
|
+
if (!plannedEvent) {
|
|
7839
|
+
return false;
|
|
7840
|
+
}
|
|
7841
|
+
if (isBlockedByForwardingRule(plannedEvent.EventDataType, plannedEvent.EventAttributes, forwarder)) {
|
|
7842
|
+
return false;
|
|
7843
|
+
}
|
|
7844
|
+
var eventCategory = plannedEvent.EventCategory;
|
|
7845
|
+
return !isBlockedByEventFilter(plannedEvent.EventDataType, KitFilterHelper.hashEventName(plannedEvent.EventName, eventCategory), KitFilterHelper.hashEventType(eventCategory), forwarder);
|
|
7846
|
+
}
|
|
7741
7847
|
function filterEventAttributes(messageType, eventCategory, eventName, attributes, forwarder) {
|
|
7742
7848
|
if (!attributes) {
|
|
7743
7849
|
return attributes;
|
|
@@ -7888,17 +7994,12 @@ var mParticle = (function () {
|
|
|
7888
7994
|
if (mpInstance._Store.webviewBridgeEnabled || !mpInstance._Store.activeForwarders) {
|
|
7889
7995
|
return;
|
|
7890
7996
|
}
|
|
7891
|
-
var hashedEventName = KitFilterHelper.hashEventName(event.EventName, event.EventCategory);
|
|
7892
|
-
var hashedEventType = KitFilterHelper.hashEventType(event.EventCategory);
|
|
7893
7997
|
var forwardEvent = function forwardEvent(forwarder) {
|
|
7894
|
-
if (
|
|
7998
|
+
if (!isEventForwarded(event, forwarder)) {
|
|
7895
7999
|
return;
|
|
7896
8000
|
}
|
|
7897
8001
|
// Clone the event object, as we could be sending different attributes to each forwarder
|
|
7898
8002
|
var clonedEvent = extend(true, {}, event);
|
|
7899
|
-
if (isBlockedByEventFilter(event.EventDataType, hashedEventName, hashedEventType, forwarder)) {
|
|
7900
|
-
return;
|
|
7901
|
-
}
|
|
7902
8003
|
clonedEvent.EventAttributes = filterEventAttributes(event.EventDataType, event.EventCategory, event.EventName, clonedEvent.EventAttributes, forwarder);
|
|
7903
8004
|
// Check user identity filtering rules
|
|
7904
8005
|
clonedEvent.UserIdentities = filterUserIdentities(clonedEvent.UserIdentities, forwarder.userIdentityFilters);
|
|
@@ -8346,7 +8447,7 @@ var mParticle = (function () {
|
|
|
8346
8447
|
ExpandedEventCount: 0,
|
|
8347
8448
|
AppVersion: mpInstance.getAppVersion(),
|
|
8348
8449
|
AppName: mpInstance.getAppName(),
|
|
8349
|
-
Package: mpInstance._Store.SDKConfig
|
|
8450
|
+
Package: mpInstance._Store.SDKConfig.package,
|
|
8350
8451
|
ClientGeneratedId: mpInstance._Store.clientId,
|
|
8351
8452
|
DeviceId: mpInstance._Store.deviceId,
|
|
8352
8453
|
IntegrationAttributes: integrationAttributes,
|
|
@@ -8575,7 +8676,6 @@ var mParticle = (function () {
|
|
|
8575
8676
|
});
|
|
8576
8677
|
});
|
|
8577
8678
|
};
|
|
8578
|
-
|
|
8579
8679
|
return AudienceManager;
|
|
8580
8680
|
}();
|
|
8581
8681
|
|
|
@@ -10614,7 +10714,6 @@ var mParticle = (function () {
|
|
|
10614
10714
|
});
|
|
10615
10715
|
});
|
|
10616
10716
|
};
|
|
10617
|
-
|
|
10618
10717
|
this.sendIdentityRequest = function (identityApiRequest, method, callback, originalIdentityApiData, parseIdentityResponse, mpid, knownIdentities) {
|
|
10619
10718
|
var _a, _b, _c, _d;
|
|
10620
10719
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -10634,13 +10733,11 @@ var mParticle = (function () {
|
|
|
10634
10733
|
Logger.error(Messages$1.ErrorMessages.APIRequestEmpty);
|
|
10635
10734
|
return [2 /*return*/];
|
|
10636
10735
|
}
|
|
10637
|
-
|
|
10638
10736
|
Logger.verbose(Messages$1.InformationMessages.SendIdentityHttp);
|
|
10639
10737
|
if (mpInstance._Store.identityCallInFlight) {
|
|
10640
10738
|
invokeCallback(callback, HTTPCodes$1.activeIdentityRequest, 'There is currently an Identity request processing. Please wait for this to return before requesting again');
|
|
10641
10739
|
return [2 /*return*/];
|
|
10642
10740
|
}
|
|
10643
|
-
|
|
10644
10741
|
previousMPID = mpid || null;
|
|
10645
10742
|
uploadUrl = this.getUploadUrl(method, mpid);
|
|
10646
10743
|
uploader = window.fetch ? new FetchUploader(uploadUrl) : new XHRUploader(uploadUrl);
|
|
@@ -10698,7 +10795,7 @@ var mParticle = (function () {
|
|
|
10698
10795
|
responseToLog = responseText;
|
|
10699
10796
|
if (!isDevelopmentMode && (responseText === null || responseText === void 0 ? void 0 : responseText.matched_identities)) {
|
|
10700
10797
|
responseToLog = __assign(__assign({}, responseText), {
|
|
10701
|
-
matched_identities:
|
|
10798
|
+
matched_identities: _obfuscateData(responseText.matched_identities)
|
|
10702
10799
|
});
|
|
10703
10800
|
}
|
|
10704
10801
|
message = 'Received Identity Response from server: ' + JSON.stringify(responseToLog);
|
|
@@ -10753,7 +10850,6 @@ var mParticle = (function () {
|
|
|
10753
10850
|
});
|
|
10754
10851
|
});
|
|
10755
10852
|
};
|
|
10756
|
-
|
|
10757
10853
|
this.getUploadUrl = function (method, mpid) {
|
|
10758
10854
|
var uploadServiceUrl = mpInstance._Helpers.createServiceUrl(mpInstance._Store.SDKConfig.identityUrl);
|
|
10759
10855
|
var uploadUrl = method === Modify ? uploadServiceUrl + mpid + '/' + method : uploadServiceUrl + method;
|
|
@@ -10778,7 +10874,8 @@ var mParticle = (function () {
|
|
|
10778
10874
|
}
|
|
10779
10875
|
|
|
10780
10876
|
var PASSBACK_CONVERSION_TRACKING_ID = 'passbackconversiontrackingid';
|
|
10781
|
-
|
|
10877
|
+
// One signal per kind (commit, wallet, checkout_step, frame and cart), so a new kind never evicts another.
|
|
10878
|
+
var CHECKOUT_INTENT_BUFFER_LIMIT = 5;
|
|
10782
10879
|
var CHECKOUT_INTENT_MAX_AGE_MS = 30000;
|
|
10783
10880
|
var ON_SHOPPABLE_ADS_READY_METHOD = 'onShoppableAdsReady';
|
|
10784
10881
|
// The purpose of this class is to create a link between the Core mParticle SDK and the
|
|
@@ -10807,6 +10904,8 @@ var mParticle = (function () {
|
|
|
10807
10904
|
this.stopRouteChangeWatch = null;
|
|
10808
10905
|
// Checkout intent raised before the kit attached: latest per kind.
|
|
10809
10906
|
this.pendingCheckoutIntents = [];
|
|
10907
|
+
// Off until SDK init finds the Rokt connection receives auto button clicks.
|
|
10908
|
+
this.autoButtonClicksForwarded = false;
|
|
10810
10909
|
}
|
|
10811
10910
|
/**
|
|
10812
10911
|
* Sets a callback to be invoked when RoktManager becomes ready
|
|
@@ -10933,12 +11032,16 @@ var mParticle = (function () {
|
|
|
10933
11032
|
(_c = this.logger) === null || _c === void 0 ? void 0 : _c.error("RoktManager: Error in onRouteChange: ".concat(getErrorMessage(e)));
|
|
10934
11033
|
}
|
|
10935
11034
|
};
|
|
11035
|
+
RoktManager.prototype.setAutoButtonClicksForwarded = function (forwarded) {
|
|
11036
|
+
this.autoButtonClicksForwarded = forwarded;
|
|
11037
|
+
};
|
|
10936
11038
|
// Checkout intent is off unless the flag is on, and under opt-out, any Rokt
|
|
10937
|
-
// privacy launcher option,
|
|
11039
|
+
// privacy launcher option, Global Privacy Control, or a Rokt connection that
|
|
11040
|
+
// does not receive auto button clicks.
|
|
10938
11041
|
RoktManager.prototype.isCheckoutIntentSuppressed = function () {
|
|
10939
11042
|
var _a, _b;
|
|
10940
11043
|
var flags = (_b = (_a = this.store) === null || _a === void 0 ? void 0 : _a.SDKConfig) === null || _b === void 0 ? void 0 : _b.flags;
|
|
10941
|
-
if (!flags || flags[Constants.FeatureFlags.AutoButtonTracking] !== true || !this.store.isEnabled) {
|
|
11044
|
+
if (!flags || flags[Constants.FeatureFlags.AutoButtonTracking] !== true || !this.store.isEnabled || !this.autoButtonClicksForwarded) {
|
|
10942
11045
|
return true;
|
|
10943
11046
|
}
|
|
10944
11047
|
var options = this.launcherOptions || {};
|
|
@@ -11006,6 +11109,8 @@ var mParticle = (function () {
|
|
|
11006
11109
|
return _this.notifyCheckoutIntent(signal);
|
|
11007
11110
|
});
|
|
11008
11111
|
};
|
|
11112
|
+
// The Rokt kit reads this to tell core's page views, whose title and utm
|
|
11113
|
+
// attributes a filter may have removed, from ones logged by hand.
|
|
11009
11114
|
RoktManager.prototype.isAutoLogPageViewEnabled = function () {
|
|
11010
11115
|
var _a, _b, _c;
|
|
11011
11116
|
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;
|
|
@@ -11153,7 +11258,6 @@ var mParticle = (function () {
|
|
|
11153
11258
|
});
|
|
11154
11259
|
});
|
|
11155
11260
|
};
|
|
11156
|
-
|
|
11157
11261
|
RoktManager.prototype.logFilteredIdentity = function (identityType) {
|
|
11158
11262
|
var _a;
|
|
11159
11263
|
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.verbose("mParticle.Rokt selectPlacements did not add the ".concat(identityType, " user identity to the attributes because the Rokt connection does not allow it"));
|
|
@@ -11223,7 +11327,6 @@ var mParticle = (function () {
|
|
|
11223
11327
|
});
|
|
11224
11328
|
});
|
|
11225
11329
|
};
|
|
11226
|
-
|
|
11227
11330
|
RoktManager.prototype.setExtensionData = function (extensionData) {
|
|
11228
11331
|
if (!this.isReady()) {
|
|
11229
11332
|
this.deferredCall('setExtensionData', extensionData);
|
|
@@ -11272,12 +11375,10 @@ var mParticle = (function () {
|
|
|
11272
11375
|
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.verbose('mParticle.Rokt.terminate called before the Rokt kit was ready. Nothing to terminate.');
|
|
11273
11376
|
return [2 /*return*/];
|
|
11274
11377
|
}
|
|
11275
|
-
|
|
11276
11378
|
if (!isFunction(this.kit.terminate)) {
|
|
11277
11379
|
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.error('mParticle.Rokt.terminate is not supported by the attached Rokt Kit version.');
|
|
11278
11380
|
return [2 /*return*/];
|
|
11279
11381
|
}
|
|
11280
|
-
|
|
11281
11382
|
_d.label = 1;
|
|
11282
11383
|
case 1:
|
|
11283
11384
|
_d.trys.push([1, 3,, 4]);
|
|
@@ -11295,7 +11396,6 @@ var mParticle = (function () {
|
|
|
11295
11396
|
});
|
|
11296
11397
|
});
|
|
11297
11398
|
};
|
|
11298
|
-
|
|
11299
11399
|
RoktManager.prototype.onShoppableAdsReady = function (callback) {
|
|
11300
11400
|
if (!this.kit || !this.isShoppableAdsLoaded) {
|
|
11301
11401
|
this.deferredCall('onShoppableAdsReady', callback);
|
|
@@ -11318,7 +11418,7 @@ var mParticle = (function () {
|
|
|
11318
11418
|
} catch (e) {
|
|
11319
11419
|
(_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('RoktManager: Error flushing onShoppableAdsReady: ' + e);
|
|
11320
11420
|
}
|
|
11321
|
-
_this.messageQueue
|
|
11421
|
+
_this.messageQueue.delete(key);
|
|
11322
11422
|
}
|
|
11323
11423
|
});
|
|
11324
11424
|
this.isShoppableAdsLoaded = true;
|
|
@@ -11357,7 +11457,6 @@ var mParticle = (function () {
|
|
|
11357
11457
|
});
|
|
11358
11458
|
});
|
|
11359
11459
|
};
|
|
11360
|
-
|
|
11361
11460
|
RoktManager.prototype.getLocalSessionAttributes = function () {
|
|
11362
11461
|
return this.store.getLocalSessionAttributes();
|
|
11363
11462
|
};
|
|
@@ -11463,7 +11562,7 @@ var mParticle = (function () {
|
|
|
11463
11562
|
if (resolve) {
|
|
11464
11563
|
resolve(resolvedResult);
|
|
11465
11564
|
}
|
|
11466
|
-
})
|
|
11565
|
+
}).catch(handleError);
|
|
11467
11566
|
} catch (error) {
|
|
11468
11567
|
handleError(error);
|
|
11469
11568
|
}
|
|
@@ -11475,7 +11574,7 @@ var mParticle = (function () {
|
|
|
11475
11574
|
RoktManager.prototype.deferredCall = function (methodName, payload) {
|
|
11476
11575
|
var _this = this;
|
|
11477
11576
|
return new Promise(function (resolve, reject) {
|
|
11478
|
-
var messageId = "".concat(methodName, "_").concat(
|
|
11577
|
+
var messageId = "".concat(methodName, "_").concat(_generateUniqueId());
|
|
11479
11578
|
_this.queueMessage({
|
|
11480
11579
|
messageId: messageId,
|
|
11481
11580
|
methodName: methodName,
|
|
@@ -11538,14 +11637,11 @@ var mParticle = (function () {
|
|
|
11538
11637
|
if (!currentValue) {
|
|
11539
11638
|
return true; // New value exists but no current value
|
|
11540
11639
|
}
|
|
11541
|
-
|
|
11542
11640
|
if (currentValue !== newValue) {
|
|
11543
11641
|
return true; // Values are different
|
|
11544
11642
|
}
|
|
11545
|
-
|
|
11546
11643
|
return false; // Values are the same
|
|
11547
11644
|
};
|
|
11548
|
-
|
|
11549
11645
|
return RoktManager;
|
|
11550
11646
|
}();
|
|
11551
11647
|
|
|
@@ -11679,23 +11775,71 @@ var mParticle = (function () {
|
|
|
11679
11775
|
eventType: EventType.Other
|
|
11680
11776
|
};
|
|
11681
11777
|
};
|
|
11778
|
+
// Every attribute buildButtonClickEvent can set apart from the marker and the
|
|
11779
|
+
// hostname, which is the same on every click on this page. A spec checks the
|
|
11780
|
+
// two stay in step.
|
|
11781
|
+
var CLICK_ATTRIBUTES = ['label_source', 'element', 'type', 'path', 'label', 'aria_label', 'icon', 'element_id', 'element_name', 'test_id', 'controls', 'form', 'link_type', 'link_domain', 'file_extension', 'checked'];
|
|
11782
|
+
// Which check keeps some or all auto button clicks from a kit, or undefined
|
|
11783
|
+
// when the kit receives every one. A forwarding rule on an attribute whose
|
|
11784
|
+
// value differs between clicks lets some through and not others. Each other
|
|
11785
|
+
// check goes through isEventForwarded on its own, so together they decide as
|
|
11786
|
+
// it does for each click.
|
|
11787
|
+
var getButtonClickBlocker = function getButtonClickBlocker(forwarder, kitBlocker) {
|
|
11788
|
+
var _a;
|
|
11789
|
+
var rule = forwarder.filteringEventAttributeValue;
|
|
11790
|
+
if (rule && rule.eventAttributeValue && CLICK_ATTRIBUTES.some(function (name) {
|
|
11791
|
+
return KitFilterHelper.hashAttributeConditionalForwarding(name) === rule.eventAttributeName;
|
|
11792
|
+
})) {
|
|
11793
|
+
return 'forwarding rule';
|
|
11794
|
+
}
|
|
11795
|
+
var click = {
|
|
11796
|
+
EventName: BUTTON_CLICK_EVENT_NAME,
|
|
11797
|
+
EventCategory: EventType.Other,
|
|
11798
|
+
EventDataType: MessageType$1.PageEvent,
|
|
11799
|
+
EventAttributes: (_a = {}, _a[AUTO_BUTTON_ATTRIBUTE] = true, _a.hostname = window.location.hostname, _a)
|
|
11800
|
+
};
|
|
11801
|
+
var checks = [['data plan', {}], ['forwarding rule', {
|
|
11802
|
+
filteringEventAttributeValue: rule
|
|
11803
|
+
}], ['event name filter', {
|
|
11804
|
+
eventNameFilters: forwarder.eventNameFilters
|
|
11805
|
+
}], ['event type filter', {
|
|
11806
|
+
eventTypeFilters: forwarder.eventTypeFilters
|
|
11807
|
+
}]];
|
|
11808
|
+
var failed = checks.find(function (_a) {
|
|
11809
|
+
var filters = _a[1];
|
|
11810
|
+
return !isEventForwarded(click, filters, kitBlocker);
|
|
11811
|
+
});
|
|
11812
|
+
return failed ? failed[0] : undefined;
|
|
11813
|
+
};
|
|
11682
11814
|
|
|
11683
11815
|
// Finds the control a click landed on, for both checkout intent and button
|
|
11684
11816
|
// descriptions. Rokt's own elements, disabled controls and anything under
|
|
11685
11817
|
// data-mp-no-track are never returned.
|
|
11686
11818
|
var CONTROL_SELECTOR = 'button, input[type="submit"], input[type="button"], input[type="image"], [role="button"]';
|
|
11687
|
-
|
|
11688
|
-
|
|
11819
|
+
var EDITABLE = '[contenteditable]:not([contenteditable="false"])';
|
|
11820
|
+
// One step up the composed tree: the slot, the parent, then the shadow host.
|
|
11821
|
+
function composedParent(node) {
|
|
11822
|
+
var root = isFunction(node.getRootNode) ? node.getRootNode() : null;
|
|
11823
|
+
return node.assignedSlot || node.parentElement || root && root.host || null;
|
|
11824
|
+
}
|
|
11825
|
+
// Tests the element and its ancestors over the composed tree: slots, parents,
|
|
11826
|
+
// then shadow hosts. With `until`, stops before reaching that ancestor.
|
|
11827
|
+
function someComposedAncestor(element, test, until) {
|
|
11689
11828
|
var node = element;
|
|
11690
|
-
while (node) {
|
|
11691
|
-
if (node
|
|
11829
|
+
while (node && node !== until) {
|
|
11830
|
+
if (test(node)) {
|
|
11692
11831
|
return true;
|
|
11693
11832
|
}
|
|
11694
|
-
|
|
11695
|
-
node = node.assignedSlot || node.parentElement || root && root.host || null;
|
|
11833
|
+
node = composedParent(node);
|
|
11696
11834
|
}
|
|
11697
11835
|
return false;
|
|
11698
11836
|
}
|
|
11837
|
+
// closest() over the composed tree, starting at the element itself.
|
|
11838
|
+
function hasComposedAncestor(element, selector) {
|
|
11839
|
+
return someComposedAncestor(element, function (node) {
|
|
11840
|
+
return node.matches(selector);
|
|
11841
|
+
});
|
|
11842
|
+
}
|
|
11699
11843
|
var ROKT_SELECTOR = '[data-core-type], #rokt-controller-frame';
|
|
11700
11844
|
// A Rokt frame or script is served from a /wsdk/ path.
|
|
11701
11845
|
function hasRoktSrc(element) {
|
|
@@ -11741,8 +11885,27 @@ var mParticle = (function () {
|
|
|
11741
11885
|
}
|
|
11742
11886
|
return host.matches(CARD_FORM_BUTTON) ? 'checkout_step' : 'wallet';
|
|
11743
11887
|
}
|
|
11888
|
+
// A merchant-built wallet button that names its wallet in an id or test id, such
|
|
11889
|
+
// as `venmo-pay-btn` or `applePayButton`. The name must also say button and not
|
|
11890
|
+
// name a part or a wrapper of one, so a wallet's logo, label or container never
|
|
11891
|
+
// counts, nor a button that only chooses a wallet. Affirm is also an English
|
|
11892
|
+
// verb, so it counts only beside a payment word. Only the element's own
|
|
11893
|
+
// attributes are read.
|
|
11894
|
+
var WALLET_IN_NAME = /(?:^| )(?:apple ?pay|google ?pay|g ?pay|amazon ?pay|cash ?app|venmo|pay ?pal|shop ?pay|klarna|after ?pay|clear ?pay|sezzle|samsung ?pay)(?= |$)/;
|
|
11895
|
+
var AFFIRM_IN_NAME = /(?:^| )affirm(?= |$)/;
|
|
11896
|
+
var PAYMENT_IN_NAME = /(?:^| )(?:pay|payment|checkout)(?= |$)/;
|
|
11897
|
+
var BUTTON_IN_NAME = /(?:^| )(?:btn|button)(?= |$)/;
|
|
11898
|
+
var PART_IN_NAME = /(?:^| )(?:logo|icon|image|img|label|text|caption|message|messaging|info|tooltip|help|learn|terms|promo|banner|container|wrapper|wrap|placeholder|form|section|panel|modal|list|group|select|option|tab|toggle|radio|choice|method)(?= |$)/;
|
|
11899
|
+
function nameWords(value) {
|
|
11900
|
+
return (value || '').slice(0, 64).replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[^A-Za-z0-9]+/g, ' ').trim().toLowerCase();
|
|
11901
|
+
}
|
|
11902
|
+
function isWalletNamed(element) {
|
|
11903
|
+
return [element.getAttribute('id'), element.getAttribute('data-testid')].map(nameWords).some(function (words) {
|
|
11904
|
+
return (WALLET_IN_NAME.test(words) || AFFIRM_IN_NAME.test(words) && PAYMENT_IN_NAME.test(words)) && BUTTON_IN_NAME.test(words) && !PART_IN_NAME.test(words);
|
|
11905
|
+
});
|
|
11906
|
+
}
|
|
11744
11907
|
function isWalletElement(element) {
|
|
11745
|
-
return vendorButtonKind(element) === 'wallet';
|
|
11908
|
+
return vendorButtonKind(element) === 'wallet' || isWalletNamedControl(element);
|
|
11746
11909
|
}
|
|
11747
11910
|
// Pickers and toggles choose a payment method or open a panel; they never pay.
|
|
11748
11911
|
var PICKER = 'input[type="radio"], input[type="checkbox"], label, [role="radio"], [role="tab"], [role="option"], ' + '[role="checkbox"], [role="switch"], [role="menuitemradio"], [role="menuitemcheckbox"], [aria-checked], [aria-selected]';
|
|
@@ -11750,25 +11913,53 @@ var mParticle = (function () {
|
|
|
11750
11913
|
// Pay-later and financing messages link to details rather than pay.
|
|
11751
11914
|
var MESSAGING = 'klarna-placement, afterpay-placement, square-placement, .affirm-as-low-as, .affirm-site-modal, ' + '.affirm-product-modal, [data-pp-message]';
|
|
11752
11915
|
// A control that is still logged as a click but never counts as checkout intent.
|
|
11753
|
-
|
|
11916
|
+
// A cart button can open a flyout, so aria-expanded alone never quiets one.
|
|
11917
|
+
function isQuietIntentControl(control, isVendorWallet, isCart) {
|
|
11918
|
+
if (isCart === void 0) {
|
|
11919
|
+
isCart = false;
|
|
11920
|
+
}
|
|
11754
11921
|
if (control.matches(PICKER)) {
|
|
11755
11922
|
return true;
|
|
11756
11923
|
}
|
|
11757
11924
|
if (hasComposedAncestor(control, PICKER_GROUP)) {
|
|
11758
11925
|
return true;
|
|
11759
11926
|
}
|
|
11760
|
-
if (!isVendorWallet && (control.hasAttribute('aria-expanded') || control.hasAttribute('aria-pressed'))) {
|
|
11927
|
+
if (!isVendorWallet && (!isCart && control.hasAttribute('aria-expanded') || control.hasAttribute('aria-pressed'))) {
|
|
11761
11928
|
return true;
|
|
11762
11929
|
}
|
|
11763
11930
|
return hasComposedAncestor(control, MESSAGING);
|
|
11764
11931
|
}
|
|
11765
11932
|
// A checkbox or radio is a control of its own only when no button or link
|
|
11766
|
-
// around it takes the click, so a tappable row
|
|
11933
|
+
// around it takes the click, so a tappable row stays the control.
|
|
11767
11934
|
var OPTION_INPUT = 'input[type="checkbox"], input[type="radio"]';
|
|
11768
|
-
//
|
|
11769
|
-
//
|
|
11770
|
-
//
|
|
11771
|
-
|
|
11935
|
+
// Anything that chooses, toggles or takes typing. One between a click's target
|
|
11936
|
+
// and a wallet-named element, or inside that element, keeps it from becoming
|
|
11937
|
+
// the tap. Hidden inputs and button-like inputs are not fields.
|
|
11938
|
+
var NAME_BARRIER = PICKER + ', ' + PICKER_GROUP + ', input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="image"]), ' + 'select, textarea, option, optgroup, datalist, [aria-expanded], [aria-pressed], ' + EDITABLE;
|
|
11939
|
+
// What owns a click on anything inside it: a picker, a real control or link,
|
|
11940
|
+
// a toggle, or an editable region.
|
|
11941
|
+
var NAME_HOLDER = PICKER + ', ' + CONTROL_SELECTOR + ', a[href], ' + VENDOR_BUTTON + ', [aria-expanded], [aria-pressed], ' + EDITABLE;
|
|
11942
|
+
// A wallet-named element that may stand as a control by its name alone: not
|
|
11943
|
+
// itself a picker, toggle or form field; not inside a picker, a real button or
|
|
11944
|
+
// link, a toggle, an editable region or another wallet-named element, which own
|
|
11945
|
+
// the click with their own state; and holding no picker or field, in its open
|
|
11946
|
+
// shadow root either, so a payment-method row with its own radio or select
|
|
11947
|
+
// never becomes a wallet tap.
|
|
11948
|
+
function isWalletNamedControl(element) {
|
|
11949
|
+
if (!isWalletNamed(element) || element.matches(NAME_BARRIER)) {
|
|
11950
|
+
return false;
|
|
11951
|
+
}
|
|
11952
|
+
for (var node = composedParent(element); node; node = composedParent(node)) {
|
|
11953
|
+
if (node.matches(NAME_HOLDER) || isWalletNamed(node)) {
|
|
11954
|
+
return false;
|
|
11955
|
+
}
|
|
11956
|
+
}
|
|
11957
|
+
var shadow = element.shadowRoot;
|
|
11958
|
+
return !element.querySelector(NAME_BARRIER) && !(shadow && shadow.querySelector(NAME_BARRIER));
|
|
11959
|
+
}
|
|
11960
|
+
// Whether a click is never tracked: it has no element, or lands on a Rokt
|
|
11961
|
+
// element or under data-mp-no-track. Reads at most 12 entries of the path.
|
|
11962
|
+
function isExcludedPath(path) {
|
|
11772
11963
|
var entries = path.slice(0, 12);
|
|
11773
11964
|
var first;
|
|
11774
11965
|
for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
|
|
@@ -11779,41 +11970,60 @@ var mParticle = (function () {
|
|
|
11779
11970
|
}
|
|
11780
11971
|
}
|
|
11781
11972
|
if (!first || hasComposedAncestor(first, ROKT_SELECTOR + ', [data-mp-no-track]')) {
|
|
11973
|
+
return true;
|
|
11974
|
+
}
|
|
11975
|
+
for (var _a = 0, entries_2 = entries; _a < entries_2.length; _a++) {
|
|
11976
|
+
var target = entries_2[_a];
|
|
11977
|
+
if (target instanceof Element && hasRoktSrc(target)) {
|
|
11978
|
+
return true;
|
|
11979
|
+
}
|
|
11980
|
+
}
|
|
11981
|
+
return false;
|
|
11982
|
+
}
|
|
11983
|
+
// Walks at most 12 entries of an event's composed path for the nearest control.
|
|
11984
|
+
// A wallet-named element counts as one unless a picker, toggle or form field
|
|
11985
|
+
// lies between it and the target or a picker holds it, so choosing an option in
|
|
11986
|
+
// or around it never becomes its tap. Excluded clicks give nothing; disabled controls are skipped, a
|
|
11987
|
+
// wallet-named one also by its `disabled` class. A caller that has already
|
|
11988
|
+
// found the path not excluded passes `included` so the walk runs once.
|
|
11989
|
+
function findControl(path, included) {
|
|
11990
|
+
if (included === void 0) {
|
|
11991
|
+
included = false;
|
|
11992
|
+
}
|
|
11993
|
+
if (!included && isExcludedPath(path)) {
|
|
11782
11994
|
return undefined;
|
|
11783
11995
|
}
|
|
11784
11996
|
var control;
|
|
11785
11997
|
var option;
|
|
11786
|
-
|
|
11787
|
-
|
|
11998
|
+
var picked = false;
|
|
11999
|
+
for (var _i = 0, _a = path.slice(0, 12); _i < _a.length; _i++) {
|
|
12000
|
+
var target = _a[_i];
|
|
12001
|
+
if (control) {
|
|
12002
|
+
break;
|
|
12003
|
+
}
|
|
11788
12004
|
if (!(target instanceof Element)) {
|
|
11789
12005
|
continue;
|
|
11790
12006
|
}
|
|
11791
|
-
if (
|
|
11792
|
-
return undefined;
|
|
11793
|
-
}
|
|
11794
|
-
if (!control && target.matches(CONTROL_SELECTOR + ', a[href], ' + VENDOR_BUTTON)) {
|
|
12007
|
+
if (target.matches(CONTROL_SELECTOR + ', a[href], ' + VENDOR_BUTTON) || !picked && isWalletNamedControl(target)) {
|
|
11795
12008
|
control = target;
|
|
11796
|
-
} else
|
|
11797
|
-
option
|
|
12009
|
+
} else {
|
|
12010
|
+
if (!option && target.matches(OPTION_INPUT)) {
|
|
12011
|
+
option = target;
|
|
12012
|
+
}
|
|
12013
|
+
picked = picked || target.matches(NAME_BARRIER);
|
|
11798
12014
|
}
|
|
11799
12015
|
}
|
|
11800
12016
|
control = control || option;
|
|
11801
|
-
if (!control || control.matches(':disabled, [aria-disabled="true"]')) {
|
|
12017
|
+
if (!control || control.matches(':disabled, [aria-disabled="true"]') || control.classList.contains('disabled') && isWalletNamed(control)) {
|
|
11802
12018
|
return undefined;
|
|
11803
12019
|
}
|
|
11804
12020
|
return control;
|
|
11805
12021
|
}
|
|
11806
|
-
var EDITABLE = '[contenteditable]:not([contenteditable="false"])';
|
|
11807
12022
|
// Text that may hold what a shopper typed or what the page opted out: form
|
|
11808
12023
|
// fields, editable regions, and anything under data-mp-no-track.
|
|
11809
12024
|
function isUnsafeTextSource(element) {
|
|
11810
12025
|
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);
|
|
11811
12026
|
}
|
|
11812
|
-
// A label around a checkbox or radio holds no typed text, so only the other
|
|
11813
|
-
// fields rule its text out.
|
|
11814
|
-
function isUnsafeLabelSource(label) {
|
|
11815
|
-
return !!label.querySelector('input:not([type="checkbox"]):not([type="radio"]), select, textarea, [data-mp-no-track], ' + EDITABLE) || hasComposedAncestor(label, 'select, datalist, textarea, [data-mp-no-track], ' + EDITABLE);
|
|
11816
|
-
}
|
|
11817
12027
|
|
|
11818
12028
|
// Masks a button label before it is logged. Button text can carry personal data,
|
|
11819
12029
|
// so this is the privacy boundary for every label and aria-label.
|
|
@@ -11830,10 +12040,12 @@ var mParticle = (function () {
|
|
|
11830
12040
|
}
|
|
11831
12041
|
}
|
|
11832
12042
|
var digitPatterns = createDigitPatterns();
|
|
12043
|
+
// Longer raw labels are dropped, not cut short.
|
|
12044
|
+
var MAX_RAW_LABEL_LENGTH = 128;
|
|
11833
12045
|
// The label as logged: lower case, at most 64 characters, digits and amounts
|
|
11834
12046
|
// masked. Dropped when it looks like an email, phone or card number.
|
|
11835
12047
|
function maskLabel(raw) {
|
|
11836
|
-
if (!raw || raw.length >
|
|
12048
|
+
if (!raw || raw.length > MAX_RAW_LABEL_LENGTH || !digitPatterns) {
|
|
11837
12049
|
return undefined;
|
|
11838
12050
|
}
|
|
11839
12051
|
var text = raw.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
@@ -11855,24 +12067,139 @@ var mParticle = (function () {
|
|
|
11855
12067
|
}
|
|
11856
12068
|
return control.matches('a[href]:not([role="button"])') ? 'link' : 'role';
|
|
11857
12069
|
}
|
|
12070
|
+
var NOT_RENDERED = 'script, style, template, [hidden], [aria-hidden="true"], [inert]';
|
|
12071
|
+
// The most nodes read for one label source, and images looked at for one
|
|
12072
|
+
// button. Each costs a style lookup in the click handler.
|
|
12073
|
+
var MAX_READS = 64;
|
|
12074
|
+
function assignedNodes(element) {
|
|
12075
|
+
var slot = element;
|
|
12076
|
+
return isFunction(slot.assignedNodes) ? slot.assignedNodes() : [];
|
|
12077
|
+
}
|
|
12078
|
+
// Left out of what is drawn by its parent: a light child of an open shadow host
|
|
12079
|
+
// that no slot takes, or a slot's own child while nodes are assigned to the
|
|
12080
|
+
// slot. A closed shadow root can't be seen, so its host's children count.
|
|
12081
|
+
function isUnslotted(node) {
|
|
12082
|
+
var parent = node.parentElement;
|
|
12083
|
+
if (!parent) {
|
|
12084
|
+
return false;
|
|
12085
|
+
}
|
|
12086
|
+
return parent.shadowRoot ? !node.assignedSlot : assignedNodes(parent).length > 0;
|
|
12087
|
+
}
|
|
12088
|
+
// Hides the element and all it holds, whatever their own style says.
|
|
12089
|
+
// Screen-reader-only text is clipped rather than hidden, so it is not. An SVG
|
|
12090
|
+
// title is never drawn but names its icon, so it is not either.
|
|
12091
|
+
function hidesSubtree(element) {
|
|
12092
|
+
if (element.matches(NOT_RENDERED) || isUnslotted(element)) {
|
|
12093
|
+
return true;
|
|
12094
|
+
}
|
|
12095
|
+
return !(element instanceof SVGTitleElement) && window.getComputedStyle(element).display === 'none';
|
|
12096
|
+
}
|
|
12097
|
+
// Visibility is inherited and a child can set it back to visible, so only the
|
|
12098
|
+
// element's own counts.
|
|
12099
|
+
function isVisible(element) {
|
|
12100
|
+
var visibility = window.getComputedStyle(element).visibility;
|
|
12101
|
+
return visibility !== 'hidden' && visibility !== 'collapse';
|
|
12102
|
+
}
|
|
12103
|
+
function isShown(element, until) {
|
|
12104
|
+
return !someComposedAncestor(element, hidesSubtree, until);
|
|
12105
|
+
}
|
|
12106
|
+
// A shadow host shows a light child only through a slot, and only while
|
|
12107
|
+
// nothing from that slot up to the host hides it.
|
|
12108
|
+
function isShownThroughSlot(node, host) {
|
|
12109
|
+
var slot = node.assignedSlot;
|
|
12110
|
+
return !!slot && isShown(slot, host);
|
|
12111
|
+
}
|
|
12112
|
+
// Whitespace hides nothing, so a node holding only whitespace is taken as it
|
|
12113
|
+
// is, without a style lookup: an icon's paths cost nothing. A slot's text can
|
|
12114
|
+
// sit in the nodes assigned to it, so a slot is never blank.
|
|
12115
|
+
function isBlank(node) {
|
|
12116
|
+
return !/\S/.test(node.textContent || '') && !(node instanceof Element && (node.matches('slot') || !!node.querySelector('slot')));
|
|
12117
|
+
}
|
|
12118
|
+
// The text a sighted user or a screen reader gets from an element, whitespace
|
|
12119
|
+
// collapsed. Undefined when that runs past what maskLabel keeps, takes more
|
|
12120
|
+
// than MAX_READS nodes to read, or takes in a form field through a slot, so a
|
|
12121
|
+
// label is never cut short at a node boundary.
|
|
12122
|
+
function renderedText(root) {
|
|
12123
|
+
var text = '';
|
|
12124
|
+
var reads = 0;
|
|
12125
|
+
var append = function append(value) {
|
|
12126
|
+
text = (text + value).replace(/\s+/g, ' ');
|
|
12127
|
+
};
|
|
12128
|
+
var _read = function read(element) {
|
|
12129
|
+
if (hidesSubtree(element)) {
|
|
12130
|
+
return true;
|
|
12131
|
+
}
|
|
12132
|
+
var assigned = assignedNodes(element);
|
|
12133
|
+
var nodes = assigned.length ? assigned : element.childNodes;
|
|
12134
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
12135
|
+
var node = nodes[i];
|
|
12136
|
+
var isText = node.nodeType === Node.TEXT_NODE;
|
|
12137
|
+
if (!isText && !(node instanceof Element)) {
|
|
12138
|
+
continue;
|
|
12139
|
+
}
|
|
12140
|
+
if (isBlank(node)) {
|
|
12141
|
+
append(node.textContent);
|
|
12142
|
+
continue;
|
|
12143
|
+
}
|
|
12144
|
+
if (++reads > MAX_READS) {
|
|
12145
|
+
return false;
|
|
12146
|
+
}
|
|
12147
|
+
if (element.shadowRoot && !isShownThroughSlot(node, element)) {
|
|
12148
|
+
continue;
|
|
12149
|
+
}
|
|
12150
|
+
if (isText) {
|
|
12151
|
+
if (isVisible(node.assignedSlot || element)) {
|
|
12152
|
+
append(node.nodeValue);
|
|
12153
|
+
}
|
|
12154
|
+
} else if (
|
|
12155
|
+
// Assigned nodes sit outside the label source, where its own
|
|
12156
|
+
// form field check never looked.
|
|
12157
|
+
assigned.length && isUnsafeTextSource(node) || !_read(node)) {
|
|
12158
|
+
return false;
|
|
12159
|
+
}
|
|
12160
|
+
if (text.trim().length > MAX_RAW_LABEL_LENGTH) {
|
|
12161
|
+
return false;
|
|
12162
|
+
}
|
|
12163
|
+
}
|
|
12164
|
+
return true;
|
|
12165
|
+
};
|
|
12166
|
+
return _read(root) ? text.trim() : undefined;
|
|
12167
|
+
}
|
|
12168
|
+
// '' when the element can't name the button, undefined when its text can't be
|
|
12169
|
+
// read in full.
|
|
11858
12170
|
function textOf(element) {
|
|
11859
|
-
return element && !isUnsafeTextSource(element) && element
|
|
12171
|
+
return element && !isUnsafeTextSource(element) && isShown(element) ? renderedText(element) : '';
|
|
12172
|
+
}
|
|
12173
|
+
// The control itself is already known to be shown.
|
|
12174
|
+
function shownImageAlt(control) {
|
|
12175
|
+
var images = control.querySelectorAll('img[alt]');
|
|
12176
|
+
for (var i = 0; i < images.length && i < MAX_READS; i++) {
|
|
12177
|
+
if (isShown(images[i], control) && isVisible(images[i])) {
|
|
12178
|
+
return nonBlank(images[i].getAttribute('alt'));
|
|
12179
|
+
}
|
|
12180
|
+
}
|
|
12181
|
+
return undefined;
|
|
11860
12182
|
}
|
|
11861
12183
|
function nonBlank(value) {
|
|
11862
12184
|
return value && value.trim() ? value : undefined;
|
|
11863
12185
|
}
|
|
11864
12186
|
// The button's name as a screen reader would read it, simplified: the first of
|
|
11865
|
-
// aria-labelledby, aria-label, own text (or an inner image's alt), title,
|
|
11866
|
-
// inputs their value
|
|
11867
|
-
//
|
|
11868
|
-
// children, so a typed value is never read.
|
|
12187
|
+
// aria-labelledby, aria-label, own text (or an inner image's alt), title, and
|
|
12188
|
+
// for inputs their value. The text sources are skipped for controls that hold
|
|
12189
|
+
// form fields or more than four children, so a typed value is never read.
|
|
11869
12190
|
function accessibleName(control) {
|
|
11870
12191
|
var labelledBy = control.getAttribute('aria-labelledby');
|
|
11871
12192
|
if (labelledBy) {
|
|
11872
12193
|
var root_1 = isFunction(control.getRootNode) ? control.getRootNode() : document;
|
|
11873
|
-
var
|
|
12194
|
+
var parts = labelledBy.split(/\s+/).map(function (id) {
|
|
11874
12195
|
return textOf(root_1.getElementById ? root_1.getElementById(id) : null);
|
|
11875
|
-
})
|
|
12196
|
+
});
|
|
12197
|
+
if (parts.indexOf(undefined) !== -1) {
|
|
12198
|
+
return {
|
|
12199
|
+
source: 'none'
|
|
12200
|
+
};
|
|
12201
|
+
}
|
|
12202
|
+
var text = parts.filter(function (part) {
|
|
11876
12203
|
return !!part;
|
|
11877
12204
|
}).join(' ');
|
|
11878
12205
|
if (text) {
|
|
@@ -11916,27 +12243,21 @@ var mParticle = (function () {
|
|
|
11916
12243
|
source: 'value'
|
|
11917
12244
|
};
|
|
11918
12245
|
}
|
|
11919
|
-
} else if (type === 'checkbox' || type === 'radio') {
|
|
11920
|
-
// The label the browser ties to the box, never the box's value.
|
|
11921
|
-
var label = control.labels ? control.labels[0] : undefined;
|
|
11922
|
-
var text = label && !isUnsafeLabelSource(label) && label.textContent ? label.textContent.trim() : '';
|
|
11923
|
-
if (text) {
|
|
11924
|
-
return {
|
|
11925
|
-
raw: text,
|
|
11926
|
-
source: 'label'
|
|
11927
|
-
};
|
|
11928
|
-
}
|
|
11929
12246
|
}
|
|
11930
|
-
} else if (readable) {
|
|
11931
|
-
var text =
|
|
12247
|
+
} else if (readable && isShown(control)) {
|
|
12248
|
+
var text = renderedText(control);
|
|
12249
|
+
if (text === undefined) {
|
|
12250
|
+
return {
|
|
12251
|
+
source: 'none'
|
|
12252
|
+
};
|
|
12253
|
+
}
|
|
11932
12254
|
if (text) {
|
|
11933
12255
|
return {
|
|
11934
12256
|
raw: text,
|
|
11935
12257
|
source: 'text'
|
|
11936
12258
|
};
|
|
11937
12259
|
}
|
|
11938
|
-
var
|
|
11939
|
-
var alt = image ? nonBlank(image.getAttribute('alt')) : undefined;
|
|
12260
|
+
var alt = shownImageAlt(control);
|
|
11940
12261
|
if (alt) {
|
|
11941
12262
|
return {
|
|
11942
12263
|
raw: alt,
|
|
@@ -12034,9 +12355,12 @@ var mParticle = (function () {
|
|
|
12034
12355
|
return form ? stableIdentifier(form.getAttribute('id')) : undefined;
|
|
12035
12356
|
}
|
|
12036
12357
|
// The browser's normalised type: a button defaults to 'submit' and an unknown
|
|
12037
|
-
// input type reads as 'text'.
|
|
12038
|
-
|
|
12039
|
-
|
|
12358
|
+
// input type reads as 'text'. A native button or input reports its own type, a
|
|
12359
|
+
// wallet-named one included; any other control, such as a role button or a
|
|
12360
|
+
// vendor's wallet element, reads as 'button'.
|
|
12361
|
+
function controlType(control) {
|
|
12362
|
+
var tag = control.tagName.toLowerCase();
|
|
12363
|
+
return tag === 'button' || tag === 'input' ? control.type : 'button';
|
|
12040
12364
|
}
|
|
12041
12365
|
// Reads the link's own resolved URL parts, never its full href: only the hostname of
|
|
12042
12366
|
// a link that leaves the site and the extension of a file it downloads travel. Navigation
|
|
@@ -12079,9 +12403,38 @@ var mParticle = (function () {
|
|
|
12079
12403
|
type: 'same_page'
|
|
12080
12404
|
} : undefined;
|
|
12081
12405
|
}
|
|
12406
|
+
// Checkboxes and radios, and the roles that act as them. A role can list
|
|
12407
|
+
// fallbacks, so any of its tokens counts.
|
|
12408
|
+
var CHOICE = 'input[type="checkbox"], input[type="radio"], [role~="checkbox"], [role~="radio"], ' + '[role~="switch"], [role~="menuitemcheckbox"], [role~="menuitemradio"]';
|
|
12409
|
+
var RADIO_ROLE = '[role~="radio"], [role~="menuitemradio"]';
|
|
12410
|
+
function boxOf(element) {
|
|
12411
|
+
var box = element instanceof HTMLLabelElement ? element.control : element;
|
|
12412
|
+
return box && box.matches(CHOICE) ? box : null;
|
|
12413
|
+
}
|
|
12414
|
+
// The box a click on this control picks. One the click passed through below
|
|
12415
|
+
// the control comes first: querySelector can't reach a slotted box, a box in a
|
|
12416
|
+
// shadow root or the box of a label inside the control, and a row can hold
|
|
12417
|
+
// more than one box. Else the control itself, the box it labels, or the first
|
|
12418
|
+
// one it wraps, as on an option row whose name repeats the option's text.
|
|
12419
|
+
function choiceOf(control, path) {
|
|
12420
|
+
for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
|
|
12421
|
+
var target = path_1[_i];
|
|
12422
|
+
if (target === control) {
|
|
12423
|
+
break;
|
|
12424
|
+
}
|
|
12425
|
+
var box = target instanceof Element ? boxOf(target) : null;
|
|
12426
|
+
if (box) {
|
|
12427
|
+
return box;
|
|
12428
|
+
}
|
|
12429
|
+
}
|
|
12430
|
+
return boxOf(control) || control.querySelector(CHOICE);
|
|
12431
|
+
}
|
|
12082
12432
|
// Describes a clicked control for auto button tracking. A link counts only when page
|
|
12083
12433
|
// views can't see the click, and carries its link type.
|
|
12084
|
-
function describeButtonClick(control) {
|
|
12434
|
+
function describeButtonClick(control, path) {
|
|
12435
|
+
if (path === void 0) {
|
|
12436
|
+
path = [];
|
|
12437
|
+
}
|
|
12085
12438
|
var link;
|
|
12086
12439
|
if (control.matches('a[href]:not([role="button"])')) {
|
|
12087
12440
|
link = describeLink(control);
|
|
@@ -12090,28 +12443,41 @@ var mParticle = (function () {
|
|
|
12090
12443
|
}
|
|
12091
12444
|
}
|
|
12092
12445
|
var element = elementKind(control);
|
|
12093
|
-
var
|
|
12094
|
-
|
|
12446
|
+
var box = control instanceof HTMLInputElement && (control.type === 'checkbox' || control.type === 'radio') ? control : undefined;
|
|
12447
|
+
// A box's text sent with its state is the shopper's answer, and the text
|
|
12448
|
+
// can be a name, an address or a sensitive choice, so a box goes by its
|
|
12449
|
+
// hand-written identifiers alone. So does a control acting as one or
|
|
12450
|
+
// wrapping one, and a label, whose click the browser passes on to its box
|
|
12451
|
+
// as a second event.
|
|
12452
|
+
var choice = box || choiceOf(control, path);
|
|
12453
|
+
// A checkbox's identifiers name its question, which its state answers. A
|
|
12454
|
+
// radio's id, test id and the panel it opens name the option picked, so
|
|
12455
|
+
// only the name of its input, the question, goes with it. A wrapper's own
|
|
12456
|
+
// name, or a role radio's, can name the option instead.
|
|
12457
|
+
var radio = choice instanceof HTMLInputElement ? choice.type === 'radio' : !!choice && choice.matches(RADIO_ROLE);
|
|
12458
|
+
var named = !radio ? control : choice instanceof HTMLInputElement ? choice : null;
|
|
12459
|
+
var name = choice ? undefined : accessibleName(control);
|
|
12460
|
+
var label = name ? maskLabel(name.raw) : undefined;
|
|
12095
12461
|
return {
|
|
12096
12462
|
label: label,
|
|
12097
12463
|
labelSource: label ? name.source : 'none',
|
|
12098
12464
|
// Kept on its own as well: it often says more than the visible text,
|
|
12099
12465
|
// such as which item a remove button removes.
|
|
12100
|
-
ariaLabel: maskLabel(control.getAttribute('aria-label')),
|
|
12101
|
-
icon: iconHint(control),
|
|
12102
|
-
elementId: stableIdentifier(control.getAttribute('id')),
|
|
12103
|
-
elementName: stableIdentifier(
|
|
12104
|
-
testId: firstIdentifier(control, TEST_ID_ATTRIBUTES),
|
|
12105
|
-
controls: controlsIdentifier(control),
|
|
12466
|
+
ariaLabel: choice ? undefined : maskLabel(control.getAttribute('aria-label')),
|
|
12467
|
+
icon: choice ? undefined : iconHint(control),
|
|
12468
|
+
elementId: radio ? undefined : stableIdentifier(control.getAttribute('id')),
|
|
12469
|
+
elementName: stableIdentifier(named && named.getAttribute('name')),
|
|
12470
|
+
testId: radio ? undefined : firstIdentifier(control, TEST_ID_ATTRIBUTES),
|
|
12471
|
+
controls: radio ? undefined : controlsIdentifier(control),
|
|
12106
12472
|
form: formIdentifier(control),
|
|
12107
12473
|
element: element,
|
|
12108
|
-
type: controlType(control
|
|
12474
|
+
type: controlType(control),
|
|
12109
12475
|
linkType: link ? link.type : undefined,
|
|
12110
12476
|
linkDomain: link ? link.domain : undefined,
|
|
12111
12477
|
fileExtension: link ? link.fileExtension : undefined,
|
|
12112
12478
|
// The box as the click left it: the browser toggles it before the click
|
|
12113
12479
|
// reaches listeners, and puts it back if the click is cancelled.
|
|
12114
|
-
checked:
|
|
12480
|
+
checked: box ? box.checked : undefined
|
|
12115
12481
|
};
|
|
12116
12482
|
}
|
|
12117
12483
|
|
|
@@ -12128,6 +12494,10 @@ var mParticle = (function () {
|
|
|
12128
12494
|
var linkCommits = new RegExp('^(?:(?:place|submit|complete)(?!(?: [^ ]+){0,2} (?:another|new|next)(?: |$))(?: [^ ]+){0,2} (?:order|purchase)|' + otherCommits + ')$');
|
|
12129
12495
|
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)$');
|
|
12130
12496
|
var steps = new RegExp('^(?:' + ["(?:let's |lets |go to |proceed to |secure )?(?:check ?out|checkout)(?: now| securely| as guest)?", '(?:continue|proceed) to (?:payment|review|billing|shipping|delivery|order review)|review (?:my |your )?order', 'guest checkout|(?:pay|continue|check ?out) (?:with|by) (?:card|credit card|debit card|credit or debit card)', '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('|') + ')$');
|
|
12497
|
+
var CART_NOUN = '(?:(?:my|your|the) )?(?:shopping )?(?:cart|bag|basket)';
|
|
12498
|
+
// "Add to bag", "View cart", "Shopping bag", "Bag", and a count badge such as
|
|
12499
|
+
// "1 item in bag", which reads "item in bag" once digits are stripped.
|
|
12500
|
+
var cartLabels = new RegExp('^(?:add(?: [^ ]+){0,5} to ' + CART_NOUN + '|(?:(?:view|open|go to) )?' + CART_NOUN + '(?: items?)?' + '|items? in ' + CART_NOUN + ')$');
|
|
12131
12501
|
var WALLET_NAMES = 'apple pay|google pay|paypal|venmo|amazon pay|shop pay|klarna|afterpay|clearpay|cash app(?: pay)?|affirm|sezzle|samsung pay';
|
|
12132
12502
|
var WALLET_VERBS = 'pay|buy|purchase|order|book|donate|subscribe|check ?out|checkout|express checkout|continue|proceed|' + 'complete purchase|place (?:my |your )?order';
|
|
12133
12503
|
// "Pay with Klarna", "Buy with GPay", "Continue to PayPal".
|
|
@@ -12203,6 +12573,14 @@ var mParticle = (function () {
|
|
|
12203
12573
|
return !!label;
|
|
12204
12574
|
});
|
|
12205
12575
|
}
|
|
12576
|
+
// A cart button is often a bare icon, so its title names it when nothing else
|
|
12577
|
+
// does. Only the cart rule reads the title.
|
|
12578
|
+
function isCartControl(control, cores) {
|
|
12579
|
+
var title = cores.length ? undefined : normalize(control.getAttribute('title'));
|
|
12580
|
+
return (title ? [coreLabel(title)] : cores).some(function (text) {
|
|
12581
|
+
return cartLabels.test(text);
|
|
12582
|
+
});
|
|
12583
|
+
}
|
|
12206
12584
|
// "Buy now" and "Purchase" count only on a checkout route, and never on a plain
|
|
12207
12585
|
// link, which is navigation.
|
|
12208
12586
|
function isCommitLabel(text, isLink, pathname) {
|
|
@@ -12214,20 +12592,33 @@ var mParticle = (function () {
|
|
|
12214
12592
|
// Classifies a clicked or submitting control found by findControl, in order:
|
|
12215
12593
|
// pickers, toggles and pay-later messages never count; a vendor's own wallet
|
|
12216
12594
|
// button counts before any label rule; a confirm label on any source counts
|
|
12217
|
-
// before the look-alike exclusions veto; then wallet
|
|
12595
|
+
// before the look-alike exclusions veto; then a wallet-named control, wallet
|
|
12596
|
+
// phrases and steps. A cart label, links included, counts only where none of
|
|
12597
|
+
// those do.
|
|
12218
12598
|
function classifyIntent(control, pathname) {
|
|
12219
12599
|
var vendor = vendorButtonKind(control);
|
|
12220
|
-
if (
|
|
12600
|
+
if (vendor === 'none') {
|
|
12221
12601
|
return undefined;
|
|
12222
12602
|
}
|
|
12223
12603
|
if (vendor) {
|
|
12224
|
-
return {
|
|
12604
|
+
return isQuietIntentControl(control, vendor === 'wallet') ? undefined : {
|
|
12225
12605
|
kind: vendor,
|
|
12226
12606
|
source: 'vendor'
|
|
12227
12607
|
};
|
|
12228
12608
|
}
|
|
12229
12609
|
var texts = labels(control);
|
|
12230
12610
|
var cores = texts.map(coreLabel);
|
|
12611
|
+
var cart = isCartControl(control, cores) ? {
|
|
12612
|
+
kind: 'cart',
|
|
12613
|
+
source: 'label'
|
|
12614
|
+
} : undefined;
|
|
12615
|
+
if (isQuietIntentControl(control, false, !!cart)) {
|
|
12616
|
+
return undefined;
|
|
12617
|
+
}
|
|
12618
|
+
// A bag button that opens a flyout counts only as a cart click.
|
|
12619
|
+
if (cart && control.hasAttribute('aria-expanded')) {
|
|
12620
|
+
return cart;
|
|
12621
|
+
}
|
|
12231
12622
|
var isLink = control.matches('a[href]:not([role="button"])');
|
|
12232
12623
|
if (cores.some(function (text) {
|
|
12233
12624
|
return isCommitLabel(text, isLink, pathname);
|
|
@@ -12240,7 +12631,13 @@ var mParticle = (function () {
|
|
|
12240
12631
|
if (texts.concat(cores).some(function (text) {
|
|
12241
12632
|
return englishExclusions.test(text) || otherExclusions.test(text);
|
|
12242
12633
|
})) {
|
|
12243
|
-
return
|
|
12634
|
+
return cart;
|
|
12635
|
+
}
|
|
12636
|
+
if (isWalletNamedControl(control)) {
|
|
12637
|
+
return {
|
|
12638
|
+
kind: 'wallet',
|
|
12639
|
+
source: 'name'
|
|
12640
|
+
};
|
|
12244
12641
|
}
|
|
12245
12642
|
var isButton = !isLink && control.matches(CONTROL_SELECTOR);
|
|
12246
12643
|
for (var _i = 0, cores_1 = cores; _i < cores_1.length; _i++) {
|
|
@@ -12264,7 +12661,7 @@ var mParticle = (function () {
|
|
|
12264
12661
|
};
|
|
12265
12662
|
}
|
|
12266
12663
|
}
|
|
12267
|
-
return
|
|
12664
|
+
return cart;
|
|
12268
12665
|
}
|
|
12269
12666
|
// The host is the domain or one of its subdomains, never a look-alike suffix.
|
|
12270
12667
|
function isOnDomain(host, domain) {
|
|
@@ -12307,6 +12704,8 @@ var mParticle = (function () {
|
|
|
12307
12704
|
var WIN_BTN_KEY = '__mpBtn__';
|
|
12308
12705
|
var THROTTLE_MS = 2000;
|
|
12309
12706
|
var SUBMIT_AFTER_COMMIT_CLICK_MS = 1000;
|
|
12707
|
+
// Apple Pay must begin inside the tap's own handler, so the tap is this recent.
|
|
12708
|
+
var APPLE_PAY_AFTER_CLICK_MS = 1000;
|
|
12310
12709
|
var PATH_LIMIT = 12;
|
|
12311
12710
|
// A ceiling, not a dedupe: a held Enter key or browser automation fires a
|
|
12312
12711
|
// trusted click per repeat, and each one is forwarded to every kit.
|
|
@@ -12345,6 +12744,63 @@ var mParticle = (function () {
|
|
|
12345
12744
|
setTimeout(fn, 0);
|
|
12346
12745
|
}
|
|
12347
12746
|
};
|
|
12747
|
+
// Apple Pay's session opens only from a shopper's tap, whatever button the page
|
|
12748
|
+
// draws for it, so its opening is a wallet tap no label or name rule can miss.
|
|
12749
|
+
// The wrapper keeps the page's own begin under this key, to put it back.
|
|
12750
|
+
var APPLE_PAY_HOOK = '__mpApplePayHook__';
|
|
12751
|
+
var applePayPrototype = function applePayPrototype(win) {
|
|
12752
|
+
var session = win['ApplePaySession'];
|
|
12753
|
+
return session && session.prototype;
|
|
12754
|
+
};
|
|
12755
|
+
// Wraps ApplePaySession.prototype.begin once per window, so the active tracker
|
|
12756
|
+
// hears that a session began. The wrapper reads no arguments and no payment
|
|
12757
|
+
// data, and returns or throws exactly what the original does. Nothing here can
|
|
12758
|
+
// throw into SDK init: a throwing getter or a frozen prototype leaves Apple Pay
|
|
12759
|
+
// as it was.
|
|
12760
|
+
var hookApplePay = function hookApplePay(win) {
|
|
12761
|
+
try {
|
|
12762
|
+
var prototype = applePayPrototype(win);
|
|
12763
|
+
var begin_1 = prototype && prototype.begin;
|
|
12764
|
+
if (!isFunction(begin_1) || begin_1[APPLE_PAY_HOOK]) {
|
|
12765
|
+
return;
|
|
12766
|
+
}
|
|
12767
|
+
var opened = function opened() {
|
|
12768
|
+
var args = [];
|
|
12769
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
12770
|
+
args[_i] = arguments[_i];
|
|
12771
|
+
}
|
|
12772
|
+
var result = begin_1.apply(this, args);
|
|
12773
|
+
try {
|
|
12774
|
+
var tracker = getActiveButtonTracker();
|
|
12775
|
+
if (tracker) {
|
|
12776
|
+
tracker.onApplePayBegin();
|
|
12777
|
+
}
|
|
12778
|
+
} catch (e) {
|
|
12779
|
+
// A session the page began is never disturbed by tracking.
|
|
12780
|
+
}
|
|
12781
|
+
return result;
|
|
12782
|
+
};
|
|
12783
|
+
// Not enumerable, so the marker doesn't show among the method's own keys.
|
|
12784
|
+
Object.defineProperty(opened, APPLE_PAY_HOOK, {
|
|
12785
|
+
value: begin_1
|
|
12786
|
+
});
|
|
12787
|
+
prototype.begin = opened;
|
|
12788
|
+
} catch (e) {
|
|
12789
|
+
// Apple Pay stays as the page set it up.
|
|
12790
|
+
}
|
|
12791
|
+
};
|
|
12792
|
+
// Puts the page's own begin back, unless another script has wrapped it since.
|
|
12793
|
+
var unhookApplePay = function unhookApplePay(win) {
|
|
12794
|
+
try {
|
|
12795
|
+
var prototype = applePayPrototype(win);
|
|
12796
|
+
var begin = prototype && prototype.begin;
|
|
12797
|
+
if (isFunction(begin) && isFunction(begin[APPLE_PAY_HOOK])) {
|
|
12798
|
+
prototype.begin = begin[APPLE_PAY_HOOK];
|
|
12799
|
+
}
|
|
12800
|
+
} catch (e) {
|
|
12801
|
+
// Apple Pay stays as it is.
|
|
12802
|
+
}
|
|
12803
|
+
};
|
|
12348
12804
|
var CAPTURE_PASSIVE = {
|
|
12349
12805
|
capture: true,
|
|
12350
12806
|
passive: true
|
|
@@ -12362,6 +12818,9 @@ var mParticle = (function () {
|
|
|
12362
12818
|
// Bumped on reset so classification queued before it is dropped.
|
|
12363
12819
|
this.generation = 0;
|
|
12364
12820
|
this.lastCommitClick = -Infinity;
|
|
12821
|
+
// The latest trusted click: whether the page excluded it, and the intent it
|
|
12822
|
+
// already carries to the kit, with its source, if any.
|
|
12823
|
+
this.lastClick = null;
|
|
12365
12824
|
this.lastSent = {};
|
|
12366
12825
|
this.pagePath = null;
|
|
12367
12826
|
this.pageEvents = 0;
|
|
@@ -12445,8 +12904,40 @@ var mParticle = (function () {
|
|
|
12445
12904
|
win[WIN_BTN_KEY] = {};
|
|
12446
12905
|
}
|
|
12447
12906
|
win[WIN_BTN_KEY].tracker = this;
|
|
12907
|
+
// Only where checkout intent can reach the kit; a shopper who opts back in mid-page gets no session
|
|
12908
|
+
// signal until the next page load.
|
|
12909
|
+
if (!this.isKitSuppressed()) {
|
|
12910
|
+
hookApplePay(win);
|
|
12911
|
+
}
|
|
12448
12912
|
this.log('listening');
|
|
12449
12913
|
};
|
|
12914
|
+
// An Apple Pay session begun right after a tracked tap counts as a wallet
|
|
12915
|
+
// tap for the kit; one begun with no such tap, or from a tap the page
|
|
12916
|
+
// excluded, does not. A tap that reads as a confirm tap reaches the kit as
|
|
12917
|
+
// one. It is delivered on a timer, not after the next paint, since the
|
|
12918
|
+
// payment sheet may hold painting back: a tap that already reads as a
|
|
12919
|
+
// wallet goes now under its own rule, and the throttle drops the copy
|
|
12920
|
+
// queued for the paint.
|
|
12921
|
+
ButtonTracker.prototype.onApplePayBegin = function () {
|
|
12922
|
+
var _this = this;
|
|
12923
|
+
var t = Date.now();
|
|
12924
|
+
var click = this.lastClick;
|
|
12925
|
+
if (!this.active || !click || click.excluded || click.kind === 'commit' || t - click.t > APPLE_PAY_AFTER_CLICK_MS || this.isKitSuppressed()) {
|
|
12926
|
+
return;
|
|
12927
|
+
}
|
|
12928
|
+
var pathname = window.location.pathname;
|
|
12929
|
+
var generation = this.generation;
|
|
12930
|
+
setTimeout(function () {
|
|
12931
|
+
if (generation !== _this.generation) {
|
|
12932
|
+
return;
|
|
12933
|
+
}
|
|
12934
|
+
try {
|
|
12935
|
+
_this.emit('wallet', click.kind === 'wallet' ? click.source : 'api', 'apple_pay', t, pathname);
|
|
12936
|
+
} catch (e) {
|
|
12937
|
+
_this.log('[error] Apple Pay delivery threw: ' + e);
|
|
12938
|
+
}
|
|
12939
|
+
}, 0);
|
|
12940
|
+
};
|
|
12450
12941
|
ButtonTracker.prototype.teardown = function () {
|
|
12451
12942
|
var win = btnWindow();
|
|
12452
12943
|
if (win) {
|
|
@@ -12460,6 +12951,7 @@ var mParticle = (function () {
|
|
|
12460
12951
|
var state = win[WIN_BTN_KEY];
|
|
12461
12952
|
if (state && state.tracker === this) {
|
|
12462
12953
|
state.tracker = undefined;
|
|
12954
|
+
unhookApplePay(win);
|
|
12463
12955
|
}
|
|
12464
12956
|
}
|
|
12465
12957
|
this.active = false;
|
|
@@ -12479,6 +12971,7 @@ var mParticle = (function () {
|
|
|
12479
12971
|
this.generation++;
|
|
12480
12972
|
this.pending = [];
|
|
12481
12973
|
this.lastCommitClick = -Infinity;
|
|
12974
|
+
this.lastClick = null;
|
|
12482
12975
|
this.lastSent = {};
|
|
12483
12976
|
this.pagePath = null;
|
|
12484
12977
|
this.pageEvents = 0;
|
|
@@ -12514,9 +13007,11 @@ var mParticle = (function () {
|
|
|
12514
13007
|
var unconfirmedCommit = false;
|
|
12515
13008
|
if (layer === 'click') {
|
|
12516
13009
|
// The composed path is only populated during dispatch.
|
|
12517
|
-
var
|
|
13010
|
+
var path = isFunction(event.composedPath) ? event.composedPath().slice(0, PATH_LIMIT) : [];
|
|
13011
|
+
var excluded = isExcludedPath(path);
|
|
13012
|
+
var control = excluded ? undefined : findControl(path, true);
|
|
12518
13013
|
if (control) {
|
|
12519
|
-
click = logsEvents ? describeButtonClick(control) : undefined;
|
|
13014
|
+
click = logsEvents ? describeButtonClick(control, path) : undefined;
|
|
12520
13015
|
if (click && click.checked !== undefined) {
|
|
12521
13016
|
box = control;
|
|
12522
13017
|
}
|
|
@@ -12525,6 +13020,12 @@ var mParticle = (function () {
|
|
|
12525
13020
|
kind = intent ? intent.kind : undefined;
|
|
12526
13021
|
source = intent ? intent.source : undefined;
|
|
12527
13022
|
}
|
|
13023
|
+
this.lastClick = {
|
|
13024
|
+
t: t,
|
|
13025
|
+
excluded: excluded,
|
|
13026
|
+
kind: kind,
|
|
13027
|
+
source: source
|
|
13028
|
+
};
|
|
12528
13029
|
} else if (layer === 'submit') {
|
|
12529
13030
|
var submitter = event.submitter;
|
|
12530
13031
|
var control = submitter ? findControl([submitter]) : undefined;
|
|
@@ -13618,6 +14119,7 @@ var mParticle = (function () {
|
|
|
13618
14119
|
mpInstance._NativeSdkHelpers.initializeSessionAttributes(apiKey);
|
|
13619
14120
|
// Button tracking never runs as a native passthrough.
|
|
13620
14121
|
resetButtonTracking();
|
|
14122
|
+
mpInstance._RoktManager.setAutoButtonClicksForwarded(false);
|
|
13621
14123
|
} else {
|
|
13622
14124
|
// Main SDK initialization flow
|
|
13623
14125
|
// Load any settings/identities/attributes from cookie or localStorage
|
|
@@ -13662,6 +14164,15 @@ var mParticle = (function () {
|
|
|
13662
14164
|
// https://go.mparticle.com/work/SQDSDKS-7339
|
|
13663
14165
|
mpInstance._RoktManager.init(roktConfig_1, createRoktFilteredUser(currentUserMPID), mpInstance.Identity, mpInstance._Store, mpInstance.Logger, roktOptions, mpInstance.captureTiming, mpInstance._ErrorReportingDispatcher, mpInstance._LoggingDispatcher, mpInstance._IntegrationCapture, createRoktFilteredUser);
|
|
13664
14166
|
}
|
|
14167
|
+
// Checkout intent comes from the shopper's clicks, so it goes to the
|
|
14168
|
+
// Rokt kit only when that kit receives them. Set before
|
|
14169
|
+
// processForwarders: the kit can read it during this init, from its own
|
|
14170
|
+
// init or from a returning shopper's landing page view.
|
|
14171
|
+
var buttonClickBlocker = roktConfig_1 ? getButtonClickBlocker(roktConfig_1, kitBlocker) : undefined;
|
|
14172
|
+
if (buttonClickBlocker && getFeatureFlag(AutoButtonTracking)) {
|
|
14173
|
+
mpInstance.Logger.warning("Checkout intent is off: the ".concat(buttonClickBlocker, " keeps some or all Auto Button Click events from the Rokt kit."));
|
|
14174
|
+
}
|
|
14175
|
+
mpInstance._RoktManager.setAutoButtonClicksForwarded(!!roktConfig_1 && !buttonClickBlocker);
|
|
13665
14176
|
mpInstance._Forwarders.processForwarders(config, mpInstance._APIClient.prepareForwardingStats);
|
|
13666
14177
|
mpInstance._Forwarders.processPixelConfigs(config);
|
|
13667
14178
|
// Checks if session is created, resumed, or needs to be ended
|
|
@@ -13696,8 +14207,6 @@ var mParticle = (function () {
|
|
|
13696
14207
|
// so it stops listening. Re-created if the flag is re-enabled later.
|
|
13697
14208
|
mpInstance._PageViewTracker.teardown();
|
|
13698
14209
|
}
|
|
13699
|
-
// Button clicks are logged for any workspace that opts in. Checkout
|
|
13700
|
-
// intent also goes to the Rokt kit when this init configures one.
|
|
13701
14210
|
if (getFeatureFlag(AutoButtonTracking)) {
|
|
13702
14211
|
if (!mpInstance._ButtonTracker) {
|
|
13703
14212
|
mpInstance._ButtonTracker = new ButtonTracker(mpInstance);
|
|
@@ -14308,8 +14817,8 @@ var mParticle = (function () {
|
|
|
14308
14817
|
this.configurePixel = function (settings) {
|
|
14309
14818
|
self.getInstance().configurePixel(settings);
|
|
14310
14819
|
};
|
|
14311
|
-
this._setIntegrationDelay = function (moduleId,
|
|
14312
|
-
self.getInstance()._setIntegrationDelay(moduleId,
|
|
14820
|
+
this._setIntegrationDelay = function (moduleId, boolean) {
|
|
14821
|
+
self.getInstance()._setIntegrationDelay(moduleId, boolean);
|
|
14313
14822
|
};
|
|
14314
14823
|
this._getIntegrationDelays = function () {
|
|
14315
14824
|
return self.getInstance()._getIntegrationDelays();
|