@mparticle/web-sdk 3.27.0 → 3.29.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 +2 -2
- package/dist/mparticle.common.js +1 -1
- package/dist/mparticle.esm.js +1 -1
- package/dist/mparticle.js +341 -108
- package/dist/types/src/buttonTracking/buttonDescription.d.ts +2 -1
- package/dist/types/src/buttonTracking/buttonTracker.d.ts +0 -6
- package/dist/types/src/buttonTracking/checkoutIntent.d.ts +2 -0
- package/dist/types/src/pathMasking.d.ts +2 -0
- package/package.json +1 -1
- package/src/batchUploader.ts +25 -9
- package/src/buttonTracking/buttonDescription.ts +266 -46
- package/src/buttonTracking/buttonTracker.ts +22 -62
- package/src/buttonTracking/checkoutIntent.ts +56 -2
- package/src/pageViewTracker.ts +3 -1
- package/src/pathMasking.ts +116 -9
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.29.0";
|
|
208
208
|
|
|
209
209
|
var Constants = {
|
|
210
210
|
sdkVersion: version,
|
|
@@ -2756,6 +2756,14 @@ var mParticle = (function () {
|
|
|
2756
2756
|
// A valid escape left after decoding stopped, as when a literal `%` before it
|
|
2757
2757
|
// can't be decoded again. What it hides is unknown, so it counts as sensitive.
|
|
2758
2758
|
var LEFTOVER_ESCAPE = /%[0-9A-F]{2}/i;
|
|
2759
|
+
// Pages under a personal route that name no one: the sign-in, sign-up and
|
|
2760
|
+
// account steps sites keep there, as in `/u/login` or `/u/mfa-sms-challenge`.
|
|
2761
|
+
var ACCOUNT_PAGE = /^(?:log-?in|log-?out|sign-?in|sign-?out|sign-?up|register|registration|create-account|account|settings|forgot-password|reset-password|password|challenge|consent|callback|authorize|continue|mfa(?:-[a-z]+)*)$/i;
|
|
2762
|
+
// Whether a segment after a route goes as `*`: always after an order route, and
|
|
2763
|
+
// after a personal route unless it is one of the account pages kept there.
|
|
2764
|
+
function hidesSegmentAfter(route, segment) {
|
|
2765
|
+
return hidesNextSegment(route) && !(PERSONAL_ROUTE.test(route) && ACCOUNT_PAGE.test(segment));
|
|
2766
|
+
}
|
|
2759
2767
|
function hidesNextSegment(route) {
|
|
2760
2768
|
return PERSONAL_ROUTE.test(route) || ORDER_ROUTE.test(route) && !PRODUCT_ORDER_WORD.test(route);
|
|
2761
2769
|
}
|
|
@@ -2832,11 +2840,91 @@ var mParticle = (function () {
|
|
|
2832
2840
|
return INNER_CASE_SWITCH.test(piece);
|
|
2833
2841
|
}) && isToken(joined) || pieces.filter(mixesLettersAndDigits).length >= 2 && MIXED_TOKEN.test(joined);
|
|
2834
2842
|
}
|
|
2843
|
+
// A word a product name is made of: three or more letters, lowercase or
|
|
2844
|
+
// Capitalised. A code's mixed case or capital-letter groups aren't words.
|
|
2845
|
+
var isWord = function isWord(piece) {
|
|
2846
|
+
return /^(?:[a-z]{3,}|[A-Z][a-z]{2,})$/.test(piece);
|
|
2847
|
+
};
|
|
2848
|
+
var UUID_IN_TEXT = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
2849
|
+
// Route words whose next segment names a product, as in `/p/<name>` or
|
|
2850
|
+
// `/product/<name>`. Only there can words beside a token be a product's name
|
|
2851
|
+
// rather than a person's or part of a code.
|
|
2852
|
+
var PRODUCT_ROUTE = /^(?:p|product|products|item|items|dp|shop|catalog)$/i;
|
|
2853
|
+
// A segment that holds a token goes as `*`. After a product route, only the
|
|
2854
|
+
// token goes, so `/p/trail-runner-gtx-zq7Kd93Lm41` keeps its name as
|
|
2855
|
+
// `/p/trail-runner-gtx-*`.
|
|
2856
|
+
// Every piece that isn't a word in an unbroken run with the token goes with it
|
|
2857
|
+
// as one `*`, so the rest of a UUID or of a code cut into short pieces never
|
|
2858
|
+
// stays. A piece that can't be decoded or decodes to a separator counts as a
|
|
2859
|
+
// token, and so does any piece that mixes letters and digits or switches case
|
|
2860
|
+
// inside, as a code's short pieces do. The segment goes whole as `*` when no
|
|
2861
|
+
// piece is a word or it holds a UUID. Undefined when it holds no token. `segment` is as the page
|
|
2862
|
+
// has it, `text` decoded, `route` the route word before it.
|
|
2863
|
+
function maskTokenSegment(segment, text, route) {
|
|
2864
|
+
if (!isTokenSegment(text)) {
|
|
2865
|
+
return undefined;
|
|
2866
|
+
}
|
|
2867
|
+
// A UUID anywhere in the decoded text, however its hyphens are escaped,
|
|
2868
|
+
// takes the whole segment.
|
|
2869
|
+
if (!route || !PRODUCT_ROUTE.test(route) || UUID_IN_TEXT.test(text)) {
|
|
2870
|
+
return '*';
|
|
2871
|
+
}
|
|
2872
|
+
var parts = segment.split(/([-_.~])/);
|
|
2873
|
+
var raws = parts.filter(function (_, i) {
|
|
2874
|
+
return i % 2 === 0;
|
|
2875
|
+
});
|
|
2876
|
+
var separators = parts.filter(function (_, i) {
|
|
2877
|
+
return i % 2 === 1;
|
|
2878
|
+
});
|
|
2879
|
+
var decoded = raws.map(function (raw) {
|
|
2880
|
+
return decodeFully(raw);
|
|
2881
|
+
});
|
|
2882
|
+
var kinds = decoded.map(function (piece) {
|
|
2883
|
+
if (piece === undefined || /[-_.~]/.test(piece)) {
|
|
2884
|
+
return 'token';
|
|
2885
|
+
}
|
|
2886
|
+
var plain = piece.replace(/\+/g, '');
|
|
2887
|
+
return isToken(plain) || mixesLettersAndDigits(plain) || INNER_CASE_SWITCH.test(plain) ? 'token' : isWord(plain) ? 'word' : 'other';
|
|
2888
|
+
});
|
|
2889
|
+
// Each unbroken run of pieces that aren't words is hidden when it holds a
|
|
2890
|
+
// token.
|
|
2891
|
+
var masked = kinds.map(function () {
|
|
2892
|
+
return false;
|
|
2893
|
+
});
|
|
2894
|
+
for (var start = 0; start < kinds.length;) {
|
|
2895
|
+
if (kinds[start] === 'word') {
|
|
2896
|
+
start++;
|
|
2897
|
+
continue;
|
|
2898
|
+
}
|
|
2899
|
+
var end = start;
|
|
2900
|
+
while (end < kinds.length && kinds[end] !== 'word') {
|
|
2901
|
+
end++;
|
|
2902
|
+
}
|
|
2903
|
+
// A run reads as a token also when only its pieces joined do.
|
|
2904
|
+
if (kinds.slice(start, end).indexOf('token') !== -1 || isToken(raws.slice(start, end).join(''))) {
|
|
2905
|
+
for (var k = start; k < end; k++) {
|
|
2906
|
+
masked[k] = true;
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
start = end;
|
|
2910
|
+
}
|
|
2911
|
+
if (masked.indexOf(true) === -1 || kinds.indexOf('word') === -1) {
|
|
2912
|
+
return '*';
|
|
2913
|
+
}
|
|
2914
|
+
var out = '';
|
|
2915
|
+
for (var k = 0; k < raws.length; k++) {
|
|
2916
|
+
if (masked[k] && k > 0 && masked[k - 1]) {
|
|
2917
|
+
continue;
|
|
2918
|
+
}
|
|
2919
|
+
out += (k > 0 ? separators[k - 1] : '') + (masked[k] ? '*' : raws[k]);
|
|
2920
|
+
}
|
|
2921
|
+
return out;
|
|
2922
|
+
}
|
|
2835
2923
|
// The path a click logs. A segment that can single out one shopper
|
|
2836
|
-
// goes as `*`: one holding an @, `;` or `=`,
|
|
2837
|
-
// that names a person, carries a code or opens an order.
|
|
2838
|
-
// exactly as the page has them, plain
|
|
2839
|
-
// they name the product.
|
|
2924
|
+
// goes as `*`: one holding an @, `;` or `=`, an id, or the one after a route
|
|
2925
|
+
// that names a person, carries a code or opens an order. A token inside a name
|
|
2926
|
+
// goes as `*` alone. Kept segments stay exactly as the page has them, plain
|
|
2927
|
+
// numbers included, since on a product page they name the product.
|
|
2840
2928
|
function pagePath(pathname) {
|
|
2841
2929
|
var segments = pathname.split('/');
|
|
2842
2930
|
return segments.map(function (segment, i) {
|
|
@@ -2844,9 +2932,13 @@ var mParticle = (function () {
|
|
|
2844
2932
|
var route = i > 0 ? routeWord(routeSegment(segments, i)) : '';
|
|
2845
2933
|
// A route word that can't be decoded may be any route, so the
|
|
2846
2934
|
// segment after it is hidden too.
|
|
2847
|
-
var hiddenByRoute = i > 0 && !!text && (route === undefined ||
|
|
2935
|
+
var hiddenByRoute = i > 0 && !!text && (route === undefined || hidesSegmentAfter(route, text));
|
|
2848
2936
|
// An encoded slash hides several segments in one, so it goes whole.
|
|
2849
|
-
|
|
2937
|
+
if (text === undefined || /[@;=/]/.test(text) || LEFTOVER_ESCAPE.test(text) || hiddenByRoute) {
|
|
2938
|
+
return '*';
|
|
2939
|
+
}
|
|
2940
|
+
var masked = maskTokenSegment(segment, text, route);
|
|
2941
|
+
return masked === undefined ? segment : masked;
|
|
2850
2942
|
}).join('/');
|
|
2851
2943
|
}
|
|
2852
2944
|
// A token long enough that no page or product name looks like it: 16 or more
|
|
@@ -3496,7 +3588,9 @@ var mParticle = (function () {
|
|
|
3496
3588
|
title: title,
|
|
3497
3589
|
isAutoPageView: this.isAutoPageView
|
|
3498
3590
|
}));
|
|
3499
|
-
|
|
3591
|
+
// A title can name the shopper or an order, so it stays out of the log
|
|
3592
|
+
// line, for the same reason describePage omits param values.
|
|
3593
|
+
this.log("[fire] deferred flush -> _Events.logEvent(PageView) (page: ".concat(describePage(page), ")"));
|
|
3500
3594
|
this.mpInstance._Events.logEvent(event);
|
|
3501
3595
|
};
|
|
3502
3596
|
PageViewTracker.prototype.log = function (message) {
|
|
@@ -4270,13 +4364,12 @@ var mParticle = (function () {
|
|
|
4270
4364
|
// TODO: https://go.mparticle.com/work/SQDSDKS-5165
|
|
4271
4365
|
(_d = this.batchesQueuedForProcessing).unshift.apply(_d, batchesThatDidNotUpload);
|
|
4272
4366
|
}
|
|
4273
|
-
// Update Offline Storage with current state of batch queue
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
// Local Storage once we can confirm they are successfully uploaded.
|
|
4367
|
+
// Update Offline Storage with current state of batch queue. Storage was
|
|
4368
|
+
// purged before sending. A beacon the browser accepted counts as sent, so
|
|
4369
|
+
// the beacon path writes nothing unless the browser refused a batch: then
|
|
4370
|
+
// `batchesThatDidNotUpload` holds it, and it is stored since the page may
|
|
4371
|
+
// be going away.
|
|
4372
|
+
if (this.offlineStorageEnabled && this.batchVault && (!useBeacon || !isEmpty(batchesThatDidNotUpload))) {
|
|
4280
4373
|
this.storeBatchesQueuedForProcessing();
|
|
4281
4374
|
}
|
|
4282
4375
|
if (triggerFuture && !this.destroyed) {
|
|
@@ -4333,7 +4426,7 @@ var mParticle = (function () {
|
|
|
4333
4426
|
};
|
|
4334
4427
|
BatchUploader.prototype.uploadBatches = function (batches, useBeacon) {
|
|
4335
4428
|
return __awaiter(this, void 0, void 0, function () {
|
|
4336
|
-
var uploads, uploadsToLog, i, fetchPayload, blob, response, e_1;
|
|
4429
|
+
var uploads, uploadsToLog, refused, i, fetchPayload, blob, response, e_1;
|
|
4337
4430
|
return __generator(this, function (_a) {
|
|
4338
4431
|
switch (_a.label) {
|
|
4339
4432
|
case 0:
|
|
@@ -4348,6 +4441,7 @@ var mParticle = (function () {
|
|
|
4348
4441
|
this.mpInstance.Logger.verbose("Uploading batches: ".concat(JSON.stringify(uploadsToLog)));
|
|
4349
4442
|
this.mpInstance.Logger.verbose("Batch count: ".concat(uploads.length));
|
|
4350
4443
|
}
|
|
4444
|
+
refused = [];
|
|
4351
4445
|
i = 0;
|
|
4352
4446
|
_a.label = 1;
|
|
4353
4447
|
case 1:
|
|
@@ -4364,7 +4458,13 @@ var mParticle = (function () {
|
|
|
4364
4458
|
blob = new Blob([fetchPayload.body], {
|
|
4365
4459
|
type: 'text/plain;charset=UTF-8'
|
|
4366
4460
|
});
|
|
4367
|
-
|
|
4461
|
+
// The browser refuses a beacon it won't send, such as one over its
|
|
4462
|
+
// size limit, which says nothing about the next batch. Keep it and
|
|
4463
|
+
// try the rest. Only false is a refusal: a wrapper that returns
|
|
4464
|
+
// nothing has still sent it.
|
|
4465
|
+
if (navigator.sendBeacon(this.uploadUrl, blob) === false) {
|
|
4466
|
+
refused.push(uploads[i]);
|
|
4467
|
+
}
|
|
4368
4468
|
return [3 /*break*/, 5];
|
|
4369
4469
|
case 2:
|
|
4370
4470
|
_a.trys.push([2, 4,, 5]);
|
|
@@ -4395,6 +4495,10 @@ var mParticle = (function () {
|
|
|
4395
4495
|
i++;
|
|
4396
4496
|
return [3 /*break*/, 1];
|
|
4397
4497
|
case 6:
|
|
4498
|
+
if (!isEmpty(refused)) {
|
|
4499
|
+
this.mpInstance.Logger.warning("The browser refused the exit upload of ".concat(refused.length, " batch(es). Keeping them for the next upload."));
|
|
4500
|
+
return [2 /*return*/, refused];
|
|
4501
|
+
}
|
|
4398
4502
|
return [2 /*return*/, null];
|
|
4399
4503
|
}
|
|
4400
4504
|
});
|
|
@@ -12483,12 +12587,14 @@ var mParticle = (function () {
|
|
|
12483
12587
|
function textOf(element) {
|
|
12484
12588
|
return element && !isUnsafeTextSource(element) && isShown(element) ? renderedText(element) : '';
|
|
12485
12589
|
}
|
|
12486
|
-
// The control itself is already known to be shown.
|
|
12590
|
+
// The control itself is already known to be shown. An inline icon names
|
|
12591
|
+
// itself with aria-label rather than alt.
|
|
12487
12592
|
function shownImageAlt(control) {
|
|
12488
|
-
var images = control.querySelectorAll('img[alt]');
|
|
12593
|
+
var images = control.querySelectorAll('img[alt], svg[aria-label], [role="img"][aria-label]');
|
|
12489
12594
|
for (var i = 0; i < images.length && i < MAX_READS; i++) {
|
|
12490
|
-
|
|
12491
|
-
|
|
12595
|
+
var alt = nonBlank(images[i].getAttribute('alt') || images[i].getAttribute('aria-label'));
|
|
12596
|
+
if (alt && isShown(images[i], control) && isVisible(images[i])) {
|
|
12597
|
+
return alt;
|
|
12492
12598
|
}
|
|
12493
12599
|
}
|
|
12494
12600
|
return undefined;
|
|
@@ -12742,7 +12848,11 @@ var mParticle = (function () {
|
|
|
12742
12848
|
}
|
|
12743
12849
|
var segments = path.split('/');
|
|
12744
12850
|
var masked = segments.map(function (segment, i) {
|
|
12745
|
-
|
|
12851
|
+
if (i > 0 && segment && hidesSegmentAfter(routeSegment(segments, i), segment)) {
|
|
12852
|
+
return '*';
|
|
12853
|
+
}
|
|
12854
|
+
var tokens = maskTokenSegment(segment, segment, i > 0 ? routeSegment(segments, i) : undefined);
|
|
12855
|
+
return tokens === undefined ? segment : tokens;
|
|
12746
12856
|
});
|
|
12747
12857
|
return maskNumbers(masked.join('/'));
|
|
12748
12858
|
}
|
|
@@ -12887,15 +12997,14 @@ var mParticle = (function () {
|
|
|
12887
12997
|
fileExtension: link ? link.fileExtension : undefined,
|
|
12888
12998
|
// The box as the click left it: the browser toggles it before the click
|
|
12889
12999
|
// reaches listeners, and puts it back if the click is cancelled.
|
|
12890
|
-
checked: box ? box.checked : undefined
|
|
12891
|
-
pick: choice || control.matches('[role~="tab"]') ? true : undefined
|
|
13000
|
+
checked: box ? box.checked : undefined
|
|
12892
13001
|
};
|
|
12893
13002
|
}
|
|
12894
13003
|
// The question a picker answers, from the group around it: the group's
|
|
12895
13004
|
// aria-label, aria-labelledby or legend, masked like a label and cut at its
|
|
12896
13005
|
// first colon, since a legend can repeat the pick ("Color: Navy"). Never the
|
|
12897
13006
|
// option's own text, which is the shopper's answer.
|
|
12898
|
-
var NAMED_GROUP = '[role="radiogroup"], [role="listbox"], [role="group"], fieldset';
|
|
13007
|
+
var NAMED_GROUP = '[role="radiogroup"], [role="listbox"], [role="group"], fieldset, ' + '[role="region"][aria-label], [role="region"][aria-labelledby]';
|
|
12899
13008
|
// A control such as the button that opens a list box shows the option picked.
|
|
12900
13009
|
var NOT_A_QUESTION = CHOICE + ', ' + CONTROL_SELECTOR + ', a[href], input, select, textarea, slot, [aria-haspopup], [role~="button"], ' + '[role~="link"], [role~="tab"], [role~="combobox"]';
|
|
12901
13010
|
var OPTION_KINDS = ['radio', 'menuitemradio', 'checkbox', 'menuitemcheckbox', 'switch', 'option'];
|
|
@@ -12920,6 +13029,95 @@ var mParticle = (function () {
|
|
|
12920
13029
|
var name = choice.getAttribute('name');
|
|
12921
13030
|
return option instanceof HTMLInputElement && option.type === choice.type && !!name && option.getAttribute('name') === name && option.form === choice.form;
|
|
12922
13031
|
}
|
|
13032
|
+
function isTransparent(element, until) {
|
|
13033
|
+
return someComposedAncestor(element, function (node) {
|
|
13034
|
+
return window.getComputedStyle(node).opacity === '0';
|
|
13035
|
+
}, until);
|
|
13036
|
+
}
|
|
13037
|
+
// Content that is never painted where it sits: SVG that is only referenced or
|
|
13038
|
+
// read, and a canvas's fallback, which some browsers still give boxes.
|
|
13039
|
+
var UNDRAWN = 'svg title, svg desc, svg metadata, svg defs, svg symbol, svg clipPath, ' + 'svg mask, svg pattern, svg marker, svg linearGradient, ' + 'svg radialGradient, svg filter, canvas';
|
|
13040
|
+
// Whether text sits in any closed details element outside its summary.
|
|
13041
|
+
function isInClosedDetails(text, parent) {
|
|
13042
|
+
var details = parent.closest('details:not([open])');
|
|
13043
|
+
while (details) {
|
|
13044
|
+
var summary = Array.from(details.children).filter(function (child) {
|
|
13045
|
+
return child.tagName === 'SUMMARY';
|
|
13046
|
+
})[0];
|
|
13047
|
+
if (!summary || !summary.contains(text)) {
|
|
13048
|
+
return true;
|
|
13049
|
+
}
|
|
13050
|
+
details = details.parentElement ? details.parentElement.closest('details:not([open])') : null;
|
|
13051
|
+
}
|
|
13052
|
+
return false;
|
|
13053
|
+
}
|
|
13054
|
+
// Whether the browser shows an element through every ancestor it renders
|
|
13055
|
+
// under, shadow trees included, which a walk of light ancestors can't see
|
|
13056
|
+
// into when a shadow root is closed. Where it can't say (checkVisibility
|
|
13057
|
+
// arrived in Chrome 105, Firefox 125 and Safari 17.4), the element counts as
|
|
13058
|
+
// not shown, so option rows name no group in older browsers.
|
|
13059
|
+
function isComposedVisible(element) {
|
|
13060
|
+
var check = element.checkVisibility;
|
|
13061
|
+
return isFunction(check) &&
|
|
13062
|
+
// Older Chrome versions read the first two names, newer browsers the rest.
|
|
13063
|
+
check.call(element, {
|
|
13064
|
+
checkOpacity: true,
|
|
13065
|
+
checkVisibilityCSS: true,
|
|
13066
|
+
opacityProperty: true,
|
|
13067
|
+
visibilityProperty: true
|
|
13068
|
+
});
|
|
13069
|
+
}
|
|
13070
|
+
// Whether the browser lays a text node out in a box of its own. Text it never
|
|
13071
|
+
// draws, such as an SVG definition, canvas fallback, closed details or an
|
|
13072
|
+
// unslotted child of a shadow host, has none. Where boxes can't be read, the
|
|
13073
|
+
// text counts as not drawn.
|
|
13074
|
+
function isDrawn(text) {
|
|
13075
|
+
var range = isFunction(document.createRange) ? document.createRange() : null;
|
|
13076
|
+
if (!range || !isFunction(range.getClientRects)) {
|
|
13077
|
+
return false;
|
|
13078
|
+
}
|
|
13079
|
+
range.selectNodeContents(text);
|
|
13080
|
+
return Array.from(range.getClientRects()).some(function (box) {
|
|
13081
|
+
return box.width > 0 && box.height > 0;
|
|
13082
|
+
});
|
|
13083
|
+
}
|
|
13084
|
+
// Whether a row shows text of its own: a drawn, shown, non-transparent text
|
|
13085
|
+
// node, outside any form field, editable area or opted-out part, beside
|
|
13086
|
+
// nothing but the row's own input. Not a row with a shadow root, slot or
|
|
13087
|
+
// custom element, whose rendered text a walk of its light children can't see.
|
|
13088
|
+
function showsText(row, own) {
|
|
13089
|
+
if (isUnsafeTextSource(row, own) || hasComposedAncestor(own, '[data-mp-no-track]') || row.shadowRoot || row.tagName.indexOf('-') !== -1 || row.querySelector('slot')) {
|
|
13090
|
+
return false;
|
|
13091
|
+
}
|
|
13092
|
+
// A row too large to check in full counts as showing nothing.
|
|
13093
|
+
var descendants = row.querySelectorAll('*');
|
|
13094
|
+
if (descendants.length > MAX_READS) {
|
|
13095
|
+
return false;
|
|
13096
|
+
}
|
|
13097
|
+
for (var i = 0; i < descendants.length; i++) {
|
|
13098
|
+
if (descendants[i].shadowRoot || descendants[i].tagName.indexOf('-') !== -1) {
|
|
13099
|
+
return false;
|
|
13100
|
+
}
|
|
13101
|
+
}
|
|
13102
|
+
var walker = document.createTreeWalker(row, NodeFilter.SHOW_TEXT);
|
|
13103
|
+
for (var i = 0; i < MAX_READS && walker.nextNode(); i++) {
|
|
13104
|
+
var parent_1 = walker.currentNode.parentElement;
|
|
13105
|
+
if (parent_1 && nonBlank(walker.currentNode.nodeValue) &&
|
|
13106
|
+
// Undrawn content, and the body of a closed details element,
|
|
13107
|
+
// which browsers still give layout boxes.
|
|
13108
|
+
!parent_1.closest(UNDRAWN) && !isInClosedDetails(walker.currentNode, parent_1) && isShown(parent_1, row) && isVisible(parent_1) && !isTransparent(parent_1, row) && isComposedVisible(parent_1) && isDrawn(walker.currentNode)) {
|
|
13109
|
+
return true;
|
|
13110
|
+
}
|
|
13111
|
+
}
|
|
13112
|
+
return false;
|
|
13113
|
+
}
|
|
13114
|
+
// A native input inside a shown control of its own, such as a menu option row
|
|
13115
|
+
// with a styled radio, which shows text of its own and doesn't also hold the
|
|
13116
|
+
// option picked.
|
|
13117
|
+
function showsRow(option, choice, group) {
|
|
13118
|
+
var row = option instanceof HTMLInputElement && option.parentElement ? option.parentElement.closest(CONTROL_SELECTOR) : null;
|
|
13119
|
+
return !!row && group.contains(row) && !row.contains(choice) && isShown(row, group) && isVisible(row) && !isTransparent(row, group) && showsText(row, option);
|
|
13120
|
+
}
|
|
12923
13121
|
// Whether a group offers another answer to the question picked: a shown option
|
|
12924
13122
|
// to the same question that isn't part of the one picked. A card around one
|
|
12925
13123
|
// address radio and a "make default" checkbox, or around a role radio and the
|
|
@@ -12928,12 +13126,11 @@ var mParticle = (function () {
|
|
|
12928
13126
|
var options = group.querySelectorAll(CHOICE);
|
|
12929
13127
|
for (var i = 0; i < options.length; i++) {
|
|
12930
13128
|
var option = options[i];
|
|
12931
|
-
if (!option.contains(choice) && !choice.contains(option) && sameQuestion(option, choice) && isShown(option, group) && isVisible(option) &&
|
|
13129
|
+
if (!option.contains(choice) && !choice.contains(option) && sameQuestion(option, choice) && isShown(option, group) && isVisible(option) && (
|
|
12932
13130
|
// A fully transparent option, as a native input behind a styled
|
|
12933
|
-
// one often is, shows the shopper no second answer
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
}, group)) {
|
|
13131
|
+
// one often is, shows the shopper no second answer, unless a
|
|
13132
|
+
// shown row around it does.
|
|
13133
|
+
!isTransparent(option, group) || showsRow(option, choice, group))) {
|
|
12937
13134
|
return true;
|
|
12938
13135
|
}
|
|
12939
13136
|
}
|
|
@@ -12951,27 +13148,30 @@ var mParticle = (function () {
|
|
|
12951
13148
|
});
|
|
12952
13149
|
return labels;
|
|
12953
13150
|
}
|
|
12954
|
-
// The group asking the question a pick answers. A role radio's
|
|
12955
|
-
// radiogroup and an option's its list box; a role toggle, such as
|
|
12956
|
-
// stands alone. A native box's is the nearest group offering other
|
|
12957
|
-
// since a group around one option, such as a saved address card, can
|
|
12958
|
-
// named by that option.
|
|
12959
|
-
|
|
13151
|
+
// The name of the group asking the question a pick answers. A role radio's
|
|
13152
|
+
// group is its radiogroup and an option's its list box; a role toggle, such as
|
|
13153
|
+
// a switch, stands alone. A native box's is the nearest group offering other
|
|
13154
|
+
// answers, since a group around one option, such as a saved address card, can
|
|
13155
|
+
// be named by that option. A region with no usable name is passed over, so a
|
|
13156
|
+
// group around it can still name the pick. Each group's name is read once.
|
|
13157
|
+
function groupName(choice) {
|
|
12960
13158
|
if (!(choice instanceof HTMLInputElement)) {
|
|
12961
13159
|
var container = choice.matches('[role~="option"]') ? choice.closest('[role="listbox"]') : choice.matches('[role~="radio"]') ? choice.closest('[role="radiogroup"]') : null;
|
|
12962
|
-
return container && holdsOptions(container, choice) ? container :
|
|
13160
|
+
return container && holdsOptions(container, choice) ? nameOf(container) : undefined;
|
|
12963
13161
|
}
|
|
12964
13162
|
var group = choice.closest(NAMED_GROUP);
|
|
12965
|
-
while (group
|
|
13163
|
+
while (group) {
|
|
13164
|
+
if (holdsOptions(group, choice)) {
|
|
13165
|
+
var name_2 = nameOf(group);
|
|
13166
|
+
if (name_2 || !group.matches('[role="region"]')) {
|
|
13167
|
+
return name_2;
|
|
13168
|
+
}
|
|
13169
|
+
}
|
|
12966
13170
|
group = group.parentElement ? group.parentElement.closest(NAMED_GROUP) : null;
|
|
12967
13171
|
}
|
|
12968
|
-
return
|
|
13172
|
+
return undefined;
|
|
12969
13173
|
}
|
|
12970
|
-
function
|
|
12971
|
-
var group = questionGroup(choice);
|
|
12972
|
-
if (!group) {
|
|
12973
|
-
return undefined;
|
|
12974
|
-
}
|
|
13174
|
+
function nameOf(group) {
|
|
12975
13175
|
var raw = nonBlank(group.getAttribute('aria-label'));
|
|
12976
13176
|
if (!raw) {
|
|
12977
13177
|
var ids = group.getAttribute('aria-labelledby');
|
|
@@ -12979,7 +13179,11 @@ var mParticle = (function () {
|
|
|
12979
13179
|
return child.tagName === 'LEGEND';
|
|
12980
13180
|
})[0] : undefined;
|
|
12981
13181
|
var root_2 = isFunction(group.getRootNode) ? group.getRootNode() : document;
|
|
12982
|
-
var
|
|
13182
|
+
var target = ids ? root_2.getElementById ? root_2.getElementById(ids.trim().split(/\s+/)[0]) : null : legend || null;
|
|
13183
|
+
// A region opened by an accordion header outside a tab list is named
|
|
13184
|
+
// by the header's first heading, never the rest of what it shows.
|
|
13185
|
+
var header_1 = target && group.matches('[role="region"]') && (target.getAttribute('role') || '').trim() === 'tab' && !hasComposedAncestor(target, '[role~="tablist"]') && !target.querySelector(NOT_A_QUESTION) ? target : null;
|
|
13186
|
+
var source_1 = header_1 ? header_1.querySelector('h1, h2, h3, h4, h5, h6, [role="heading"]') : target;
|
|
12983
13187
|
// A label that is, holds or sits in an option or a control, or in an
|
|
12984
13188
|
// option's label, would name the answer.
|
|
12985
13189
|
var holder = source_1 ? source_1.closest('label') : null;
|
|
@@ -12990,7 +13194,9 @@ var mParticle = (function () {
|
|
|
12990
13194
|
return source_1.contains(label) || label.contains(source_1);
|
|
12991
13195
|
});
|
|
12992
13196
|
});
|
|
12993
|
-
raw = source_1 && !labelsOption && !
|
|
13197
|
+
raw = source_1 && !labelsOption && !someComposedAncestor(source_1, function (node) {
|
|
13198
|
+
return node !== header_1 && node.matches(NOT_A_QUESTION);
|
|
13199
|
+
}) && !source_1.querySelector(NOT_A_QUESTION) && !(holder && (holder.control || holder.querySelector(CHOICE))) ? textOf(source_1) : undefined;
|
|
12994
13200
|
}
|
|
12995
13201
|
return raw ? maskLabel(raw.split(':')[0]) : undefined;
|
|
12996
13202
|
}
|
|
@@ -13008,9 +13214,9 @@ var mParticle = (function () {
|
|
|
13008
13214
|
return PAYMENT_QUESTION.test(nameWords(words));
|
|
13009
13215
|
});
|
|
13010
13216
|
}
|
|
13011
|
-
// What names a payment option: the radio's accessible name
|
|
13012
|
-
// image of each label,
|
|
13013
|
-
function
|
|
13217
|
+
// What names a payment option to shoppers: the radio's accessible name and the
|
|
13218
|
+
// text and image of each label, masked like a label.
|
|
13219
|
+
function shownOptionNames(radio) {
|
|
13014
13220
|
var names = [maskLabel(accessibleName(radio).raw)];
|
|
13015
13221
|
var labels = radio instanceof HTMLInputElement && radio.labels ? Array.from(radio.labels).slice(0, 2) : [];
|
|
13016
13222
|
labels.forEach(function (label) {
|
|
@@ -13019,11 +13225,18 @@ var mParticle = (function () {
|
|
|
13019
13225
|
names.push(maskLabel(shownImageAlt(label)));
|
|
13020
13226
|
}
|
|
13021
13227
|
});
|
|
13022
|
-
names
|
|
13228
|
+
return names;
|
|
13229
|
+
}
|
|
13230
|
+
function distinctNames(names) {
|
|
13023
13231
|
return names.filter(function (name, index) {
|
|
13024
13232
|
return !!name && names.indexOf(name) === index;
|
|
13025
13233
|
});
|
|
13026
13234
|
}
|
|
13235
|
+
// What names a payment option: the names shoppers read, then its hand-written
|
|
13236
|
+
// ids.
|
|
13237
|
+
function optionNames(radio) {
|
|
13238
|
+
return distinctNames(shownOptionNames(radio).concat(stableIdentifier(radio.getAttribute('id')), firstIdentifier(radio, TEST_ID_ATTRIBUTES)));
|
|
13239
|
+
}
|
|
13027
13240
|
function describePaymentChoice(control, path) {
|
|
13028
13241
|
if (path === void 0) {
|
|
13029
13242
|
path = [];
|
|
@@ -13038,6 +13251,39 @@ var mParticle = (function () {
|
|
|
13038
13251
|
names: names
|
|
13039
13252
|
} : undefined;
|
|
13040
13253
|
}
|
|
13254
|
+
var CHECKED_RADIO = 'input[type="radio"]:checked, [role~="radio"][aria-checked="true"], [role~="menuitemradio"][aria-checked="true"]';
|
|
13255
|
+
// The payment radio the page holds checked, such as a card option checked by
|
|
13256
|
+
// default that the shopper never clicked. A page holding more than one option
|
|
13257
|
+
// names no single method, so it describes nothing. One option drawn twice
|
|
13258
|
+
// counts once: a role radio wrapping its native input, a hidden input a
|
|
13259
|
+
// component library keeps beside its role radio, or one group rendered for two
|
|
13260
|
+
// layouts, whose copies share the names shoppers read but not their ids. An
|
|
13261
|
+
// option shoppers see no name for is told apart by its ids.
|
|
13262
|
+
function describeCheckedPaymentChoice() {
|
|
13263
|
+
var radios = Array.from(document.querySelectorAll(CHECKED_RADIO)).filter(function (radio) {
|
|
13264
|
+
return isPicked(radio) && radio.getAttribute('aria-hidden') !== 'true' && !hasComposedAncestor(radio, '[data-mp-no-track]') && isPaymentQuestion(radio);
|
|
13265
|
+
});
|
|
13266
|
+
var choices = radios.filter(function (radio) {
|
|
13267
|
+
return !radios.some(function (other) {
|
|
13268
|
+
return other !== radio && radio.contains(other);
|
|
13269
|
+
});
|
|
13270
|
+
}).map(function (radio) {
|
|
13271
|
+
var shown = distinctNames(shownOptionNames(radio));
|
|
13272
|
+
var names = optionNames(radio);
|
|
13273
|
+
return {
|
|
13274
|
+
radio: radio,
|
|
13275
|
+
names: names,
|
|
13276
|
+
option: (shown.length ? shown : names).join('\n')
|
|
13277
|
+
};
|
|
13278
|
+
});
|
|
13279
|
+
var options = new Set(choices.map(function (choice) {
|
|
13280
|
+
return choice.option;
|
|
13281
|
+
}));
|
|
13282
|
+
return options.size === 1 && choices[0].names.length ? {
|
|
13283
|
+
radio: choices[0].radio,
|
|
13284
|
+
names: choices[0].names
|
|
13285
|
+
} : undefined;
|
|
13286
|
+
}
|
|
13041
13287
|
// Read once the page has handled the click, which may have cancelled it.
|
|
13042
13288
|
function isPicked(radio) {
|
|
13043
13289
|
return radio instanceof HTMLInputElement ? radio.checked : radio.getAttribute('aria-checked') === 'true';
|
|
@@ -13045,7 +13291,10 @@ var mParticle = (function () {
|
|
|
13045
13291
|
|
|
13046
13292
|
// A checkout segment may name its kind or its version, as /express-checkout, /checkout-v2 and /checkout-external do.
|
|
13047
13293
|
// Other hyphenated words make it a content slug, such as /help/checkout-faq or /products/self-checkout-kiosk.
|
|
13048
|
-
var
|
|
13294
|
+
var CHECKOUT_SEGMENT = '(?:(?:express|guest|quick|secure|complete|one-?page|single-?page|mobile)-)?checkouts?' + '(?:-(?:v\\d+|\\d+|flow|external|process|step(?:-\\d+)?))?';
|
|
13295
|
+
var checkoutPath = new RegExp('(?:^|\\/)(?:' + CHECKOUT_SEGMENT + '|payment|order|kasse|paiement|commander?|pago)(?:\\/|$)', 'i');
|
|
13296
|
+
// A route with a checkout segment itself, not just a payment or order one, which account pages also use.
|
|
13297
|
+
var checkoutSegmentPath = new RegExp('(?:^|\\/)' + CHECKOUT_SEGMENT + '(?:\\/|$)', 'i');
|
|
13049
13298
|
// A confirmation page under a checkout route, such as /checkout/thankyou/<order> or /checkouts/<token>/thank_you, comes
|
|
13050
13299
|
// after the confirm click. "Complete" and "success" mark one only as the last word of a segment, so /complete-checkout
|
|
13051
13300
|
// and /checkout/complete-registration stay checkout routes.
|
|
@@ -13066,6 +13315,9 @@ var mParticle = (function () {
|
|
|
13066
13315
|
var linkCommits = new RegExp('^(?:(?:place|submit|complete)(?!(?: [^ ]+){0,2} (?:another|new|next)(?: |$))(?: [^ ]+){0,2} (?:order|purchase)|' + otherCommits + ')$');
|
|
13067
13316
|
var routeGatedCommits = new RegExp('^(?:buy now|purchase|register(?: now)?|complete (?:my |your )?registration|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)$');
|
|
13068
13317
|
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('|') + ')$');
|
|
13318
|
+
// "Save and continue" also saves profile, address and card forms, so it is a step only on a checkout segment's route
|
|
13319
|
+
// that isn't an account or confirmation page.
|
|
13320
|
+
var routeGatedSteps = /^save and continue$/;
|
|
13069
13321
|
var CART_NOUN = '(?:(?:my|your|the) )?(?:shopping )?(?:cart|bag|basket)';
|
|
13070
13322
|
// "Add to bag" and "Add 2 to cart": a click that puts something in the cart.
|
|
13071
13323
|
var addToCartLabels = new RegExp('^add(?: [^ ]+){0,5} to ' + CART_NOUN + '$');
|
|
@@ -13078,6 +13330,9 @@ var mParticle = (function () {
|
|
|
13078
13330
|
var walletPhrases = new RegExp('^(?:(?:' + WALLET_VERBS + ')(?: now| securely)? (?:with|using|via|through|by) (?:' + WALLET_NAMES + ')(?: express(?: checkout)?| checkout)?|(?:continue|proceed|go) (?:to|with) (?:' + WALLET_NAMES + '))$');
|
|
13079
13331
|
// A wallet's own name on a button: "PayPal", "Klarna checkout", "Apple Pay logo".
|
|
13080
13332
|
var walletBrands = new RegExp('^(?:' + WALLET_NAMES + ')(?: (?:checkout|check out|express(?: checkout)?|express pay|buy now|pay now|pay|now)' + '| (?:logo|icon|mark|button)(?: (?:logo|icon|mark|button))*)?$');
|
|
13333
|
+
// The wallets the Rokt kit reads from a click's label or identifiers, as words:
|
|
13334
|
+
// "Place order with PayPal!" and "paypal-pay-button" both name PayPal.
|
|
13335
|
+
var KIT_WALLET_WORDS = /(?:^| )(?:apple ?pay|google ?pay|g ?pay|android ?pay|venmo|pay ?pal|amazon ?pay|shop ?pay|klarna|after ?pay|clear ?pay|affirm|cash ?app|sezzle|samsung ?pay)(?= |$)/;
|
|
13081
13336
|
// Exact HTTPS hosts of hosted wallet button frames.
|
|
13082
13337
|
var walletFrameFamilies = {
|
|
13083
13338
|
'pay.google.com': 'google_pay',
|
|
@@ -13272,7 +13527,7 @@ var mParticle = (function () {
|
|
|
13272
13527
|
source: 'brand'
|
|
13273
13528
|
};
|
|
13274
13529
|
}
|
|
13275
|
-
if (steps.test(text)) {
|
|
13530
|
+
if (steps.test(text) || isButton && routeGatedSteps.test(text) && checkoutSegmentPath.test(pathname) && !confirmationPath.test(pathname) && !accountStepPath.test(pathname)) {
|
|
13276
13531
|
return {
|
|
13277
13532
|
kind: 'checkout_step',
|
|
13278
13533
|
source: 'label'
|
|
@@ -13281,6 +13536,24 @@ var mParticle = (function () {
|
|
|
13281
13536
|
}
|
|
13282
13537
|
return cart;
|
|
13283
13538
|
}
|
|
13539
|
+
// The Rokt kit's own word split, so "PayPal!" and "paypal2" both read as
|
|
13540
|
+
// "paypal" here as they do there.
|
|
13541
|
+
function kitWords(value) {
|
|
13542
|
+
return (value || '').replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase().replace(/[^a-z]+/g, ' ').trim();
|
|
13543
|
+
}
|
|
13544
|
+
// A confirm control may name its wallet, so "Place order with PayPal" and a
|
|
13545
|
+
// "Place order" button with id paypal-pay-button are confirm clicks that still
|
|
13546
|
+
// name their own method. A logged click is judged on the fields the kit reads
|
|
13547
|
+
// a method from; a click that isn't logged, on the control's own label and
|
|
13548
|
+
// identifiers.
|
|
13549
|
+
function namesWallet(control, click) {
|
|
13550
|
+
var values = click ? [click.label, click.ariaLabel, click.elementId, click.elementName, click.testId] : labelSources(control).concat(['id', 'name'].concat(TEST_ID_ATTRIBUTES).map(function (attribute) {
|
|
13551
|
+
return control.getAttribute(attribute);
|
|
13552
|
+
}));
|
|
13553
|
+
return values.some(function (value) {
|
|
13554
|
+
return KIT_WALLET_WORDS.test(kitWords(value));
|
|
13555
|
+
});
|
|
13556
|
+
}
|
|
13284
13557
|
// The host is the domain or one of its subdomains, never a look-alike suffix.
|
|
13285
13558
|
function isOnDomain(host, domain) {
|
|
13286
13559
|
return host === domain || host.length > domain.length && host.slice(host.length - domain.length - 1) === '.' + domain;
|
|
@@ -13366,9 +13639,6 @@ var mParticle = (function () {
|
|
|
13366
13639
|
// A ceiling, not a dedupe: a held Enter key or browser automation fires a
|
|
13367
13640
|
// trusted click per repeat, and each one is forwarded to every kit.
|
|
13368
13641
|
var PAGE_EVENT_LIMIT = 100;
|
|
13369
|
-
// A click uploads at once. Taps that follow it this quickly are held and share
|
|
13370
|
-
// one upload once they stop.
|
|
13371
|
-
var UPLOAD_DEBOUNCE_MS = 100;
|
|
13372
13642
|
var btnWindow = function btnWindow() {
|
|
13373
13643
|
return typeof window === 'undefined' ? null : window;
|
|
13374
13644
|
};
|
|
@@ -13486,10 +13756,6 @@ var mParticle = (function () {
|
|
|
13486
13756
|
this.pageEvents = 0;
|
|
13487
13757
|
// Clicks read at dispatch and waiting for the next paint.
|
|
13488
13758
|
this.pending = [];
|
|
13489
|
-
this.uploadTimerId = null;
|
|
13490
|
-
this.uploadHeld = false;
|
|
13491
|
-
// Set while the page is left, when the SDK's exit upload sends the queue.
|
|
13492
|
-
this.leaving = false;
|
|
13493
13759
|
this.drain = function () {
|
|
13494
13760
|
var queue = _this.pending;
|
|
13495
13761
|
_this.pending = [];
|
|
@@ -13503,14 +13769,8 @@ var mParticle = (function () {
|
|
|
13503
13769
|
// beforeunload, the first of them.
|
|
13504
13770
|
this.onHide = function (event) {
|
|
13505
13771
|
if (event.type === 'beforeunload' || event.type === 'pagehide' || document.visibilityState === 'hidden') {
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
_this.drain();
|
|
13509
|
-
} finally {
|
|
13510
|
-
_this.leaving = false;
|
|
13511
|
-
}
|
|
13512
|
-
// The SDK's exit upload sends the queue, held taps included.
|
|
13513
|
-
_this.cancelUpload();
|
|
13772
|
+
// The SDK's exit upload then sends these clicks with the queue.
|
|
13773
|
+
_this.drain();
|
|
13514
13774
|
}
|
|
13515
13775
|
};
|
|
13516
13776
|
this.onPageShow = function (event) {
|
|
@@ -13637,7 +13897,6 @@ var mParticle = (function () {
|
|
|
13637
13897
|
this.lastSent = {};
|
|
13638
13898
|
this.pagePath = null;
|
|
13639
13899
|
this.pageEvents = 0;
|
|
13640
|
-
this.cancelUpload();
|
|
13641
13900
|
};
|
|
13642
13901
|
ButtonTracker.prototype.enqueue = function (delivery) {
|
|
13643
13902
|
this.pending.push(delivery);
|
|
@@ -13669,6 +13928,7 @@ var mParticle = (function () {
|
|
|
13669
13928
|
var source;
|
|
13670
13929
|
var adds;
|
|
13671
13930
|
var choice;
|
|
13931
|
+
var checkedNames;
|
|
13672
13932
|
var family;
|
|
13673
13933
|
var unconfirmedCommit = false;
|
|
13674
13934
|
if (layer === 'click') {
|
|
@@ -13701,6 +13961,15 @@ var mParticle = (function () {
|
|
|
13701
13961
|
source = intent ? intent.source : undefined;
|
|
13702
13962
|
adds = intent ? intent.adds : undefined;
|
|
13703
13963
|
choice = signalsKit ? describePaymentChoice(control, path) : undefined;
|
|
13964
|
+
// A confirm or checkout-step click picks no radio itself, so it
|
|
13965
|
+
// carries the one the page holds checked as read here. It is not
|
|
13966
|
+
// read again at delivery: the page's own handlers may clear or
|
|
13967
|
+
// replace it by then. A wallet tap names its own method, and
|
|
13968
|
+
// so does a confirm that names a wallet.
|
|
13969
|
+
if (!choice && (kind === 'commit' || kind === 'checkout_step') && !namesWallet(control, click)) {
|
|
13970
|
+
var checked = describeCheckedPaymentChoice();
|
|
13971
|
+
checkedNames = checked ? checked.names : undefined;
|
|
13972
|
+
}
|
|
13704
13973
|
}
|
|
13705
13974
|
this.lastClick = {
|
|
13706
13975
|
t: t,
|
|
@@ -13755,6 +14024,8 @@ var mParticle = (function () {
|
|
|
13755
14024
|
// with this pick.
|
|
13756
14025
|
if (choice && isPicked(choice.radio)) {
|
|
13757
14026
|
_this.pickPayment(t, pathname, choice.names);
|
|
14027
|
+
} else if (checkedNames) {
|
|
14028
|
+
_this.pickPayment(t, pathname, checkedNames);
|
|
13758
14029
|
} else if (family) {
|
|
13759
14030
|
_this.pickPayment(t, pathname, undefined, family);
|
|
13760
14031
|
}
|
|
@@ -13781,47 +14052,9 @@ var mParticle = (function () {
|
|
|
13781
14052
|
this.pageEvents++;
|
|
13782
14053
|
// Restarts an expired session, as public logEvent() and page views do.
|
|
13783
14054
|
this.mpInstance._SessionManager.resetSessionTimer();
|
|
13784
|
-
|
|
14055
|
+
// No upload of its own, which a click that opens another page would
|
|
14056
|
+
// cancel: with batching on, the next batch or the exit upload sends it.
|
|
13785
14057
|
this.mpInstance._Events.logEvent(asLink ? buildLinkClickEvent(click, pathname) : buildButtonClickEvent(click, pathname));
|
|
13786
|
-
// Only a click waiting for the batch interval is sent early: a held
|
|
13787
|
-
// one goes out when released, and with batching off it already has.
|
|
13788
|
-
// A pick, such as a checkbox, a radio or a tab, waits for the batch,
|
|
13789
|
-
// since choosing an option doesn't leave the page the way a confirm
|
|
13790
|
-
// click can.
|
|
13791
|
-
if (!this.leaving && !click.pick && this.queuedForUpload() > queued) {
|
|
13792
|
-
this.uploadNow();
|
|
13793
|
-
}
|
|
13794
|
-
};
|
|
13795
|
-
// Events waiting in the upload queue for the next batch interval.
|
|
13796
|
-
ButtonTracker.prototype.queuedForUpload = function () {
|
|
13797
|
-
var uploader = this.mpInstance._APIClient.uploader;
|
|
13798
|
-
return uploader ? uploader.eventsQueuedForProcessing.length : 0;
|
|
13799
|
-
};
|
|
13800
|
-
// Uploads a click without waiting for the batch interval. A tap inside the
|
|
13801
|
-
// wait after the last one is held, and each restarts the wait, so a burst
|
|
13802
|
-
// after the first click goes out in one request once the taps stop.
|
|
13803
|
-
ButtonTracker.prototype.uploadNow = function () {
|
|
13804
|
-
var _this = this;
|
|
13805
|
-
if (this.uploadTimerId === null) {
|
|
13806
|
-
this.mpInstance.upload();
|
|
13807
|
-
} else {
|
|
13808
|
-
clearTimeout(this.uploadTimerId);
|
|
13809
|
-
this.uploadHeld = true;
|
|
13810
|
-
}
|
|
13811
|
-
this.uploadTimerId = setTimeout(function () {
|
|
13812
|
-
_this.uploadTimerId = null;
|
|
13813
|
-
if (_this.uploadHeld) {
|
|
13814
|
-
_this.uploadHeld = false;
|
|
13815
|
-
_this.mpInstance.upload();
|
|
13816
|
-
}
|
|
13817
|
-
}, UPLOAD_DEBOUNCE_MS);
|
|
13818
|
-
};
|
|
13819
|
-
ButtonTracker.prototype.cancelUpload = function () {
|
|
13820
|
-
if (this.uploadTimerId !== null) {
|
|
13821
|
-
clearTimeout(this.uploadTimerId);
|
|
13822
|
-
this.uploadTimerId = null;
|
|
13823
|
-
}
|
|
13824
|
-
this.uploadHeld = false;
|
|
13825
14058
|
};
|
|
13826
14059
|
ButtonTracker.prototype.emit = function (kind, source, layer, t, pathname, adds) {
|
|
13827
14060
|
if (this.isKitSuppressed()) {
|