@mparticle/web-sdk 3.21.0 → 3.22.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 +84 -34
- package/dist/mparticle.esm.js +84 -34
- package/dist/mparticle.js +365 -74
- package/dist/types/src/batchUploader.d.ts +2 -0
- package/dist/types/src/buttonTracking/buttonDescription.d.ts +2 -1
- package/dist/types/src/buttonTracking/buttonTracker.d.ts +6 -0
- package/dist/types/src/buttonTracking/checkoutIntent.d.ts +7 -1
- package/dist/types/src/buttonTracking/controls.d.ts +3 -0
- package/dist/types/src/roktManager.d.ts +4 -1
- package/package.json +1 -1
- package/src/batchUploader.ts +33 -1
- package/src/buttonTracking/buttonClickEvent.ts +3 -0
- package/src/buttonTracking/buttonDescription.ts +24 -3
- package/src/buttonTracking/buttonTracker.ts +92 -8
- package/src/buttonTracking/checkoutIntent.ts +199 -66
- package/src/buttonTracking/controls.ts +150 -32
- package/src/pageViewTracker.ts +7 -1
- package/src/roktManager.ts +16 -1
package/dist/mparticle.js
CHANGED
|
@@ -204,7 +204,7 @@ var mParticle = (function () {
|
|
|
204
204
|
Base64: Base64$1
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
-
var version = "3.
|
|
207
|
+
var version = "3.22.0";
|
|
208
208
|
|
|
209
209
|
var Constants = {
|
|
210
210
|
sdkVersion: version,
|
|
@@ -3014,6 +3014,11 @@ var mParticle = (function () {
|
|
|
3014
3014
|
this.exitHandler = null;
|
|
3015
3015
|
this.visibilityChangeHandler = null;
|
|
3016
3016
|
this.destroyed = false;
|
|
3017
|
+
// The order this page's uploads first picked each batch up in, by request
|
|
3018
|
+
// id. Batches enter uploads in creation order, so this keeps that order for
|
|
3019
|
+
// batches that share a millisecond.
|
|
3020
|
+
this.batchOrder = new Map();
|
|
3021
|
+
this.nextBatchOrder = 0;
|
|
3017
3022
|
this.mpInstance = mpInstance;
|
|
3018
3023
|
this.uploadIntervalMillis = uploadInterval;
|
|
3019
3024
|
this.batchingEnabled = uploadInterval >= BatchUploader.MINIMUM_INTERVAL_MILLIS;
|
|
@@ -3299,8 +3304,9 @@ var mParticle = (function () {
|
|
|
3299
3304
|
useBeacon = false;
|
|
3300
3305
|
}
|
|
3301
3306
|
return __awaiter(this, void 0, void 0, function () {
|
|
3302
|
-
var currentUser, currentEvents, newBatches, batchesToUpload, batchesThatDidNotUpload;
|
|
3307
|
+
var currentUser, currentEvents, newBatches, batchesToUpload, batchesThatDidNotUpload, retriedIds;
|
|
3303
3308
|
var _b, _c, _d;
|
|
3309
|
+
var _this_1 = this;
|
|
3304
3310
|
return __generator(this, function (_e) {
|
|
3305
3311
|
switch (_e.label) {
|
|
3306
3312
|
case 0:
|
|
@@ -3326,9 +3332,22 @@ var mParticle = (function () {
|
|
|
3326
3332
|
}
|
|
3327
3333
|
batchesToUpload = this.batchesQueuedForProcessing;
|
|
3328
3334
|
this.batchesQueuedForProcessing = [];
|
|
3335
|
+
batchesToUpload.forEach(function (batch) {
|
|
3336
|
+
if (!_this_1.batchOrder.has(batch.source_request_id)) {
|
|
3337
|
+
_this_1.batchOrder.set(batch.source_request_id, _this_1.nextBatchOrder++);
|
|
3338
|
+
}
|
|
3339
|
+
});
|
|
3329
3340
|
return [4 /*yield*/, this.uploadBatches(batchesToUpload, useBeacon)];
|
|
3330
3341
|
case 1:
|
|
3331
3342
|
batchesThatDidNotUpload = _e.sent();
|
|
3343
|
+
retriedIds = (batchesThatDidNotUpload || []).map(function (batch) {
|
|
3344
|
+
return batch.source_request_id;
|
|
3345
|
+
});
|
|
3346
|
+
batchesToUpload.forEach(function (batch) {
|
|
3347
|
+
if (retriedIds.indexOf(batch.source_request_id) === -1) {
|
|
3348
|
+
_this_1.batchOrder["delete"](batch.source_request_id);
|
|
3349
|
+
}
|
|
3350
|
+
});
|
|
3332
3351
|
// Batches that do not successfully upload are added back to the process queue
|
|
3333
3352
|
// in the order they were created so that we can attempt re-transmission in
|
|
3334
3353
|
// the same sequence. This is to prevent any potential data corruption.
|
|
@@ -3355,7 +3374,19 @@ var mParticle = (function () {
|
|
|
3355
3374
|
};
|
|
3356
3375
|
|
|
3357
3376
|
BatchUploader.prototype.storeBatchesQueuedForProcessing = function () {
|
|
3358
|
-
var
|
|
3377
|
+
var _this_1 = this;
|
|
3378
|
+
var _a;
|
|
3379
|
+
// Another upload may have stored its failed batches while this one was
|
|
3380
|
+
// in flight. Merge them in creation order, whichever upload finished
|
|
3381
|
+
// first, so they are not overwritten and quota trimming drops the oldest.
|
|
3382
|
+
var orderOf = function orderOf(batch) {
|
|
3383
|
+
var _a;
|
|
3384
|
+
return (_a = _this_1.batchOrder.get(batch.source_request_id)) !== null && _a !== void 0 ? _a : -1;
|
|
3385
|
+
};
|
|
3386
|
+
var batches = __spreadArray(__spreadArray([], this.batchesQueuedForProcessing, true), (_a = this.batchVault.retrieve()) !== null && _a !== void 0 ? _a : [], true).sort(function (a, b) {
|
|
3387
|
+
var _a, _b;
|
|
3388
|
+
return orderOf(a) - orderOf(b) || ((_a = a.timestamp_unixtime_ms) !== null && _a !== void 0 ? _a : 0) - ((_b = b.timestamp_unixtime_ms) !== null && _b !== void 0 ? _b : 0);
|
|
3389
|
+
});
|
|
3359
3390
|
// Nothing to persist: reset offline storage to an empty state.
|
|
3360
3391
|
if (isEmpty(batches)) {
|
|
3361
3392
|
this.batchVault.store([]);
|
|
@@ -6869,11 +6900,12 @@ var mParticle = (function () {
|
|
|
6869
6900
|
// Pagination
|
|
6870
6901
|
'page', 'limit', 'offset', 'cursor', 'per_page',
|
|
6871
6902
|
// Site search term, as the common platforms spell it. Matching is
|
|
6872
|
-
// case-insensitive
|
|
6873
|
-
|
|
6903
|
+
// case-insensitive. `s` is WordPress and WooCommerce search; other
|
|
6904
|
+
// single-letter names are too generic. `oq` is a storefront search term.
|
|
6905
|
+
'q', 's', 'oq', 'search', 'query', 'keyword', 'keywords', 'term', 'text', 'searchTerm', 'searchText', 'search_query', 'search_text', 'Ntt',
|
|
6874
6906
|
// Product and variant identifiers. `cid` is not a category: it is a
|
|
6875
6907
|
// customer id on many sites.
|
|
6876
|
-
'pid', 'productId', 'sku', 'skuId', 'variant',
|
|
6908
|
+
'pid', 'productId', 'sku', 'skuId', 'variant', 'color', 'colour', 'size',
|
|
6877
6909
|
// Category
|
|
6878
6910
|
'category',
|
|
6879
6911
|
// Referral
|
|
@@ -10795,6 +10827,15 @@ var mParticle = (function () {
|
|
|
10795
10827
|
var nav = typeof navigator === 'undefined' ? undefined : navigator;
|
|
10796
10828
|
return !!nav && nav.globalPrivacyControl === true;
|
|
10797
10829
|
};
|
|
10830
|
+
// True when the kit can wait for the shopper's confirm click: signals are not
|
|
10831
|
+
// suppressed and the browser exposes the composed path the detector reads.
|
|
10832
|
+
// Decided from config alone, so tracker setup and teardown never change it.
|
|
10833
|
+
RoktManager.prototype.isCheckoutIntentLive = function () {
|
|
10834
|
+
if (this.isCheckoutIntentSuppressed()) {
|
|
10835
|
+
return false;
|
|
10836
|
+
}
|
|
10837
|
+
return typeof Event !== 'undefined' && isFunction(Event.prototype.composedPath);
|
|
10838
|
+
};
|
|
10798
10839
|
// Reads `this.kit` at call time; before a kit attaches the signal is buffered
|
|
10799
10840
|
// with the path it was raised on.
|
|
10800
10841
|
RoktManager.prototype.notifyCheckoutIntent = function (signal, pathname) {
|
|
@@ -11495,6 +11536,9 @@ var mParticle = (function () {
|
|
|
11495
11536
|
data[key] = optional[key];
|
|
11496
11537
|
}
|
|
11497
11538
|
}
|
|
11539
|
+
if (click.checked !== undefined) {
|
|
11540
|
+
data.checked = click.checked;
|
|
11541
|
+
}
|
|
11498
11542
|
return {
|
|
11499
11543
|
messageType: MessageType$1.PageEvent,
|
|
11500
11544
|
name: BUTTON_CLICK_EVENT_NAME,
|
|
@@ -11519,43 +11563,109 @@ var mParticle = (function () {
|
|
|
11519
11563
|
}
|
|
11520
11564
|
return false;
|
|
11521
11565
|
}
|
|
11566
|
+
var ROKT_SELECTOR = '[data-core-type], #rokt-controller-frame';
|
|
11567
|
+
// A Rokt frame or script is served from a /wsdk/ path.
|
|
11568
|
+
function hasRoktSrc(element) {
|
|
11569
|
+
var src = element.getAttribute('src');
|
|
11570
|
+
if (!src) {
|
|
11571
|
+
return false;
|
|
11572
|
+
}
|
|
11573
|
+
try {
|
|
11574
|
+
return new URL(src, 'https://example.test').pathname.indexOf('/wsdk/') !== -1;
|
|
11575
|
+
} catch (e) {
|
|
11576
|
+
// An unparseable src is not a Rokt element.
|
|
11577
|
+
return false;
|
|
11578
|
+
}
|
|
11579
|
+
}
|
|
11522
11580
|
// Rokt's own placements and frames never count as partner checkout controls.
|
|
11523
11581
|
function isRoktElement(element) {
|
|
11524
|
-
|
|
11525
|
-
|
|
11582
|
+
return hasComposedAncestor(element, ROKT_SELECTOR) || hasRoktSrc(element);
|
|
11583
|
+
}
|
|
11584
|
+
// Wallet and quick-pay buttons as their vendors render them: official custom
|
|
11585
|
+
// elements and documented classes, ids and attributes.
|
|
11586
|
+
var WALLET_SIGNATURE = ['apple-pay-button', '.apple-pay-button', '.gpay-button', '.gpay-card-info-container', '#gpay-button-online-api-id', 'paypal-button', 'venmo-button', 'paypal-pay-later-button', 'paypal-credit-button', 'shop-pay-button', 'shop-pay-wallet-button', 'shopify-accelerated-checkout', 'shopify-accelerated-checkout-cart', 'shopify-apple-pay-button', 'shopify-google-pay-button', 'shopify-paypal-button', 'shopify-amazon-pay-button', '.amazonpay-button-focusable', '#AmazonPayButton', '[data-amazon-pay-button]', '[data-afterpay-entry-point]'].join(', ');
|
|
11587
|
+
// PayPal's card button opens a card form, so it is a step rather than a wallet.
|
|
11588
|
+
var CARD_FORM_BUTTON = 'paypal-basic-card-button';
|
|
11589
|
+
var VENDOR_BUTTON = WALLET_SIGNATURE + ', ' + CARD_FORM_BUTTON;
|
|
11590
|
+
// Apple's set-up and top-up buttons manage a wallet rather than pay with it.
|
|
11591
|
+
var NOT_A_PAYMENT = ['set-up', 'add-money', 'reload', 'top-up'].map(function (type) {
|
|
11592
|
+
return 'apple-pay-button[type="' + type + '"]';
|
|
11593
|
+
}).join(', ');
|
|
11594
|
+
// What a vendor's own button markup says about a control. Only the control or
|
|
11595
|
+
// the shadow host that owns it counts, so a merchant wrapper reusing a vendor
|
|
11596
|
+
// class never turns its children into wallet buttons.
|
|
11597
|
+
function vendorButtonKind(control) {
|
|
11598
|
+
var host = control.matches(VENDOR_BUTTON) ? control : null;
|
|
11599
|
+
if (!host) {
|
|
11600
|
+
var root = isFunction(control.getRootNode) ? control.getRootNode() : null;
|
|
11601
|
+
host = root && root.host && root.host.matches(VENDOR_BUTTON) ? root.host : null;
|
|
11602
|
+
}
|
|
11603
|
+
if (!host) {
|
|
11604
|
+
return undefined;
|
|
11526
11605
|
}
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
try {
|
|
11530
|
-
if (new URL(src, 'https://example.test').pathname.indexOf('/wsdk/') !== -1) {
|
|
11531
|
-
return true;
|
|
11532
|
-
}
|
|
11533
|
-
} catch (e) {
|
|
11534
|
-
// An unparseable src is not a Rokt element.
|
|
11535
|
-
}
|
|
11606
|
+
if (host.matches(NOT_A_PAYMENT)) {
|
|
11607
|
+
return 'none';
|
|
11536
11608
|
}
|
|
11537
|
-
return
|
|
11609
|
+
return host.matches(CARD_FORM_BUTTON) ? 'checkout_step' : 'wallet';
|
|
11538
11610
|
}
|
|
11539
11611
|
function isWalletElement(element) {
|
|
11540
|
-
return element
|
|
11612
|
+
return vendorButtonKind(element) === 'wallet';
|
|
11541
11613
|
}
|
|
11614
|
+
// Pickers and toggles choose a payment method or open a panel; they never pay.
|
|
11615
|
+
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]';
|
|
11616
|
+
var PICKER_GROUP = '[role="radiogroup"], [role="tablist"], [role="listbox"]';
|
|
11617
|
+
// Pay-later and financing messages link to details rather than pay.
|
|
11618
|
+
var MESSAGING = 'klarna-placement, afterpay-placement, square-placement, .affirm-as-low-as, .affirm-site-modal, ' + '.affirm-product-modal, [data-pp-message]';
|
|
11619
|
+
// A control that is still logged as a click but never counts as checkout intent.
|
|
11620
|
+
function isQuietIntentControl(control, isVendorWallet) {
|
|
11621
|
+
if (control.matches(PICKER)) {
|
|
11622
|
+
return true;
|
|
11623
|
+
}
|
|
11624
|
+
if (hasComposedAncestor(control, PICKER_GROUP)) {
|
|
11625
|
+
return true;
|
|
11626
|
+
}
|
|
11627
|
+
if (!isVendorWallet && (control.hasAttribute('aria-expanded') || control.hasAttribute('aria-pressed'))) {
|
|
11628
|
+
return true;
|
|
11629
|
+
}
|
|
11630
|
+
return hasComposedAncestor(control, MESSAGING);
|
|
11631
|
+
}
|
|
11632
|
+
// A checkbox or radio is a control of its own only when no button or link
|
|
11633
|
+
// around it takes the click, so a tappable row keeps its own name.
|
|
11634
|
+
var OPTION_INPUT = 'input[type="checkbox"], input[type="radio"]';
|
|
11542
11635
|
// Walks at most 12 entries of an event's composed path for the nearest control.
|
|
11543
|
-
// Rokt elements
|
|
11636
|
+
// One composed walk from the target rules out Rokt elements and anything under
|
|
11637
|
+
// data-mp-no-track; disabled controls are skipped.
|
|
11544
11638
|
function findControl(path) {
|
|
11639
|
+
var entries = path.slice(0, 12);
|
|
11640
|
+
var first;
|
|
11641
|
+
for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
|
|
11642
|
+
var target = entries_1[_i];
|
|
11643
|
+
if (target instanceof Element) {
|
|
11644
|
+
first = target;
|
|
11645
|
+
break;
|
|
11646
|
+
}
|
|
11647
|
+
}
|
|
11648
|
+
if (!first || hasComposedAncestor(first, ROKT_SELECTOR + ', [data-mp-no-track]')) {
|
|
11649
|
+
return undefined;
|
|
11650
|
+
}
|
|
11545
11651
|
var control;
|
|
11546
|
-
|
|
11547
|
-
|
|
11652
|
+
var option;
|
|
11653
|
+
for (var _a = 0, entries_2 = entries; _a < entries_2.length; _a++) {
|
|
11654
|
+
var target = entries_2[_a];
|
|
11548
11655
|
if (!(target instanceof Element)) {
|
|
11549
11656
|
continue;
|
|
11550
11657
|
}
|
|
11551
|
-
if (
|
|
11658
|
+
if (hasRoktSrc(target)) {
|
|
11552
11659
|
return undefined;
|
|
11553
11660
|
}
|
|
11554
|
-
if (!control &&
|
|
11661
|
+
if (!control && target.matches(CONTROL_SELECTOR + ', a[href], ' + VENDOR_BUTTON)) {
|
|
11555
11662
|
control = target;
|
|
11663
|
+
} else if (!control && !option && target.matches(OPTION_INPUT)) {
|
|
11664
|
+
option = target;
|
|
11556
11665
|
}
|
|
11557
11666
|
}
|
|
11558
|
-
|
|
11667
|
+
control = control || option;
|
|
11668
|
+
if (!control || control.matches(':disabled, [aria-disabled="true"]')) {
|
|
11559
11669
|
return undefined;
|
|
11560
11670
|
}
|
|
11561
11671
|
return control;
|
|
@@ -11566,6 +11676,11 @@ var mParticle = (function () {
|
|
|
11566
11676
|
function isUnsafeTextSource(element) {
|
|
11567
11677
|
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);
|
|
11568
11678
|
}
|
|
11679
|
+
// A label around a checkbox or radio holds no typed text, so only the other
|
|
11680
|
+
// fields rule its text out.
|
|
11681
|
+
function isUnsafeLabelSource(label) {
|
|
11682
|
+
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);
|
|
11683
|
+
}
|
|
11569
11684
|
|
|
11570
11685
|
// Masks a button label before it is logged. Button text can carry personal data,
|
|
11571
11686
|
// so this is the privacy boundary for every label and aria-label.
|
|
@@ -11614,9 +11729,10 @@ var mParticle = (function () {
|
|
|
11614
11729
|
return value && value.trim() ? value : undefined;
|
|
11615
11730
|
}
|
|
11616
11731
|
// The button's name as a screen reader would read it, simplified: the first of
|
|
11617
|
-
// aria-labelledby, aria-label, own text (or an inner image's alt), title,
|
|
11618
|
-
// inputs their value
|
|
11619
|
-
//
|
|
11732
|
+
// aria-labelledby, aria-label, own text (or an inner image's alt), title, for
|
|
11733
|
+
// inputs their value, and for checkboxes and radios their label's text. The text
|
|
11734
|
+
// sources are skipped for controls that hold form fields or more than four
|
|
11735
|
+
// children, so a typed value is never read.
|
|
11620
11736
|
function accessibleName(control) {
|
|
11621
11737
|
var labelledBy = control.getAttribute('aria-labelledby');
|
|
11622
11738
|
if (labelledBy) {
|
|
@@ -11667,6 +11783,16 @@ var mParticle = (function () {
|
|
|
11667
11783
|
source: 'value'
|
|
11668
11784
|
};
|
|
11669
11785
|
}
|
|
11786
|
+
} else if (type === 'checkbox' || type === 'radio') {
|
|
11787
|
+
// The label the browser ties to the box, never the box's value.
|
|
11788
|
+
var label = control.labels ? control.labels[0] : undefined;
|
|
11789
|
+
var text = label && !isUnsafeLabelSource(label) && label.textContent ? label.textContent.trim() : '';
|
|
11790
|
+
if (text) {
|
|
11791
|
+
return {
|
|
11792
|
+
raw: text,
|
|
11793
|
+
source: 'label'
|
|
11794
|
+
};
|
|
11795
|
+
}
|
|
11670
11796
|
}
|
|
11671
11797
|
} else if (readable) {
|
|
11672
11798
|
var text = textOf(control);
|
|
@@ -11849,34 +11975,80 @@ var mParticle = (function () {
|
|
|
11849
11975
|
type: controlType(control, element),
|
|
11850
11976
|
linkType: link ? link.type : undefined,
|
|
11851
11977
|
linkDomain: link ? link.domain : undefined,
|
|
11852
|
-
fileExtension: link ? link.fileExtension : undefined
|
|
11978
|
+
fileExtension: link ? link.fileExtension : undefined,
|
|
11979
|
+
// The box as the click left it: the browser toggles it before the click
|
|
11980
|
+
// reaches listeners, and puts it back if the click is cancelled.
|
|
11981
|
+
checked: control instanceof HTMLInputElement && (control.type === 'checkbox' || control.type === 'radio') ? control.checked : undefined
|
|
11853
11982
|
};
|
|
11854
11983
|
}
|
|
11855
11984
|
|
|
11856
11985
|
var checkoutPath = /(?:^|\/)(?:checkouts?|payment|order|kasse|paiement|commander?|pago)(?:\/|$)/i;
|
|
11857
|
-
|
|
11858
|
-
|
|
11986
|
+
// English look-alikes are whole words; the other languages stay substring
|
|
11987
|
+
// matches, since several of them do not separate words with spaces.
|
|
11988
|
+
var englishExclusions = new RegExp('\\b(?:coupon|promo(?: code)?|newsletter|sign in|sign up|log in|login|add to (?:cart|bag|basket)|' + 'gift card balance|(?:add|apply|redeem|check|enter|use) (?:a |an |your )?gift card|pay later|' + 'interest free|as low as|learn more|prequalify|apply (?:now|for)|payment plan|available|accepted|' + 'we accept|set up|add money|reload|top up|add to (?:apple|google) wallet|connect|link your|unlink|' + 'remove|delete|edit|change|manage|use a different|switch)\\b|^pay in$|\\bpay in (?:installments|instalments)\\b');
|
|
11989
|
+
var otherExclusions = new RegExp(['gutschein|anmelden|warenkorb|bon cadeau|connexion|panier|cup\u00F3n|iniciar sesi\u00F3n|carrito', 'carrello|codice sconto|accedi|buono regalo|winkelwagen|kortingscode|inloggen|cadeaubon|achteraf betalen', '\u30AB\u30FC\u30C8|\u30AF\u30FC\u30DD\u30F3|\u30ED\u30B0\u30A4\u30F3|\u4F1A\u54E1\u767B\u9332|\u30AE\u30D5\u30C8|\u5F8C\u6255\u3044|\u30E1\u30EB\u30DE\u30AC'].join('|'));
|
|
11990
|
+
var englishCommits = '(?:(?:agree|accept|review) and )?(?:place|submit|complete|confirm|finali[sz]e)' + '(?!(?: [^ ]+){0,2} (?:another|new|next)(?: |$))(?! (?:[^ ]+ )?gift card)' + '(?: [^ ]+){0,2} (?:order|purchase|booking|reservation|payment|checkout|transaction)' + '(?: now| securely| and pay| (?:with|using) [^ ]+(?: [^ ]+)?)?(?: (?:total|subtotal|items?|tickets?))?' + '|pay(?: securely)?(?: now)?|(?:confirm|agree|accept|submit|order|book) and pay' + '|pay and place (?:my |your )?order|order with obligation to pay';
|
|
11991
|
+
var otherCommits = ['(?:jetzt )?(?:zahlungs|kosten)pflichtig (?:bestellen|buchen)|(?:jetzt )?bezahlen|kauf abschlie\u00DFen|bestellung abschlie\u00DFen', 'commander et payer|payer(?: maintenant)?|(?:confirmer|finaliser) (?:ma |la )?commande', "valider et payer|confirmer et payer|commande avec obligation de paiement|finaliser l'achat", '(?:realizar|confirmar|completar) (?:el |mi |la )?(?:pedido|compra|reserva)|finalizar (?:el |mi )?pedido|pagar(?: ahora)?', 'pedido con obligaci\u00F3n de pago', "(?:effettua|conferma|concludi|completa) (?:l'|il )?(?:ordine|acquisto)|ordine con obbligo di pagamento", 'paga(?: ora| adesso)?|ordina e paga', '(?:nu )?betalen|bestellen en betalen|bestelling plaatsen|plaats bestelling|bestelling afronden', 'bestelling met betalingsverplichting', '(?:\u3053\u306E\u5185\u5BB9\u3067|\u4E0A\u8A18\u306E?\u5185\u5BB9\u3067)?\u3054?(?:\u6CE8\u6587|\u8CFC\u5165|\u4E88\u7D04|\u6C7A\u6E08|\u304A?\u652F\u6255\u3044?)\u3092?(?:\u78BA\u5B9A|\u5B8C\u4E86)(?:\u3059\u308B)?', '(?:\u3053\u306E\u5185\u5BB9\u3067|\u4E0A\u8A18\u306E?\u5185\u5BB9\u3067)\u3054?(?:\u6CE8\u6587|\u8CFC\u5165|\u4E88\u7D04)\u3059\u308B|\u3054?\u6CE8\u6587\u3059\u308B|\u6C7A\u6E08\u3059\u308B|(?:\u4ECA\u3059\u3050|\u3092)?\u652F\u6255\u3046'].join('|');
|
|
11992
|
+
var commits = new RegExp('^(?:' + englishCommits + '|' + otherCommits + ')$');
|
|
11993
|
+
// A plain link is navigation, so in English only an order or purchase it
|
|
11994
|
+
// places, submits or completes counts as the confirm click.
|
|
11995
|
+
var linkCommits = new RegExp('^(?:(?:place|submit|complete)(?!(?: [^ ]+){0,2} (?:another|new|next)(?: |$))(?: [^ ]+){0,2} (?:order|purchase)|' + otherCommits + ')$');
|
|
11859
11996
|
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)$');
|
|
11860
|
-
var steps = new RegExp('^(?:' + ['
|
|
11861
|
-
var
|
|
11862
|
-
|
|
11863
|
-
|
|
11997
|
+
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('|') + ')$');
|
|
11998
|
+
var WALLET_NAMES = 'apple pay|google pay|paypal|venmo|amazon pay|shop pay|klarna|afterpay|clearpay|cash app(?: pay)?|affirm|sezzle|samsung pay';
|
|
11999
|
+
var WALLET_VERBS = 'pay|buy|purchase|order|book|donate|subscribe|check ?out|checkout|express checkout|continue|proceed|' + 'complete purchase|place (?:my |your )?order';
|
|
12000
|
+
// "Pay with Klarna", "Buy with GPay", "Continue to PayPal".
|
|
12001
|
+
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 + '))$');
|
|
12002
|
+
// A wallet's own name on a button: "PayPal", "Klarna checkout", "Apple Pay logo".
|
|
12003
|
+
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))*)?$');
|
|
12004
|
+
// Exact HTTPS hosts of hosted wallet button frames.
|
|
12005
|
+
var walletFrameHosts = new Set(['pay.google.com', 'applepay.cdn-apple.com', 'payments.amazon.com', 'pay.shopify.com']);
|
|
12006
|
+
// PayPal serves its buttons and its card fields and pay-later messages from
|
|
12007
|
+
// the same hosts; only the button frames are wallet taps.
|
|
12008
|
+
var paypalFrameHosts = new Set(['www.paypal.com', 'www.sandbox.paypal.com']);
|
|
12009
|
+
var PAYPAL_BUTTON_FRAME = /^\/smart\/(?:buttons|menu)(?:\/|$)/;
|
|
12010
|
+
// Stripe's quick-pay button frames; its other frames are card and form fields.
|
|
12011
|
+
var STRIPE_WALLET_FRAME = /\/elements-inner-(?:payment-request|express-checkout)-/;
|
|
12012
|
+
// Hosted card-entry and fingerprint frames: focus there is typing card details.
|
|
12013
|
+
var CARD_FRAME_DOMAINS = ['adyen.com', 'braintreegateway.com', 'cybersource.com', 'squarecdn.com', 'checkout.com'];
|
|
11864
12014
|
// Keep these classes ASCII-only in source bytes to avoid mojibake parse failures
|
|
11865
12015
|
// when a host page forces non-UTF-8 decoding before JavaScript parse.
|
|
11866
12016
|
var TYPOGRAPHIC_APOSTROPHES = /[\u2019\u2018]/g;
|
|
11867
12017
|
var CURRENCY_AND_NUMBER_CHARS = /[0-9\uFF10-\uFF19$\u20AC\u00A3\u00A5\uFFE5\u20B9\u5186.,\uFF0C]/g;
|
|
12018
|
+
var BRACKETED = /\([^()]*\)|\[[^[\]]*\]|\uFF08[^\uFF08\uFF09]*\uFF09|\uFF3B[^\uFF3B\uFF3D]*\uFF3D|\u3010[^\u3010\u3011]*\u3011/g;
|
|
12019
|
+
// Spaces, punctuation, separators, arrows and symbols, CJK and full-width forms
|
|
12020
|
+
// included, plus emoji halves and variation selectors.
|
|
12021
|
+
var EDGE = "[\\s!-/:-@\\[-`{-~\\u00A1-\\u00BF\\u00D7\\u00F7\\u2000-\\u206F\\u20A0-\\u20CF\\u2190-\\u2BFF" + "\\u3000-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u30FB\\uD800-\\uDFFF\\uFE00-\\uFE0F" + "\\uFF01-\\uFF0F\\uFF1A-\\uFF20\\uFF3B-\\uFF40\\uFF5B-\\uFF65]+";
|
|
12022
|
+
var EDGE_SYMBOLS = new RegExp('^' + EDGE + '|' + EDGE + '$', 'g');
|
|
11868
12023
|
function getIntentRouteClass(pathname) {
|
|
11869
12024
|
return checkoutPath.test(pathname) ? 'checkout_path' : 'other';
|
|
11870
12025
|
}
|
|
12026
|
+
var SEPARATORS = /[-_|\u00B7\u2022]/g;
|
|
12027
|
+
var CURRENCY_CODES = /(?:^| )(?:usd|aud|cad|nzd|gbp|eur|chf|sgd|hkd|jpy|inr|mxn)(?= |$)|\b(?:a|au|ca|c|us|nz|hk|s)\$/g;
|
|
12028
|
+
var WALLET_ALIASES = [[/\bapplepay\b/g, 'apple pay'], [/\bg ?pay\b/g, 'google pay'], [/\bamazonpay\b/g, 'amazon pay'], [/\bshoppay\b/g, 'shop pay'], [/\bcashapp\b/g, 'cash app']];
|
|
12029
|
+
// Labels longer than this are sentences: they neither match nor veto intent.
|
|
12030
|
+
var MAX_LABEL_WORDS = 8;
|
|
11871
12031
|
function normalize(text) {
|
|
11872
12032
|
if (!text || text.length > 128) {
|
|
11873
12033
|
return undefined;
|
|
11874
12034
|
}
|
|
11875
|
-
|
|
12035
|
+
var label = text.toLowerCase().replace(TYPOGRAPHIC_APOSTROPHES, "'").replace(/[&+]/g, ' and ').replace(SEPARATORS, ' ').replace(CURRENCY_CODES, ' ').replace(CURRENCY_AND_NUMBER_CHARS, '').replace(/\s+/g, ' ').trim();
|
|
12036
|
+
WALLET_ALIASES.forEach(function (_a) {
|
|
12037
|
+
var alias = _a[0],
|
|
12038
|
+
name = _a[1];
|
|
12039
|
+
label = label.replace(alias, name);
|
|
12040
|
+
});
|
|
12041
|
+
return label.split(' ').length > MAX_LABEL_WORDS ? undefined : label;
|
|
12042
|
+
}
|
|
12043
|
+
// The label without bracketed asides or the separators and symbols left at its
|
|
12044
|
+
// edges, so "place order -" and "place order ( items)" both read "place order".
|
|
12045
|
+
function coreLabel(text) {
|
|
12046
|
+
var core = text.replace(BRACKETED, ' ').replace(/\s+/g, ' ').replace(EDGE_SYMBOLS, '');
|
|
12047
|
+
return core || text.replace(EDGE_SYMBOLS, '');
|
|
11876
12048
|
}
|
|
11877
12049
|
// Labels come from aria-label then visible text. Only submit and button inputs
|
|
11878
|
-
// have their value read, so a typed field value is never touched.
|
|
11879
|
-
//
|
|
12050
|
+
// have their value read, so a typed field value is never touched. A control
|
|
12051
|
+
// holding form fields or more than four children gives only its aria-label.
|
|
11880
12052
|
function labelSources(element) {
|
|
11881
12053
|
if (element instanceof HTMLInputElement) {
|
|
11882
12054
|
var type = element.type;
|
|
@@ -11889,7 +12061,7 @@ var mParticle = (function () {
|
|
|
11889
12061
|
return [element.getAttribute('aria-label'), text];
|
|
11890
12062
|
}
|
|
11891
12063
|
if (element.children.length > 4 || isUnsafeTextSource(element)) {
|
|
11892
|
-
return [];
|
|
12064
|
+
return [element.getAttribute('aria-label')];
|
|
11893
12065
|
}
|
|
11894
12066
|
return [element.getAttribute('aria-label'), element.textContent];
|
|
11895
12067
|
}
|
|
@@ -11898,53 +12070,97 @@ var mParticle = (function () {
|
|
|
11898
12070
|
return !!label;
|
|
11899
12071
|
});
|
|
11900
12072
|
}
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
12073
|
+
// "Buy now" and "Purchase" count only on a checkout route, and never on a plain
|
|
12074
|
+
// link, which is navigation.
|
|
12075
|
+
function isCommitLabel(text, isLink, pathname) {
|
|
12076
|
+
if (isLink) {
|
|
12077
|
+
return linkCommits.test(text);
|
|
11904
12078
|
}
|
|
11905
|
-
|
|
11906
|
-
|
|
12079
|
+
return commits.test(text) || routeGatedCommits.test(text) && getIntentRouteClass(pathname) === 'checkout_path';
|
|
12080
|
+
}
|
|
12081
|
+
// Classifies a clicked or submitting control found by findControl, in order:
|
|
12082
|
+
// pickers, toggles and pay-later messages never count; a vendor's own wallet
|
|
12083
|
+
// button counts before any label rule; a confirm label on any source counts
|
|
12084
|
+
// before the look-alike exclusions veto; then wallet phrases and steps.
|
|
12085
|
+
function classifyIntent(control, pathname) {
|
|
12086
|
+
var vendor = vendorButtonKind(control);
|
|
12087
|
+
if (isQuietIntentControl(control, vendor === 'wallet') || vendor === 'none') {
|
|
12088
|
+
return undefined;
|
|
11907
12089
|
}
|
|
11908
|
-
if (
|
|
11909
|
-
return
|
|
12090
|
+
if (vendor) {
|
|
12091
|
+
return {
|
|
12092
|
+
kind: vendor,
|
|
12093
|
+
source: 'vendor'
|
|
12094
|
+
};
|
|
11910
12095
|
}
|
|
11911
|
-
return undefined;
|
|
11912
|
-
}
|
|
11913
|
-
// Classifies a clicked or submitting control found by findControl.
|
|
11914
|
-
function classifyIntentControl(control, pathname) {
|
|
11915
12096
|
var texts = labels(control);
|
|
11916
|
-
|
|
11917
|
-
|
|
12097
|
+
var cores = texts.map(coreLabel);
|
|
12098
|
+
var isLink = control.matches('a[href]:not([role="button"])');
|
|
12099
|
+
if (cores.some(function (text) {
|
|
12100
|
+
return isCommitLabel(text, isLink, pathname);
|
|
11918
12101
|
})) {
|
|
11919
|
-
return
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
button = control.matches(CONTROL_SELECTOR) || isWalletElement(control);
|
|
12102
|
+
return {
|
|
12103
|
+
kind: 'commit',
|
|
12104
|
+
source: 'label'
|
|
12105
|
+
};
|
|
11924
12106
|
}
|
|
11925
|
-
if (
|
|
11926
|
-
return
|
|
12107
|
+
if (texts.concat(cores).some(function (text) {
|
|
12108
|
+
return englishExclusions.test(text) || otherExclusions.test(text);
|
|
12109
|
+
})) {
|
|
12110
|
+
return undefined;
|
|
11927
12111
|
}
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
var
|
|
11931
|
-
if (
|
|
11932
|
-
return
|
|
12112
|
+
var isButton = !isLink && control.matches(CONTROL_SELECTOR);
|
|
12113
|
+
for (var _i = 0, cores_1 = cores; _i < cores_1.length; _i++) {
|
|
12114
|
+
var text = cores_1[_i];
|
|
12115
|
+
if (isButton && walletPhrases.test(text)) {
|
|
12116
|
+
return {
|
|
12117
|
+
kind: 'wallet',
|
|
12118
|
+
source: 'label'
|
|
12119
|
+
};
|
|
12120
|
+
}
|
|
12121
|
+
if (isButton && walletBrands.test(text)) {
|
|
12122
|
+
return {
|
|
12123
|
+
kind: 'wallet',
|
|
12124
|
+
source: 'brand'
|
|
12125
|
+
};
|
|
12126
|
+
}
|
|
12127
|
+
if (steps.test(text)) {
|
|
12128
|
+
return {
|
|
12129
|
+
kind: 'checkout_step',
|
|
12130
|
+
source: 'label'
|
|
12131
|
+
};
|
|
11933
12132
|
}
|
|
11934
12133
|
}
|
|
11935
12134
|
return undefined;
|
|
11936
12135
|
}
|
|
11937
|
-
//
|
|
12136
|
+
// The host is the domain or one of its subdomains, never a look-alike suffix.
|
|
12137
|
+
function isOnDomain(host, domain) {
|
|
12138
|
+
return host === domain || host.length > domain.length && host.slice(host.length - domain.length - 1) === '.' + domain;
|
|
12139
|
+
}
|
|
12140
|
+
// Focus moving into a visible hosted wallet button counts as a wallet click;
|
|
12141
|
+
// focus into hosted card fields or messages is only a frame.
|
|
11938
12142
|
function classifyIntentFrame(target, win) {
|
|
11939
12143
|
if (win.document.visibilityState !== 'visible' || !(target instanceof HTMLIFrameElement)) {
|
|
11940
12144
|
return undefined;
|
|
11941
12145
|
}
|
|
11942
|
-
if (isRoktElement(target)) {
|
|
12146
|
+
if (isRoktElement(target) || hasComposedAncestor(target, '[data-mp-no-track]')) {
|
|
11943
12147
|
return undefined;
|
|
11944
12148
|
}
|
|
11945
12149
|
try {
|
|
11946
12150
|
var url = new URL(target.src);
|
|
11947
|
-
|
|
12151
|
+
var host_1 = url.protocol === 'https:' ? url.hostname : '';
|
|
12152
|
+
if (paypalFrameHosts.has(host_1)) {
|
|
12153
|
+
return PAYPAL_BUTTON_FRAME.test(url.pathname) ? 'wallet' : 'frame';
|
|
12154
|
+
}
|
|
12155
|
+
if (walletFrameHosts.has(host_1)) {
|
|
12156
|
+
return 'wallet';
|
|
12157
|
+
}
|
|
12158
|
+
if (host_1 === 'js.stripe.com') {
|
|
12159
|
+
return STRIPE_WALLET_FRAME.test(url.pathname) ? 'wallet' : 'frame';
|
|
12160
|
+
}
|
|
12161
|
+
if (CARD_FRAME_DOMAINS.some(function (domain) {
|
|
12162
|
+
return isOnDomain(host_1, domain);
|
|
12163
|
+
})) {
|
|
11948
12164
|
return 'frame';
|
|
11949
12165
|
}
|
|
11950
12166
|
} catch (e) {
|
|
@@ -11962,6 +12178,9 @@ var mParticle = (function () {
|
|
|
11962
12178
|
// A ceiling, not a dedupe: a held Enter key or browser automation fires a
|
|
11963
12179
|
// trusted click per repeat, and each one is forwarded to every kit.
|
|
11964
12180
|
var PAGE_EVENT_LIMIT = 100;
|
|
12181
|
+
// A click uploads at once. Taps that follow it this quickly are held and share
|
|
12182
|
+
// one upload once they stop.
|
|
12183
|
+
var UPLOAD_DEBOUNCE_MS = 100;
|
|
11965
12184
|
var btnWindow = function btnWindow() {
|
|
11966
12185
|
return typeof window === 'undefined' ? null : window;
|
|
11967
12186
|
};
|
|
@@ -12001,7 +12220,7 @@ var mParticle = (function () {
|
|
|
12001
12220
|
passive: true
|
|
12002
12221
|
};
|
|
12003
12222
|
// Logs every button click as an mParticle event, and hands checkout intent to the
|
|
12004
|
-
// Rokt kit as a frozen {kind, t}. Submits and wallet frame focus feed the kit only.
|
|
12223
|
+
// Rokt kit as a frozen {kind, t, source, pathname}. Submits and wallet frame focus feed the kit only.
|
|
12005
12224
|
var ButtonTracker = /** @class */function () {
|
|
12006
12225
|
function ButtonTracker(mpInstance) {
|
|
12007
12226
|
var _this = this;
|
|
@@ -12015,6 +12234,10 @@ var mParticle = (function () {
|
|
|
12015
12234
|
this.pageEvents = 0;
|
|
12016
12235
|
// Clicks read at dispatch and waiting for the next paint.
|
|
12017
12236
|
this.pending = [];
|
|
12237
|
+
this.uploadTimerId = null;
|
|
12238
|
+
this.uploadHeld = false;
|
|
12239
|
+
// Set while the page is left, when the SDK's exit upload sends the queue.
|
|
12240
|
+
this.leaving = false;
|
|
12018
12241
|
this.drain = function () {
|
|
12019
12242
|
var queue = _this.pending;
|
|
12020
12243
|
_this.pending = [];
|
|
@@ -12028,7 +12251,14 @@ var mParticle = (function () {
|
|
|
12028
12251
|
// beforeunload, the first of them.
|
|
12029
12252
|
this.onHide = function (event) {
|
|
12030
12253
|
if (event.type === 'beforeunload' || event.type === 'pagehide' || document.visibilityState === 'hidden') {
|
|
12031
|
-
_this.
|
|
12254
|
+
_this.leaving = true;
|
|
12255
|
+
try {
|
|
12256
|
+
_this.drain();
|
|
12257
|
+
} finally {
|
|
12258
|
+
_this.leaving = false;
|
|
12259
|
+
}
|
|
12260
|
+
// The SDK's exit upload sends the queue, held taps included.
|
|
12261
|
+
_this.cancelUpload();
|
|
12032
12262
|
}
|
|
12033
12263
|
};
|
|
12034
12264
|
this.onPageShow = function (event) {
|
|
@@ -12119,6 +12349,7 @@ var mParticle = (function () {
|
|
|
12119
12349
|
this.lastSent = {};
|
|
12120
12350
|
this.pagePath = null;
|
|
12121
12351
|
this.pageEvents = 0;
|
|
12352
|
+
this.cancelUpload();
|
|
12122
12353
|
};
|
|
12123
12354
|
ButtonTracker.prototype.enqueue = function (delivery) {
|
|
12124
12355
|
this.pending.push(delivery);
|
|
@@ -12144,15 +12375,22 @@ var mParticle = (function () {
|
|
|
12144
12375
|
// button that disables or relabels itself is seen as it was clicked.
|
|
12145
12376
|
// Logging and the kit hand-off wait for the next paint.
|
|
12146
12377
|
var click;
|
|
12378
|
+
var box;
|
|
12147
12379
|
var kind;
|
|
12380
|
+
var source;
|
|
12148
12381
|
var unconfirmedCommit = false;
|
|
12149
12382
|
if (layer === 'click') {
|
|
12150
12383
|
// The composed path is only populated during dispatch.
|
|
12151
12384
|
var control = findControl(isFunction(event.composedPath) ? event.composedPath().slice(0, PATH_LIMIT) : []);
|
|
12152
12385
|
if (control) {
|
|
12153
12386
|
click = logsEvents ? describeButtonClick(control) : undefined;
|
|
12387
|
+
if (click && click.checked !== undefined) {
|
|
12388
|
+
box = control;
|
|
12389
|
+
}
|
|
12154
12390
|
// Checkout intent is only worked out when the kit can use it.
|
|
12155
|
-
|
|
12391
|
+
var intent = signalsKit ? classifyIntent(control, pathname) : undefined;
|
|
12392
|
+
kind = intent ? intent.kind : undefined;
|
|
12393
|
+
source = intent ? intent.source : undefined;
|
|
12156
12394
|
}
|
|
12157
12395
|
} else if (layer === 'submit') {
|
|
12158
12396
|
var submitter = event.submitter;
|
|
@@ -12161,8 +12399,11 @@ var mParticle = (function () {
|
|
|
12161
12399
|
// commit click, which is known once that click is delivered.
|
|
12162
12400
|
if (!submitter) {
|
|
12163
12401
|
kind = 'commit';
|
|
12402
|
+
source = 'submit';
|
|
12164
12403
|
} else if (control) {
|
|
12165
|
-
|
|
12404
|
+
var intent = classifyIntent(control, pathname);
|
|
12405
|
+
kind = intent ? intent.kind : undefined;
|
|
12406
|
+
source = intent ? intent.source : undefined;
|
|
12166
12407
|
}
|
|
12167
12408
|
unconfirmedCommit = !submitter;
|
|
12168
12409
|
}
|
|
@@ -12176,14 +12417,23 @@ var mParticle = (function () {
|
|
|
12176
12417
|
// Read after the blur settles: some browsers have not moved
|
|
12177
12418
|
// focus into the frame yet when the window blurs.
|
|
12178
12419
|
kind = classifyIntentFrame(document.activeElement, window);
|
|
12420
|
+
source = 'frame';
|
|
12179
12421
|
} else if (unconfirmedCommit && t - _this.lastCommitClick > SUBMIT_AFTER_COMMIT_CLICK_MS) {
|
|
12180
12422
|
kind = undefined;
|
|
12181
12423
|
}
|
|
12182
12424
|
if (click && logsEvents) {
|
|
12425
|
+
// The box as this click left it. The browser toggles it
|
|
12426
|
+
// before dispatch and puts it back once a cancelled click
|
|
12427
|
+
// is over, so only a cancelled click changes the reading
|
|
12428
|
+
// taken at dispatch: a checkbox to the other state, a
|
|
12429
|
+
// radio to however it was restored.
|
|
12430
|
+
if (box && event.defaultPrevented) {
|
|
12431
|
+
click.checked = box.type === 'checkbox' ? !click.checked : box.checked;
|
|
12432
|
+
}
|
|
12183
12433
|
_this.logClick(click, pathname);
|
|
12184
12434
|
}
|
|
12185
12435
|
if (kind) {
|
|
12186
|
-
_this.emit(kind, layer, t, pathname);
|
|
12436
|
+
_this.emit(kind, source, layer, t, pathname);
|
|
12187
12437
|
}
|
|
12188
12438
|
} catch (e) {
|
|
12189
12439
|
_this.log('[error] delivery threw: ' + e);
|
|
@@ -12205,9 +12455,48 @@ var mParticle = (function () {
|
|
|
12205
12455
|
this.pageEvents++;
|
|
12206
12456
|
// Restarts an expired session, as public logEvent() and page views do.
|
|
12207
12457
|
this.mpInstance._SessionManager.resetSessionTimer();
|
|
12458
|
+
var queued = this.queuedForUpload();
|
|
12208
12459
|
this.mpInstance._Events.logEvent(buildButtonClickEvent(click, pathname));
|
|
12460
|
+
// Only a click waiting for the batch interval is sent early: a held
|
|
12461
|
+
// one goes out when released, and with batching off it already has.
|
|
12462
|
+
// A checkbox or radio waits for the batch, since choosing an option
|
|
12463
|
+
// doesn't leave the page the way a confirm click can.
|
|
12464
|
+
if (!this.leaving && click.checked === undefined && this.queuedForUpload() > queued) {
|
|
12465
|
+
this.uploadNow();
|
|
12466
|
+
}
|
|
12467
|
+
};
|
|
12468
|
+
// Events waiting in the upload queue for the next batch interval.
|
|
12469
|
+
ButtonTracker.prototype.queuedForUpload = function () {
|
|
12470
|
+
var uploader = this.mpInstance._APIClient.uploader;
|
|
12471
|
+
return uploader ? uploader.eventsQueuedForProcessing.length : 0;
|
|
12472
|
+
};
|
|
12473
|
+
// Uploads a click without waiting for the batch interval. A tap inside the
|
|
12474
|
+
// wait after the last one is held, and each restarts the wait, so a burst
|
|
12475
|
+
// after the first click goes out in one request once the taps stop.
|
|
12476
|
+
ButtonTracker.prototype.uploadNow = function () {
|
|
12477
|
+
var _this = this;
|
|
12478
|
+
if (this.uploadTimerId === null) {
|
|
12479
|
+
this.mpInstance.upload();
|
|
12480
|
+
} else {
|
|
12481
|
+
clearTimeout(this.uploadTimerId);
|
|
12482
|
+
this.uploadHeld = true;
|
|
12483
|
+
}
|
|
12484
|
+
this.uploadTimerId = setTimeout(function () {
|
|
12485
|
+
_this.uploadTimerId = null;
|
|
12486
|
+
if (_this.uploadHeld) {
|
|
12487
|
+
_this.uploadHeld = false;
|
|
12488
|
+
_this.mpInstance.upload();
|
|
12489
|
+
}
|
|
12490
|
+
}, UPLOAD_DEBOUNCE_MS);
|
|
12491
|
+
};
|
|
12492
|
+
ButtonTracker.prototype.cancelUpload = function () {
|
|
12493
|
+
if (this.uploadTimerId !== null) {
|
|
12494
|
+
clearTimeout(this.uploadTimerId);
|
|
12495
|
+
this.uploadTimerId = null;
|
|
12496
|
+
}
|
|
12497
|
+
this.uploadHeld = false;
|
|
12209
12498
|
};
|
|
12210
|
-
ButtonTracker.prototype.emit = function (kind, layer, t, pathname) {
|
|
12499
|
+
ButtonTracker.prototype.emit = function (kind, source, layer, t, pathname) {
|
|
12211
12500
|
if (this.isKitSuppressed()) {
|
|
12212
12501
|
return;
|
|
12213
12502
|
}
|
|
@@ -12224,7 +12513,9 @@ var mParticle = (function () {
|
|
|
12224
12513
|
};
|
|
12225
12514
|
this.mpInstance._RoktManager.notifyCheckoutIntent(Object.freeze({
|
|
12226
12515
|
kind: kind,
|
|
12227
|
-
t: t
|
|
12516
|
+
t: t,
|
|
12517
|
+
source: source,
|
|
12518
|
+
pathname: pathname
|
|
12228
12519
|
}), pathname);
|
|
12229
12520
|
};
|
|
12230
12521
|
// Events follow the SDK's opt-out like any other event.
|