Search code examples
javascriptgoogle-analyticsuniversal-analyticsevent-tracking

Immediately invoked functions with constructors / Universal Analytics


I'm trying to figure out how tracking / analytics scripts work. There's an optimised version of the Google Analytics code:

<script>
  (function(window, document, variableName, scriptElement, firstScript) {
    window['GoogleAnalyticsObject'] = variableName;
    window[variableName] || (window[variableName] = function() {
      (window[variableName].q = window[variableName].q || []).push(arguments);
    });
    window[variableName].l = +new Date;
    scriptElement = document.createElement('script'),
    firstScript = document.scripts[0];
    scriptElement.src = 'https://127.0.0.1:3000/analytics.js';
    firstScript.parentNode.insertBefore(scriptElement, firstScript)
  }(window, document, 'ga'));

  ga('create', 'UA-XXXX-Y');
  ga('send', 'pageview');
</script>

Loading a custom script, I can't figure out how the ga() functions work. I tried various IIFE and constructors already, but don't get the 'create' and 'send' events.

How do I see these events on the server?

update

I managed to abstract my way to the queue, now wondering how I can create an async queue to send these events to the server. Any suggestions?

(function() {
  var ga = function(a) {
    return void 0 != a && -1 < (a.constructor + '').indexOf('String');
  };
  var sa = function(a) {
    return a ? a.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '') : '';
  };
  var gb = ga(window.GoogleAnalyticsObject) && sa(window.GoogleAnalyticsObject) || 'ga';
  var Window = window;
  var Document = document;

  console.log(Window[gb].q);

})(window);

Solution

  • The function is merely pushing all the calls arguments into an array. It's later picked up by the analytics.js through window.GoogleAnalyticsObject. Google doesn't seem to provide a non-minified version of analytics.js but a quick debeautify and a search you end up with:

    var gb = qa(window.GoogleAnalyticsObject) && sa(window.GoogleAnalyticsObject) || "ga"
    

    The qa checks if it's a String and the sa function just clears the name a little bit.

    So, where else do they use gb? Assignments only happen somewhere else:

    The N.N function:

    N.N = function() {
            "ga" != gb && J(49);
            var a = O[gb];
            if (!a || 42 != a.answer) {
                N.L = a && a.l;
                N.loaded = !0;
                var b = O[gb] = N;
                X("create", b, b.create);
                X("remove", b, b.remove);
                X("getByName", b, b.j, 5);
                X("getAll", b, b.getAll, 6);
                b = pc.prototype;
                X("get", b, b.get, 7);
                X("set", b, b.set, 4);
                X("send", b, b.send);
                b = Ya.prototype;
                X("get", b, b.get);
                X("set", b, b.set);
                if (!Ud() && !Ba) {
                    a: {
                        for (var b = M.getElementsByTagName("script"), c = 0; c < b.length && 100 > c; c++) {
                            var d = b[c].src;
                            if (d && 0 == d.indexOf("https://www.google-analytics.com/analytics")) {
                                J(33);
                                b = !0;
                                break a
                            }
                        }
                        b = !1
                    }
                    b && (Ba = !0)
                }
                Ud() || Ba || !Ed(new Od) || (J(36), Ba = !0);
                (O.gaplugins = O.gaplugins || {}).Linker = Dc;
                b = Dc.prototype;
                Yd.set("linker", Dc);
                X("decorate", b, b.ca, 20);
                X("autoLink", b, b.S, 25);
                Yd.set("displayfeatures", fd);
                Yd.set("adfeatures", fd);
                a = a && a.q;
                ka(a) ? Z.D.apply(N, a) : J(50)
            }
        };
    

    Assignment happens on:

    var a = O[gb]; //O is window
    

    The ga function you are using on your script, will soon be replaced by something else (N):

    var b = O[gb] = N;
    

    Here's N:

    var N = function(a) {
            J(1);
            Z.D.apply(Z, [arguments])
        };
    

    Where is the queue being used?

    a = a && a.q;
    ka(a) ? Z.D.apply(N, a) : J(50)
    

    The Z.D function seems the one that's executing your arguments. Which uses more minified functions. I suggest you keep looking from here.