Skip to content

Commit

Permalink
Improve the analytics.js shim to handle more ways of using the ga API
Browse files Browse the repository at this point in the history
It turns out that a website can do something like

    ga(tracker => { ... });

and the function should be called. That wasn't handled yet, so let's
add the necessary logic from Mozilla's shim script[1] (but adjusted as
necessary).

It also turns out that websites sometimes have their own inline
definition of ga(), that assigns any call arguments to the ga.q
queue. The calls in the queue are supposed to be made with the real
ga() function, once that has been loaded. This also wasn't handled
yet in our shim.

I notice that we still do not handle further ways of using the ga API,
in the future it might be worth just switching to Mozilla's script
entirely.

1 - https://searchfox.org/mozilla-central/source/browser/extensions/webcompat/shims/google-analytics-and-tag-manager.js
  • Loading branch information
kzar committed Feb 15, 2024
1 parent ba0d8ce commit 84faaef
Showing 1 changed file with 42 additions and 17 deletions.
59 changes: 42 additions & 17 deletions surrogates/analytics.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Partially based on https://searchfox.org/mozilla-central/source/browser/extensions/webcompat/shims/google-analytics-and-tag-manager.js

(() => {
'use strict';
const noop = () => {};
Expand All @@ -8,17 +14,7 @@
};
const gaPointer = window.GoogleAnalyticsObject = (window.GoogleAnalyticsObject === undefined) ? 'ga' : window.GoogleAnalyticsObject;
const datalayer = window.dataLayer;
// execute callback if exists, see https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#hitCallback
const ga = function () {
const params = Array.from(arguments);
params.forEach((param) => {
if (param instanceof Object && typeof param.hitCallback === 'function') {
try {
param.hitCallback();
} catch (error) {}
}
});
};

const Tracker = new Proxy({}, {
get (target, prop) {
if (prop === 'get') {
Expand All @@ -36,6 +32,33 @@
return noop;
}
});

let callQueue = null;
if (window[gaPointer] && Array.isArray(window[gaPointer].q)) {
callQueue = window[gaPointer].q;
}

// Execute callback if exists.
// Note: There are other ways of using the API that aren't handled here yet.
const ga = function () {
const params = Array.from(arguments);

if (params.length === 1 && typeof params[0] === 'function') {
try {
params[0](Tracker);
} catch (error) {}
return undefined;
}

// See https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#hitCallback
params.forEach((param) => {
if (param instanceof Object && typeof param.hitCallback === 'function') {
try {
param.hitCallback();
} catch (error) {}
}
});
};
ga.answer = 42;
ga.loaded = true;
ga.create = function () { return new Proxy({}, noopHandler); };
Expand All @@ -50,12 +73,6 @@
} catch (error) {}
}

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// gaplugin wrapper based on Mozilla's Google analytics shim:
// https://searchfox.org/mozilla-central/source/browser/extensions/webcompat/shims/google-analytics-and-tag-manager.js
if (!(window.gaplugins && window.gaplugins.Linker)) {
window.gaplugins = window.gaplugins || {};
window.gaplugins.Linker = class {
Expand All @@ -68,4 +85,12 @@
passthrough () {}
};
}

if (callQueue) {
for (const args of callQueue) {
try {
ga(...args);
} catch (e) { }
}
}
})();

0 comments on commit 84faaef

Please sign in to comment.