views:

420

answers:

5

Unfortunately, this may not be a valid Code-Golf question as it is likely Javascript only; however, since this is likely to be the only useful-in-the-real-world code-golf contest I'm going to go ahead and post it.


The Google Analytics Asyncronous Tracking snippet is used by many websites.

The script goes a little something like this:

<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXX-X']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();

</script>

Winner will be determined by the shortest RAW DEFLATE (there is a difference between HTTP 1.1 DEFLATE (aka zlib) and RAW DEFLATE) compressed code by byte-count that will load and initialize Async Google Analytics on a page.

In the case of a tie, winner will be determined by raw character count. If it we still have a tie we'll decide by last edit/time submitted.

Some Rules:

  • The gaq || [] check is not required and should be removed.
  • must be protocol "aware" (http vs https).
  • must not pollute the global namespace (except for the _gaq var).
  • must be copy-pastable to any (X)HTML document, i.e., not dependent on the page's markup.
  • must work in all A-Grade browsers.
  • This does NOT have to pass JSLINT or any HTML validators.
  • must set the async flag.
  • must use this deflator for the byte count of the deflate-compressed output.

Tip:

  • Understand the basics of the DEFLATE algorithm. And more importantly, LZ77 compression.


UDPATE 216/275

Since my original version has been beaten I'll go ahead and post it here:
Note: this has a bug where async gets set to false for all "http" requests

(function(d,t,g){_gaq=[["_setAccount","UA-XXXXX-X"],["_trackPageview"]];g=d.createElement(t);g.src=(g.async=location.protocol[5]?"//ssl":"//www")+".google-analytics.com/ga.js";(t=d.getElementsByTagName(t)[0]).parentNode.insertBefore(g,t)})(document,"script")
+4  A: 

Updated with versions tested in FF3.6, Opera10, Chrome6, MSIE8:

194/270: with async, with getElementsByTagName cached

(_gaq=document.createElement('script')).src=(/^....s/.test(location)?'//ssl':'//www')+'.google-analytics.com/ga.js',(_gaq.a=_gaq.async=document.getElementsByTagName('script')[0]).parentNode.insertBefore(_gaq,_gaq.a),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']]

192/297: with async, no cache

(_gaq=document.createElement('script')).src=(/^....s/.test(location)?'//ssl':'//www')+'.google-analytics.com/ga.js',_gaq.async=document.getElementsByTagName('script')[0].parentNode.insertBefore(_gaq,document.getElementsByTagName('script')[0]),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']]

189/259: no async, with getElementsByTagName cached

(_gaq=document.createElement('script')).src=(/^....s/.test(location)?'//ssl':'//www')+'.google-analytics.com/ga.js',(_gaq.a=document.getElementsByTagName('script')[0]).parentNode.insertBefore(_gaq,_gaq.a),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']]

188/286: no async, no cache

(_gaq=document.createElement('script')).src=(/^....s/.test(location)?'//ssl':'//www')+'.google-analytics.com/ga.js',document.getElementsByTagName('script')[0].parentNode.insertBefore(_gaq,document.getElementsByTagName('script')[0]),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']]

184/242, no async, appendChild (no cache needed), unknown if it's supported everywhere

(_gaq=document.createElement('script')).src=(/^....s/.test(location)?'//ssl':'//www')+'.google-analytics.com/ga.js',document.getElementsByTagName('script')[0].parentNode.appendChild(_gaq),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']]

Credits:

  • casablanca: /^https/.test(location)
  • matyr: relative path, commas between statements, assignment to async
  • some: no anonymous function and usage of _gaq, non-cacheing of getElementsByTagName, move assignment of async, /^....s/
  • David Murdoch drop type="text/javascript"

See comments on this post for more information

Since this post now is community wiki and the accepted answer, I removed my first attempts (you can find them in the revision history if you are interested) and only have the latest revisions visible. See the comments on this post for more information. /some

some
Could probably save some bytes by using 1 instead of true, and if one could assume that the first script has the right type, one can use that type for the created script element.
some
Good start! Lots of improvements can be made here. My personal best is 216/275.
David Murdoch
@David Murdoch: Yeah, there is a lot to do. But when I decrease the original size, the deflated stays around 250... I run your code and that gave me 249! Well, I give it some more tries, now when I have seen that you updated the rules.
some
@some: `""+location` is a nice idea. You should still be able to use `[4]` instead of `charAt`.
casablanca
@casablanca: I could use [4] in every browser on the world, except... [drums] ...wait for it.... MSIE!
some
The code I pasted in the question is copy+paste from Google. It wouldn't have been any fun for anyone else If I pasted the version I already made. :-)
David Murdoch
@David Murdoch: I guessed it was the original code from Google. Here is a 190 byte version based on matyr: _gaq=document.createElement('script'),_gaq.async=_gaq.src=/s:/.test(location)?'//ssl.google-analytics.com/ga.js':'//google-analytics.com/ga.js',document.getElementsByTagName('script')[0].parentNode.insertBefore(_gaq,document.getElementsByTagName('script')[0]),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
some
@some, wrap the code in your comment with "`"s to get inline code formatting.
David Murdoch
@David Murdoch: Will do next time, didn't know that was possible. But I don't know if it really would make this code more readable ;)
some
189 (188 without the last semicolon): `(_gaq=document.createElement('script')).async=_gaq.src=/s:/.test(location)?'//ssl.google-analytics.com/ga.js':'//google-analytics.com/ga.js',document.getElementsByTagName('script')[0].parentNode.insertBefore(_gaq,document.getElementsByTagName('script')[0]),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
186 if the order of script's isn't important (using appendChild instead of insertBefore): `(_gaq=document.createElement('script')).async=_gaq.src=/s:/.test(location)?'//ssl.google-analytics.com/ga.js':'//google-analytics.com/ga.js',document.getElementsByTagName('script')[0].parentNode.appendChild(_gaq),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
171: by using documentElement (http://googlecode.blogspot.com/2009/12/google-analytics-launches-asynchronous.html): `(_gaq=document.createElement('script')).async=_gaq.src=/s:/.test(location)?'//ssl.google-analytics.com/ga.js':'//google-analytics.com/ga.js',document.documentElement.firstChild.appendChild(_gaq),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
By dropping async since it isn't necessary: 163: `(_gaq=document.createElement('script')).src=/s:/.test(location)?'//ssl.google-analytics.com/ga.js':'//google-analytics.com/ga.js',document.documentElement.firstChild.appendChild(_gaq),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
And with the safer https check: 165: `(_gaq=document.createElement('script')).src=/^https/.test(location)?'//ssl.google-analytics.com/ga.js':'//google-analytics.com/ga.js',document.documentElement.firstChild.appendChild(_gaq),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
@some: Nice work! The `www` shouldn't be dropped for cache reasons. The `document.documentElement.firstChild.appendChild(_gaq)` method fails in some cases on some browsers (http://addyosmani.com/links/2010/05/12/appendchild-vs-insertbefore/) which is why the Analytics team changed the recommended code back in February to use `prependChild`.
David Murdoch
@David Murdoch: I suspected that it was something like that. Well, without async it's 189, but with 195: `(_gaq=document.createElement('script')).async=_gaq.src=/^https/.test(location)?'//ssl.google-analytics.com/ga.js':'//www.google-analytics.com/ga.js',document.getElementsByTagName('script')[0].parentNode.insertBefore(_gaq,document.getElementsByTagName('script')[0]),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
194: `(_gaq=document.createElement('script')).src=/^https/.test(location)?'//ssl.google-analytics.com/ga.js':'//www.google-analytics.com/ga.js',_gaq.async=document.getElementsByTagName('script')[0].parentNode.insertBefore(_gaq,document.getElementsByTagName('script')[0]),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
@some, could you go ahead and edit your answer with the above script?
David Murdoch
197 (when using the expensive getElememebtsByTagName only once): `(_gaq=document.createElement('script')).src=/^https/.test(location)?'//ssl.google-analytics.com/ga.js':'//www.google-analytics.com/ga.js',(_gaq.async=_gaq.a=document.getElementsByTagName('script')[0]).parentNode.insertBefore(_gaq,_gaq.a),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
@David Murdoch: Updated the answer with the latest versions. Don't thing there is much more to do unless there is some other way to get the element where it should be inserted. I was playing around with setting null at insertBefore, but that makes it work like appendChild, and I'm not sure if it loads it async then.
some
193: One more byte! `(_gaq=document.createElement('script')).src=/^....s/.test(location)?'//ssl.google-analytics.com/ga.js':'//www.google-analytics.com/ga.js',_gaq.async=document.getElementsByTagName('script')[0].parentNode.insertBefore(_gaq,document.getElementsByTagName('script')[0]),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];`
some
I think we've got it as small as possible (when deflated) without causing problems in IE.
David Murdoch
@David Murdoch: I think so too. This was very fun! I have spent much more time than I should on this, but it was worth it! So, what are you going to do with the 56 bytes that you save on each request? :) And may I ask where you are going to use it? Could be nice to know.
some
I was looking at the new jquery mobile site and saw that it wasn't served compressed and it just got me thinking how silly that was. I often condense my HTML as well as compress it and jQuery team just didn't even seem to care. That got me thinking about what I can compress even further on sites I develop...and viola...this question ensues. The first site I may use it on will be ready in a few weeks...it should get 1000+ daily users within 3 months so we'll see how it pans out. I am hoping to get the full HTML source for the home page to fit in one TCP packet.
David Murdoch
@David Murdoch: Interesting! Now I better understand why. Good luck!
some
@David Murdoch: Using async as a variable to InsertBefore don't work in FF3.6 since it's not a string. (I didn't test it in the other browsers). Rewrote the whole answer with the latest versions.
some
@David Murdoch: Sorry for taking credit for `/^....s/` but my comment with that optimization was published 10 minutes before your edit, and appendChild 6h before your edit.
some
+2  A: 

Updated in accordance with updated rules:

Including the <script> tags: Deflated Length is: 226 bytes. Original length is: 298 bytes.

Without the <script> tags: Deflated Length is: 216 bytes. Original length is: 281 bytes.

<script>_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];(function(d,g){g=d.createElement('script');g.async=true;g.src='http'+(/^https/.test(location)?'s://ssl':'://www')+'.google-analytics.com/ga.js';d=d.getElementsByTagName('script')[0];d.parentNode.insertBefore(g,d);})(document);</script>

Throwing in @some's ideas (only checking location[4] and using 1 for true) saves 4 more bytes (212), but I can't take credit for that.

casablanca
hint: ` type="text/javascript"` isn't required.
David Murdoch
@David Murdoch: Updated as per your relaxed rules.
casablanca
@casablanca: Very nice! 212/271 is incredible.
some
Squeezed one more byte of of the compressed version... 211.
some
@Casablanca: Noticed that location[4] can't be used since it isn't a normal string.
some
@some: Oh yes, you're right about that. The regex might be better then, since it saves using `protocol` and it repeats `http` which is good for deflate.
casablanca
A: 

This is probably my best attempt, considering my inexperience with Javascript. Not much going on here:

_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
(function(d,e){e=d.createElement('script');e.id='s';e.async=1;
e.src='http'+('s'==d.location.protocol[5]?'s://ssl':'://www')+'.google-analytics.com/ga.js';
d=d.getElementByTagName('script');d.parentNode.insertBefore(e,d);})(document);

EDIT

Fixed the bugs @some talked about.

Deflated length: 219 bytes
Original length: 285 bytes

Michael Foukarakis
@Michael Foukarakis: Sorry to tell you that there are some errors in your code: p is global, document.protocol don't exist (at least not in firefox) and you can't get the id of element s since it hasn't been inserted in the dom yet.
some
But on the positive side, Deflated Length is: 211 bytes.Original length is: 265 bytes (remove line feed and the space after the comma in the array.
some
@some: You're right, I guess I need to study some more. :-)
Michael Foukarakis
I forgot to mention that [5] should be [4] if you want to test for 's'. And the sad thing is that you cant use [x] in msie, must use charat.
some
+2  A: 

(1) first attempt

_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']],function(s,g){g=document.createElement(s),g.async=g.src=(/^https/.test(location)?'//ssl.':'//')+'google-analytics.com/ga.js',s=document.getElementsByTagName(s)[0],s.parentNode.insertBefore(g,s)}('script')

Deflated Length is: 199 bytes. Original length is: 259 bytes. 23.17% savings

(2) (1) + some's 4th

_gaq=document.createElement('script'),_gaq.async=_gaq.src=(/^https/.test(location)?'//ssl.':'//')+'google-analytics.com/ga.js',document.getElementsByTagName('script')[0].parentNode.insertBefore(_gaq,document.getElementsByTagName('script')[0]),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']]

Deflated Length is: 192 bytes. Original length is: 297 bytes. 35.35% savings

matyr
Wow! No protocol and using commas instead of semicolon... and it still works in FF, Chrome, Opera AND MSIE! Wonderful! You should however put a semicolon at the end (makeing it 193 bytes), so that the code can be pasted anywhere (for example if the code is minified together with other code). I'm still surprised that it works without protocol in the script tag, because it dosn't work in the browser address bar. It shouldn't work it the server listens to both http ans https. But for this case it works since the ssl-server only listen on htts. Great!
some
Forgot to mention the clever use of assigning a value to async!
some
A little more browser-friendly version (caches the result of getElementByTagName) for 196 bytes (194 without the last semicolon): _gaq=document.createElement('script'),_gaq.async=_gaq.src=(/^https/.test(location)?'//ssl.':'//')+'google-analytics.com/ga.js',(_gaq.s=document.getElementsByTagName('script')[0]).parentNode.insertBefore(_gaq,_gaq.s),_gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
some
@matyr. Dropping `www` is clever but will spoils the any advantage gained from "cross-site" caching. Every cold-cache (hasn't been to your site) visitor will need download the non-www ga.js.
David Murdoch
@matyr: Just realized why it works without specifying protocol. +1 for you!
some
A: 
(function(d,l,t,g){_gaq=[["_setAccount","UA-XXXXX-X"],["_trackPageview"]];(g=d.createElement(t)).src=(l[5]?"ssl.":"")+"google-analytics.com/ga.js";g.async=1;(t=d.getElementsByTagName(t)[0]).parentNode.appendChild(g,t)})(document,location.protocol,"script")

Deflated Length is: 206 bytes. Original length is: 256 bytes. 19.53% savings

vs. no async:

(function(d,l,t,g){_gaq=[["_setAccount","UA-XXXXX-X"],["_trackPageview"]];(g=d.createElement(t)).src=(l[5]?"ssl.":"")+"google-analytics.com/ga.js";(t=d.getElementsByTagName(t)[0]).parentNode.appendChild(g,t)})(document,location.protocol,"script")

Deflated Length is: 199 bytes. Original length is: 246 bytes. 19.11% savings

--

a little bit shorter would be this:

var d=document,t="script",g;_gaq=[["_setAccount","UA-XXXXX-X"],["_trackPageview"]];(g=d.createElement(t)).src=(location.protocol[5]?"ssl.":"")+"google-analytics.com/ga.js";g.async=1;(t=d.getElementsByTagName(t)[0]).parentNode.appendChild(g,t)

Deflated Length is: 196 bytes. Original length is: 242 bytes. 19.01% savings

vs. no async:

var d=document,t="script",g;_gaq=[["_setAccount","UA-XXXXX-X"],["_trackPageview"]];(g=d.createElement(t)).src=(location.protocol[5]?"ssl.":"")+"google-analytics.com/ga.js";(t=d.getElementsByTagName(t)[0]).parentNode.appendChild(g,t)

Deflated Length is: 188 bytes. Original length is: 232 bytes. 18.88% savings

--

deflated 188 bytes and original 232 bytes is as short as i can go... ;)

Tobias
have you actually tested any of these? I think you'll find that not a single one will load analytics. See http://paulirish.com/2010/the-protocol-relative-url/ for more information.
David Murdoch
ah dang it. yeah i took them out later and forgot about the error i got... damnit... well... gonna try again in a bit.
Tobias