From 0e4a3ce113cc78afe25e7f7ea74d92d3c2fabfa4 Mon Sep 17 00:00:00 2001 From: Jacob Ransom Date: Fri, 17 Nov 2023 17:16:54 +1300 Subject: [PATCH 1/6] Re-applying PRs 594 and 599 adjusting for recent changes --- Template/Tag/MatomoTag.web.js | 375 +++++++++++++++++++++--- javascripts/tagmanager.js | 20 ++ javascripts/tagmanager.min.js | 11 +- tests/resources/trackerTestExample.html | 49 ++++ 4 files changed, 408 insertions(+), 47 deletions(-) create mode 100644 tests/resources/trackerTestExample.html diff --git a/Template/Tag/MatomoTag.web.js b/Template/Tag/MatomoTag.web.js index 15d0a548f..a5803aab2 100644 --- a/Template/Tag/MatomoTag.web.js +++ b/Template/Tag/MatomoTag.web.js @@ -13,7 +13,21 @@ } }; - window._paq = window._paq || []; + // Make sure that window._paq always exists + if (!window._paq) { + window._paq = []; + } + // Store the initial state of window._paq so that we can apply it to all the configs + // We use the stringify and parse to make sure that we have a copy and not a reference + var initialPaq = window._paq && window._paq.length ? JSON.parse(JSON.stringify(window._paq)) : initialPaq || []; + var remainingPaq = []; + var indexesToRemove = []; + // Clear window._paq to prevent things from being tracked too early + while (window._paq.length > 0) { + window._paq.pop(); + } + + var hasProcessedRemainingTrackings = false; if ('object' !== typeof window.matomoPluginAsyncInit) { window.matomoPluginAsyncInit = []; @@ -69,6 +83,15 @@ } } + function removeIndexIfExists(index) + { + if (index < 0) { + return; + } + + indexesToRemove.push(index); + } + var configuredTrackers = {}; return function (parameters, TagManager) { @@ -90,11 +113,15 @@ if (!parameters.matomoConfig || !parameters.matomoConfig.name) { return; } - + // this is the matomoConfig variable name and the only way to differentiate two different tracker // configurations var variableName = parameters.matomoConfig.name; + var setCustomDimensionIndexes = []; + indexesToRemove = []; + var localPaq = JSON.parse(JSON.stringify(initialPaq)); + // we need to fetch matomoConfig again in case some parameters changed meanwhile that are variables... // eg userId might be a variable and it's value might be different now var matomoConfig = parameters.get('matomoConfig', {}); @@ -118,43 +145,230 @@ } configuredTrackers[variableName] = tracker; - if (matomoConfig.requireCookieConsent) { - tracker.requireCookieConsent(); + // NOTE: When a new config is created, it should probably be added to this list + // There might already be some configs missing from this list + var setUserIdIndex = setSiteIdIndex = setTrackerUrlIndex + = requireCookieConsentIndex = disableBrowserFeatureDetectionIndex + = disableCookiesIndex = enableCrossDomainLinkingIndex = cookieSameSiteIndex + = setVisitorCookieTimeoutIndex = setReferralCookieTimeoutIndex + = setSessionCookieTimeoutIndex = setSecureCookieIndex = cookiePathIndex + = cookieNamePrefixIndex = cookieDomainIndex = setDomainsIndex + = alwaysUseSendBeaconIndex = disableAlwaysUseSendBeaconIndex + = setRequestMethodIndex = enableLinkTrackingIndex = enableFileTrackingIndex + = requireConsentIndex = enableDoNotTrackIndex + = disablePerformanceTrackingIndex = appendToTrackingUrlIndex + = setRequestContentTypeIndex = setCustomRequestProcessingIndex + = enableJSErrorTrackingIndex = enableHeartBeatTimerIndex + = trackAllContentImpressionsIndex = trackVisibleContentImpressionsIndex + = disableFormAnalyticsIndex = disableMediaAnalyticsIndex = -1; + for (k = 0; k < localPaq.length; k++) { + // This should only be an array. Skip if it's not + if (!TagManager.utils.isArray(localPaq[k])) { + continue; + } + var name = localPaq[k][0]; + switch (name) { + case 'setUserId': + setUserIdIndex = k; + // Mark this one for removal right away since we don't want it to override the container + removeIndexIfExists(k); + break; + case 'setSiteId': + setSiteIdIndex = k; + // Mark this one for removal right away since we don't want it to override the container + removeIndexIfExists(k); + break; + case 'setTrackerUrl': + setTrackerUrlIndex = k; + // Mark this one for removal right away since we don't want it to override the container + removeIndexIfExists(k); + break; + case 'requireCookieConsent': + requireCookieConsentIndex = k; + break; + case 'disableBrowserFeatureDetection': + disableBrowserFeatureDetectionIndex = k; + break; + case 'disableCookies': + disableCookiesIndex = k; + break; + case 'enableCrossDomainLinking': + enableCrossDomainLinkingIndex = k; + break; + case 'cookieSameSite': + cookieSameSiteIndex = k; + break; + case 'setVisitorCookieTimeout': + setVisitorCookieTimeoutIndex = k; + break; + case 'setReferralCookieTimeout': + setReferralCookieTimeoutIndex = k; + break; + case 'setSessionCookieTimeout': + setSessionCookieTimeoutIndex = k; + break; + case 'setSecureCookie': + setSecureCookieIndex = k; + break; + case 'cookiePath': + cookiePathIndex = k; + break; + case 'cookieNamePrefix': + cookieNamePrefixIndex = k; + break; + case 'cookieDomain': + cookieDomainIndex = k; + break; + case 'setDomains': + setDomainsIndex = k; + break; + case 'alwaysUseSendBeacon': + alwaysUseSendBeaconIndex = k; + break; + case 'disableAlwaysUseSendBeacon': + disableAlwaysUseSendBeaconIndex = k; + break; + case 'enableLinkTracking': + enableLinkTrackingIndex = k; + break; + case 'enableFileTracking': + enableFileTrackingIndex = k; + break; + case 'requireConsent': + requireConsentIndex = k; + break; + case 'enableDoNotTrack': + enableDoNotTrackIndex = k; + break; + case 'disablePerformanceTracking': + disablePerformanceTrackingIndex = k; + break; + case 'appendToTrackingUrl': + appendToTrackingUrlIndex = k; + break; + case 'setRequestContentType': + setRequestContentTypeIndex = k; + break; + case 'setCustomRequestProcessing': + setCustomRequestProcessingIndex = k; + break; + case 'enableJSErrorTracking': + enableJSErrorTrackingIndex = k; + break; + case 'enableHeartBeatTimer': + enableHeartBeatTimerIndex = k; + break; + case 'trackAllContentImpressions': + trackAllContentImpressionsIndex = k; + break; + case 'trackVisibleContentImpressions': + trackVisibleContentImpressionsIndex = k; + break; + case 'FormAnalytics::disable': + case 'FormAnalytics::disableFormAnalytics': + case 'FormAnalytics.disableFormAnalytics': + case 'disableFormAnalytics': + disableFormAnalyticsIndex = k; + break; + case 'MediaAnalytics::disable': + case 'MediaAnalytics::disableMediaAnalytics': + case 'MediaAnalytics.disableMediaAnalytics': + case 'disableMediaAnalytics': + disableMediaAnalyticsIndex = k; + break; + case 'setCustomDimension': + // There could be multiple, so let's push the indexes onto an array + setCustomDimensionIndexes.push(k); + break; + } } - if (matomoConfig.disableBrowserFeatureDetection && typeof tracker.disableBrowserFeatureDetection === 'function') { + if (matomoConfig.requireCookieConsent || requireCookieConsentIndex !== -1) { + tracker.requireCookieConsent(); + removeIndexIfExists(requireCookieConsentIndex); + } + + if ((matomoConfig.disableBrowserFeatureDetection || disableBrowserFeatureDetectionIndex !== -1) && typeof tracker.disableBrowserFeatureDetection === 'function') { tracker.disableBrowserFeatureDetection(); + removeIndexIfExists(disableBrowserFeatureDetectionIndex); } - if (matomoConfig.disableCookies) { + if (matomoConfig.disableCookies || disableCookiesIndex !== -1) { tracker.disableCookies(); + removeIndexIfExists(disableCookiesIndex); } - if (matomoConfig.enableCrossDomainLinking) { + if (matomoConfig.enableCrossDomainLinking || enableCrossDomainLinkingIndex !== -1) { tracker.enableCrossDomainLinking(); - tracker.setCrossDomainLinkingTimeout(matomoConfig.crossDomainLinkingTimeout); + removeIndexIfExists(enableCrossDomainLinkingIndex); } - if (matomoConfig.cookieSameSite) { + if (cookieSameSiteIndex !== -1 && localPaq[cookieSameSiteIndex].length === 2) { + tracker.setCookieSameSite(localPaq[cookieSameSiteIndex][1]); + removeIndexIfExists(cookieSameSiteIndex); + } else if (matomoConfig.cookieSameSite) { tracker.setCookieSameSite(matomoConfig.cookieSameSite); } - if (matomoConfig.setSecureCookie) { + if (setVisitorCookieTimeoutIndex !== -1 && localPaq[setVisitorCookieTimeoutIndex].length === 2) { + tracker.setVisitorCookieTimeout(localPaq[setVisitorCookieTimeoutIndex][1]); + removeIndexIfExists(setVisitorCookieTimeoutIndex); + } else if (matomoConfig.customCookieTimeOutEnable) { + tracker.setVisitorCookieTimeout(matomoConfig.customCookieTimeOut * 86400); + } + + if (setReferralCookieTimeoutIndex !== -1 && localPaq[setReferralCookieTimeoutIndex].length === 2) { + tracker.setReferralCookieTimeout(localPaq[setReferralCookieTimeoutIndex][1]); + removeIndexIfExists(setReferralCookieTimeoutIndex); + } else if (matomoConfig.customCookieTimeOutEnable) { + tracker.setReferralCookieTimeout(matomoConfig.referralCookieTimeOut * 86400); + } + + if (setSessionCookieTimeoutIndex !== -1 && localPaq[setSessionCookieTimeoutIndex].length === 2) { + tracker.setSessionCookieTimeout(localPaq[setSessionCookieTimeoutIndex][1]); + removeIndexIfExists(setSessionCookieTimeoutIndex); + } else if (matomoConfig.customCookieTimeOutEnable) { + tracker.setSessionCookieTimeout(matomoConfig.sessionCookieTimeOut * 60); + } + + if (matomoConfig.setSecureCookie || setSecureCookieIndex !== -1) { tracker.setSecureCookie(true); + removeIndexIfExists(setSecureCookieIndex); } - if (matomoConfig.cookiePath) { + if (cookiePathIndex !== -1 && localPaq[cookiePathIndex].length === 2) { + tracker.setCookiePath(localPaq[cookiePathIndex][1]); + removeIndexIfExists(cookiePathIndex); + } else if (matomoConfig.cookiePath) { tracker.setCookiePath(matomoConfig.cookiePath); } - if (matomoConfig.cookieNamePrefix) { - tracker.setCookieNamePrefix(matomoConfig.cookieNamePrefix); + if (cookieNamePrefixIndex !== -1 && localPaq[cookieNamePrefixIndex].length === 2) { + tracker.setCookiePath(localPaq[cookieNamePrefixIndex][1]); + removeIndexIfExists(cookieNamePrefixIndex); + } else if (matomoConfig.cookieNamePrefix) { + tracker.setCookiePath(matomoConfig.cookieNamePrefix); } - if (matomoConfig.cookieDomain) { + if (cookieDomainIndex !== -1 && localPaq[cookieDomainIndex].length === 2) { + tracker.setCookieDomain(localPaq[cookieDomainIndex][1]); + removeIndexIfExists(cookieDomainIndex); + } else if (matomoConfig.cookieDomain) { tracker.setCookieDomain(matomoConfig.cookieDomain); } + // If paq.push(['setDomains' has been called, override the Matomo config domains + if (setDomainsIndex !== -1 && localPaq[setDomainsIndex].length === 2) { + var domainsArray = localPaq[setDomainsIndex][1]; + // It's valid to provide a string if there's only one domain + if (typeof localPaq[setDomainsIndex][1] === 'string') { + domainsArray = [localPaq[setDomainsIndex][1]]; + } + if (TagManager.utils.isArray(domainsArray)) { + matomoConfig.domains = domainsArray; + } + removeIndexIfExists(setDomainsIndex); + } if (matomoConfig.domains && TagManager.utils.isArray(matomoConfig.domains) && matomoConfig.domains.length) { @@ -173,67 +387,110 @@ tracker.setDomains(domains); } - if (matomoConfig.alwaysUseSendBeacon) { + if (matomoConfig.alwaysUseSendBeacon || alwaysUseSendBeaconIndex !== -1) { tracker.alwaysUseSendBeacon(); + removeIndexIfExists(alwaysUseSendBeaconIndex); } - if (matomoConfig.disableAlwaysUseSendBeacon) { + if (matomoConfig.disableAlwaysUseSendBeacon || disableAlwaysUseSendBeaconIndex !== -1) { tracker.disableAlwaysUseSendBeacon(); + removeIndexIfExists(disableAlwaysUseSendBeaconIndex); } - - if (matomoConfig.forceRequestMethod) { + + if (appendToTrackingUrlIndex !== -1 && localPaq[appendToTrackingUrlIndex].length === 2 + && typeof localPaq[appendToTrackingUrlIndex][1] === 'string' + && ['POST', 'GET'].includes(localPaq[appendToTrackingUrlIndex][1].toUpperCase())) { + tracker.setRequestMethod(localPaq[appendToTrackingUrlIndex][1]); + if (localPaq[appendToTrackingUrlIndex][1].toUpperCase() === 'POST' + && setRequestContentTypeIndex !== -1 && localPaq[setRequestContentTypeIndex].length === 2 + && typeof localPaq[setRequestContentTypeIndex][1] === 'string' && localPaq[setRequestContentTypeIndex][1].length > 0) { + tracker.setRequestContentType(localPaq[setRequestContentTypeIndex][1]); + removeIndexIfExists(setRequestContentTypeIndex); + } + removeIndexIfExists(appendToTrackingUrlIndex); + } else if (matomoConfig.forceRequestMethod && typeof matomoConfig.requestMethod === 'string' + && ['POST', 'GET'].includes(matomoConfig.requestMethod.toUpperCase())) { tracker.setRequestMethod(matomoConfig.requestMethod); - if(matomoConfig.requestMethod == 'POST'){ + if(matomoConfig.requestMethod.toUpperCase() === 'POST'){ tracker.setRequestContentType(matomoConfig.requestContentType); } } - if (matomoConfig.enableLinkTracking) { + if (matomoConfig.enableLinkTracking || enableLinkTrackingIndex !== -1) { tracker.enableLinkTracking(); + removeIndexIfExists(enableLinkTrackingIndex); } - if (matomoConfig.enableFileTracking) { + if (matomoConfig.enableFileTracking || enableFileTrackingIndex !== -1) { tracker.enableFileTracking(); + removeIndexIfExists(enableFileTrackingIndex); } - if (matomoConfig.requireConsent) { + if (matomoConfig.requireConsent || requireConsentIndex !== -1) { tracker.requireConsent(); + removeIndexIfExists(requireConsentIndex); } - if (matomoConfig.enableDoNotTrack) { + if (matomoConfig.enableDoNotTrack || enableDoNotTrackIndex !== -1) { tracker.setDoNotTrack(1); + removeIndexIfExists(enableDoNotTrackIndex); } - if (matomoConfig.disablePerformanceTracking) { + if (matomoConfig.disablePerformanceTracking || disablePerformanceTrackingIndex !== -1) { tracker.disablePerformanceTracking(); + removeIndexIfExists(disablePerformanceTrackingIndex); } - - if (typeof matomoConfig.appendToTrackingUrl === 'string' && matomoConfig.appendToTrackingUrl.length > 0) { + + if (appendToTrackingUrlIndex !== -1 && localPaq[appendToTrackingUrlIndex].length === 2 + && typeof localPaq[appendToTrackingUrlIndex][1] === 'string' && localPaq[appendToTrackingUrlIndex][1].length > 0) { + tracker.appendToTrackingUrl(localPaq[appendToTrackingUrlIndex][1]); + removeIndexIfExists(appendToTrackingUrlIndex); + } else if (typeof matomoConfig.appendToTrackingUrl === 'string' && matomoConfig.appendToTrackingUrl.length > 0) { tracker.appendToTrackingUrl(matomoConfig.appendToTrackingUrl); } - - if(typeof matomoConfig.customRequestProcessing === 'function' - && matomoConfig.customRequestProcessing.length >= 1 ) { + + if (setCustomRequestProcessingIndex !== -1 && localPaq[setCustomRequestProcessingIndex].length === 2 + && typeof localPaq[setCustomRequestProcessingIndex][1] === 'function' && localPaq[setCustomRequestProcessingIndex][1].length >= 1) { + tracker.setCustomRequestProcessing(localPaq[setCustomRequestProcessingIndex][1]); + removeIndexIfExists(setCustomRequestProcessingIndex); + } else if(typeof matomoConfig.customRequestProcessing === 'function' && matomoConfig.customRequestProcessing.length >= 1) { tracker.setCustomRequestProcessing(matomoConfig.customRequestProcessing); } - if (matomoConfig.enableJSErrorTracking) { + if (matomoConfig.enableJSErrorTracking || enableJSErrorTrackingIndex !== -1) { tracker.enableJSErrorTracking(); + removeIndexIfExists(enableJSErrorTrackingIndex); } - if (matomoConfig.enableHeartBeatTimer) { - tracker.enableHeartBeatTimer(matomoConfig.heartBeatTime); + + if (matomoConfig.enableHeartBeatTimer || enableHeartBeatTimerIndex !== -1) { + if (enableHeartBeatTimerIndex !== -1 && localPaq[enableHeartBeatTimerIndex].length === 2) { + tracker.enableHeartBeatTimer(localPaq[enableHeartBeatTimerIndex][1]); + } else if (matomoConfig.heartBeatTime) { + tracker.enableHeartBeatTimer(matomoConfig.heartBeatTime); + } else { + tracker.enableHeartBeatTimer(); + } + removeIndexIfExists(enableHeartBeatTimerIndex); } - if (matomoConfig.trackAllContentImpressions) { + + if (matomoConfig.trackAllContentImpressions || trackAllContentImpressionsIndex !== -1) { tracker.trackAllContentImpressions(); + removeIndexIfExists(trackAllContentImpressionsIndex); } - if (matomoConfig.trackVisibleContentImpressions) { + + if (matomoConfig.trackVisibleContentImpressions || trackVisibleContentImpressionsIndex !== -1) { tracker.trackVisibleContentImpressions(); + removeIndexIfExists(trackVisibleContentImpressionsIndex); } - if (matomoConfig.hasOwnProperty('enableFormAnalytics') && !matomoConfig.enableFormAnalytics && window.Matomo && window.Matomo.FormAnalytics && typeof window.Matomo.FormAnalytics.disableFormAnalytics === 'function') { + + if (((matomoConfig.hasOwnProperty('enableFormAnalytics') && !matomoConfig.enableFormAnalytics) || disableFormAnalyticsIndex !== -1) && window.Matomo && window.Matomo.FormAnalytics && typeof window.Matomo.FormAnalytics.disableFormAnalytics === 'function') { window.Matomo.FormAnalytics.disableFormAnalytics(); + removeIndexIfExists(disableFormAnalyticsIndex); } - if (matomoConfig.hasOwnProperty('enableMediaAnalytics') && !matomoConfig.enableMediaAnalytics && window.Matomo && window.Matomo.MediaAnalytics && typeof window.Matomo.MediaAnalytics.disableMediaAnalytics === 'function') { + + if (((matomoConfig.hasOwnProperty('enableMediaAnalytics') && !matomoConfig.enableMediaAnalytics) || disableMediaAnalyticsIndex !== -1) && window.Matomo && window.Matomo.MediaAnalytics && typeof window.Matomo.MediaAnalytics.disableMediaAnalytics === 'function') { window.Matomo.MediaAnalytics.disableMediaAnalytics(); + removeIndexIfExists(disableMediaAnalyticsIndex); } } @@ -257,6 +514,23 @@ lastIdSite = possiblyUpdatedMatomoUrl; } + // Add any custom dimensions added to _paq to the collection + if (setCustomDimensionIndexes.length) { + if (!matomoConfig.customDimensions + || !TagManager.utils.isArray(matomoConfig.customDimensions)) { + matomoConfig.customDimensions = []; + } + for (indexIndex = 0; indexIndex < setCustomDimensionIndexes.length; indexIndex++) { + var customDim = localPaq[setCustomDimensionIndexes[indexIndex]]; + if (TagManager.utils.isArray(customDim) && customDim.length === 3) { + matomoConfig.customDimensions.push({ + index: customDim[1], + value: customDim[2], + }); + } + removeIndexIfExists(setCustomDimensionIndexes[indexIndex]); + } + } if (matomoConfig.customDimensions && TagManager.utils.isArray(matomoConfig.customDimensions) && matomoConfig.customDimensions.length) { @@ -269,6 +543,28 @@ } } + // Sort the indexes greatest to least so that removing one won't affect the others + indexesToRemove.sort().reverse(); + var arrayLength = indexesToRemove.length; + for (indexRemove = 0; indexRemove < arrayLength; indexRemove++) { + localPaq.splice(indexesToRemove[indexRemove], 1); + } + + // Keep a list of all of the non-config requests to process later + if (localPaq.length && !remainingPaq.length) { + remainingPaq = localPaq; + } + + // If the remaining _paq values haven't been processed yet, process them + // We wait till now so that all configs are applied first + var applyRemainingPaqEntries = parameters.get('applyRemainingPaqEntries', false); + if (!hasProcessedRemainingTrackings && remainingPaq.length && applyRemainingPaqEntries) { + hasProcessedRemainingTrackings = true; + for (trackingIndex = 0; trackingIndex < remainingPaq.length; trackingIndex++) { + window._paq.push(remainingPaq[trackingIndex]); + } + } + if (tracker) { var trackingType = parameters.get('trackingType'); @@ -280,16 +576,11 @@ var customUrl = parameters.get('customUrl'); if (customUrl) { tracker.setCustomUrl(customUrl); - } - if (matomoConfig.customCookieTimeOutEnable) { - tracker.setVisitorCookieTimeout(matomoConfig.customCookieTimeOut * 86400); - tracker.setReferralCookieTimeout(matomoConfig.referralCookieTimeOut * 86400); - tracker.setSessionCookieTimeout(matomoConfig.sessionCookieTimeOut * 60); } if (parameters.get('isEcommerceView')) { tracker.setEcommerceView(parameters.get('productSKU'), parameters.get('productName'), parameters.get('categoryName'), parameters.get('price')); - } + } tracker.trackPageView(); } else if (trackingType === 'event') { diff --git a/javascripts/tagmanager.js b/javascripts/tagmanager.js index 3fb12c661..b051fb43a 100644 --- a/javascripts/tagmanager.js +++ b/javascripts/tagmanager.js @@ -1675,8 +1675,28 @@ } if (container.tags && utils.isArray(container.tags)) { + // Build a collection of the names of all the Matomo configs + var hasMatomoTag = false; + var matomoConfigNames = []; for (i = 0; i < container.tags.length; i++) { tagDefinition = container.tags[i]; + if (tagDefinition.type === 'Matomo' && matomoConfigNames.indexOf(tagDefinition.parameters.matomoConfig.name) === -1) { + hasMatomoTag = true; + matomoConfigNames.push(tagDefinition.parameters.matomoConfig.name); + } + } + for (i = 0; i < container.tags.length; i++) { + tagDefinition = container.tags[i]; + // If the Matomo config name is in the list, remove it + if (hasMatomoTag && tagDefinition.parameters.matomoConfig && matomoConfigNames.indexOf(tagDefinition.parameters.matomoConfig.name) !== -1) { + matomoConfigNames.splice(tagDefinition.parameters.matomoConfig.name, 1); + } + // If all the names have been removed, that means all configs are found + // If configs are ready, set tag ready to apply all postponed _paq entries + // This is because we postpone _paq entries to apply them on all trackers + if (hasMatomoTag && matomoConfigNames.length === 0) { + tagDefinition.parameters.applyRemainingPaqEntries = true; + } tag = new Tag(tagDefinition, this); this.tags.push(tag); diff --git a/javascripts/tagmanager.min.js b/javascripts/tagmanager.min.js index 34236963c..a7e4d6eb5 100644 --- a/javascripts/tagmanager.min.js +++ b/javascripts/tagmanager.min.js @@ -33,10 +33,11 @@ L=undefined}if((!B.isDefined(L)||L===null||L===false)&&B.isDefined(this.defaultV }}})}};this.addReferencedTag=function(i){this.referencedTags.push(i)};this.getReferencedTags=function(){return this.referencedTags};this.meetsConditions=function(){var L,M;for(L=0;L0;if(L){var G,J;G=H.split("::");J=G[0];H=G[1];if("object"===typeof l[J]&&B.isFunction(l[J][H])){l[J][H].apply(l[J],M)}}else{if(H&&H in l&&B.isFunction(l[H])){l[H].apply(l,M)}else{y.error("method "+H+" is not valid")}}}}B.setMethodWrapIfNeeded(a._mtm,"push",g);var v;for(v=0;v0;if(L){var G,J;G=H.split("::");J=G[0];H=G[1];if("object"===typeof l[J]&&B.isFunction(l[J][H])){l[J][H].apply(l[J],M)}}else{if(H&&H in l&&B.isFunction(l[H])){l[H].apply(l,M)}else{y.error("method "+H+" is not valid")}}}}B.setMethodWrapIfNeeded(a._mtm,"push",g);var v;for(v=0;v + + + + + + + + + + +
+

Hello World!

+ +
+ + \ No newline at end of file From 0e799d7a55978428e104efd4ea90d2535b083514 Mon Sep 17 00:00:00 2001 From: Jacob Ransom Date: Wed, 22 Nov 2023 16:03:32 +1300 Subject: [PATCH 2/6] Adding test HTML I've been trying --- .../resources/anotherTrackerTestExample.html | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 tests/resources/anotherTrackerTestExample.html diff --git a/tests/resources/anotherTrackerTestExample.html b/tests/resources/anotherTrackerTestExample.html new file mode 100644 index 000000000..91a171fea --- /dev/null +++ b/tests/resources/anotherTrackerTestExample.html @@ -0,0 +1,58 @@ + + + + + + + + + + +
+

Hello World!

+
+

Loading JS...

+ + From 7c74cfd96458fd7e89d6b4d7b29798abf1028b1e Mon Sep 17 00:00:00 2001 From: Jacob Ransom Date: Thu, 23 Nov 2023 12:40:48 +1300 Subject: [PATCH 3/6] Updating example to how I reproduced some intermittent tracking loss --- .../resources/anotherTrackerTestExample.html | 68 ++++++++++++------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/tests/resources/anotherTrackerTestExample.html b/tests/resources/anotherTrackerTestExample.html index 91a171fea..c343ca61c 100644 --- a/tests/resources/anotherTrackerTestExample.html +++ b/tests/resources/anotherTrackerTestExample.html @@ -1,5 +1,12 @@ + + + + + + + + +

Hello World!

-

Loading JS...

+
+
+
+
+
From a71fb684de7d311bd3f228d41fe4f77a3b345bd3 Mon Sep 17 00:00:00 2001 From: Jacob Ransom Date: Tue, 5 Dec 2023 12:47:33 +1300 Subject: [PATCH 4/6] Fixed issue with array management and cleaned up --- Template/Tag/MatomoTag.web.js | 103 ++++++++++++++++------------------ 1 file changed, 47 insertions(+), 56 deletions(-) diff --git a/Template/Tag/MatomoTag.web.js b/Template/Tag/MatomoTag.web.js index a5803aab2..8f06361c2 100644 --- a/Template/Tag/MatomoTag.web.js +++ b/Template/Tag/MatomoTag.web.js @@ -19,9 +19,8 @@ } // Store the initial state of window._paq so that we can apply it to all the configs // We use the stringify and parse to make sure that we have a copy and not a reference - var initialPaq = window._paq && window._paq.length ? JSON.parse(JSON.stringify(window._paq)) : initialPaq || []; - var remainingPaq = []; - var indexesToRemove = []; + var localPaq = window._paq && window._paq.length ? JSON.parse(JSON.stringify(window._paq)) : localPaq || []; + var indexesOfConfigs = []; // Clear window._paq to prevent things from being tracked too early while (window._paq.length > 0) { window._paq.pop(); @@ -83,13 +82,13 @@ } } - function removeIndexIfExists(index) + function markIndexAsConfig(index) { if (index < 0) { return; } - indexesToRemove.push(index); + indexesOfConfigs.push(index); } var configuredTrackers = {}; @@ -119,8 +118,7 @@ var variableName = parameters.matomoConfig.name; var setCustomDimensionIndexes = []; - indexesToRemove = []; - var localPaq = JSON.parse(JSON.stringify(initialPaq)); + indexesOfConfigs = []; // we need to fetch matomoConfig again in case some parameters changed meanwhile that are variables... // eg userId might be a variable and it's value might be different now @@ -171,17 +169,17 @@ case 'setUserId': setUserIdIndex = k; // Mark this one for removal right away since we don't want it to override the container - removeIndexIfExists(k); + markIndexAsConfig(k); break; case 'setSiteId': setSiteIdIndex = k; // Mark this one for removal right away since we don't want it to override the container - removeIndexIfExists(k); + markIndexAsConfig(k); break; case 'setTrackerUrl': setTrackerUrlIndex = k; // Mark this one for removal right away since we don't want it to override the container - removeIndexIfExists(k); + markIndexAsConfig(k); break; case 'requireCookieConsent': requireCookieConsentIndex = k; @@ -285,74 +283,74 @@ if (matomoConfig.requireCookieConsent || requireCookieConsentIndex !== -1) { tracker.requireCookieConsent(); - removeIndexIfExists(requireCookieConsentIndex); + markIndexAsConfig(requireCookieConsentIndex); } if ((matomoConfig.disableBrowserFeatureDetection || disableBrowserFeatureDetectionIndex !== -1) && typeof tracker.disableBrowserFeatureDetection === 'function') { tracker.disableBrowserFeatureDetection(); - removeIndexIfExists(disableBrowserFeatureDetectionIndex); + markIndexAsConfig(disableBrowserFeatureDetectionIndex); } if (matomoConfig.disableCookies || disableCookiesIndex !== -1) { tracker.disableCookies(); - removeIndexIfExists(disableCookiesIndex); + markIndexAsConfig(disableCookiesIndex); } if (matomoConfig.enableCrossDomainLinking || enableCrossDomainLinkingIndex !== -1) { tracker.enableCrossDomainLinking(); - removeIndexIfExists(enableCrossDomainLinkingIndex); + markIndexAsConfig(enableCrossDomainLinkingIndex); } if (cookieSameSiteIndex !== -1 && localPaq[cookieSameSiteIndex].length === 2) { tracker.setCookieSameSite(localPaq[cookieSameSiteIndex][1]); - removeIndexIfExists(cookieSameSiteIndex); + markIndexAsConfig(cookieSameSiteIndex); } else if (matomoConfig.cookieSameSite) { tracker.setCookieSameSite(matomoConfig.cookieSameSite); } if (setVisitorCookieTimeoutIndex !== -1 && localPaq[setVisitorCookieTimeoutIndex].length === 2) { tracker.setVisitorCookieTimeout(localPaq[setVisitorCookieTimeoutIndex][1]); - removeIndexIfExists(setVisitorCookieTimeoutIndex); + markIndexAsConfig(setVisitorCookieTimeoutIndex); } else if (matomoConfig.customCookieTimeOutEnable) { tracker.setVisitorCookieTimeout(matomoConfig.customCookieTimeOut * 86400); } if (setReferralCookieTimeoutIndex !== -1 && localPaq[setReferralCookieTimeoutIndex].length === 2) { tracker.setReferralCookieTimeout(localPaq[setReferralCookieTimeoutIndex][1]); - removeIndexIfExists(setReferralCookieTimeoutIndex); + markIndexAsConfig(setReferralCookieTimeoutIndex); } else if (matomoConfig.customCookieTimeOutEnable) { tracker.setReferralCookieTimeout(matomoConfig.referralCookieTimeOut * 86400); } if (setSessionCookieTimeoutIndex !== -1 && localPaq[setSessionCookieTimeoutIndex].length === 2) { tracker.setSessionCookieTimeout(localPaq[setSessionCookieTimeoutIndex][1]); - removeIndexIfExists(setSessionCookieTimeoutIndex); + markIndexAsConfig(setSessionCookieTimeoutIndex); } else if (matomoConfig.customCookieTimeOutEnable) { tracker.setSessionCookieTimeout(matomoConfig.sessionCookieTimeOut * 60); } if (matomoConfig.setSecureCookie || setSecureCookieIndex !== -1) { tracker.setSecureCookie(true); - removeIndexIfExists(setSecureCookieIndex); + markIndexAsConfig(setSecureCookieIndex); } if (cookiePathIndex !== -1 && localPaq[cookiePathIndex].length === 2) { tracker.setCookiePath(localPaq[cookiePathIndex][1]); - removeIndexIfExists(cookiePathIndex); + markIndexAsConfig(cookiePathIndex); } else if (matomoConfig.cookiePath) { tracker.setCookiePath(matomoConfig.cookiePath); } if (cookieNamePrefixIndex !== -1 && localPaq[cookieNamePrefixIndex].length === 2) { tracker.setCookiePath(localPaq[cookieNamePrefixIndex][1]); - removeIndexIfExists(cookieNamePrefixIndex); + markIndexAsConfig(cookieNamePrefixIndex); } else if (matomoConfig.cookieNamePrefix) { tracker.setCookiePath(matomoConfig.cookieNamePrefix); } if (cookieDomainIndex !== -1 && localPaq[cookieDomainIndex].length === 2) { tracker.setCookieDomain(localPaq[cookieDomainIndex][1]); - removeIndexIfExists(cookieDomainIndex); + markIndexAsConfig(cookieDomainIndex); } else if (matomoConfig.cookieDomain) { tracker.setCookieDomain(matomoConfig.cookieDomain); } @@ -367,7 +365,7 @@ if (TagManager.utils.isArray(domainsArray)) { matomoConfig.domains = domainsArray; } - removeIndexIfExists(setDomainsIndex); + markIndexAsConfig(setDomainsIndex); } if (matomoConfig.domains && TagManager.utils.isArray(matomoConfig.domains) @@ -389,12 +387,12 @@ if (matomoConfig.alwaysUseSendBeacon || alwaysUseSendBeaconIndex !== -1) { tracker.alwaysUseSendBeacon(); - removeIndexIfExists(alwaysUseSendBeaconIndex); + markIndexAsConfig(alwaysUseSendBeaconIndex); } if (matomoConfig.disableAlwaysUseSendBeacon || disableAlwaysUseSendBeaconIndex !== -1) { tracker.disableAlwaysUseSendBeacon(); - removeIndexIfExists(disableAlwaysUseSendBeaconIndex); + markIndexAsConfig(disableAlwaysUseSendBeaconIndex); } if (appendToTrackingUrlIndex !== -1 && localPaq[appendToTrackingUrlIndex].length === 2 @@ -405,9 +403,9 @@ && setRequestContentTypeIndex !== -1 && localPaq[setRequestContentTypeIndex].length === 2 && typeof localPaq[setRequestContentTypeIndex][1] === 'string' && localPaq[setRequestContentTypeIndex][1].length > 0) { tracker.setRequestContentType(localPaq[setRequestContentTypeIndex][1]); - removeIndexIfExists(setRequestContentTypeIndex); + markIndexAsConfig(setRequestContentTypeIndex); } - removeIndexIfExists(appendToTrackingUrlIndex); + markIndexAsConfig(appendToTrackingUrlIndex); } else if (matomoConfig.forceRequestMethod && typeof matomoConfig.requestMethod === 'string' && ['POST', 'GET'].includes(matomoConfig.requestMethod.toUpperCase())) { tracker.setRequestMethod(matomoConfig.requestMethod); @@ -418,33 +416,33 @@ if (matomoConfig.enableLinkTracking || enableLinkTrackingIndex !== -1) { tracker.enableLinkTracking(); - removeIndexIfExists(enableLinkTrackingIndex); + markIndexAsConfig(enableLinkTrackingIndex); } if (matomoConfig.enableFileTracking || enableFileTrackingIndex !== -1) { tracker.enableFileTracking(); - removeIndexIfExists(enableFileTrackingIndex); + markIndexAsConfig(enableFileTrackingIndex); } if (matomoConfig.requireConsent || requireConsentIndex !== -1) { tracker.requireConsent(); - removeIndexIfExists(requireConsentIndex); + markIndexAsConfig(requireConsentIndex); } if (matomoConfig.enableDoNotTrack || enableDoNotTrackIndex !== -1) { tracker.setDoNotTrack(1); - removeIndexIfExists(enableDoNotTrackIndex); + markIndexAsConfig(enableDoNotTrackIndex); } if (matomoConfig.disablePerformanceTracking || disablePerformanceTrackingIndex !== -1) { tracker.disablePerformanceTracking(); - removeIndexIfExists(disablePerformanceTrackingIndex); + markIndexAsConfig(disablePerformanceTrackingIndex); } if (appendToTrackingUrlIndex !== -1 && localPaq[appendToTrackingUrlIndex].length === 2 && typeof localPaq[appendToTrackingUrlIndex][1] === 'string' && localPaq[appendToTrackingUrlIndex][1].length > 0) { tracker.appendToTrackingUrl(localPaq[appendToTrackingUrlIndex][1]); - removeIndexIfExists(appendToTrackingUrlIndex); + markIndexAsConfig(appendToTrackingUrlIndex); } else if (typeof matomoConfig.appendToTrackingUrl === 'string' && matomoConfig.appendToTrackingUrl.length > 0) { tracker.appendToTrackingUrl(matomoConfig.appendToTrackingUrl); } @@ -452,14 +450,14 @@ if (setCustomRequestProcessingIndex !== -1 && localPaq[setCustomRequestProcessingIndex].length === 2 && typeof localPaq[setCustomRequestProcessingIndex][1] === 'function' && localPaq[setCustomRequestProcessingIndex][1].length >= 1) { tracker.setCustomRequestProcessing(localPaq[setCustomRequestProcessingIndex][1]); - removeIndexIfExists(setCustomRequestProcessingIndex); + markIndexAsConfig(setCustomRequestProcessingIndex); } else if(typeof matomoConfig.customRequestProcessing === 'function' && matomoConfig.customRequestProcessing.length >= 1) { tracker.setCustomRequestProcessing(matomoConfig.customRequestProcessing); } if (matomoConfig.enableJSErrorTracking || enableJSErrorTrackingIndex !== -1) { tracker.enableJSErrorTracking(); - removeIndexIfExists(enableJSErrorTrackingIndex); + markIndexAsConfig(enableJSErrorTrackingIndex); } if (matomoConfig.enableHeartBeatTimer || enableHeartBeatTimerIndex !== -1) { @@ -470,27 +468,27 @@ } else { tracker.enableHeartBeatTimer(); } - removeIndexIfExists(enableHeartBeatTimerIndex); + markIndexAsConfig(enableHeartBeatTimerIndex); } if (matomoConfig.trackAllContentImpressions || trackAllContentImpressionsIndex !== -1) { tracker.trackAllContentImpressions(); - removeIndexIfExists(trackAllContentImpressionsIndex); + markIndexAsConfig(trackAllContentImpressionsIndex); } if (matomoConfig.trackVisibleContentImpressions || trackVisibleContentImpressionsIndex !== -1) { tracker.trackVisibleContentImpressions(); - removeIndexIfExists(trackVisibleContentImpressionsIndex); + markIndexAsConfig(trackVisibleContentImpressionsIndex); } if (((matomoConfig.hasOwnProperty('enableFormAnalytics') && !matomoConfig.enableFormAnalytics) || disableFormAnalyticsIndex !== -1) && window.Matomo && window.Matomo.FormAnalytics && typeof window.Matomo.FormAnalytics.disableFormAnalytics === 'function') { window.Matomo.FormAnalytics.disableFormAnalytics(); - removeIndexIfExists(disableFormAnalyticsIndex); + markIndexAsConfig(disableFormAnalyticsIndex); } if (((matomoConfig.hasOwnProperty('enableMediaAnalytics') && !matomoConfig.enableMediaAnalytics) || disableMediaAnalyticsIndex !== -1) && window.Matomo && window.Matomo.MediaAnalytics && typeof window.Matomo.MediaAnalytics.disableMediaAnalytics === 'function') { window.Matomo.MediaAnalytics.disableMediaAnalytics(); - removeIndexIfExists(disableMediaAnalyticsIndex); + markIndexAsConfig(disableMediaAnalyticsIndex); } } @@ -528,7 +526,7 @@ value: customDim[2], }); } - removeIndexIfExists(setCustomDimensionIndexes[indexIndex]); + markIndexAsConfig(setCustomDimensionIndexes[indexIndex]); } } if (matomoConfig.customDimensions @@ -543,25 +541,18 @@ } } - // Sort the indexes greatest to least so that removing one won't affect the others - indexesToRemove.sort().reverse(); - var arrayLength = indexesToRemove.length; - for (indexRemove = 0; indexRemove < arrayLength; indexRemove++) { - localPaq.splice(indexesToRemove[indexRemove], 1); - } - - // Keep a list of all of the non-config requests to process later - if (localPaq.length && !remainingPaq.length) { - remainingPaq = localPaq; - } - // If the remaining _paq values haven't been processed yet, process them // We wait till now so that all configs are applied first var applyRemainingPaqEntries = parameters.get('applyRemainingPaqEntries', false); - if (!hasProcessedRemainingTrackings && remainingPaq.length && applyRemainingPaqEntries) { + if (!hasProcessedRemainingTrackings && localPaq.length > indexesOfConfigs.length && applyRemainingPaqEntries) { hasProcessedRemainingTrackings = true; - for (trackingIndex = 0; trackingIndex < remainingPaq.length; trackingIndex++) { - window._paq.push(remainingPaq[trackingIndex]); + for (trackingIndex = 0; trackingIndex < localPaq.length; trackingIndex++) { + // Skip config items since they've already been processed + if (indexesOfConfigs.indexOf(trackingIndex) >= 0) { + continue; + } + + window._paq.push(localPaq[trackingIndex]); } } From ffbf8a9b34dba3a2d939970e7f28cfa3a9f38950 Mon Sep 17 00:00:00 2001 From: Jacob Ransom Date: Tue, 5 Dec 2023 12:50:50 +1300 Subject: [PATCH 5/6] Adding another test file --- .../resources/complexTrackerTestExample.html | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/resources/complexTrackerTestExample.html diff --git a/tests/resources/complexTrackerTestExample.html b/tests/resources/complexTrackerTestExample.html new file mode 100644 index 000000000..3a531651f --- /dev/null +++ b/tests/resources/complexTrackerTestExample.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + +
+

Hello World!

+
+
+
+
+
+
+ + From d03bd7af06fb8d8fadc016ac5df36288ebc8ce2d Mon Sep 17 00:00:00 2001 From: Jacob Ransom Date: Wed, 6 Dec 2023 15:56:59 +1300 Subject: [PATCH 6/6] Adding config back in that was accidentally removed --- Template/Tag/MatomoTag.web.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Template/Tag/MatomoTag.web.js b/Template/Tag/MatomoTag.web.js index 8f06361c2..5782283ba 100644 --- a/Template/Tag/MatomoTag.web.js +++ b/Template/Tag/MatomoTag.web.js @@ -147,7 +147,8 @@ // There might already be some configs missing from this list var setUserIdIndex = setSiteIdIndex = setTrackerUrlIndex = requireCookieConsentIndex = disableBrowserFeatureDetectionIndex - = disableCookiesIndex = enableCrossDomainLinkingIndex = cookieSameSiteIndex + = disableCookiesIndex = enableCrossDomainLinkingIndex + = setCrossDomainLinkingTimeoutIndex = cookieSameSiteIndex = setVisitorCookieTimeoutIndex = setReferralCookieTimeoutIndex = setSessionCookieTimeoutIndex = setSecureCookieIndex = cookiePathIndex = cookieNamePrefixIndex = cookieDomainIndex = setDomainsIndex @@ -193,6 +194,9 @@ case 'enableCrossDomainLinking': enableCrossDomainLinkingIndex = k; break; + case 'setCrossDomainLinkingTimeout': + setCrossDomainLinkingTimeoutIndex = k; + break; case 'cookieSameSite': cookieSameSiteIndex = k; break; @@ -301,6 +305,13 @@ markIndexAsConfig(enableCrossDomainLinkingIndex); } + if (setCrossDomainLinkingTimeoutIndex !== -1 && localPaq[setCrossDomainLinkingTimeoutIndex].length === 2) { + tracker.setCrossDomainLinkingTimeout(localPaq[setCrossDomainLinkingTimeoutIndex][1]); + markIndexAsConfig(setCrossDomainLinkingTimeoutIndex); + } else if (matomoConfig.enableCrossDomainLinking && matomoConfig.crossDomainLinkingTimeout) { + tracker.setCrossDomainLinkingTimeout(matomoConfig.crossDomainLinkingTimeout); + } + if (cookieSameSiteIndex !== -1 && localPaq[cookieSameSiteIndex].length === 2) { tracker.setCookieSameSite(localPaq[cookieSameSiteIndex][1]); markIndexAsConfig(cookieSameSiteIndex);