@mparticle/web-sdk 2.76.0 → 2.78.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 +24 -9
- package/dist/mparticle.esm.js +24 -9
- package/dist/mparticle.js +219 -5
- package/dist/types/src/mp-instance.d.ts +2 -0
- package/dist/types/src/pageViewTracker.d.ts +21 -0
- package/dist/types/src/utils.d.ts +3 -2
- package/package.json +1 -1
- package/src/cookieSyncManager.ts +13 -2
- package/src/mp-instance.ts +9 -0
- package/src/pageViewTracker.ts +267 -0
- package/src/utils.ts +25 -5
package/dist/mparticle.common.js
CHANGED
|
@@ -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 = "2.
|
|
18
|
+
var version = "2.78.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",AddToCart:"addToCart",RemoveFromCart:"removeFromCart",ClearCart:"clearCart",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",localStorageProductsV4:"mprtcl-prodv4",cookieNameV4:"mprtcl-v4",currentStorageName:"mprtcl-v4",currentStorageProductsName:"mprtcl-prodv4"},DefaultConfig:{cookieDomain:null,cookieExpiration:365,logLevel:null,timeout:300,sessionTimeout:30,maxProducts:20,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
|
|
@@ -147,7 +147,7 @@ var Messages$a=Constants.Messages,createCookieString=function(a){return replaceC
|
|
|
147
147
|
// -1e11 -> //-100000000000,
|
|
148
148
|
"".concat(1e7,"-").concat(1e3,"-").concat(4e3,"-").concat(8e3,"-").concat(1e11).replace(/[018]/g,// zeroes, ones, and eights with
|
|
149
149
|
generateUniqueId// random hex digits
|
|
150
|
-
)},getRampNumber=function(a){if(!a)return 100;var b=generateHash(a);return Math.abs(b%100)+1},isObject=function(a){var b=Object.prototype.toString.call(a);return "[object Object]"===b||"[object Error]"===b},parseNumber=function(a){if(isNaN(a)||!isFinite(a))return 0;var b=parseFloat(a);return isNaN(b)?0:b},parseSettingsString=function(a){try{return a?JSON.parse(a.replace(/"/g,"\"")):[]}catch(a){throw new Error("Settings string contains invalid JSON")}},parseStringOrNumber=function(a){return isStringOrNumber(a)?a:null},replaceCommasWithPipes=function(a){return a.replace(/,/g,"|")},replacePipesWithCommas=function(a){return a.replace(/\|/g,",")},replaceApostrophesWithQuotes=function(a){return a.replace(/\'/g,"\"")},replaceQuotesWithApostrophes=function(a){return a.replace(/\"/g,"'")},replaceMPID=function(a,b){return a.replace("%%mpid%%",b)},replaceAmpWithAmpersand=function(a){return a.replace(/&/g,"&")},createCookieSyncUrl=function(a,b,c,d){var
|
|
150
|
+
)},getRampNumber=function(a){if(!a)return 100;var b=generateHash(a);return Math.abs(b%100)+1},isObject=function(a){var b=Object.prototype.toString.call(a);return "[object Object]"===b||"[object Error]"===b},parseNumber=function(a){if(isNaN(a)||!isFinite(a))return 0;var b=parseFloat(a);return isNaN(b)?0:b},parseSettingsString=function(a){try{return a?JSON.parse(a.replace(/"/g,"\"")):[]}catch(a){throw new Error("Settings string contains invalid JSON")}},parseStringOrNumber=function(a){return isStringOrNumber(a)?a:null},replaceCommasWithPipes=function(a){return a.replace(/,/g,"|")},replacePipesWithCommas=function(a){return a.replace(/\|/g,",")},replaceApostrophesWithQuotes=function(a){return a.replace(/\'/g,"\"")},replaceQuotesWithApostrophes=function(a){return a.replace(/\"/g,"'")},replaceMPID=function(a,b){return a.replace("%%mpid%%",b)},replaceAmpWithAmpersand=function(a){return a.replace(/&/g,"&")},createCookieSyncUrl=function(a,b,c,d,e){var f=replaceAmpWithAmpersand(b),g=c?replaceAmpWithAmpersand(c):null,h=replaceMPID(f,a),i=g?replaceMPID(g,a):"",j=h+encodeURIComponent(i);if(d){var k=j.includes("?")?"&":"?";j+="".concat(k,"domain=").concat(d);}if(e){var k=j.includes("?")?"&":"?";j+="".concat(k,"google_hm=").concat(e);}return j},WEB_SAFE_BASE64_REPLACEMENTS={"+":"-","/":"_","=":""},toWebSafeBase64=function(a){return btoa(a).replace(/[+/=]/g,function(a){return WEB_SAFE_BASE64_REPLACEMENTS[a]})},returnConvertedBoolean=function(a){return "false"!==a&&"0"!==a&&!!a},decoded=function(a){return decodeURIComponent(a.replace(/\+/g," "))},converted=function(a){return 0===a.indexOf("\"")&&(a=a.slice(1,-1).replace(/\\"/g,"\"").replace(/\\\\/g,"\\")),a},isString=function(a){return "string"==typeof a},isNumber=function(a){return "number"==typeof a},isBoolean=function(a){return "boolean"==typeof a},isFunction=function(a){return "function"==typeof a},isValidAttributeValue=function(a){return a!==void 0&&!isObject(a)&&!Array.isArray(a)},isValidCustomFlagProperty=function(a){return isNumber(a)||isString(a)||isBoolean(a)},toDataPlanSlug=function(a){// Make sure we are only acting on strings or numbers
|
|
151
151
|
return isStringOrNumber(a)?a.toString().toLowerCase().replace(/[^0-9a-zA-Z]+/g,"_"):""},isDataPlanSlug=function(a){return a===toDataPlanSlug(a)},isStringOrNumber=function(a){return isString(a)||isNumber(a)},isEmpty=function(a){return null==a||!(Object.keys(a)||a).length},moveElementToEnd=function(a,b){return a.slice(0,b).concat(a.slice(b+1),a[b])},queryStringParser=function(a,b){void 0===b&&(b=[]);var c,d={},e={};if(!a)return d;if("undefined"!=typeof URL&&"undefined"!=typeof URLSearchParams){var f=new URL(a);c=new URLSearchParams(f.search);}else c=queryStringParserFallback(a);return (c.forEach(function(a,b){e[b.toLowerCase()]=a;}),isEmpty(b))?e:(b.forEach(function(a){var b=e[a.toLowerCase()];b&&(d[a]=b);}),d)},queryStringParserFallback=function(a){var b={},c=a.split("?")[1]||"",d=c.split("&");return d.forEach(function(a){var c=a.split("="),d=c[0],e=c.slice(1),f=e.join("=");if(d&&void 0!==f)try{b[d]=decodeURIComponent(f||"");}catch(a){console.error("Failed to decode value for key ".concat(d,": ").concat(a));}}),{get:function get(a){return b[a]},forEach:function forEach(a){for(var c in b)b.hasOwnProperty(c)&&a(b[c],c);}}},getCookies=function(a){// Helper function to parse cookies from document.cookie
|
|
152
152
|
var b=function parseCookies(){try{return "undefined"==typeof window?[]:window.document.cookie.split(";").map(function(a){return a.trim()})}catch(a){return console.error("Unable to parse cookies",a),[]}}();// Helper function to filter cookies by keys
|
|
153
153
|
// Parse cookies from document.cookie
|
|
@@ -158,7 +158,11 @@ if(Array.isArray(a))return a.map(function(a){return obfuscateData(a)});// Handle
|
|
|
158
158
|
if(isObject(a)){var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=obfuscateData(a[c]));return b}// For primitives and other types, return the type as a string
|
|
159
159
|
return _typeof$1(a)},obfuscateDevData=function(a,b){return b?a:obfuscateData(a)};/**
|
|
160
160
|
* Returns a value between 1-100 inclusive.
|
|
161
|
-
*/ //
|
|
161
|
+
*/ // Google's cookie matching service requires `google_hm` values to be web-safe
|
|
162
|
+
// base64 (RFC 4648 §5) without padding, otherwise the write to the hosted
|
|
163
|
+
// match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
|
|
164
|
+
// (String#replaceAll is not available in our ES5 browser targets.)
|
|
165
|
+
// FIXME: REFACTOR for V3
|
|
162
166
|
// only used in store.js to sanitize server-side formatting of
|
|
163
167
|
// booleans when checking for `isDevelopmentMode`
|
|
164
168
|
// Should be removed in v3
|
|
@@ -632,19 +636,23 @@ var PARTNER_MODULE_IDS={AdobeEventForwarder:11,DoubleclickDFP:41,AppNexus:50,Lot
|
|
|
632
636
|
// Private
|
|
633
637
|
this.attemptCookieSync=function(c,d){var e,f=a._Store,g=f.pixelConfigurations,h=f.webviewBridgeEnabled;if(c&&!h&&(null===(e=a._CookieConsentManager)||void 0===e||!e.getNoFunctional()))// When noFunctional is true, persistence is not saved, so we cannot track cookie sync
|
|
634
638
|
// dates. Skip cookie sync to avoid running it on every page load.
|
|
635
|
-
{var i=a._Persistence.getPersistence();isEmpty(i)||g.forEach(function(e){var f,g,h=!1,
|
|
636
|
-
|
|
639
|
+
{var i=a._Persistence.getPersistence();isEmpty(i)||g.forEach(function(e){var f,g,h,j=!1,k=e.filteringConsentRuleValues,l=e.pixelUrl,m=e.redirectUrl,n=e.moduleId,o=e.settings,// Tells you how often we should do a cookie sync (in days)
|
|
640
|
+
p=e.frequencyCap,q=(k||{}).values;// set requiresConsent to false to start each additional pixel configuration
|
|
637
641
|
// set to true only if filteringConsenRuleValues.values.length exists
|
|
638
642
|
// Filtering rules as defined in UI
|
|
639
|
-
if(!isEmpty(
|
|
643
|
+
if(!isEmpty(l)&&(isEmpty(q)||(j=!0),!(j&&d))&&!(n===PARTNER_MODULE_IDS.Rokt&&a._CookieConsentManager.getNoTargeting()))// For Rokt, block cookie sync when noTargeting privacy flag is true
|
|
640
644
|
// If MPID is new to cookies, we should not try to perform the cookie sync
|
|
641
645
|
// because a cookie sync can only occur once a user either consents or doesn't.
|
|
642
646
|
// we should not check if it's enabled if the user has a blank consent
|
|
643
647
|
// The Trade Desk requires a URL parameter for GDPR enabled users.
|
|
644
648
|
// It is optional but to simplify the code, we add it for all Trade
|
|
645
649
|
// // Desk cookie syncs.
|
|
646
|
-
//
|
|
647
|
-
|
|
650
|
+
// Google Marketing Platform accepts a web-safe base64-encoded MPID via the
|
|
651
|
+
// `google_hm` query parameter, but only for accounts provisioned for Google
|
|
652
|
+
// Hosted Matching — Google returns errors otherwise. It is therefore gated
|
|
653
|
+
// behind the opt-in `enableHmTag` setting (off by default), which the server
|
|
654
|
+
// delivers as the string 'True' when the UI toggle is enabled.
|
|
655
|
+
{var r=a._Consent.isEnabledForUserConsent;if(r(k,a.Identity.getCurrentUser())){var s=null!==(g=null===(f=i[c])||void 0===f?void 0:f.csd)&&void 0!==g?g:{},t=s[n]||null;if(isLastSyncDateExpired(p,t)){var u=n===PARTNER_MODULE_IDS.TradeDesk?window.location.hostname:void 0,v="true"===(null===(h=null===o||void 0===o?void 0:o.enableHmTag)||void 0===h?void 0:h.toLowerCase()),w=n===PARTNER_MODULE_IDS.DoubleclickDFP,x=w&&v?toWebSafeBase64(c):void 0,y=createCookieSyncUrl(c,l,m,u,x);b.performCookieSync(y,n.toString(),c,s);}}}});}},this.performCookieSync=function(b,c,d,e){var f=document.createElement("img");a.Logger.verbose(InformationMessages.CookieSync),f.onload=function(){e[c]=new Date().getTime(),a._Persistence.saveUserCookieSyncDatesToPersistence(d,e);},f.src=b;};}var isLastSyncDateExpired=function(a,b){// If there is no lastSyncDate, then there is no previous cookie sync, so we should sync the cookie
|
|
648
656
|
return !b||new Date().getTime()>new Date(b).getTime()+a*DAYS_IN_MILLISECONDS;// Otherwise, compare the last sync date to determine if it should do a cookie sync again
|
|
649
657
|
};
|
|
650
658
|
|
|
@@ -1650,6 +1658,13 @@ var ErrorReportingDispatcher=/** @class */function(){function a(){this.services=
|
|
|
1650
1658
|
|
|
1651
1659
|
var LoggingDispatcher=/** @class */function(){function a(){this.services=[];}return a.prototype.register=function(a){this.services.push(a);},a.prototype.log=function(a){var b=this;this.services.forEach(function(c){var d;try{c.log(a);}catch(a){null===(d=b.logger)||void 0===d?void 0:d.error("Error in LoggingService: "+a);}});},a}();
|
|
1652
1660
|
|
|
1661
|
+
var WRAPPED_MARKER="__mpApvWrapped__",PageViewTracker=/** @class */function(){function a(a){this.lastPath=null,this.isActive=!1,this.originalPushState=null,this.originalReplaceState=null,this.pushStateWrapper=null,this.replaceStateWrapper=null,this.popStateListener=null,this.mpInstance=a;}return a.prototype.isSupportedEnvironment=function(){return "undefined"!=typeof window&&"undefined"!=typeof window.history&&"function"==typeof window.history.pushState&&"function"==typeof window.addEventListener},a.prototype.init=function(){return this.mpInstance.Logger.verbose("mParticle APV: [init] PageViewTracker Init"),this.isSupportedEnvironment()?void(this.isActive&&(this.mpInstance.Logger.verbose("mParticle APV: [init] starting (teardown-first for idempotency)"),this.teardown()),this.isActive=!0,this.lastPath=this.getCurrentKey(),this.mpInstance.Logger.verbose("mParticle APV: [init] seeded lastPath: ".concat(this.lastPath)),this.patchHistoryMethods(),this.addNavigationListeners(),this.mpInstance.Logger.verbose("mParticle APV: [init] patched pushState/replaceState + listening for popstate")):void this.mpInstance.Logger.verbose("mParticle APV: [init] unsupported environment (no History API), not starting")},a.prototype.patchHistoryMethods=function(){var a=this,b=window.history.pushState;if(b[WRAPPED_MARKER])return void this.mpInstance.Logger.verbose("mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap");var c=window.history.pushState,d=window.history.replaceState;this.originalPushState=c,this.originalReplaceState=d;var e=function(){for(var b=[],d=0;d<arguments.length;d++)b[d]=arguments[d];var e=c.apply(this,b);return a.safeHandleNavigation("pushState"),e},f=function(){for(var b=[],c=0;c<arguments.length;c++)b[c]=arguments[c];var e=d.apply(this,b);return a.safeHandleNavigation("replaceState"),e};Object.defineProperty(e,WRAPPED_MARKER,{value:!0,enumerable:!1}),Object.defineProperty(f,WRAPPED_MARKER,{value:!0,enumerable:!1}),this.pushStateWrapper=e,this.replaceStateWrapper=f;try{window.history.pushState=e,window.history.replaceState=f;}catch(a){this.mpInstance.Logger.verbose("mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ".concat(a));try{window.history.pushState===e&&(window.history.pushState=c),window.history.replaceState===f&&(window.history.replaceState=d);}catch(a){this.mpInstance.Logger.verbose("mParticle APV: [error] failed to restore history methods after patch failure: ".concat(a));}this.pushStateWrapper=null,this.replaceStateWrapper=null,this.originalPushState=null,this.originalReplaceState=null;}},a.prototype.addNavigationListeners=function(){var a=this;this.popStateListener=function(){return a.safeHandleNavigation("popstate")},window.addEventListener("popstate",this.popStateListener);},a.prototype.safeHandleNavigation=function(a){try{this.handleNavigation(a);}catch(b){this.mpInstance.Logger.verbose("mParticle APV: [error] navigation handler threw (".concat(a,"), page view skipped: ").concat(b));}},a.prototype.getCurrentKey=function(){return window.location.pathname},a.prototype.handleNavigation=function(a){var b=this,c=this.getCurrentKey();if(this.mpInstance.Logger.verbose("mParticle APV: [detect] navigation signal (source: ".concat(a,", candidatePath: ").concat(c,", lastPath: ").concat(this.lastPath,")")),c===this.lastPath)return void this.mpInstance.Logger.verbose("mParticle APV: [dedupe] pathname unchanged, skipping (source: ".concat(a,", path: ").concat(c,")"));this.mpInstance.Logger.verbose("mParticle APV: [accept] pathname changed, scheduling fire (source: ".concat(a,", from: ").concat(this.lastPath,", to: ").concat(c,")")),this.lastPath=c;// Snapshot the path now: it is already settled at this point, and a
|
|
1662
|
+
// same-tick navigation would otherwise overwrite window.location
|
|
1663
|
+
// before the deferred flush reads it. The title is intentionally read
|
|
1664
|
+
// later (in the deferred flush) so the router's post-navigation render
|
|
1665
|
+
// commit has a chance to update document.title first.
|
|
1666
|
+
setTimeout(function(){return b.isActive?void(b.mpInstance._SessionManager.resetSessionTimer(),b.firePageView(c)):void b.mpInstance.Logger.verbose("mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)")},0);},a.prototype.firePageView=function(a){var b=window.document.title;this.mpInstance.Logger.verbose("mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(a,", title: ").concat(b,")")),this.mpInstance._Events.logEvent({messageType:MessageType$1.PageView,name:"PageView",data:{hostname:window.location.hostname,title:b,path:a},eventType:EventType.Unknown});},a.prototype.teardown=function(){this.popStateListener&&(window.removeEventListener("popstate",this.popStateListener),this.popStateListener=null);var a=null!==this.pushStateWrapper&&window.history.pushState===this.pushStateWrapper;this.originalPushState&&(a?(window.history.pushState=this.originalPushState,this.mpInstance.Logger.verbose("mParticle APV: [teardown] restored original pushState")):this.mpInstance.Logger.verbose("mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op"),this.originalPushState=null,this.pushStateWrapper=null);var b=null!==this.replaceStateWrapper&&window.history.replaceState===this.replaceStateWrapper;this.originalReplaceState&&(b?(window.history.replaceState=this.originalReplaceState,this.mpInstance.Logger.verbose("mParticle APV: [teardown] restored original replaceState")):this.mpInstance.Logger.verbose("mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op"),this.originalReplaceState=null,this.replaceStateWrapper=null),this.isActive=!1;},a}();
|
|
1667
|
+
|
|
1653
1668
|
var Messages=Constants.Messages,HTTPCodes=Constants.HTTPCodes,FeatureFlags=Constants.FeatureFlags,CaptureIntegrationSpecificIdsV2Modes=Constants.CaptureIntegrationSpecificIdsV2Modes,ReportBatching=FeatureFlags.ReportBatching,CaptureIntegrationSpecificIds=FeatureFlags.CaptureIntegrationSpecificIds,CaptureIntegrationSpecificIdsV2=FeatureFlags.CaptureIntegrationSpecificIdsV2,StartingInitialization=Messages.InformationMessages.StartingInitialization;/**
|
|
1654
1669
|
* <p>All of the following methods can be called on the primary mParticle class. In version 2.10.0, we introduced <a href="https://docs.mparticle.com/developers/sdk/web/multiple-instances/">multiple instances</a>. If you are using multiple instances (self hosted environments only), you should call these methods on each instance.</p>
|
|
1655
1670
|
* <p>In current versions of mParticle, if your site has one instance, that instance name is 'default_instance'. Any methods called on mParticle on a site with one instance will be mapped to the `default_instance`.</p>
|
|
@@ -1827,7 +1842,7 @@ if((null===(d=c._APIClient)||void 0===d?void 0:d.uploader)&&c._APIClient.uploade
|
|
|
1827
1842
|
var k=c.Identity.getCurrentUser(),l=k?k.getMPID():null,m=k?k.getUserIdentities().userIdentities:{};c._Store.SDKConfig.identifyRequest=c._Store.hasInvalidIdentifyRequest()?{userIdentities:m}:c._Store.SDKConfig.identifyRequest,i(ReportBatching)&&c._ForwardingStatsUploader.startForwardingStatsTimer();// https://go.mparticle.com/work/SQDSDKS-7639
|
|
1828
1843
|
var n=i(CaptureIntegrationSpecificIds),o=i(CaptureIntegrationSpecificIdsV2),p=o&&o!==CaptureIntegrationSpecificIdsV2Modes.None||!0===n;if(p){var q;n||o===CaptureIntegrationSpecificIdsV2Modes.All?q="all":o===CaptureIntegrationSpecificIdsV2Modes.RoktOnly&&(q="roktonly"),c._IntegrationCapture=new IntegrationCapture(q),c._IntegrationCapture.capture();}// Configure Rokt Manager with user and filtered user
|
|
1829
1844
|
var r=parseConfig(b,"Rokt",181);if(r){var s=null===(e=r.settings)||void 0===e?void 0:e.accountId;c._Store.setRoktAccountId(s);var t=r.userAttributeFilters,u=filteredMparticleUser(l,{userAttributeFilters:t},c),v={sandbox:null===b||void 0===b?void 0:b.isDevelopmentMode,launcherOptions:null===b||void 0===b?void 0:b.launcherOptions,domain:null===b||void 0===b?void 0:b.domain};// https://go.mparticle.com/work/SQDSDKS-7339
|
|
1830
|
-
c._RoktManager.init(r,u,c.Identity,c._Store,c.Logger,v,c.captureTiming,c._ErrorReportingDispatcher,c._LoggingDispatcher,c._IntegrationCapture);}c._Forwarders.processForwarders(b,c._APIClient.prepareForwardingStats),c._Forwarders.processPixelConfigs(b),c._SessionManager.initialize(),c._Events.logAST(),i(j)
|
|
1845
|
+
c._RoktManager.init(r,u,c.Identity,c._Store,c.Logger,v,c.captureTiming,c._ErrorReportingDispatcher,c._LoggingDispatcher,c._IntegrationCapture);}c._Forwarders.processForwarders(b,c._APIClient.prepareForwardingStats),c._Forwarders.processPixelConfigs(b),c._SessionManager.initialize(),c._Events.logAST(),i(j)?(c._Events.logPageView(),!c._PageViewTracker&&(c._PageViewTracker=new PageViewTracker(c)),c._PageViewTracker.init()):c._PageViewTracker&&c._PageViewTracker.teardown(),processIdentityCallback(c,k,l,m);}// We will continue to clear out the ready queue as part of the initial init flow
|
|
1831
1846
|
// if an identify request is unnecessary, such as if there is an existing session
|
|
1832
1847
|
(c._Store.mpid&&!c._Store.identifyCalled||c._Store.webviewBridgeEnabled)&&(c._Store.isInitialized=!0,null===(f=c._CookieConsentManager)||void 0===f?void 0:f.syncNoTargetingAttribute(c.Identity.getCurrentUser()),c._preInit.readyQueue=processReadyQueue(c._preInit.readyQueue,c.Logger)),null===(g=c.processQueueOnNoFunctional)||void 0===g?void 0:g.call(c),c._Store.isFirstRun&&(c._Store.isFirstRun=!1);}// https://go.mparticle.com/work/SQDSDKS-7061
|
|
1833
1848
|
function createKitBlocker(a,b){var c,d,e,f;/* There are three ways a data plan object for blocking can be passed to the SDK:
|
package/dist/mparticle.esm.js
CHANGED
|
@@ -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 = "2.
|
|
18
|
+
var version = "2.78.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",AddToCart:"addToCart",RemoveFromCart:"removeFromCart",ClearCart:"clearCart",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",localStorageProductsV4:"mprtcl-prodv4",cookieNameV4:"mprtcl-v4",currentStorageName:"mprtcl-v4",currentStorageProductsName:"mprtcl-prodv4"},DefaultConfig:{cookieDomain:null,cookieExpiration:365,logLevel:null,timeout:300,sessionTimeout:30,maxProducts:20,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
|
|
@@ -147,7 +147,7 @@ var Messages$a=Constants.Messages,createCookieString=function(a){return replaceC
|
|
|
147
147
|
// -1e11 -> //-100000000000,
|
|
148
148
|
"".concat(1e7,"-").concat(1e3,"-").concat(4e3,"-").concat(8e3,"-").concat(1e11).replace(/[018]/g,// zeroes, ones, and eights with
|
|
149
149
|
generateUniqueId// random hex digits
|
|
150
|
-
)},getRampNumber=function(a){if(!a)return 100;var b=generateHash(a);return Math.abs(b%100)+1},isObject=function(a){var b=Object.prototype.toString.call(a);return "[object Object]"===b||"[object Error]"===b},parseNumber=function(a){if(isNaN(a)||!isFinite(a))return 0;var b=parseFloat(a);return isNaN(b)?0:b},parseSettingsString=function(a){try{return a?JSON.parse(a.replace(/"/g,"\"")):[]}catch(a){throw new Error("Settings string contains invalid JSON")}},parseStringOrNumber=function(a){return isStringOrNumber(a)?a:null},replaceCommasWithPipes=function(a){return a.replace(/,/g,"|")},replacePipesWithCommas=function(a){return a.replace(/\|/g,",")},replaceApostrophesWithQuotes=function(a){return a.replace(/\'/g,"\"")},replaceQuotesWithApostrophes=function(a){return a.replace(/\"/g,"'")},replaceMPID=function(a,b){return a.replace("%%mpid%%",b)},replaceAmpWithAmpersand=function(a){return a.replace(/&/g,"&")},createCookieSyncUrl=function(a,b,c,d){var
|
|
150
|
+
)},getRampNumber=function(a){if(!a)return 100;var b=generateHash(a);return Math.abs(b%100)+1},isObject=function(a){var b=Object.prototype.toString.call(a);return "[object Object]"===b||"[object Error]"===b},parseNumber=function(a){if(isNaN(a)||!isFinite(a))return 0;var b=parseFloat(a);return isNaN(b)?0:b},parseSettingsString=function(a){try{return a?JSON.parse(a.replace(/"/g,"\"")):[]}catch(a){throw new Error("Settings string contains invalid JSON")}},parseStringOrNumber=function(a){return isStringOrNumber(a)?a:null},replaceCommasWithPipes=function(a){return a.replace(/,/g,"|")},replacePipesWithCommas=function(a){return a.replace(/\|/g,",")},replaceApostrophesWithQuotes=function(a){return a.replace(/\'/g,"\"")},replaceQuotesWithApostrophes=function(a){return a.replace(/\"/g,"'")},replaceMPID=function(a,b){return a.replace("%%mpid%%",b)},replaceAmpWithAmpersand=function(a){return a.replace(/&/g,"&")},createCookieSyncUrl=function(a,b,c,d,e){var f=replaceAmpWithAmpersand(b),g=c?replaceAmpWithAmpersand(c):null,h=replaceMPID(f,a),i=g?replaceMPID(g,a):"",j=h+encodeURIComponent(i);if(d){var k=j.includes("?")?"&":"?";j+="".concat(k,"domain=").concat(d);}if(e){var k=j.includes("?")?"&":"?";j+="".concat(k,"google_hm=").concat(e);}return j},WEB_SAFE_BASE64_REPLACEMENTS={"+":"-","/":"_","=":""},toWebSafeBase64=function(a){return btoa(a).replace(/[+/=]/g,function(a){return WEB_SAFE_BASE64_REPLACEMENTS[a]})},returnConvertedBoolean=function(a){return "false"!==a&&"0"!==a&&!!a},decoded=function(a){return decodeURIComponent(a.replace(/\+/g," "))},converted=function(a){return 0===a.indexOf("\"")&&(a=a.slice(1,-1).replace(/\\"/g,"\"").replace(/\\\\/g,"\\")),a},isString=function(a){return "string"==typeof a},isNumber=function(a){return "number"==typeof a},isBoolean=function(a){return "boolean"==typeof a},isFunction=function(a){return "function"==typeof a},isValidAttributeValue=function(a){return a!==void 0&&!isObject(a)&&!Array.isArray(a)},isValidCustomFlagProperty=function(a){return isNumber(a)||isString(a)||isBoolean(a)},toDataPlanSlug=function(a){// Make sure we are only acting on strings or numbers
|
|
151
151
|
return isStringOrNumber(a)?a.toString().toLowerCase().replace(/[^0-9a-zA-Z]+/g,"_"):""},isDataPlanSlug=function(a){return a===toDataPlanSlug(a)},isStringOrNumber=function(a){return isString(a)||isNumber(a)},isEmpty=function(a){return null==a||!(Object.keys(a)||a).length},moveElementToEnd=function(a,b){return a.slice(0,b).concat(a.slice(b+1),a[b])},queryStringParser=function(a,b){void 0===b&&(b=[]);var c,d={},e={};if(!a)return d;if("undefined"!=typeof URL&&"undefined"!=typeof URLSearchParams){var f=new URL(a);c=new URLSearchParams(f.search);}else c=queryStringParserFallback(a);return (c.forEach(function(a,b){e[b.toLowerCase()]=a;}),isEmpty(b))?e:(b.forEach(function(a){var b=e[a.toLowerCase()];b&&(d[a]=b);}),d)},queryStringParserFallback=function(a){var b={},c=a.split("?")[1]||"",d=c.split("&");return d.forEach(function(a){var c=a.split("="),d=c[0],e=c.slice(1),f=e.join("=");if(d&&void 0!==f)try{b[d]=decodeURIComponent(f||"");}catch(a){console.error("Failed to decode value for key ".concat(d,": ").concat(a));}}),{get:function get(a){return b[a]},forEach:function forEach(a){for(var c in b)b.hasOwnProperty(c)&&a(b[c],c);}}},getCookies=function(a){// Helper function to parse cookies from document.cookie
|
|
152
152
|
var b=function parseCookies(){try{return "undefined"==typeof window?[]:window.document.cookie.split(";").map(function(a){return a.trim()})}catch(a){return console.error("Unable to parse cookies",a),[]}}();// Helper function to filter cookies by keys
|
|
153
153
|
// Parse cookies from document.cookie
|
|
@@ -158,7 +158,11 @@ if(Array.isArray(a))return a.map(function(a){return obfuscateData(a)});// Handle
|
|
|
158
158
|
if(isObject(a)){var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=obfuscateData(a[c]));return b}// For primitives and other types, return the type as a string
|
|
159
159
|
return _typeof$1(a)},obfuscateDevData=function(a,b){return b?a:obfuscateData(a)};/**
|
|
160
160
|
* Returns a value between 1-100 inclusive.
|
|
161
|
-
*/ //
|
|
161
|
+
*/ // Google's cookie matching service requires `google_hm` values to be web-safe
|
|
162
|
+
// base64 (RFC 4648 §5) without padding, otherwise the write to the hosted
|
|
163
|
+
// match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
|
|
164
|
+
// (String#replaceAll is not available in our ES5 browser targets.)
|
|
165
|
+
// FIXME: REFACTOR for V3
|
|
162
166
|
// only used in store.js to sanitize server-side formatting of
|
|
163
167
|
// booleans when checking for `isDevelopmentMode`
|
|
164
168
|
// Should be removed in v3
|
|
@@ -632,19 +636,23 @@ var PARTNER_MODULE_IDS={AdobeEventForwarder:11,DoubleclickDFP:41,AppNexus:50,Lot
|
|
|
632
636
|
// Private
|
|
633
637
|
this.attemptCookieSync=function(c,d){var e,f=a._Store,g=f.pixelConfigurations,h=f.webviewBridgeEnabled;if(c&&!h&&(null===(e=a._CookieConsentManager)||void 0===e||!e.getNoFunctional()))// When noFunctional is true, persistence is not saved, so we cannot track cookie sync
|
|
634
638
|
// dates. Skip cookie sync to avoid running it on every page load.
|
|
635
|
-
{var i=a._Persistence.getPersistence();isEmpty(i)||g.forEach(function(e){var f,g,h=!1,
|
|
636
|
-
|
|
639
|
+
{var i=a._Persistence.getPersistence();isEmpty(i)||g.forEach(function(e){var f,g,h,j=!1,k=e.filteringConsentRuleValues,l=e.pixelUrl,m=e.redirectUrl,n=e.moduleId,o=e.settings,// Tells you how often we should do a cookie sync (in days)
|
|
640
|
+
p=e.frequencyCap,q=(k||{}).values;// set requiresConsent to false to start each additional pixel configuration
|
|
637
641
|
// set to true only if filteringConsenRuleValues.values.length exists
|
|
638
642
|
// Filtering rules as defined in UI
|
|
639
|
-
if(!isEmpty(
|
|
643
|
+
if(!isEmpty(l)&&(isEmpty(q)||(j=!0),!(j&&d))&&!(n===PARTNER_MODULE_IDS.Rokt&&a._CookieConsentManager.getNoTargeting()))// For Rokt, block cookie sync when noTargeting privacy flag is true
|
|
640
644
|
// If MPID is new to cookies, we should not try to perform the cookie sync
|
|
641
645
|
// because a cookie sync can only occur once a user either consents or doesn't.
|
|
642
646
|
// we should not check if it's enabled if the user has a blank consent
|
|
643
647
|
// The Trade Desk requires a URL parameter for GDPR enabled users.
|
|
644
648
|
// It is optional but to simplify the code, we add it for all Trade
|
|
645
649
|
// // Desk cookie syncs.
|
|
646
|
-
//
|
|
647
|
-
|
|
650
|
+
// Google Marketing Platform accepts a web-safe base64-encoded MPID via the
|
|
651
|
+
// `google_hm` query parameter, but only for accounts provisioned for Google
|
|
652
|
+
// Hosted Matching — Google returns errors otherwise. It is therefore gated
|
|
653
|
+
// behind the opt-in `enableHmTag` setting (off by default), which the server
|
|
654
|
+
// delivers as the string 'True' when the UI toggle is enabled.
|
|
655
|
+
{var r=a._Consent.isEnabledForUserConsent;if(r(k,a.Identity.getCurrentUser())){var s=null!==(g=null===(f=i[c])||void 0===f?void 0:f.csd)&&void 0!==g?g:{},t=s[n]||null;if(isLastSyncDateExpired(p,t)){var u=n===PARTNER_MODULE_IDS.TradeDesk?window.location.hostname:void 0,v="true"===(null===(h=null===o||void 0===o?void 0:o.enableHmTag)||void 0===h?void 0:h.toLowerCase()),w=n===PARTNER_MODULE_IDS.DoubleclickDFP,x=w&&v?toWebSafeBase64(c):void 0,y=createCookieSyncUrl(c,l,m,u,x);b.performCookieSync(y,n.toString(),c,s);}}}});}},this.performCookieSync=function(b,c,d,e){var f=document.createElement("img");a.Logger.verbose(InformationMessages.CookieSync),f.onload=function(){e[c]=new Date().getTime(),a._Persistence.saveUserCookieSyncDatesToPersistence(d,e);},f.src=b;};}var isLastSyncDateExpired=function(a,b){// If there is no lastSyncDate, then there is no previous cookie sync, so we should sync the cookie
|
|
648
656
|
return !b||new Date().getTime()>new Date(b).getTime()+a*DAYS_IN_MILLISECONDS;// Otherwise, compare the last sync date to determine if it should do a cookie sync again
|
|
649
657
|
};
|
|
650
658
|
|
|
@@ -1650,6 +1658,13 @@ var ErrorReportingDispatcher=/** @class */function(){function a(){this.services=
|
|
|
1650
1658
|
|
|
1651
1659
|
var LoggingDispatcher=/** @class */function(){function a(){this.services=[];}return a.prototype.register=function(a){this.services.push(a);},a.prototype.log=function(a){var b=this;this.services.forEach(function(c){var d;try{c.log(a);}catch(a){null===(d=b.logger)||void 0===d?void 0:d.error("Error in LoggingService: "+a);}});},a}();
|
|
1652
1660
|
|
|
1661
|
+
var WRAPPED_MARKER="__mpApvWrapped__",PageViewTracker=/** @class */function(){function a(a){this.lastPath=null,this.isActive=!1,this.originalPushState=null,this.originalReplaceState=null,this.pushStateWrapper=null,this.replaceStateWrapper=null,this.popStateListener=null,this.mpInstance=a;}return a.prototype.isSupportedEnvironment=function(){return "undefined"!=typeof window&&"undefined"!=typeof window.history&&"function"==typeof window.history.pushState&&"function"==typeof window.addEventListener},a.prototype.init=function(){return this.mpInstance.Logger.verbose("mParticle APV: [init] PageViewTracker Init"),this.isSupportedEnvironment()?void(this.isActive&&(this.mpInstance.Logger.verbose("mParticle APV: [init] starting (teardown-first for idempotency)"),this.teardown()),this.isActive=!0,this.lastPath=this.getCurrentKey(),this.mpInstance.Logger.verbose("mParticle APV: [init] seeded lastPath: ".concat(this.lastPath)),this.patchHistoryMethods(),this.addNavigationListeners(),this.mpInstance.Logger.verbose("mParticle APV: [init] patched pushState/replaceState + listening for popstate")):void this.mpInstance.Logger.verbose("mParticle APV: [init] unsupported environment (no History API), not starting")},a.prototype.patchHistoryMethods=function(){var a=this,b=window.history.pushState;if(b[WRAPPED_MARKER])return void this.mpInstance.Logger.verbose("mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap");var c=window.history.pushState,d=window.history.replaceState;this.originalPushState=c,this.originalReplaceState=d;var e=function(){for(var b=[],d=0;d<arguments.length;d++)b[d]=arguments[d];var e=c.apply(this,b);return a.safeHandleNavigation("pushState"),e},f=function(){for(var b=[],c=0;c<arguments.length;c++)b[c]=arguments[c];var e=d.apply(this,b);return a.safeHandleNavigation("replaceState"),e};Object.defineProperty(e,WRAPPED_MARKER,{value:!0,enumerable:!1}),Object.defineProperty(f,WRAPPED_MARKER,{value:!0,enumerable:!1}),this.pushStateWrapper=e,this.replaceStateWrapper=f;try{window.history.pushState=e,window.history.replaceState=f;}catch(a){this.mpInstance.Logger.verbose("mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ".concat(a));try{window.history.pushState===e&&(window.history.pushState=c),window.history.replaceState===f&&(window.history.replaceState=d);}catch(a){this.mpInstance.Logger.verbose("mParticle APV: [error] failed to restore history methods after patch failure: ".concat(a));}this.pushStateWrapper=null,this.replaceStateWrapper=null,this.originalPushState=null,this.originalReplaceState=null;}},a.prototype.addNavigationListeners=function(){var a=this;this.popStateListener=function(){return a.safeHandleNavigation("popstate")},window.addEventListener("popstate",this.popStateListener);},a.prototype.safeHandleNavigation=function(a){try{this.handleNavigation(a);}catch(b){this.mpInstance.Logger.verbose("mParticle APV: [error] navigation handler threw (".concat(a,"), page view skipped: ").concat(b));}},a.prototype.getCurrentKey=function(){return window.location.pathname},a.prototype.handleNavigation=function(a){var b=this,c=this.getCurrentKey();if(this.mpInstance.Logger.verbose("mParticle APV: [detect] navigation signal (source: ".concat(a,", candidatePath: ").concat(c,", lastPath: ").concat(this.lastPath,")")),c===this.lastPath)return void this.mpInstance.Logger.verbose("mParticle APV: [dedupe] pathname unchanged, skipping (source: ".concat(a,", path: ").concat(c,")"));this.mpInstance.Logger.verbose("mParticle APV: [accept] pathname changed, scheduling fire (source: ".concat(a,", from: ").concat(this.lastPath,", to: ").concat(c,")")),this.lastPath=c;// Snapshot the path now: it is already settled at this point, and a
|
|
1662
|
+
// same-tick navigation would otherwise overwrite window.location
|
|
1663
|
+
// before the deferred flush reads it. The title is intentionally read
|
|
1664
|
+
// later (in the deferred flush) so the router's post-navigation render
|
|
1665
|
+
// commit has a chance to update document.title first.
|
|
1666
|
+
setTimeout(function(){return b.isActive?void(b.mpInstance._SessionManager.resetSessionTimer(),b.firePageView(c)):void b.mpInstance.Logger.verbose("mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)")},0);},a.prototype.firePageView=function(a){var b=window.document.title;this.mpInstance.Logger.verbose("mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(a,", title: ").concat(b,")")),this.mpInstance._Events.logEvent({messageType:MessageType$1.PageView,name:"PageView",data:{hostname:window.location.hostname,title:b,path:a},eventType:EventType.Unknown});},a.prototype.teardown=function(){this.popStateListener&&(window.removeEventListener("popstate",this.popStateListener),this.popStateListener=null);var a=null!==this.pushStateWrapper&&window.history.pushState===this.pushStateWrapper;this.originalPushState&&(a?(window.history.pushState=this.originalPushState,this.mpInstance.Logger.verbose("mParticle APV: [teardown] restored original pushState")):this.mpInstance.Logger.verbose("mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op"),this.originalPushState=null,this.pushStateWrapper=null);var b=null!==this.replaceStateWrapper&&window.history.replaceState===this.replaceStateWrapper;this.originalReplaceState&&(b?(window.history.replaceState=this.originalReplaceState,this.mpInstance.Logger.verbose("mParticle APV: [teardown] restored original replaceState")):this.mpInstance.Logger.verbose("mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op"),this.originalReplaceState=null,this.replaceStateWrapper=null),this.isActive=!1;},a}();
|
|
1667
|
+
|
|
1653
1668
|
var Messages=Constants.Messages,HTTPCodes=Constants.HTTPCodes,FeatureFlags=Constants.FeatureFlags,CaptureIntegrationSpecificIdsV2Modes=Constants.CaptureIntegrationSpecificIdsV2Modes,ReportBatching=FeatureFlags.ReportBatching,CaptureIntegrationSpecificIds=FeatureFlags.CaptureIntegrationSpecificIds,CaptureIntegrationSpecificIdsV2=FeatureFlags.CaptureIntegrationSpecificIdsV2,StartingInitialization=Messages.InformationMessages.StartingInitialization;/**
|
|
1654
1669
|
* <p>All of the following methods can be called on the primary mParticle class. In version 2.10.0, we introduced <a href="https://docs.mparticle.com/developers/sdk/web/multiple-instances/">multiple instances</a>. If you are using multiple instances (self hosted environments only), you should call these methods on each instance.</p>
|
|
1655
1670
|
* <p>In current versions of mParticle, if your site has one instance, that instance name is 'default_instance'. Any methods called on mParticle on a site with one instance will be mapped to the `default_instance`.</p>
|
|
@@ -1827,7 +1842,7 @@ if((null===(d=c._APIClient)||void 0===d?void 0:d.uploader)&&c._APIClient.uploade
|
|
|
1827
1842
|
var k=c.Identity.getCurrentUser(),l=k?k.getMPID():null,m=k?k.getUserIdentities().userIdentities:{};c._Store.SDKConfig.identifyRequest=c._Store.hasInvalidIdentifyRequest()?{userIdentities:m}:c._Store.SDKConfig.identifyRequest,i(ReportBatching)&&c._ForwardingStatsUploader.startForwardingStatsTimer();// https://go.mparticle.com/work/SQDSDKS-7639
|
|
1828
1843
|
var n=i(CaptureIntegrationSpecificIds),o=i(CaptureIntegrationSpecificIdsV2),p=o&&o!==CaptureIntegrationSpecificIdsV2Modes.None||!0===n;if(p){var q;n||o===CaptureIntegrationSpecificIdsV2Modes.All?q="all":o===CaptureIntegrationSpecificIdsV2Modes.RoktOnly&&(q="roktonly"),c._IntegrationCapture=new IntegrationCapture(q),c._IntegrationCapture.capture();}// Configure Rokt Manager with user and filtered user
|
|
1829
1844
|
var r=parseConfig(b,"Rokt",181);if(r){var s=null===(e=r.settings)||void 0===e?void 0:e.accountId;c._Store.setRoktAccountId(s);var t=r.userAttributeFilters,u=filteredMparticleUser(l,{userAttributeFilters:t},c),v={sandbox:null===b||void 0===b?void 0:b.isDevelopmentMode,launcherOptions:null===b||void 0===b?void 0:b.launcherOptions,domain:null===b||void 0===b?void 0:b.domain};// https://go.mparticle.com/work/SQDSDKS-7339
|
|
1830
|
-
c._RoktManager.init(r,u,c.Identity,c._Store,c.Logger,v,c.captureTiming,c._ErrorReportingDispatcher,c._LoggingDispatcher,c._IntegrationCapture);}c._Forwarders.processForwarders(b,c._APIClient.prepareForwardingStats),c._Forwarders.processPixelConfigs(b),c._SessionManager.initialize(),c._Events.logAST(),i(j)
|
|
1845
|
+
c._RoktManager.init(r,u,c.Identity,c._Store,c.Logger,v,c.captureTiming,c._ErrorReportingDispatcher,c._LoggingDispatcher,c._IntegrationCapture);}c._Forwarders.processForwarders(b,c._APIClient.prepareForwardingStats),c._Forwarders.processPixelConfigs(b),c._SessionManager.initialize(),c._Events.logAST(),i(j)?(c._Events.logPageView(),!c._PageViewTracker&&(c._PageViewTracker=new PageViewTracker(c)),c._PageViewTracker.init()):c._PageViewTracker&&c._PageViewTracker.teardown(),processIdentityCallback(c,k,l,m);}// We will continue to clear out the ready queue as part of the initial init flow
|
|
1831
1846
|
// if an identify request is unnecessary, such as if there is an existing session
|
|
1832
1847
|
(c._Store.mpid&&!c._Store.identifyCalled||c._Store.webviewBridgeEnabled)&&(c._Store.isInitialized=!0,null===(f=c._CookieConsentManager)||void 0===f?void 0:f.syncNoTargetingAttribute(c.Identity.getCurrentUser()),c._preInit.readyQueue=processReadyQueue(c._preInit.readyQueue,c.Logger)),null===(g=c.processQueueOnNoFunctional)||void 0===g?void 0:g.call(c),c._Store.isFirstRun&&(c._Store.isFirstRun=!1);}// https://go.mparticle.com/work/SQDSDKS-7061
|
|
1833
1848
|
function createKitBlocker(a,b){var c,d,e,f;/* There are three ways a data plan object for blocking can be passed to the SDK:
|
package/dist/mparticle.js
CHANGED
|
@@ -203,7 +203,7 @@ var mParticle = (function () {
|
|
|
203
203
|
Base64: Base64$1
|
|
204
204
|
};
|
|
205
205
|
|
|
206
|
-
var version = "2.
|
|
206
|
+
var version = "2.78.0";
|
|
207
207
|
|
|
208
208
|
var Constants = {
|
|
209
209
|
sdkVersion: version,
|
|
@@ -684,7 +684,7 @@ var mParticle = (function () {
|
|
|
684
684
|
var replaceAmpWithAmpersand = function replaceAmpWithAmpersand(value) {
|
|
685
685
|
return value.replace(/&/g, '&');
|
|
686
686
|
};
|
|
687
|
-
var createCookieSyncUrl = function createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain) {
|
|
687
|
+
var createCookieSyncUrl = function createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid) {
|
|
688
688
|
var modifiedPixelUrl = replaceAmpWithAmpersand(pixelUrl);
|
|
689
689
|
var modifiedDirectUrl = redirectUrl ? replaceAmpWithAmpersand(redirectUrl) : null;
|
|
690
690
|
var url = replaceMPID(modifiedPixelUrl, mpid);
|
|
@@ -694,8 +694,26 @@ var mParticle = (function () {
|
|
|
694
694
|
var separator = fullUrl.includes('?') ? '&' : '?';
|
|
695
695
|
fullUrl += "".concat(separator, "domain=").concat(domain);
|
|
696
696
|
}
|
|
697
|
+
if (base64Mpid) {
|
|
698
|
+
var separator = fullUrl.includes('?') ? '&' : '?';
|
|
699
|
+
fullUrl += "".concat(separator, "google_hm=").concat(base64Mpid);
|
|
700
|
+
}
|
|
697
701
|
return fullUrl;
|
|
698
702
|
};
|
|
703
|
+
// Google's cookie matching service requires `google_hm` values to be web-safe
|
|
704
|
+
// base64 (RFC 4648 §5) without padding, otherwise the write to the hosted
|
|
705
|
+
// match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
|
|
706
|
+
// (String#replaceAll is not available in our ES5 browser targets.)
|
|
707
|
+
var WEB_SAFE_BASE64_REPLACEMENTS = {
|
|
708
|
+
'+': '-',
|
|
709
|
+
'/': '_',
|
|
710
|
+
'=': ''
|
|
711
|
+
};
|
|
712
|
+
var toWebSafeBase64 = function toWebSafeBase64(value) {
|
|
713
|
+
return btoa(value).replace(/[+/=]/g, function (c) {
|
|
714
|
+
return WEB_SAFE_BASE64_REPLACEMENTS[c];
|
|
715
|
+
});
|
|
716
|
+
};
|
|
699
717
|
// FIXME: REFACTOR for V3
|
|
700
718
|
// only used in store.js to sanitize server-side formatting of
|
|
701
719
|
// booleans when checking for `isDevelopmentMode`
|
|
@@ -4190,7 +4208,7 @@ var mParticle = (function () {
|
|
|
4190
4208
|
return;
|
|
4191
4209
|
}
|
|
4192
4210
|
pixelConfigurations.forEach(function (pixelSettings) {
|
|
4193
|
-
var _a, _b;
|
|
4211
|
+
var _a, _b, _c;
|
|
4194
4212
|
// set requiresConsent to false to start each additional pixel configuration
|
|
4195
4213
|
// set to true only if filteringConsenRuleValues.values.length exists
|
|
4196
4214
|
var requiresConsent = false;
|
|
@@ -4199,6 +4217,7 @@ var mParticle = (function () {
|
|
|
4199
4217
|
pixelUrl = pixelSettings.pixelUrl,
|
|
4200
4218
|
redirectUrl = pixelSettings.redirectUrl,
|
|
4201
4219
|
moduleId = pixelSettings.moduleId,
|
|
4220
|
+
settings = pixelSettings.settings,
|
|
4202
4221
|
// Tells you how often we should do a cookie sync (in days)
|
|
4203
4222
|
frequencyCap = pixelSettings.frequencyCap;
|
|
4204
4223
|
var values = (filteringConsentRuleValues || {}).values;
|
|
@@ -4231,8 +4250,15 @@ var mParticle = (function () {
|
|
|
4231
4250
|
// It is optional but to simplify the code, we add it for all Trade
|
|
4232
4251
|
// // Desk cookie syncs.
|
|
4233
4252
|
var domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
|
|
4234
|
-
//
|
|
4235
|
-
|
|
4253
|
+
// Google Marketing Platform accepts a web-safe base64-encoded MPID via the
|
|
4254
|
+
// `google_hm` query parameter, but only for accounts provisioned for Google
|
|
4255
|
+
// Hosted Matching — Google returns errors otherwise. It is therefore gated
|
|
4256
|
+
// behind the opt-in `enableHmTag` setting (off by default), which the server
|
|
4257
|
+
// delivers as the string 'True' when the UI toggle is enabled.
|
|
4258
|
+
var enableHmTag = ((_c = settings === null || settings === void 0 ? void 0 : settings.enableHmTag) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === 'true';
|
|
4259
|
+
var isDoubleClickModule = moduleId === PARTNER_MODULE_IDS.DoubleclickDFP;
|
|
4260
|
+
var base64Mpid = isDoubleClickModule && enableHmTag ? toWebSafeBase64(mpid) : undefined;
|
|
4261
|
+
var fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid);
|
|
4236
4262
|
self.performCookieSync(fullUrl, moduleId.toString(), mpid, cookieSyncDates);
|
|
4237
4263
|
});
|
|
4238
4264
|
};
|
|
@@ -11145,6 +11171,188 @@ var mParticle = (function () {
|
|
|
11145
11171
|
return LoggingDispatcher;
|
|
11146
11172
|
}();
|
|
11147
11173
|
|
|
11174
|
+
var WRAPPED_MARKER = '__mpApvWrapped__';
|
|
11175
|
+
var PageViewTracker = /** @class */function () {
|
|
11176
|
+
function PageViewTracker(mpInstance) {
|
|
11177
|
+
this.lastPath = null;
|
|
11178
|
+
this.isActive = false;
|
|
11179
|
+
this.originalPushState = null;
|
|
11180
|
+
this.originalReplaceState = null;
|
|
11181
|
+
this.pushStateWrapper = null;
|
|
11182
|
+
this.replaceStateWrapper = null;
|
|
11183
|
+
this.popStateListener = null;
|
|
11184
|
+
this.mpInstance = mpInstance;
|
|
11185
|
+
}
|
|
11186
|
+
PageViewTracker.prototype.isSupportedEnvironment = function () {
|
|
11187
|
+
return typeof window !== 'undefined' && typeof window.history !== 'undefined' && typeof window.history.pushState === 'function' && typeof window.addEventListener === 'function';
|
|
11188
|
+
};
|
|
11189
|
+
PageViewTracker.prototype.init = function () {
|
|
11190
|
+
this.mpInstance.Logger.verbose('mParticle APV: [init] PageViewTracker Init');
|
|
11191
|
+
if (!this.isSupportedEnvironment()) {
|
|
11192
|
+
this.mpInstance.Logger.verbose('mParticle APV: [init] unsupported environment (no History API), not starting');
|
|
11193
|
+
return;
|
|
11194
|
+
}
|
|
11195
|
+
if (this.isActive) {
|
|
11196
|
+
this.mpInstance.Logger.verbose('mParticle APV: [init] starting (teardown-first for idempotency)');
|
|
11197
|
+
this.teardown();
|
|
11198
|
+
}
|
|
11199
|
+
this.isActive = true;
|
|
11200
|
+
this.lastPath = this.getCurrentKey();
|
|
11201
|
+
this.mpInstance.Logger.verbose("mParticle APV: [init] seeded lastPath: ".concat(this.lastPath));
|
|
11202
|
+
this.patchHistoryMethods();
|
|
11203
|
+
this.addNavigationListeners();
|
|
11204
|
+
this.mpInstance.Logger.verbose('mParticle APV: [init] patched pushState/replaceState + listening for popstate');
|
|
11205
|
+
};
|
|
11206
|
+
PageViewTracker.prototype.patchHistoryMethods = function () {
|
|
11207
|
+
var self = this;
|
|
11208
|
+
var installed = window.history.pushState;
|
|
11209
|
+
if (installed[WRAPPED_MARKER]) {
|
|
11210
|
+
this.mpInstance.Logger.verbose('mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap');
|
|
11211
|
+
return;
|
|
11212
|
+
}
|
|
11213
|
+
var originalPushState = window.history.pushState;
|
|
11214
|
+
var originalReplaceState = window.history.replaceState;
|
|
11215
|
+
this.originalPushState = originalPushState;
|
|
11216
|
+
this.originalReplaceState = originalReplaceState;
|
|
11217
|
+
var pushStateWrapper = function pushStateWrapper() {
|
|
11218
|
+
var args = [];
|
|
11219
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
11220
|
+
args[_i] = arguments[_i];
|
|
11221
|
+
}
|
|
11222
|
+
var result = originalPushState.apply(this, args);
|
|
11223
|
+
self.safeHandleNavigation('pushState');
|
|
11224
|
+
return result;
|
|
11225
|
+
};
|
|
11226
|
+
var replaceStateWrapper = function replaceStateWrapper() {
|
|
11227
|
+
var args = [];
|
|
11228
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
11229
|
+
args[_i] = arguments[_i];
|
|
11230
|
+
}
|
|
11231
|
+
var result = originalReplaceState.apply(this, args);
|
|
11232
|
+
self.safeHandleNavigation('replaceState');
|
|
11233
|
+
return result;
|
|
11234
|
+
};
|
|
11235
|
+
Object.defineProperty(pushStateWrapper, WRAPPED_MARKER, {
|
|
11236
|
+
value: true,
|
|
11237
|
+
enumerable: false
|
|
11238
|
+
});
|
|
11239
|
+
Object.defineProperty(replaceStateWrapper, WRAPPED_MARKER, {
|
|
11240
|
+
value: true,
|
|
11241
|
+
enumerable: false
|
|
11242
|
+
});
|
|
11243
|
+
this.pushStateWrapper = pushStateWrapper;
|
|
11244
|
+
this.replaceStateWrapper = replaceStateWrapper;
|
|
11245
|
+
try {
|
|
11246
|
+
window.history.pushState = pushStateWrapper;
|
|
11247
|
+
window.history.replaceState = replaceStateWrapper;
|
|
11248
|
+
} catch (e) {
|
|
11249
|
+
this.mpInstance.Logger.verbose("mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ".concat(e));
|
|
11250
|
+
try {
|
|
11251
|
+
if (window.history.pushState === pushStateWrapper) {
|
|
11252
|
+
window.history.pushState = originalPushState;
|
|
11253
|
+
}
|
|
11254
|
+
if (window.history.replaceState === replaceStateWrapper) {
|
|
11255
|
+
window.history.replaceState = originalReplaceState;
|
|
11256
|
+
}
|
|
11257
|
+
} catch (restoreError) {
|
|
11258
|
+
this.mpInstance.Logger.verbose("mParticle APV: [error] failed to restore history methods after patch failure: ".concat(restoreError));
|
|
11259
|
+
}
|
|
11260
|
+
this.pushStateWrapper = null;
|
|
11261
|
+
this.replaceStateWrapper = null;
|
|
11262
|
+
this.originalPushState = null;
|
|
11263
|
+
this.originalReplaceState = null;
|
|
11264
|
+
}
|
|
11265
|
+
};
|
|
11266
|
+
PageViewTracker.prototype.addNavigationListeners = function () {
|
|
11267
|
+
var _this = this;
|
|
11268
|
+
this.popStateListener = function () {
|
|
11269
|
+
return _this.safeHandleNavigation('popstate');
|
|
11270
|
+
};
|
|
11271
|
+
window.addEventListener('popstate', this.popStateListener);
|
|
11272
|
+
};
|
|
11273
|
+
PageViewTracker.prototype.safeHandleNavigation = function (source) {
|
|
11274
|
+
try {
|
|
11275
|
+
this.handleNavigation(source);
|
|
11276
|
+
} catch (e) {
|
|
11277
|
+
this.mpInstance.Logger.verbose("mParticle APV: [error] navigation handler threw (".concat(source, "), page view skipped: ").concat(e));
|
|
11278
|
+
}
|
|
11279
|
+
};
|
|
11280
|
+
PageViewTracker.prototype.getCurrentKey = function () {
|
|
11281
|
+
return window.location.pathname;
|
|
11282
|
+
};
|
|
11283
|
+
PageViewTracker.prototype.handleNavigation = function (source) {
|
|
11284
|
+
var _this = this;
|
|
11285
|
+
var candidatePath = this.getCurrentKey();
|
|
11286
|
+
this.mpInstance.Logger.verbose("mParticle APV: [detect] navigation signal (source: ".concat(source, ", candidatePath: ").concat(candidatePath, ", lastPath: ").concat(this.lastPath, ")"));
|
|
11287
|
+
if (candidatePath === this.lastPath) {
|
|
11288
|
+
this.mpInstance.Logger.verbose("mParticle APV: [dedupe] pathname unchanged, skipping (source: ".concat(source, ", path: ").concat(candidatePath, ")"));
|
|
11289
|
+
return;
|
|
11290
|
+
}
|
|
11291
|
+
this.mpInstance.Logger.verbose("mParticle APV: [accept] pathname changed, scheduling fire (source: ".concat(source, ", from: ").concat(this.lastPath, ", to: ").concat(candidatePath, ")"));
|
|
11292
|
+
this.lastPath = candidatePath;
|
|
11293
|
+
// Snapshot the path now: it is already settled at this point, and a
|
|
11294
|
+
// same-tick navigation would otherwise overwrite window.location
|
|
11295
|
+
// before the deferred flush reads it. The title is intentionally read
|
|
11296
|
+
// later (in the deferred flush) so the router's post-navigation render
|
|
11297
|
+
// commit has a chance to update document.title first.
|
|
11298
|
+
var capturedPath = candidatePath;
|
|
11299
|
+
setTimeout(function () {
|
|
11300
|
+
if (!_this.isActive) {
|
|
11301
|
+
_this.mpInstance.Logger.verbose('mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)');
|
|
11302
|
+
return;
|
|
11303
|
+
}
|
|
11304
|
+
_this.mpInstance._SessionManager.resetSessionTimer();
|
|
11305
|
+
_this.firePageView(capturedPath);
|
|
11306
|
+
}, 0);
|
|
11307
|
+
};
|
|
11308
|
+
// Mirrors the event shape of the public mParticle.logPageView(), but carries
|
|
11309
|
+
// the captured SPA path rather than reading the live location.
|
|
11310
|
+
PageViewTracker.prototype.firePageView = function (path) {
|
|
11311
|
+
var title = window.document.title;
|
|
11312
|
+
this.mpInstance.Logger.verbose("mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ".concat(path, ", title: ").concat(title, ")"));
|
|
11313
|
+
this.mpInstance._Events.logEvent({
|
|
11314
|
+
messageType: MessageType$1.PageView,
|
|
11315
|
+
name: 'PageView',
|
|
11316
|
+
data: {
|
|
11317
|
+
hostname: window.location.hostname,
|
|
11318
|
+
title: title,
|
|
11319
|
+
path: path
|
|
11320
|
+
},
|
|
11321
|
+
eventType: EventType.Unknown
|
|
11322
|
+
});
|
|
11323
|
+
};
|
|
11324
|
+
PageViewTracker.prototype.teardown = function () {
|
|
11325
|
+
if (this.popStateListener) {
|
|
11326
|
+
window.removeEventListener('popstate', this.popStateListener);
|
|
11327
|
+
this.popStateListener = null;
|
|
11328
|
+
}
|
|
11329
|
+
var pushStateStillOurs = this.pushStateWrapper !== null && window.history.pushState === this.pushStateWrapper;
|
|
11330
|
+
if (this.originalPushState) {
|
|
11331
|
+
if (pushStateStillOurs) {
|
|
11332
|
+
window.history.pushState = this.originalPushState;
|
|
11333
|
+
this.mpInstance.Logger.verbose('mParticle APV: [teardown] restored original pushState');
|
|
11334
|
+
} else {
|
|
11335
|
+
this.mpInstance.Logger.verbose('mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op');
|
|
11336
|
+
}
|
|
11337
|
+
this.originalPushState = null;
|
|
11338
|
+
this.pushStateWrapper = null;
|
|
11339
|
+
}
|
|
11340
|
+
var replaceStateStillOurs = this.replaceStateWrapper !== null && window.history.replaceState === this.replaceStateWrapper;
|
|
11341
|
+
if (this.originalReplaceState) {
|
|
11342
|
+
if (replaceStateStillOurs) {
|
|
11343
|
+
window.history.replaceState = this.originalReplaceState;
|
|
11344
|
+
this.mpInstance.Logger.verbose('mParticle APV: [teardown] restored original replaceState');
|
|
11345
|
+
} else {
|
|
11346
|
+
this.mpInstance.Logger.verbose('mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op');
|
|
11347
|
+
}
|
|
11348
|
+
this.originalReplaceState = null;
|
|
11349
|
+
this.replaceStateWrapper = null;
|
|
11350
|
+
}
|
|
11351
|
+
this.isActive = false;
|
|
11352
|
+
};
|
|
11353
|
+
return PageViewTracker;
|
|
11354
|
+
}();
|
|
11355
|
+
|
|
11148
11356
|
var Messages = Constants.Messages,
|
|
11149
11357
|
HTTPCodes = Constants.HTTPCodes,
|
|
11150
11358
|
FeatureFlags = Constants.FeatureFlags,
|
|
@@ -12225,6 +12433,12 @@ var mParticle = (function () {
|
|
|
12225
12433
|
mpInstance._Events.logAST();
|
|
12226
12434
|
if (getFeatureFlag(AutoLogPageView)) {
|
|
12227
12435
|
mpInstance._Events.logPageView();
|
|
12436
|
+
if (!mpInstance._PageViewTracker) {
|
|
12437
|
+
mpInstance._PageViewTracker = new PageViewTracker(mpInstance);
|
|
12438
|
+
}
|
|
12439
|
+
mpInstance._PageViewTracker.init();
|
|
12440
|
+
} else if (mpInstance._PageViewTracker) {
|
|
12441
|
+
mpInstance._PageViewTracker.teardown();
|
|
12228
12442
|
}
|
|
12229
12443
|
processIdentityCallback(mpInstance, currentUser, currentUserMPID, currentUserIdentities);
|
|
12230
12444
|
}
|
|
@@ -20,6 +20,7 @@ import RoktManager from './roktManager';
|
|
|
20
20
|
import { ICookieConsentManager } from './cookieConsentManager';
|
|
21
21
|
import { ErrorReportingDispatcher } from './reporting/errorReportingDispatcher';
|
|
22
22
|
import { LoggingDispatcher } from './reporting/loggingDispatcher';
|
|
23
|
+
import { PageViewTracker } from './pageViewTracker';
|
|
23
24
|
export interface IErrorLogMessage {
|
|
24
25
|
message?: string;
|
|
25
26
|
name?: string;
|
|
@@ -44,6 +45,7 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK {
|
|
|
44
45
|
_IdentityAPIClient: typeof IdentityAPIClient;
|
|
45
46
|
_IntegrationCapture: IntegrationCapture;
|
|
46
47
|
_NativeSdkHelpers: INativeSdkHelpers;
|
|
48
|
+
_PageViewTracker?: PageViewTracker;
|
|
47
49
|
_Persistence: IPersistence;
|
|
48
50
|
_CookieConsentManager: ICookieConsentManager;
|
|
49
51
|
_ErrorReportingDispatcher: ErrorReportingDispatcher;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IMParticleWebSDKInstance } from './mp-instance';
|
|
2
|
+
export declare class PageViewTracker {
|
|
3
|
+
mpInstance: IMParticleWebSDKInstance;
|
|
4
|
+
private lastPath;
|
|
5
|
+
private isActive;
|
|
6
|
+
private originalPushState;
|
|
7
|
+
private originalReplaceState;
|
|
8
|
+
private pushStateWrapper;
|
|
9
|
+
private replaceStateWrapper;
|
|
10
|
+
private popStateListener;
|
|
11
|
+
constructor(mpInstance: IMParticleWebSDKInstance);
|
|
12
|
+
private isSupportedEnvironment;
|
|
13
|
+
init(): void;
|
|
14
|
+
private patchHistoryMethods;
|
|
15
|
+
private addNavigationListeners;
|
|
16
|
+
private safeHandleNavigation;
|
|
17
|
+
private getCurrentKey;
|
|
18
|
+
private handleNavigation;
|
|
19
|
+
private firePageView;
|
|
20
|
+
teardown(): void;
|
|
21
|
+
}
|
|
@@ -27,7 +27,8 @@ declare const replaceApostrophesWithQuotes: (value: string) => string;
|
|
|
27
27
|
declare const replaceQuotesWithApostrophes: (value: string) => string;
|
|
28
28
|
declare const replaceMPID: (value: string, mpid: MPID) => string;
|
|
29
29
|
declare const replaceAmpWithAmpersand: (value: string) => string;
|
|
30
|
-
declare const createCookieSyncUrl: (mpid: MPID, pixelUrl: string, redirectUrl?: string, domain?: string) => string;
|
|
30
|
+
declare const createCookieSyncUrl: (mpid: MPID, pixelUrl: string, redirectUrl?: string, domain?: string, base64Mpid?: string) => string;
|
|
31
|
+
declare const toWebSafeBase64: (value: string) => string;
|
|
31
32
|
declare const returnConvertedBoolean: (data: string | boolean | number) => boolean;
|
|
32
33
|
declare const decoded: (s: string) => string;
|
|
33
34
|
declare const converted: (s: string) => string;
|
|
@@ -81,4 +82,4 @@ declare const obfuscateData: (value: any) => any;
|
|
|
81
82
|
declare const obfuscateDevData: (data: any, isDevelopmentMode?: boolean) => any;
|
|
82
83
|
declare function extend(...args: any[]): any;
|
|
83
84
|
declare const getErrorMessage: (e: unknown, fallback?: string) => string;
|
|
84
|
-
export { createCookieString, revertCookieString, createCookieSyncUrl, valueof, AttributeValue, converted, decoded, obfuscateDevData, filterDictionaryWithHash, findKeyInObject, generateDeprecationMessage, generateHash, generateUniqueId, getErrorMessage, getRampNumber, inArray, isObject, isStringOrNumber, obfuscateData, parseConfig, parseNumber, parseSettingsString, parseStringOrNumber, replaceCommasWithPipes, replacePipesWithCommas, replaceApostrophesWithQuotes, replaceQuotesWithApostrophes, returnConvertedBoolean, toDataPlanSlug, isString, isNumber, isFunction, isDataPlanSlug, isEmpty, isValidAttributeValue, isValidCustomFlagProperty, mergeObjects, moveElementToEnd, queryStringParser, getCookies, getHref, replaceMPID, replaceAmpWithAmpersand, extend, };
|
|
85
|
+
export { createCookieString, revertCookieString, createCookieSyncUrl, toWebSafeBase64, valueof, AttributeValue, converted, decoded, obfuscateDevData, filterDictionaryWithHash, findKeyInObject, generateDeprecationMessage, generateHash, generateUniqueId, getErrorMessage, getRampNumber, inArray, isObject, isStringOrNumber, obfuscateData, parseConfig, parseNumber, parseSettingsString, parseStringOrNumber, replaceCommasWithPipes, replacePipesWithCommas, replaceApostrophesWithQuotes, replaceQuotesWithApostrophes, returnConvertedBoolean, toDataPlanSlug, isString, isNumber, isFunction, isDataPlanSlug, isEmpty, isValidAttributeValue, isValidCustomFlagProperty, mergeObjects, moveElementToEnd, queryStringParser, getCookies, getHref, replaceMPID, replaceAmpWithAmpersand, extend, };
|
package/package.json
CHANGED
package/src/cookieSyncManager.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
Dictionary,
|
|
3
3
|
isEmpty,
|
|
4
4
|
createCookieSyncUrl,
|
|
5
|
+
toWebSafeBase64,
|
|
5
6
|
} from './utils';
|
|
6
7
|
import Constants from './constants';
|
|
7
8
|
import { MPID } from '@mparticle/web-sdk';
|
|
@@ -95,6 +96,7 @@ export default function CookieSyncManager(
|
|
|
95
96
|
pixelUrl,
|
|
96
97
|
redirectUrl,
|
|
97
98
|
moduleId,
|
|
99
|
+
settings,
|
|
98
100
|
// Tells you how often we should do a cookie sync (in days)
|
|
99
101
|
frequencyCap,
|
|
100
102
|
} = pixelSettings;
|
|
@@ -137,8 +139,17 @@ export default function CookieSyncManager(
|
|
|
137
139
|
// It is optional but to simplify the code, we add it for all Trade
|
|
138
140
|
// // Desk cookie syncs.
|
|
139
141
|
const domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
|
|
143
|
+
// Google Marketing Platform accepts a web-safe base64-encoded MPID via the
|
|
144
|
+
// `google_hm` query parameter, but only for accounts provisioned for Google
|
|
145
|
+
// Hosted Matching — Google returns errors otherwise. It is therefore gated
|
|
146
|
+
// behind the opt-in `enableHmTag` setting (off by default), which the server
|
|
147
|
+
// delivers as the string 'True' when the UI toggle is enabled.
|
|
148
|
+
const enableHmTag = settings?.enableHmTag?.toLowerCase() === 'true';
|
|
149
|
+
const isDoubleClickModule = moduleId === PARTNER_MODULE_IDS.DoubleclickDFP;
|
|
150
|
+
const base64Mpid = isDoubleClickModule && enableHmTag ? toWebSafeBase64(mpid) : undefined;
|
|
151
|
+
|
|
152
|
+
const fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid);
|
|
142
153
|
|
|
143
154
|
self.performCookieSync(
|
|
144
155
|
fullUrl,
|
package/src/mp-instance.ts
CHANGED
|
@@ -57,6 +57,7 @@ import { LoggingDispatcher } from './reporting/loggingDispatcher';
|
|
|
57
57
|
import { IErrorReportingService, ILoggingService } from './reporting/types';
|
|
58
58
|
import { logDeprecatedMethodUsage } from './reporting/deprecatedMethodLogger';
|
|
59
59
|
import { normalizeRoktLauncherOptions } from './roktLauncherOptions';
|
|
60
|
+
import { PageViewTracker } from './pageViewTracker';
|
|
60
61
|
|
|
61
62
|
export interface IErrorLogMessage {
|
|
62
63
|
message?: string;
|
|
@@ -87,6 +88,7 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK {
|
|
|
87
88
|
_IdentityAPIClient: typeof IdentityAPIClient;
|
|
88
89
|
_IntegrationCapture: IntegrationCapture;
|
|
89
90
|
_NativeSdkHelpers: INativeSdkHelpers;
|
|
91
|
+
_PageViewTracker?: PageViewTracker;
|
|
90
92
|
_Persistence: IPersistence;
|
|
91
93
|
_CookieConsentManager: ICookieConsentManager;
|
|
92
94
|
_ErrorReportingDispatcher: ErrorReportingDispatcher;
|
|
@@ -1585,6 +1587,13 @@ function completeSDKInitialization(apiKey, config, mpInstance) {
|
|
|
1585
1587
|
|
|
1586
1588
|
if (getFeatureFlag(AutoLogPageView)) {
|
|
1587
1589
|
mpInstance._Events.logPageView();
|
|
1590
|
+
|
|
1591
|
+
if (!mpInstance._PageViewTracker) {
|
|
1592
|
+
mpInstance._PageViewTracker = new PageViewTracker(mpInstance);
|
|
1593
|
+
}
|
|
1594
|
+
mpInstance._PageViewTracker.init();
|
|
1595
|
+
} else if (mpInstance._PageViewTracker) {
|
|
1596
|
+
mpInstance._PageViewTracker.teardown();
|
|
1588
1597
|
}
|
|
1589
1598
|
|
|
1590
1599
|
processIdentityCallback(
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { IMParticleWebSDKInstance } from './mp-instance';
|
|
2
|
+
import { EventType, MessageType } from './types';
|
|
3
|
+
|
|
4
|
+
type HistoryStateMethod = History['pushState'];
|
|
5
|
+
|
|
6
|
+
const WRAPPED_MARKER = '__mpApvWrapped__';
|
|
7
|
+
|
|
8
|
+
type MarkedHistoryMethod = HistoryStateMethod & {
|
|
9
|
+
[WRAPPED_MARKER]?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export class PageViewTracker {
|
|
13
|
+
mpInstance: IMParticleWebSDKInstance;
|
|
14
|
+
|
|
15
|
+
private lastPath: string | null = null;
|
|
16
|
+
private isActive = false;
|
|
17
|
+
|
|
18
|
+
private originalPushState: HistoryStateMethod | null = null;
|
|
19
|
+
private originalReplaceState: HistoryStateMethod | null = null;
|
|
20
|
+
|
|
21
|
+
private pushStateWrapper: HistoryStateMethod | null = null;
|
|
22
|
+
private replaceStateWrapper: HistoryStateMethod | null = null;
|
|
23
|
+
|
|
24
|
+
private popStateListener: (() => void) | null = null;
|
|
25
|
+
|
|
26
|
+
constructor(mpInstance: IMParticleWebSDKInstance) {
|
|
27
|
+
this.mpInstance = mpInstance;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private isSupportedEnvironment(): boolean {
|
|
31
|
+
return (
|
|
32
|
+
typeof window !== 'undefined' &&
|
|
33
|
+
typeof window.history !== 'undefined' &&
|
|
34
|
+
typeof window.history.pushState === 'function' &&
|
|
35
|
+
typeof window.addEventListener === 'function'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public init(): void {
|
|
40
|
+
this.mpInstance.Logger.verbose(
|
|
41
|
+
'mParticle APV: [init] PageViewTracker Init'
|
|
42
|
+
);
|
|
43
|
+
if (!this.isSupportedEnvironment()) {
|
|
44
|
+
this.mpInstance.Logger.verbose(
|
|
45
|
+
'mParticle APV: [init] unsupported environment (no History API), not starting'
|
|
46
|
+
);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (this.isActive) {
|
|
51
|
+
this.mpInstance.Logger.verbose(
|
|
52
|
+
'mParticle APV: [init] starting (teardown-first for idempotency)'
|
|
53
|
+
);
|
|
54
|
+
this.teardown();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.isActive = true;
|
|
58
|
+
|
|
59
|
+
this.lastPath = this.getCurrentKey();
|
|
60
|
+
|
|
61
|
+
this.mpInstance.Logger.verbose(
|
|
62
|
+
`mParticle APV: [init] seeded lastPath: ${this.lastPath}`
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
this.patchHistoryMethods();
|
|
66
|
+
this.addNavigationListeners();
|
|
67
|
+
|
|
68
|
+
this.mpInstance.Logger.verbose(
|
|
69
|
+
'mParticle APV: [init] patched pushState/replaceState + listening for popstate'
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private patchHistoryMethods(): void {
|
|
74
|
+
const self = this;
|
|
75
|
+
|
|
76
|
+
const installed = window.history.pushState as MarkedHistoryMethod;
|
|
77
|
+
if (installed[WRAPPED_MARKER]) {
|
|
78
|
+
this.mpInstance.Logger.verbose(
|
|
79
|
+
'mParticle APV: [patch] history already wrapped, skipping to avoid double-wrap'
|
|
80
|
+
);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const originalPushState = window.history.pushState;
|
|
85
|
+
const originalReplaceState = window.history.replaceState;
|
|
86
|
+
this.originalPushState = originalPushState;
|
|
87
|
+
this.originalReplaceState = originalReplaceState;
|
|
88
|
+
|
|
89
|
+
const pushStateWrapper = function(
|
|
90
|
+
this: History,
|
|
91
|
+
...args: Parameters<HistoryStateMethod>
|
|
92
|
+
): void {
|
|
93
|
+
const result = originalPushState.apply(this, args);
|
|
94
|
+
self.safeHandleNavigation('pushState');
|
|
95
|
+
return result;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const replaceStateWrapper = function(
|
|
99
|
+
this: History,
|
|
100
|
+
...args: Parameters<HistoryStateMethod>
|
|
101
|
+
): void {
|
|
102
|
+
const result = originalReplaceState.apply(this, args);
|
|
103
|
+
self.safeHandleNavigation('replaceState');
|
|
104
|
+
return result;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
Object.defineProperty(pushStateWrapper, WRAPPED_MARKER, {
|
|
108
|
+
value: true,
|
|
109
|
+
enumerable: false,
|
|
110
|
+
});
|
|
111
|
+
Object.defineProperty(replaceStateWrapper, WRAPPED_MARKER, {
|
|
112
|
+
value: true,
|
|
113
|
+
enumerable: false,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
this.pushStateWrapper = pushStateWrapper;
|
|
117
|
+
this.replaceStateWrapper = replaceStateWrapper;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
window.history.pushState = pushStateWrapper;
|
|
121
|
+
window.history.replaceState = replaceStateWrapper;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
this.mpInstance.Logger.verbose(
|
|
124
|
+
`mParticle APV: [error] failed to patch history methods (frozen/sealed), rolling back: ${e}`
|
|
125
|
+
);
|
|
126
|
+
try {
|
|
127
|
+
if (window.history.pushState === pushStateWrapper) {
|
|
128
|
+
window.history.pushState = originalPushState;
|
|
129
|
+
}
|
|
130
|
+
if (window.history.replaceState === replaceStateWrapper) {
|
|
131
|
+
window.history.replaceState = originalReplaceState;
|
|
132
|
+
}
|
|
133
|
+
} catch (restoreError) {
|
|
134
|
+
this.mpInstance.Logger.verbose(
|
|
135
|
+
`mParticle APV: [error] failed to restore history methods after patch failure: ${restoreError}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
this.pushStateWrapper = null;
|
|
139
|
+
this.replaceStateWrapper = null;
|
|
140
|
+
this.originalPushState = null;
|
|
141
|
+
this.originalReplaceState = null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private addNavigationListeners(): void {
|
|
146
|
+
this.popStateListener = () => this.safeHandleNavigation('popstate');
|
|
147
|
+
window.addEventListener('popstate', this.popStateListener);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private safeHandleNavigation(source: string): void {
|
|
151
|
+
try {
|
|
152
|
+
this.handleNavigation(source);
|
|
153
|
+
} catch (e) {
|
|
154
|
+
this.mpInstance.Logger.verbose(
|
|
155
|
+
`mParticle APV: [error] navigation handler threw (${source}), page view skipped: ${e}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private getCurrentKey(): string {
|
|
161
|
+
return window.location.pathname;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private handleNavigation(source: string): void {
|
|
165
|
+
const candidatePath = this.getCurrentKey();
|
|
166
|
+
|
|
167
|
+
this.mpInstance.Logger.verbose(
|
|
168
|
+
`mParticle APV: [detect] navigation signal (source: ${source}, candidatePath: ${candidatePath}, lastPath: ${this.lastPath})`
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (candidatePath === this.lastPath) {
|
|
172
|
+
this.mpInstance.Logger.verbose(
|
|
173
|
+
`mParticle APV: [dedupe] pathname unchanged, skipping (source: ${source}, path: ${candidatePath})`
|
|
174
|
+
);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.mpInstance.Logger.verbose(
|
|
179
|
+
`mParticle APV: [accept] pathname changed, scheduling fire (source: ${source}, from: ${this.lastPath}, to: ${candidatePath})`
|
|
180
|
+
);
|
|
181
|
+
this.lastPath = candidatePath;
|
|
182
|
+
|
|
183
|
+
// Snapshot the path now: it is already settled at this point, and a
|
|
184
|
+
// same-tick navigation would otherwise overwrite window.location
|
|
185
|
+
// before the deferred flush reads it. The title is intentionally read
|
|
186
|
+
// later (in the deferred flush) so the router's post-navigation render
|
|
187
|
+
// commit has a chance to update document.title first.
|
|
188
|
+
const capturedPath = candidatePath;
|
|
189
|
+
|
|
190
|
+
setTimeout(() => {
|
|
191
|
+
if (!this.isActive) {
|
|
192
|
+
this.mpInstance.Logger.verbose(
|
|
193
|
+
'mParticle APV: [defer] fire aborted, tracker inactive (torn down before flush)'
|
|
194
|
+
);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.mpInstance._SessionManager.resetSessionTimer();
|
|
199
|
+
this.firePageView(capturedPath);
|
|
200
|
+
}, 0);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Mirrors the event shape of the public mParticle.logPageView(), but carries
|
|
204
|
+
// the captured SPA path rather than reading the live location.
|
|
205
|
+
private firePageView(path: string): void {
|
|
206
|
+
const title = window.document.title;
|
|
207
|
+
|
|
208
|
+
this.mpInstance.Logger.verbose(
|
|
209
|
+
`mParticle APV: [fire] deferred flush -> _Events.logEvent(PageView) (path: ${path}, title: ${title})`
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
this.mpInstance._Events.logEvent({
|
|
213
|
+
messageType: MessageType.PageView,
|
|
214
|
+
name: 'PageView',
|
|
215
|
+
data: {
|
|
216
|
+
hostname: window.location.hostname,
|
|
217
|
+
title,
|
|
218
|
+
path,
|
|
219
|
+
},
|
|
220
|
+
eventType: EventType.Unknown,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
public teardown(): void {
|
|
225
|
+
if (this.popStateListener) {
|
|
226
|
+
window.removeEventListener('popstate', this.popStateListener);
|
|
227
|
+
this.popStateListener = null;
|
|
228
|
+
}
|
|
229
|
+
const pushStateStillOurs =
|
|
230
|
+
this.pushStateWrapper !== null &&
|
|
231
|
+
window.history.pushState === this.pushStateWrapper;
|
|
232
|
+
if (this.originalPushState) {
|
|
233
|
+
if (pushStateStillOurs) {
|
|
234
|
+
window.history.pushState = this.originalPushState;
|
|
235
|
+
this.mpInstance.Logger.verbose(
|
|
236
|
+
'mParticle APV: [teardown] restored original pushState'
|
|
237
|
+
);
|
|
238
|
+
} else {
|
|
239
|
+
this.mpInstance.Logger.verbose(
|
|
240
|
+
'mParticle APV: [teardown] pushState no longer ours; leaving in place, gating callback to no-op'
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
this.originalPushState = null;
|
|
244
|
+
this.pushStateWrapper = null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const replaceStateStillOurs =
|
|
248
|
+
this.replaceStateWrapper !== null &&
|
|
249
|
+
window.history.replaceState === this.replaceStateWrapper;
|
|
250
|
+
if (this.originalReplaceState) {
|
|
251
|
+
if (replaceStateStillOurs) {
|
|
252
|
+
window.history.replaceState = this.originalReplaceState;
|
|
253
|
+
this.mpInstance.Logger.verbose(
|
|
254
|
+
'mParticle APV: [teardown] restored original replaceState'
|
|
255
|
+
);
|
|
256
|
+
} else {
|
|
257
|
+
this.mpInstance.Logger.verbose(
|
|
258
|
+
'mParticle APV: [teardown] replaceState no longer ours; leaving in place, gating callback to no-op'
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
this.originalReplaceState = null;
|
|
262
|
+
this.replaceStateWrapper = null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.isActive = false;
|
|
266
|
+
}
|
|
267
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -207,7 +207,8 @@ const createCookieSyncUrl = (
|
|
|
207
207
|
mpid: MPID,
|
|
208
208
|
pixelUrl: string,
|
|
209
209
|
redirectUrl?: string,
|
|
210
|
-
domain?: string
|
|
210
|
+
domain?: string,
|
|
211
|
+
base64Mpid?: string
|
|
211
212
|
): string => {
|
|
212
213
|
const modifiedPixelUrl = replaceAmpWithAmpersand(pixelUrl);
|
|
213
214
|
const modifiedDirectUrl = redirectUrl
|
|
@@ -215,21 +216,39 @@ const createCookieSyncUrl = (
|
|
|
215
216
|
: null;
|
|
216
217
|
|
|
217
218
|
let url = replaceMPID(modifiedPixelUrl, mpid);
|
|
218
|
-
|
|
219
|
+
|
|
219
220
|
const redirect = modifiedDirectUrl
|
|
220
221
|
? replaceMPID(modifiedDirectUrl, mpid)
|
|
221
222
|
: '';
|
|
222
|
-
|
|
223
|
+
|
|
223
224
|
let fullUrl = url + encodeURIComponent(redirect);
|
|
224
|
-
|
|
225
|
+
|
|
225
226
|
if (domain) {
|
|
226
227
|
const separator = fullUrl.includes('?') ? '&' : '?';
|
|
227
228
|
fullUrl += `${separator}domain=${domain}`;
|
|
228
229
|
}
|
|
229
|
-
|
|
230
|
+
|
|
231
|
+
if (base64Mpid) {
|
|
232
|
+
const separator = fullUrl.includes('?') ? '&' : '?';
|
|
233
|
+
fullUrl += `${separator}google_hm=${base64Mpid}`;
|
|
234
|
+
}
|
|
235
|
+
|
|
230
236
|
return fullUrl;
|
|
231
237
|
};
|
|
232
238
|
|
|
239
|
+
// Google's cookie matching service requires `google_hm` values to be web-safe
|
|
240
|
+
// base64 (RFC 4648 §5) without padding, otherwise the write to the hosted
|
|
241
|
+
// match table fails: https://developers.google.com/authorized-buyers/rtb/cookie-guide
|
|
242
|
+
// (String#replaceAll is not available in our ES5 browser targets.)
|
|
243
|
+
const WEB_SAFE_BASE64_REPLACEMENTS: Dictionary<string> = {
|
|
244
|
+
'+': '-',
|
|
245
|
+
'/': '_',
|
|
246
|
+
'=': '',
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const toWebSafeBase64 = (value: string): string =>
|
|
250
|
+
btoa(value).replace(/[+/=]/g, c => WEB_SAFE_BASE64_REPLACEMENTS[c]);
|
|
251
|
+
|
|
233
252
|
// FIXME: REFACTOR for V3
|
|
234
253
|
// only used in store.js to sanitize server-side formatting of
|
|
235
254
|
// booleans when checking for `isDevelopmentMode`
|
|
@@ -650,6 +669,7 @@ export {
|
|
|
650
669
|
createCookieString,
|
|
651
670
|
revertCookieString,
|
|
652
671
|
createCookieSyncUrl,
|
|
672
|
+
toWebSafeBase64,
|
|
653
673
|
valueof,
|
|
654
674
|
AttributeValue,
|
|
655
675
|
converted,
|