Do not understand google call for analytics (one line is wrong) - javascript

I'am just playing for learning reasons with some google code.
All world is using the following google-code for analytics.
If i look at this line:
i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments) }
it must be wrong for me.
i[r] is representing i['ga']
If I do not give the argument 'ga' to the call I will get an error in the next line. That's means to me that the ||function(.. will never be called.
(function(i,s,o,g,r,a,m){ // r is the 'ga' argument
i['GoogleAnalyticsObject']=r;
i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments) }, //for what is this line?
i[r].l=1*new Date();
a=s.createElement(o),
m=s.getElementsByTagName(o)[0];
a.async=1;
a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
I can't believe that this is an issue by google. Is there something i do not know?

For me, it helps to "unminify" things a little, so if you substitute the parameters values in, this
i[r]=i[r] || function(){
(i[r].q=i[r].q||[]).push(arguments)
},
becomes this
window.ga = window.ga || function(){
(window.ga.q = window.ga.q || []).push(arguments)
},
And if you replace all the minification tricks, you get this:
// check whether the Analytics object is defined
if (!('ga' in window)){
// Analytics object not defined, so define it
window.ga = function(){
// Add tasks to the queue
window.ga.q.push(arguments);
};
Hope this helps.

Related

How to send a ga command to a tracker with no name?

We have a custom Google Analytics plugin that will be sending commands to track custom pageviews and log events. Here's a simplified repro of our scenario:
// Version 1: only works with analytics.js, not with GTM
function GaPlugin(tracker, config) {
// Simplified for SO, we actually use the config argument
// to track more interesting pages and events...
ga(tracker.get('name') + ".send", "pageview", "/virtual/page/view");
}
var ga = window[window["GoogleAnalyticsObject"] || "ga"];
if (typeof ga == "function") {
ga("provide", "myAnalytics", GaPlugin);
ga("require", "myAnalytics", { /* omitted for simplicity */ });
}
This works fine if analytics.js is included directly on the page. However, we're now including universal analytics on the page using the Google Tag Manager. Thus we ran into the dreaded error...
Command ignored. Unknown target: undefined
...as seen in the Google Analytics debugging Chrome plugin when the require command is being executed.
This question and this blogpost imply the need to set a tracker name (in GTM, using: Edit Tag => More Settings => Advanced Configuration => Set Tracker Name checkbox) but the tooltip says "Use of named trackers is highly discouraged in GTM". So instead I changed things to this:
// Version 2: crashes with GTM because ga is loaded after this code
function GaPlugin(tracker, config) {
// Simplified for SO, we actually use the config argument
// to track more interesting pages and events...
ga(tracker.get('name') + ".send", "pageview", "/virtual/page/view");
}
var ga = window[window["GoogleAnalyticsObject"] || "ga"];
if (typeof ga == "function") {
ga("provide", "myAnalytics", GaPlugin);
ga(ga.getAll()[0].get("name") + ".require", "myAnalytics", { });
}
But now I'm met with an error because ga is undefined when the above executes, because GTM will load universal analytics asynchronously, where I want to run my code to bootstrap the plugin right away. This also means I cannot place a callback on the ga command queue, because again: it doesn't exist yet.
In essence, the order of things (I think) is now:
The GTM snippet starts loading (async).
My own javascript code runs.
GTM will start loading analytics (async).
Analytics is loaded and ready to be used.
The only workaround I could think of was this:
// Version 3: ugly workaround...
function GaPlugin(tracker, config) {
// Simplified for SO, we actually use the config argument
// to track more interesting pages and events...
ga(tracker.get('name') + ".send", "pageview", "/virtual/page/view");
}
var interval = setInterval(function() {
var ga = window[window["GoogleAnalyticsObject"] || "ga"];
if (typeof ga == "function" && typeof ga.getAll == "function") {
ga("provide", "myAnalytics", GaPlugin);
ga(ga.getAll()[0].get("name") + ".require", "myAnalytics", { });
clearInterval(interval);
}
}, 250);
Isn't there a better way to do this? The GTM documentation nor the GA Plugins documentation seems to have any info on this.
As a footnote, I just realized it might also work if I mimic the tracking snippet by creating ga as a command queue myself. But that also feels like a workaround, not a solution...
Not satisfying, but let me share my workaround as an answer nonetheless, as it is in fact the solution I'm using right now. It builds on the final footnote: mimicking what the Google Analytics bootstrapping snippet does.
I'm setting up the ga object, or at least its q property, myself:
function createGoogleAnalyticsQueueIfNeeded() {
// As a workaround for: http://stackoverflow.com/questions/40587544
// We mimick: https://developers.google.com/analytics/devguides/collection/analyticsjs/tracking-snippet-reference
var gaKey = window["GoogleAnalyticsObject"] || "ga";
var ga = window[gaKey] || function () {
(window[gaKey]["q"] = window[gaKey]["q"] || []).push(arguments);
};
window[gaKey] = ga;
return ga;
}
After calling the above, you can place a command on that queue which will be executed whenever GTM has finished loading GA (which also includes an optimization to run the plugin for all trackers):
var ga = createGoogleAnalyticsQueueIfNeeded();
ga(function() {
ga("provide", "myAnalytics", GaPlugin);
ga.getAll().forEach(function(t) {
ga(t.get("name") + ".require", "myAnalytics", { });
});
});
Et voila, the provide and .require calls now run whenever GA (loaded via GTM) hollers at the callback.

Google Scripts/Basic JavaScript - Issue fixed by debugger

I'm working on a Google Scripts add on for Google Sheets, but I'm trying to get the script working before I actually set it up on the sheet. The code below works fine if I set a breakpoint somewhere in the extractNumbers function. If I just execute the code without breakpoints, I get an error:
TypeError: Cannot call method "replace" of undefined. (line 36, file "")
Here's the code:
var myVar = phoneCheck("a1","a2","o1","o2");
Logger.log(myVar);
function phoneCheck(newCell,newHome,oldCell,oldHome) {
Logger.clear();
var newCell = extractNumbers(newCell);
var oldCell = extractNumbers(oldCell);
var newHome = extractNumbers(newHome);
var oldHome = extractNumbers(oldHome);
if (newCell === oldCell) {
return newCell;
exit;
} else if (newCell === oldHome && newHome === oldCell) {
return oldCell;
exit;
}
if (newCell === '' && oldCell !== '' ) {
return oldCell;
exit;
}
if (newCell !== oldCell && newCell !== oldHome) {
return newCell;
exit;
}
return "No value found";
exit;
}
function extractNumbers(input) {
Logger.log(input);
var str = input;
return str.replace( /\D+/g, '');
}
Now I realize my if/then logic is more than a bit inelegant, but for my purposes, quick and dirty is fine. I just need it to run.
ALSO, I have read of other novice JavaScript programmers having similar issues related to the sequence of code execution. If someone would like to link to a concise source aimed at a non-advanced audience, that would be great too. Thanks!
EDIT: I put my code into a new fiddle and it works fine, but it continues to fail in Google Scripts editor unless running in debug mode with a breakpoint. The problem seems to be that the function parameters aren't available to the function unless there is a breakpoint. Anyone have access to Google Scripts that can try my updated code from https://jsfiddle.net/hrzqg64L/ ?
None of the suggestions got to the root of your problem - and neither did your answer, although you've avoided the problem by putting an enclosure around everything.
There's no AJAX, no asynchronous behavior - it's simpler than that. "Shadowing of parameters" is likewise a red herring. Bad coding practice, yes - but not a factor here.
If someone would like to link to a concise source aimed at a non-advanced audience, that would be great too.
Sorry - no such thing. I can explain what's going on, but can't guarantee it will be accessible to novices.
The exception
Let's just clarify what causes the exception, or thrown error, that you've observed.
As written, extractNumbers() will throw an exception if it has a null parameter (or any non-string parameter) passed to it. If you choose to extractNumbers() then hit "run", you'll get:
TypeError: Cannot call method "replace" of undefined. (line 36, file "")
That is telling you that on line 36, which is return str.replace( /\D+/g, '');, the variable str contains an object that is undefined (...and has no replace() method).
For bullet-proof code, you would check your parameter(s) to ensure they are valid, and handle them appropriately. Sometimes that would be with a valid default, and other times you might return an error or throw an exception that is more explicit about the parameter problems.
Running code in Google's debugger
The only way to run code in Google's Debugger is to select a function, then choose "run" or "debug". Assuming you posted all your code, you had just two functions to choose from:
phoneCheck()
extractNumbers()
Whenever Google Apps Script runs any part of a script, the entire script is loaded and scanned to find all symbols & check syntax. The scope of all symbols is noted as well, and so are any dependencies between functions and global symbols (symbols outside of any closure, or block of code).
That takes some time. To speed things up when asked to execute a specific function, the global symbols are only evaluated if they are a dependency for the requested function or the functions it may call. There is another condition that will trigger evaluation of global symbols, and that is if there is a possibility that the debugger may need to stop and display values.
When this happens, any code that is outside a closure (outside a function, for example) will be executed. This is what you observed when you set breakpoints.
Why did it work when breakpoints were set?
As explained, just having a breakpoint set triggers evaluation of global symbols.
You start this script with a few lines of code that are not in any closure:
var myVar = phoneCheck("a1","a2","o1","o2");
Logger.log(myVar);
It is that code which makes the only proper invocation of phoneCheck() with parameters. Because myVar is evaluated, phoneCheck() gets called with parameters, and in turn calls extractNumbers() with a defined parameter.
Unfortunately, because of the way the debugger works, you cannot choose to run that code yourself. You need to rely on these side-effect behaviors.
How to fix this?
Simple. Don't rely on global code to invoke functions under test. Instead, write an explicit test function, and call that.
function test_phoneCheck() {
var myVar = phoneCheck("a1","a2","o1","o2");
Logger.log(myVar);
}
Finally found the issue, but I don't fully understand it.
This question got me thinking about scope and how it might be different in the Google Script environment. I figured a simple workaround would be to enclose the entire script in its own void function, and it worked! Also, I simplified the script quite a bit with an array:
function init () {
var numberArray = ["a3", "a2", "o3", "o10"];
var myVar = phoneCheck(numberArray);
Logger.log(myVar);
function phoneCheck(myArray) {
var phoneString = '';
Logger.clear();
var arrayLength = myArray.length;
for (i = 0; i < arrayLength; i++) {
phoneString += myArray[i].replace(/\D+/g, '');
}
return phoneString;
}
}
Also, I realize the functionality of this script is different than the original, but I was really just trying to solve this problem. Now that I have, I can finish the script properly.
Thanks for all the suggestions, everyone! I learned a lot of good things, even though they turned out not to be the answer.

Get account ID from Google Analytics Tracking code on page

I am performing an audit using a custom web crawler of mine and was trying to garner the accountID for the legacy implementations of Google Analytics, but I cannot seem to get any of the JavaScript functions in _gaq to return the accountId in use. Does anybody know how to do this? All the documentation I've read really only mentions how to set variables, not how to get variables out once set for the purpose of auditing an implementation.
Thanks in advance
UPDATE
Thanks everyone! I wish I could check off all of your responses as good answers.
After a bit of testing, I have come up with the following function that handles pretty much every use case.
function getAccount() {
try {
if (_gaq) {
for (i = 0; i < _gaq.length; i++) {
if (_gaq[i][0] == "_setAccount") {
return _gaq[i][1]
}
}
}
if (_gat) {
if (_gat.fb) {
return _gat.fb
}
}
if (ga) {
return ga.getAll()[0].a.data.values[':trackingId']
}
} catch (e) { }
return ""
}
Joshua, this isn't a standard feature / get function that would be available.
You can however manually access ga object created by the tracking library.
If you for example open console for this webpage and type in:
ga.getAll()[0].a.data.values[':trackingId']
You will receive UA-5620270-1 which is probably the main Analytics Account for Stack Overflow. Similar approach should work in your case as well - and also any other attribute that's accessible:
Screen: http://fii.cz/sbdqevk
If you are prepared to accept a less than elegant solution you could use
function getAccount(_gaq) {
for (i = 0; i < _gaq.length; i++) {
if (_gaq[i][0] == "_setAccount") {
return _gaq[i][1];
}
}
}
_gaq is an array of arrays, the function simply loops through until it finds a subarray where the first element is _setAccount and returns the second element, which is the account number (_gaq.push(['_setAccount', 'UA-XXXXX-X']);).
As you are talking of legacy implementations, there are even older versions of the code so you might need more checks. The oldest version I could find on an active page was:
<script type="text/javascript">
_uacct = "UA-XXXXXXXX-X";
urchinTracker();
</script>
It's quite easy to get the Account Id from there. There is also the synchronous version of the code (sorry, can't find an example right now).
And for the current version look at Petrs answer.
According to documentation first you get all the trackers set on page
// returns an array with all the trackers
const trackers = ga.getAll();
And then, for each tracker, you can obtain the tracking id for each one getting the property
// returns the 'trackingId' propperty
const trackingId = tracker.get('trackingId'));
If you want to shorten it up, you could do
const trackingIds = ga.getAll().map(tracker => tracker.get('trackingId'));
Warning:
Remember to run this always after the ga ready.
Don't — use ga object methods outside a readyCallback as the methods
may not be available yet.
var trackers = ga.getAll();
Do — use ga object methods inside a
readyCallback as they're guaranteed to be available.
ga(function() {
var trackers = ga.getAll();
});
I just did it using an alternative method, as follows:
var _gaq = _gaq || [];
_gaq.push(function() {
var trackers, i;
trackers = _gat._getTrackers();
for (i = trackers.length - 1; i >= 0; i--) {
var account = trackers[i]._getAccount();
console.log("tracker account", account);
}
});

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

Weird Object Error in Javascript

First, I'm not a javascript developer so I don't have a great deal of experience in this if any.
I have a footer I'm inserting into an HTML page using jQuery that has the following code in it that per the client "NEEDS TO BE THERE".
<script language="JavaScript"><!--
/************* DO NOT ALTER ANYTHING BELOW THIS LINE ! **************/
var s_code=s.t();if(s_code)document.write(s_code)//--></script>
<script language="JavaScript"><!--
if(navigator.appVersion.indexOf('MSIE')>=0)document.write(unescape('%3C')+'\!-'+'-')
//--></script>
I don;t really have to worry about anything except this s.t(); line of code. I need to write a dummy Object or whatever else and include it in the header that doesn't do anything per se except prevent a javascript error from occurring.
So really I need "s" the object to be instantiated and have a function "t" attached to it that basically does nothing.
Any help is appreciated. This isn't something I want to do but given the budget and project constraints of the client I just need for this to work without a javascritp error.
thanks if you can help.
Using javascript prototype:
function s () {
}
function doSomething () {
}
s.prototype.t = doSomething;
edit: typo
var s = {
t: function(){}
};
See it in, hmm, action: http://jsbin.com/oboju
In case you're worried s is defined and don't want to override it, you can check for it first (this doesn't cover the case s.t is defined but isn't a function):
if(!s){ // check if s exists
var s = [];
}
if(!s.t){ // check if s has t
s.t = function(){};
}

Categories

Resources