@mparticle/web-sdk 3.19.1 → 3.21.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.
@@ -15,7 +15,7 @@ map:function map(a,b){var c,d,e;if(null===this)throw new TypeError(" this is nul
15
15
  filter:function filter(a/*, thisArg*/){if(void 0===this||null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if("function"!=typeof a)throw new TypeError;for(var d=[],e=2<=arguments.length?arguments[1]:void 0,f=0;f<c;f++)if(f in b){var g=b[f];a.call(e,g,f,b)&&d.push(g);}return d},// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
16
16
  isArray:function isArray(a){return "[object Array]"===Object.prototype.toString.call(a)},Base64:Base64$1};
17
17
 
18
- var version = "3.19.1";
18
+ var version = "3.21.0";
19
19
 
20
20
  var Constants={sdkVersion:version,sdkVendor:"mparticle",platform:"web",Messages:{DeprecationMessages:{MethodHasBeenDeprecated:"has been deprecated.",MethodMarkedForDeprecationPostfix:"is a deprecated method and will be removed in future releases.",AlternativeMethodPrefix:"Please use the alternate method:"},ErrorMessages:{NoToken:"A token must be specified.",EventNameInvalidType:"Event name must be a valid string value.",EventDataInvalidType:"Event data must be a valid object hash.",LoggingDisabled:"Event logging is currently disabled.",CookieParseError:"Could not parse cookie",EventEmpty:"Event object is null or undefined, cancelling send",APIRequestEmpty:"APIRequest is null or undefined, cancelling send",NoEventType:"Event type must be specified.",TransactionIdRequired:"Transaction ID is required",TransactionRequired:"A transaction attributes object is required",PromotionIdRequired:"Promotion ID is required",BadAttribute:"Attribute value cannot be object or array",BadKey:"Key value cannot be object or array",BadLogPurchase:"Transaction attributes and a product are both required to log a purchase, https://docs.mparticle.com/?javascript#measuring-transactions",AudienceAPINotEnabled:"Your workspace is not enabled to retrieve user audiences."},InformationMessages:{CookieSearch:"Searching for cookie",CookieFound:"Cookie found, parsing values",CookieNotFound:"Cookies not found",CookieSet:"Setting cookie",CookieSync:"Performing cookie sync",SendBegin:"Starting to send event",SendIdentityBegin:"Starting to send event to identity server",SendWindowsPhone:"Sending event to Windows Phone container",SendIOS:"Calling iOS path: ",SendAndroid:"Calling Android JS interface method: ",SendHttp:"Sending event to mParticle HTTP service",SendAliasHttp:"Sending alias request to mParticle HTTP service",SendIdentityHttp:"Sending event to mParticle HTTP service",StartingNewSession:"Starting new Session",StartingLogEvent:"Starting to log event",StartingLogOptOut:"Starting to log user opt in/out",StartingEndSession:"Starting to end session",StartingInitialization:"Starting to initialize",StartingLogCommerceEvent:"Starting to log commerce event",StartingAliasRequest:"Starting to Alias MPIDs",LoadingConfig:"Loading configuration options",AbandonLogEvent:"Cannot log event, logging disabled or developer token not set",AbandonAliasUsers:"Cannot Alias Users, logging disabled or developer token not set",AbandonStartSession:"Cannot start session, logging disabled or developer token not set",AbandonEndSession:"Cannot end session, logging disabled or developer token not set",NoSessionToEnd:"Cannot end session, no active session found"},ValidationMessages:{ModifyIdentityRequestUserIdentitiesPresent:"identityRequests to modify require userIdentities to be present. Request not sent to server. Please fix and try again",IdentityRequesetInvalidKey:"There is an invalid key on your identityRequest object. It can only contain a `userIdentities` object and a `onUserAlias` function. Request not sent to server. Please fix and try again.",OnUserAliasType:"The onUserAlias value must be a function.",UserIdentities:"The userIdentities key must be an object with keys of identityTypes and values of strings. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidKey:"There is an invalid identity key on your `userIdentities` object within the identityRequest. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidValues:"All user identity values must be strings or null. Request not sent to server. Please fix and try again.",AliasMissingMpid:"Alias Request must contain both a destinationMpid and a sourceMpid",AliasNonUniqueMpid:"Alias Request's destinationMpid and sourceMpid must be unique",AliasMissingTime:"Alias Request must have both a startTime and an endTime",AliasStartBeforeEndTime:"Alias Request's endTime must be later than its startTime"}},NativeSdkPaths:{LogEvent:"logEvent",SetUserTag:"setUserTag",RemoveUserTag:"removeUserTag",SetUserAttribute:"setUserAttribute",RemoveUserAttribute:"removeUserAttribute",SetSessionAttribute:"setSessionAttribute",LogOut:"logOut",SetUserAttributeList:"setUserAttributeList",RemoveAllUserAttributes:"removeAllUserAttributes",GetUserAttributesLists:"getUserAttributesLists",GetAllUserAttributes:"getAllUserAttributes",Identify:"identify",Logout:"logout",Login:"login",Modify:"modify",Alias:"aliasUsers",Upload:"upload"},StorageNames:{localStorageName:"mprtcl-api",localStorageNameV3:"mprtcl-v3",cookieName:"mprtcl-api",cookieNameV2:"mprtcl-v2",cookieNameV3:"mprtcl-v3",localStorageNameV4:"mprtcl-v4",cookieNameV4:"mprtcl-v4",currentStorageName:"mprtcl-v4"},DefaultConfig:{cookieDomain:null,cookieExpiration:365,logLevel:null,timeout:300,sessionTimeout:30,forwarderStatsTimeout:5e3,integrationDelayTimeout:5e3,maxCookieSize:3e3,aliasMaxWindow:90,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
21
21
  },DefaultBaseUrls:{v1SecureServiceUrl:"jssdks.mparticle.com/v1/JS/",v2SecureServiceUrl:"jssdks.mparticle.com/v2/JS/",v3SecureServiceUrl:"jssdks.mparticle.com/v3/JS/",configUrl:"jssdkcdns.mparticle.com/JS/v2/",identityUrl:"identity.mparticle.com/v1/",aliasUrl:"jssdks.mparticle.com/v1/identity/",userAudienceUrl:"nativesdks.mparticle.com/v1/"},// These are the paths that are used to construct the CNAME urls
@@ -1364,7 +1364,7 @@ var BUTTON_CLICK_EVENT_NAME="Auto Button Click";// One custom event per click, l
1364
1364
  // page fields that auto page views carry. No intent: classifying clicks is left
1365
1365
  // to whoever reads the data.
1366
1366
  var buildButtonClickEvent=function(a,b){// Optional fields are left out when the button has no safe value for them.
1367
- for(var c,d,e=(c={label_source:a.labelSource,element:a.element,type:a.type,path:b,hostname:window.location.hostname},c[AUTO_BUTTON_ATTRIBUTE]=!0,c),f={label:a.label,aria_label:a.ariaLabel,icon:a.icon,element_id:a.elementId,element_name:a.elementName,test_id:a.testId,controls:a.controls,form:a.form},g=0,h=Object.keys(f);g<h.length;g++)d=h[g],f[d]&&(e[d]=f[d]);return {messageType:MessageType$1.PageEvent,name:BUTTON_CLICK_EVENT_NAME,data:e,eventType:EventType.Other}};
1367
+ for(var c,d,e=(c={label_source:a.labelSource,element:a.element,type:a.type,path:b,hostname:window.location.hostname},c[AUTO_BUTTON_ATTRIBUTE]=!0,c),f={label:a.label,aria_label:a.ariaLabel,icon:a.icon,element_id:a.elementId,element_name:a.elementName,test_id:a.testId,controls:a.controls,form:a.form,link_type:a.linkType,link_domain:a.linkDomain,file_extension:a.fileExtension},g=0,h=Object.keys(f);g<h.length;g++)d=h[g],f[d]&&(e[d]=f[d]);return {messageType:MessageType$1.PageEvent,name:BUTTON_CLICK_EVENT_NAME,data:e,eventType:EventType.Other}};
1368
1368
 
1369
1369
  // descriptions. Rokt's own elements, disabled controls and anything under
1370
1370
  // data-mp-no-track are never returned.
@@ -1384,7 +1384,7 @@ function createDigitPatterns(){try{return {run:/\p{Nd}{6,}/u,number:/\p{Nd}+(?:[
1384
1384
  // masked. Dropped when it looks like an email, phone or card number.
1385
1385
  function maskLabel(a){if(a&&!(128<a.length)&&digitPatterns){var b=a.replace(/\s+/g," ").trim().toLowerCase();if(b&&-1===b.indexOf("@")&&!digitPatterns.run.test(b.replace(/[\s()\-\u2010-\u2015]/g,""))){var c=b.replace(digitPatterns.number,"#").slice(0,64).trim();return c||void 0}}}
1386
1386
 
1387
- function elementKind(a){if(isWalletElement(a))return "wallet";var b=a.tagName.toLowerCase();return "button"===b||"input"===b?b:"role"}function textOf(a){return a&&!isUnsafeTextSource(a)&&a.textContent?a.textContent.trim():""}function nonBlank(a){return a&&a.trim()?a:void 0}// The button's name as a screen reader would read it, simplified: the first of
1387
+ var DOWNLOAD_EXTENSIONS=/\.(pdf|zip|csv|txt|ics|docx?|xlsx?|pptx?|dmg|exe|apk|mp3|mp4)$/i;function elementKind(a){if(isWalletElement(a))return "wallet";var b=a.tagName.toLowerCase();return "button"===b||"input"===b?b:a.matches("a[href]:not([role=\"button\"])")?"link":"role"}function textOf(a){return a&&!isUnsafeTextSource(a)&&a.textContent?a.textContent.trim():""}function nonBlank(a){return a&&a.trim()?a:void 0}// The button's name as a screen reader would read it, simplified: the first of
1388
1388
  // aria-labelledby, aria-label, own text (or an inner image's alt), title, and for
1389
1389
  // inputs their value. The text sources are skipped for controls that hold form
1390
1390
  // fields or more than four children, so a typed value is never read.
@@ -1406,11 +1406,14 @@ function controlsIdentifier(a){var b=a.getAttribute("aria-controls");return stab
1406
1406
  // field named "id" shadows form.id.
1407
1407
  function formIdentifier(a){var b=a instanceof HTMLButtonElement||a instanceof HTMLInputElement?a.form:null;return b?stableIdentifier(b.getAttribute("id")):void 0}// The browser's normalised type: a button defaults to 'submit' and an unknown
1408
1408
  // input type reads as 'text'. Wallet and role buttons are just 'button'.
1409
- function controlType(a,b){return "button"===b||"input"===b?a.type:"button"}// Describes a clicked control for auto button tracking. Links are left to page
1410
- // views, so a link click is not described.
1411
- function describeButtonClick(a){if(!a.matches("a[href]:not([role=\"button\"])")){var b=elementKind(a),c=accessibleName(a),d=maskLabel(c.raw);return {label:d,labelSource:d?c.source:"none",// Kept on its own as well: it often says more than the visible text,
1409
+ function controlType(a,b){return "button"===b||"input"===b?a.type:"button"}// Reads the link's own resolved URL parts, never its full href: only the hostname of
1410
+ // a link that leaves the site and the extension of a file it downloads travel. Navigation
1411
+ // within the site is left to page views, and mailto:, tel: and other schemes are skipped.
1412
+ function describeLink(a){var b=(a.getAttribute("href")||"").trim();if(!b||"#"===b.charAt(0))return {type:"same_page"};if(a instanceof HTMLAnchorElement){var c=a.protocol.toLowerCase();if("javascript:"===c)return {type:"same_page"};if("http:"===c||"https:"===c){var d=a.hostname===window.location.hostname?void 0:a.hostname.toLowerCase(),e=DOWNLOAD_EXTENSIONS.exec(a.pathname);return a.hasAttribute("download")||e?{type:"download",domain:d,fileExtension:e?e[1].toLowerCase():void 0}:d?{type:"outbound",domain:d}:a.pathname===window.location.pathname&&a.search===window.location.search?{type:"same_page"}:void 0}}}// Describes a clicked control for auto button tracking. A link counts only when page
1413
+ // views can't see the click, and carries its link type.
1414
+ function describeButtonClick(a){var b;if(!(a.matches("a[href]:not([role=\"button\"])")&&(b=describeLink(a),!b))){var c=elementKind(a),d=accessibleName(a),e=maskLabel(d.raw);return {label:e,labelSource:e?d.source:"none",// Kept on its own as well: it often says more than the visible text,
1412
1415
  // such as which item a remove button removes.
1413
- ariaLabel:maskLabel(a.getAttribute("aria-label")),icon:iconHint(a),elementId:stableIdentifier(a.getAttribute("id")),elementName:stableIdentifier(a.getAttribute("name")),testId:firstIdentifier(a,TEST_ID_ATTRIBUTES),controls:controlsIdentifier(a),form:formIdentifier(a),element:b,type:controlType(a,b)}}}
1416
+ ariaLabel:maskLabel(a.getAttribute("aria-label")),icon:iconHint(a),elementId:stableIdentifier(a.getAttribute("id")),elementName:stableIdentifier(a.getAttribute("name")),testId:firstIdentifier(a,TEST_ID_ATTRIBUTES),controls:controlsIdentifier(a),form:formIdentifier(a),element:c,type:controlType(a,c),linkType:b?b.type:void 0,linkDomain:b?b.domain:void 0,fileExtension:b?b.fileExtension:void 0}}}
1414
1417
 
1415
1418
  var checkoutPath=/(?:^|\/)(?:checkouts?|payment|order|kasse|paiement|commander?|pago)(?:\/|$)/i,exclusions=new RegExp("coupon|promo|newsletter|sign in|add to cart|gift card|pay later|pay in|payment plan|learn more|gutschein|anmelden|warenkorb|bon cadeau|connexion|panier|cup\xF3n|iniciar sesi\xF3n|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"),commits=new RegExp("^(?:"+"(?:place|submit|complete|confirm|finali[sz]e)(?!(?: [^ ]+){0,2} (?:another|new|next)(?: |$))(?: [^ ]+){0,2} (?:order|purchase|booking|reservation|payment|checkout)|pay(?: securely)?(?: now)?|confirm and pay|order and pay|order with obligation to pay|(?:jetzt )?(?:zahlungs|kosten)pflichtig (?:bestellen|buchen)|(?:jetzt )?bezahlen|kauf abschlie\xDFen|bestellung abschlie\xDFen|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\xF3n 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"+")$"),routeGatedCommits=/^(?: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)$/,steps=new RegExp("^(?:"+"checkout|check out|check-out|continue to payment|proceed to checkout|zur kasse|zur kasse gehen|auschecken|weiter zur zahlung|passer \xE0 la caisse|continuer vers le paiement|proc\xE9der 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)?"+")$"),wallets=/^(?:apple pay|google pay|paypal|venmo|amazon pay|shop pay|klarna|afterpay|affirm)$/,frameHosts=new Set(["www.paypal.com","www.sandbox.paypal.com","pay.google.com","applepay.cdn-apple.com","js.stripe.com","checkoutshopper-live.adyen.com","payments.amazon.com","pay.shopify.com"]),TYPOGRAPHIC_APOSTROPHES=/[\u2019\u2018]/g,CURRENCY_AND_NUMBER_CHARS=/[0-9\uFF10-\uFF19$\u20AC\u00A3\u00A5\uFFE5\u20B9\u5186.,\uFF0C]/g;// Exact HTTPS hosts of hosted wallet and payment frames.
1416
1419
  // Keep these classes ASCII-only in source bytes to avoid mojibake parse failures
@@ -15,7 +15,7 @@ map:function map(a,b){var c,d,e;if(null===this)throw new TypeError(" this is nul
15
15
  filter:function filter(a/*, thisArg*/){if(void 0===this||null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if("function"!=typeof a)throw new TypeError;for(var d=[],e=2<=arguments.length?arguments[1]:void 0,f=0;f<c;f++)if(f in b){var g=b[f];a.call(e,g,f,b)&&d.push(g);}return d},// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
16
16
  isArray:function isArray(a){return "[object Array]"===Object.prototype.toString.call(a)},Base64:Base64$1};
17
17
 
18
- var version = "3.19.1";
18
+ var version = "3.21.0";
19
19
 
20
20
  var Constants={sdkVersion:version,sdkVendor:"mparticle",platform:"web",Messages:{DeprecationMessages:{MethodHasBeenDeprecated:"has been deprecated.",MethodMarkedForDeprecationPostfix:"is a deprecated method and will be removed in future releases.",AlternativeMethodPrefix:"Please use the alternate method:"},ErrorMessages:{NoToken:"A token must be specified.",EventNameInvalidType:"Event name must be a valid string value.",EventDataInvalidType:"Event data must be a valid object hash.",LoggingDisabled:"Event logging is currently disabled.",CookieParseError:"Could not parse cookie",EventEmpty:"Event object is null or undefined, cancelling send",APIRequestEmpty:"APIRequest is null or undefined, cancelling send",NoEventType:"Event type must be specified.",TransactionIdRequired:"Transaction ID is required",TransactionRequired:"A transaction attributes object is required",PromotionIdRequired:"Promotion ID is required",BadAttribute:"Attribute value cannot be object or array",BadKey:"Key value cannot be object or array",BadLogPurchase:"Transaction attributes and a product are both required to log a purchase, https://docs.mparticle.com/?javascript#measuring-transactions",AudienceAPINotEnabled:"Your workspace is not enabled to retrieve user audiences."},InformationMessages:{CookieSearch:"Searching for cookie",CookieFound:"Cookie found, parsing values",CookieNotFound:"Cookies not found",CookieSet:"Setting cookie",CookieSync:"Performing cookie sync",SendBegin:"Starting to send event",SendIdentityBegin:"Starting to send event to identity server",SendWindowsPhone:"Sending event to Windows Phone container",SendIOS:"Calling iOS path: ",SendAndroid:"Calling Android JS interface method: ",SendHttp:"Sending event to mParticle HTTP service",SendAliasHttp:"Sending alias request to mParticle HTTP service",SendIdentityHttp:"Sending event to mParticle HTTP service",StartingNewSession:"Starting new Session",StartingLogEvent:"Starting to log event",StartingLogOptOut:"Starting to log user opt in/out",StartingEndSession:"Starting to end session",StartingInitialization:"Starting to initialize",StartingLogCommerceEvent:"Starting to log commerce event",StartingAliasRequest:"Starting to Alias MPIDs",LoadingConfig:"Loading configuration options",AbandonLogEvent:"Cannot log event, logging disabled or developer token not set",AbandonAliasUsers:"Cannot Alias Users, logging disabled or developer token not set",AbandonStartSession:"Cannot start session, logging disabled or developer token not set",AbandonEndSession:"Cannot end session, logging disabled or developer token not set",NoSessionToEnd:"Cannot end session, no active session found"},ValidationMessages:{ModifyIdentityRequestUserIdentitiesPresent:"identityRequests to modify require userIdentities to be present. Request not sent to server. Please fix and try again",IdentityRequesetInvalidKey:"There is an invalid key on your identityRequest object. It can only contain a `userIdentities` object and a `onUserAlias` function. Request not sent to server. Please fix and try again.",OnUserAliasType:"The onUserAlias value must be a function.",UserIdentities:"The userIdentities key must be an object with keys of identityTypes and values of strings. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidKey:"There is an invalid identity key on your `userIdentities` object within the identityRequest. Request not sent to server. Please fix and try again.",UserIdentitiesInvalidValues:"All user identity values must be strings or null. Request not sent to server. Please fix and try again.",AliasMissingMpid:"Alias Request must contain both a destinationMpid and a sourceMpid",AliasNonUniqueMpid:"Alias Request's destinationMpid and sourceMpid must be unique",AliasMissingTime:"Alias Request must have both a startTime and an endTime",AliasStartBeforeEndTime:"Alias Request's endTime must be later than its startTime"}},NativeSdkPaths:{LogEvent:"logEvent",SetUserTag:"setUserTag",RemoveUserTag:"removeUserTag",SetUserAttribute:"setUserAttribute",RemoveUserAttribute:"removeUserAttribute",SetSessionAttribute:"setSessionAttribute",LogOut:"logOut",SetUserAttributeList:"setUserAttributeList",RemoveAllUserAttributes:"removeAllUserAttributes",GetUserAttributesLists:"getUserAttributesLists",GetAllUserAttributes:"getAllUserAttributes",Identify:"identify",Logout:"logout",Login:"login",Modify:"modify",Alias:"aliasUsers",Upload:"upload"},StorageNames:{localStorageName:"mprtcl-api",localStorageNameV3:"mprtcl-v3",cookieName:"mprtcl-api",cookieNameV2:"mprtcl-v2",cookieNameV3:"mprtcl-v3",localStorageNameV4:"mprtcl-v4",cookieNameV4:"mprtcl-v4",currentStorageName:"mprtcl-v4"},DefaultConfig:{cookieDomain:null,cookieExpiration:365,logLevel:null,timeout:300,sessionTimeout:30,forwarderStatsTimeout:5e3,integrationDelayTimeout:5e3,maxCookieSize:3e3,aliasMaxWindow:90,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
21
21
  },DefaultBaseUrls:{v1SecureServiceUrl:"jssdks.mparticle.com/v1/JS/",v2SecureServiceUrl:"jssdks.mparticle.com/v2/JS/",v3SecureServiceUrl:"jssdks.mparticle.com/v3/JS/",configUrl:"jssdkcdns.mparticle.com/JS/v2/",identityUrl:"identity.mparticle.com/v1/",aliasUrl:"jssdks.mparticle.com/v1/identity/",userAudienceUrl:"nativesdks.mparticle.com/v1/"},// These are the paths that are used to construct the CNAME urls
@@ -1364,7 +1364,7 @@ var BUTTON_CLICK_EVENT_NAME="Auto Button Click";// One custom event per click, l
1364
1364
  // page fields that auto page views carry. No intent: classifying clicks is left
1365
1365
  // to whoever reads the data.
1366
1366
  var buildButtonClickEvent=function(a,b){// Optional fields are left out when the button has no safe value for them.
1367
- for(var c,d,e=(c={label_source:a.labelSource,element:a.element,type:a.type,path:b,hostname:window.location.hostname},c[AUTO_BUTTON_ATTRIBUTE]=!0,c),f={label:a.label,aria_label:a.ariaLabel,icon:a.icon,element_id:a.elementId,element_name:a.elementName,test_id:a.testId,controls:a.controls,form:a.form},g=0,h=Object.keys(f);g<h.length;g++)d=h[g],f[d]&&(e[d]=f[d]);return {messageType:MessageType$1.PageEvent,name:BUTTON_CLICK_EVENT_NAME,data:e,eventType:EventType.Other}};
1367
+ for(var c,d,e=(c={label_source:a.labelSource,element:a.element,type:a.type,path:b,hostname:window.location.hostname},c[AUTO_BUTTON_ATTRIBUTE]=!0,c),f={label:a.label,aria_label:a.ariaLabel,icon:a.icon,element_id:a.elementId,element_name:a.elementName,test_id:a.testId,controls:a.controls,form:a.form,link_type:a.linkType,link_domain:a.linkDomain,file_extension:a.fileExtension},g=0,h=Object.keys(f);g<h.length;g++)d=h[g],f[d]&&(e[d]=f[d]);return {messageType:MessageType$1.PageEvent,name:BUTTON_CLICK_EVENT_NAME,data:e,eventType:EventType.Other}};
1368
1368
 
1369
1369
  // descriptions. Rokt's own elements, disabled controls and anything under
1370
1370
  // data-mp-no-track are never returned.
@@ -1384,7 +1384,7 @@ function createDigitPatterns(){try{return {run:/\p{Nd}{6,}/u,number:/\p{Nd}+(?:[
1384
1384
  // masked. Dropped when it looks like an email, phone or card number.
1385
1385
  function maskLabel(a){if(a&&!(128<a.length)&&digitPatterns){var b=a.replace(/\s+/g," ").trim().toLowerCase();if(b&&-1===b.indexOf("@")&&!digitPatterns.run.test(b.replace(/[\s()\-\u2010-\u2015]/g,""))){var c=b.replace(digitPatterns.number,"#").slice(0,64).trim();return c||void 0}}}
1386
1386
 
1387
- function elementKind(a){if(isWalletElement(a))return "wallet";var b=a.tagName.toLowerCase();return "button"===b||"input"===b?b:"role"}function textOf(a){return a&&!isUnsafeTextSource(a)&&a.textContent?a.textContent.trim():""}function nonBlank(a){return a&&a.trim()?a:void 0}// The button's name as a screen reader would read it, simplified: the first of
1387
+ var DOWNLOAD_EXTENSIONS=/\.(pdf|zip|csv|txt|ics|docx?|xlsx?|pptx?|dmg|exe|apk|mp3|mp4)$/i;function elementKind(a){if(isWalletElement(a))return "wallet";var b=a.tagName.toLowerCase();return "button"===b||"input"===b?b:a.matches("a[href]:not([role=\"button\"])")?"link":"role"}function textOf(a){return a&&!isUnsafeTextSource(a)&&a.textContent?a.textContent.trim():""}function nonBlank(a){return a&&a.trim()?a:void 0}// The button's name as a screen reader would read it, simplified: the first of
1388
1388
  // aria-labelledby, aria-label, own text (or an inner image's alt), title, and for
1389
1389
  // inputs their value. The text sources are skipped for controls that hold form
1390
1390
  // fields or more than four children, so a typed value is never read.
@@ -1406,11 +1406,14 @@ function controlsIdentifier(a){var b=a.getAttribute("aria-controls");return stab
1406
1406
  // field named "id" shadows form.id.
1407
1407
  function formIdentifier(a){var b=a instanceof HTMLButtonElement||a instanceof HTMLInputElement?a.form:null;return b?stableIdentifier(b.getAttribute("id")):void 0}// The browser's normalised type: a button defaults to 'submit' and an unknown
1408
1408
  // input type reads as 'text'. Wallet and role buttons are just 'button'.
1409
- function controlType(a,b){return "button"===b||"input"===b?a.type:"button"}// Describes a clicked control for auto button tracking. Links are left to page
1410
- // views, so a link click is not described.
1411
- function describeButtonClick(a){if(!a.matches("a[href]:not([role=\"button\"])")){var b=elementKind(a),c=accessibleName(a),d=maskLabel(c.raw);return {label:d,labelSource:d?c.source:"none",// Kept on its own as well: it often says more than the visible text,
1409
+ function controlType(a,b){return "button"===b||"input"===b?a.type:"button"}// Reads the link's own resolved URL parts, never its full href: only the hostname of
1410
+ // a link that leaves the site and the extension of a file it downloads travel. Navigation
1411
+ // within the site is left to page views, and mailto:, tel: and other schemes are skipped.
1412
+ function describeLink(a){var b=(a.getAttribute("href")||"").trim();if(!b||"#"===b.charAt(0))return {type:"same_page"};if(a instanceof HTMLAnchorElement){var c=a.protocol.toLowerCase();if("javascript:"===c)return {type:"same_page"};if("http:"===c||"https:"===c){var d=a.hostname===window.location.hostname?void 0:a.hostname.toLowerCase(),e=DOWNLOAD_EXTENSIONS.exec(a.pathname);return a.hasAttribute("download")||e?{type:"download",domain:d,fileExtension:e?e[1].toLowerCase():void 0}:d?{type:"outbound",domain:d}:a.pathname===window.location.pathname&&a.search===window.location.search?{type:"same_page"}:void 0}}}// Describes a clicked control for auto button tracking. A link counts only when page
1413
+ // views can't see the click, and carries its link type.
1414
+ function describeButtonClick(a){var b;if(!(a.matches("a[href]:not([role=\"button\"])")&&(b=describeLink(a),!b))){var c=elementKind(a),d=accessibleName(a),e=maskLabel(d.raw);return {label:e,labelSource:e?d.source:"none",// Kept on its own as well: it often says more than the visible text,
1412
1415
  // such as which item a remove button removes.
1413
- ariaLabel:maskLabel(a.getAttribute("aria-label")),icon:iconHint(a),elementId:stableIdentifier(a.getAttribute("id")),elementName:stableIdentifier(a.getAttribute("name")),testId:firstIdentifier(a,TEST_ID_ATTRIBUTES),controls:controlsIdentifier(a),form:formIdentifier(a),element:b,type:controlType(a,b)}}}
1416
+ ariaLabel:maskLabel(a.getAttribute("aria-label")),icon:iconHint(a),elementId:stableIdentifier(a.getAttribute("id")),elementName:stableIdentifier(a.getAttribute("name")),testId:firstIdentifier(a,TEST_ID_ATTRIBUTES),controls:controlsIdentifier(a),form:formIdentifier(a),element:c,type:controlType(a,c),linkType:b?b.type:void 0,linkDomain:b?b.domain:void 0,fileExtension:b?b.fileExtension:void 0}}}
1414
1417
 
1415
1418
  var checkoutPath=/(?:^|\/)(?:checkouts?|payment|order|kasse|paiement|commander?|pago)(?:\/|$)/i,exclusions=new RegExp("coupon|promo|newsletter|sign in|add to cart|gift card|pay later|pay in|payment plan|learn more|gutschein|anmelden|warenkorb|bon cadeau|connexion|panier|cup\xF3n|iniciar sesi\xF3n|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"),commits=new RegExp("^(?:"+"(?:place|submit|complete|confirm|finali[sz]e)(?!(?: [^ ]+){0,2} (?:another|new|next)(?: |$))(?: [^ ]+){0,2} (?:order|purchase|booking|reservation|payment|checkout)|pay(?: securely)?(?: now)?|confirm and pay|order and pay|order with obligation to pay|(?:jetzt )?(?:zahlungs|kosten)pflichtig (?:bestellen|buchen)|(?:jetzt )?bezahlen|kauf abschlie\xDFen|bestellung abschlie\xDFen|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\xF3n 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"+")$"),routeGatedCommits=/^(?: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)$/,steps=new RegExp("^(?:"+"checkout|check out|check-out|continue to payment|proceed to checkout|zur kasse|zur kasse gehen|auschecken|weiter zur zahlung|passer \xE0 la caisse|continuer vers le paiement|proc\xE9der 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)?"+")$"),wallets=/^(?:apple pay|google pay|paypal|venmo|amazon pay|shop pay|klarna|afterpay|affirm)$/,frameHosts=new Set(["www.paypal.com","www.sandbox.paypal.com","pay.google.com","applepay.cdn-apple.com","js.stripe.com","checkoutshopper-live.adyen.com","payments.amazon.com","pay.shopify.com"]),TYPOGRAPHIC_APOSTROPHES=/[\u2019\u2018]/g,CURRENCY_AND_NUMBER_CHARS=/[0-9\uFF10-\uFF19$\u20AC\u00A3\u00A5\uFFE5\u20B9\u5186.,\uFF0C]/g;// Exact HTTPS hosts of hosted wallet and payment frames.
1416
1419
  // Keep these classes ASCII-only in source bytes to avoid mojibake parse failures
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.19.1";
207
+ var version = "3.21.0";
208
208
 
209
209
  var Constants = {
210
210
  sdkVersion: version,
@@ -11484,7 +11484,10 @@ var mParticle = (function () {
11484
11484
  element_name: click.elementName,
11485
11485
  test_id: click.testId,
11486
11486
  controls: click.controls,
11487
- form: click.form
11487
+ form: click.form,
11488
+ link_type: click.linkType,
11489
+ link_domain: click.linkDomain,
11490
+ file_extension: click.fileExtension
11488
11491
  };
11489
11492
  for (var _i = 0, _b = Object.keys(optional); _i < _b.length; _i++) {
11490
11493
  var key = _b[_i];
@@ -11593,12 +11596,16 @@ var mParticle = (function () {
11593
11596
  return masked || undefined;
11594
11597
  }
11595
11598
 
11599
+ var DOWNLOAD_EXTENSIONS = /\.(pdf|zip|csv|txt|ics|docx?|xlsx?|pptx?|dmg|exe|apk|mp3|mp4)$/i;
11596
11600
  function elementKind(control) {
11597
11601
  if (isWalletElement(control)) {
11598
11602
  return 'wallet';
11599
11603
  }
11600
11604
  var tag = control.tagName.toLowerCase();
11601
- return tag === 'button' || tag === 'input' ? tag : 'role';
11605
+ if (tag === 'button' || tag === 'input') {
11606
+ return tag;
11607
+ }
11608
+ return control.matches('a[href]:not([role="button"])') ? 'link' : 'role';
11602
11609
  }
11603
11610
  function textOf(element) {
11604
11611
  return element && !isUnsafeTextSource(element) && element.textContent ? element.textContent.trim() : '';
@@ -11772,11 +11779,56 @@ var mParticle = (function () {
11772
11779
  function controlType(control, element) {
11773
11780
  return element === 'button' || element === 'input' ? control.type : 'button';
11774
11781
  }
11775
- // Describes a clicked control for auto button tracking. Links are left to page
11776
- // views, so a link click is not described.
11782
+ // Reads the link's own resolved URL parts, never its full href: only the hostname of
11783
+ // a link that leaves the site and the extension of a file it downloads travel. Navigation
11784
+ // within the site is left to page views, and mailto:, tel: and other schemes are skipped.
11785
+ function describeLink(link) {
11786
+ var href = (link.getAttribute('href') || '').trim();
11787
+ if (!href || href.charAt(0) === '#') {
11788
+ return {
11789
+ type: 'same_page'
11790
+ };
11791
+ }
11792
+ if (!(link instanceof HTMLAnchorElement)) {
11793
+ return undefined;
11794
+ }
11795
+ var protocol = link.protocol.toLowerCase();
11796
+ if (protocol === 'javascript:') {
11797
+ return {
11798
+ type: 'same_page'
11799
+ };
11800
+ }
11801
+ if (protocol !== 'http:' && protocol !== 'https:') {
11802
+ return undefined;
11803
+ }
11804
+ var domain = link.hostname !== window.location.hostname ? link.hostname.toLowerCase() : undefined;
11805
+ var extension = DOWNLOAD_EXTENSIONS.exec(link.pathname);
11806
+ if (link.hasAttribute('download') || extension) {
11807
+ return {
11808
+ type: 'download',
11809
+ domain: domain,
11810
+ fileExtension: extension ? extension[1].toLowerCase() : undefined
11811
+ };
11812
+ }
11813
+ if (domain) {
11814
+ return {
11815
+ type: 'outbound',
11816
+ domain: domain
11817
+ };
11818
+ }
11819
+ return link.pathname === window.location.pathname && link.search === window.location.search ? {
11820
+ type: 'same_page'
11821
+ } : undefined;
11822
+ }
11823
+ // Describes a clicked control for auto button tracking. A link counts only when page
11824
+ // views can't see the click, and carries its link type.
11777
11825
  function describeButtonClick(control) {
11826
+ var link;
11778
11827
  if (control.matches('a[href]:not([role="button"])')) {
11779
- return undefined;
11828
+ link = describeLink(control);
11829
+ if (!link) {
11830
+ return undefined;
11831
+ }
11780
11832
  }
11781
11833
  var element = elementKind(control);
11782
11834
  var name = accessibleName(control);
@@ -11794,7 +11846,10 @@ var mParticle = (function () {
11794
11846
  controls: controlsIdentifier(control),
11795
11847
  form: formIdentifier(control),
11796
11848
  element: element,
11797
- type: controlType(control, element)
11849
+ type: controlType(control, element),
11850
+ linkType: link ? link.type : undefined,
11851
+ linkDomain: link ? link.domain : undefined,
11852
+ fileExtension: link ? link.fileExtension : undefined
11798
11853
  };
11799
11854
  }
11800
11855
 
@@ -1,4 +1,5 @@
1
- export type ButtonElementKind = 'button' | 'input' | 'role' | 'wallet';
1
+ export type ButtonElementKind = 'button' | 'input' | 'role' | 'wallet' | 'link';
2
+ export type LinkType = 'same_page' | 'outbound' | 'download';
2
3
  export type ButtonLabelSource = 'labelledby' | 'aria' | 'text' | 'alt' | 'title' | 'value' | 'none';
3
4
  export interface IButtonClick {
4
5
  label?: string;
@@ -12,5 +13,8 @@ export interface IButtonClick {
12
13
  form?: string;
13
14
  element: ButtonElementKind;
14
15
  type: string;
16
+ linkType?: LinkType;
17
+ linkDomain?: string;
18
+ fileExtension?: string;
15
19
  }
16
20
  export declare function describeButtonClick(control: Element): IButtonClick | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "3.19.1",
3
+ "version": "3.21.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -94,6 +94,7 @@
94
94
  "bundle": "npm run uglify && npm run gzip",
95
95
  "report:bundled": "ls -l dist/mparticle.min.js.gz | awk '{print $5}'",
96
96
  "check:ascii:dist": "node scripts/check-ascii-dist.js",
97
+ "check:cdn-minifier": "node scripts/check-cdn-minifier.js",
97
98
  "report:bundled:human": "ls -lh dist/mparticle.min.js.gz | awk '{print $5}'"
98
99
  },
99
100
  "pre-commit": [
@@ -119,7 +120,6 @@
119
120
  "@semantic-release/exec": "^6.0.3",
120
121
  "@semantic-release/git": "^10.0.1",
121
122
  "@semantic-release/npm": "^13.1.2",
122
- "semantic-release": "^24.2.0",
123
123
  "@types/chai": "^4.2.3",
124
124
  "@types/jest": "^29.5.1",
125
125
  "@types/jest-expect-message": "^1.1.0",
@@ -139,6 +139,7 @@
139
139
  "gts": "^5.0.1",
140
140
  "jest": "^29.5.0",
141
141
  "jest-environment-jsdom": "^29.5.0",
142
+ "jsdom": "20.0.3",
142
143
  "karma": "^6.3.2",
143
144
  "karma-browserstack-launcher": "^1.6.0",
144
145
  "karma-chai": "^0.1.0",
@@ -154,6 +155,7 @@
154
155
  "pre-commit": "^1.2.2",
155
156
  "prettier": "1.18.2",
156
157
  "rollup": "^2.75.7",
158
+ "semantic-release": "^24.2.0",
157
159
  "shelljs": "^0.8.4",
158
160
  "should": "^7.1.0",
159
161
  "sinon": "^16.1.3",
@@ -33,6 +33,9 @@ export const buildButtonClickEvent = (
33
33
  test_id: click.testId,
34
34
  controls: click.controls,
35
35
  form: click.form,
36
+ link_type: click.linkType,
37
+ link_domain: click.linkDomain,
38
+ file_extension: click.fileExtension,
36
39
  };
37
40
  for (const key of Object.keys(optional)) {
38
41
  if (optional[key]) {
@@ -8,7 +8,12 @@ import { maskLabel } from './labelMasking';
8
8
 
9
9
  // Describes a clicked button for auto button tracking: its masked name, icon,
10
10
  // hand-written identifiers and kind. Never reads form field values.
11
- export type ButtonElementKind = 'button' | 'input' | 'role' | 'wallet';
11
+ export type ButtonElementKind = 'button' | 'input' | 'role' | 'wallet' | 'link';
12
+
13
+ // The link clicks page views can't see: ones that stay on the page, leave the site or download a file.
14
+ export type LinkType = 'same_page' | 'outbound' | 'download';
15
+
16
+ const DOWNLOAD_EXTENSIONS = /\.(pdf|zip|csv|txt|ics|docx?|xlsx?|pptx?|dmg|exe|apk|mp3|mp4)$/i;
12
17
 
13
18
  export type ButtonLabelSource =
14
19
  | 'labelledby'
@@ -31,6 +36,9 @@ export interface IButtonClick {
31
36
  form?: string;
32
37
  element: ButtonElementKind;
33
38
  type: string;
39
+ linkType?: LinkType;
40
+ linkDomain?: string;
41
+ fileExtension?: string;
34
42
  }
35
43
 
36
44
  function elementKind(control: Element): ButtonElementKind {
@@ -38,7 +46,10 @@ function elementKind(control: Element): ButtonElementKind {
38
46
  return 'wallet';
39
47
  }
40
48
  const tag = control.tagName.toLowerCase();
41
- return tag === 'button' || tag === 'input' ? tag : 'role';
49
+ if (tag === 'button' || tag === 'input') {
50
+ return tag;
51
+ }
52
+ return control.matches('a[href]:not([role="button"])') ? 'link' : 'role';
42
53
  }
43
54
 
44
55
  function textOf(element: Element | null): string {
@@ -230,13 +241,62 @@ function controlType(control: Element, element: ButtonElementKind): string {
230
241
  : 'button';
231
242
  }
232
243
 
233
- // Describes a clicked control for auto button tracking. Links are left to page
234
- // views, so a link click is not described.
244
+ interface ILinkDetails {
245
+ type: LinkType;
246
+ domain?: string;
247
+ fileExtension?: string;
248
+ }
249
+
250
+ // Reads the link's own resolved URL parts, never its full href: only the hostname of
251
+ // a link that leaves the site and the extension of a file it downloads travel. Navigation
252
+ // within the site is left to page views, and mailto:, tel: and other schemes are skipped.
253
+ function describeLink(link: Element): ILinkDetails | undefined {
254
+ const href = (link.getAttribute('href') || '').trim();
255
+ if (!href || href.charAt(0) === '#') {
256
+ return { type: 'same_page' };
257
+ }
258
+ if (!(link instanceof HTMLAnchorElement)) {
259
+ return undefined;
260
+ }
261
+ const protocol = link.protocol.toLowerCase();
262
+ if (protocol === 'javascript:') {
263
+ return { type: 'same_page' };
264
+ }
265
+ if (protocol !== 'http:' && protocol !== 'https:') {
266
+ return undefined;
267
+ }
268
+ const domain =
269
+ link.hostname !== window.location.hostname
270
+ ? link.hostname.toLowerCase()
271
+ : undefined;
272
+ const extension = DOWNLOAD_EXTENSIONS.exec(link.pathname);
273
+ if (link.hasAttribute('download') || extension) {
274
+ return {
275
+ type: 'download',
276
+ domain,
277
+ fileExtension: extension ? extension[1].toLowerCase() : undefined,
278
+ };
279
+ }
280
+ if (domain) {
281
+ return { type: 'outbound', domain };
282
+ }
283
+ return link.pathname === window.location.pathname &&
284
+ link.search === window.location.search
285
+ ? { type: 'same_page' }
286
+ : undefined;
287
+ }
288
+
289
+ // Describes a clicked control for auto button tracking. A link counts only when page
290
+ // views can't see the click, and carries its link type.
235
291
  export function describeButtonClick(
236
292
  control: Element
237
293
  ): IButtonClick | undefined {
294
+ let link: ILinkDetails | undefined;
238
295
  if (control.matches('a[href]:not([role="button"])')) {
239
- return undefined;
296
+ link = describeLink(control);
297
+ if (!link) {
298
+ return undefined;
299
+ }
240
300
  }
241
301
  const element = elementKind(control);
242
302
  const name = accessibleName(control);
@@ -255,5 +315,8 @@ export function describeButtonClick(
255
315
  form: formIdentifier(control),
256
316
  element,
257
317
  type: controlType(control, element),
318
+ linkType: link ? link.type : undefined,
319
+ linkDomain: link ? link.domain : undefined,
320
+ fileExtension: link ? link.fileExtension : undefined,
258
321
  };
259
322
  }