Compressing JavaScript … TO THE MAX!

          0 votes
July 6th, 2008

Here is the best recipe I've came up with so far to compress JavaScript. The goal here is to get our JavaScript file the smallest possible. In a nutshell, first we will obfuscate and then we "minify" it.

With obfuscation we'll convert all of our meaningless variables to the shortest meaningful variable needed. "Huh?" What I mean by that is basically all of our variables that we don't have access to or need, specifically variables within functions don't really need any meaning as long as they get the job done... per se... When we write code we name variables in a way so that they make sense to us, humans. There's obviously nothing wrong with that but on the computer's side of things they're just a set of instructions where the naming doesn't mean anything. totalPrice + tax = price means the same to a computer as foo + bar = result which means the same as _1 + _2 = X. We're going to use obfuscation to save space of our useless variable names.

Clarification: The variables aren't useless, their long names are.

After doing what we can with variables we then need to minify our script. This is very simple, just removing all useless white-space and line breaks. It may not sound like much but your web browser would also have an easier time parsing your code if it was all on one line.

The tools for the job

And walla! Go ahead and download these tools off their sites. For JSMin just download the C source code for now if you're on Mac or *nix but if you're on Windows they should have a zip file containing an .exe on the bottom of the page somewhere. For the prior, simply compile the source like so:


$ gcc -o jsmin jsmin.c

  1. Custom Rhino: This is what we'll use to obfuscate our JavaScript code. Custom Rhino is a modified version of Mozilla's Rhino JavaScript interpretor which has an obfuscation feature added to it.
  2. JSMin: And this would obviously be our minify tool! There are actually quite a few other tools that do the same thing JSMin does but I chose to use JSMin because its available in many programming languages. This is a plus for me because I love getting my hands dirty in other languages and it just works.

Doing It!

Lets get started with our ever so boring Hello World! test but because this project is so exciting it will make up for it! Right? Hehe anyways here goes our little JavaScript code, put this and the programs we've downloaded above all into one directory just for the sake of testing for now (you can organize them into somewhere more meaningful later) and save the JavaScript code as test.js:

var message = "Hello Richard!";
 
helloWorld = function(msg)
{
	var message = msg.toUpperCase();
	alert(message);
}
 
helloWorld(message);

Very simple. We got a global variable named message, and function named helloWorld. Now its time to pack this baby together! Open up a console window and lets do it step by step:

$ java -jar custom_rhino.jar -strict -1 -opt -c test.js > tmp.js

If you take a peek at tmp.js then Rhino should have generated this:

var message="Hello Richard!";
helloWorld=function(_1){
var _2=_1.toUpperCase();
alert(_2);
};
helloWorld(message);

What's really important to note here is that our global variable names remained the same and so did our function. The only code that is obfuscated is the code that means nothing to us anyways :-) ... Nothing in that we don't access it anyways.

Next we minify the code with JSMin:

$ ./jsmin < tmp.js > test_compressed.js

Our code should look like:

var message="Hello Richard!";helloWorld=function(_1){var _2=_1.toUpperCase();alert(_2);};helloWorld(message);

And walla! Isn't that beautiful! Of course the space savings isn't tremendous with this small script but I've tested it with Prototype v1.6.0.2 and it went from 123kb to 83kb! That's about a ~40% save right? Not so bad.

So now we've got our code obfuscated and compressed on the client end as much as possible. This should definitely help with transferring over all your JavaScript much faster and your browser should have an better time rendering it.

We can also eliminate the need for tmp.js and perform the above operation with one command:

$ java -jar custom_rhino.jar -strict -opt -1 -c test.js | ./jsmin > test_compressed.js

Conclusion

Well there you have it. Its also good to note to always start and end loops and conditions with opening and closing brackets even if you have those one liners for compatibilities sake. Your code should be packed for distribution when you are sure its at a stable state otherwise debugging can be problem and a headache fast.

If you want to take things further you can optimize your server end by adding gzip compression to Apache or whatever you use and also refactoring your code. The lesser try/catches would help as well.

Here are a few other tools:

  • Dojo Shrinksafe: An online compression tool made with Dojo's custom_rhino.jar.
  • Rhino: This is the homepage of the Rhino JavaScript interpreter that the Dojo developers extended to give obfuscation capabilities to.
  • Dean Edwards Packer: A pretty popular JavaScript packer. Source also available
  • Packer JavaScript in PHP: Same as the one above but made in PHP

Share/Save/Bookmark

Friendly Cross-Site Scripting

          0 votes
May 14th, 2008

Recently I was faced with the issue of loading an iFrame from our main site to one of our child sites and they needed to communicate with each other through JavaScript. While developing on our test servers the problem wasn't obvious because everything was on the same domain but when it was near time to deploy on the production servers the shit hit the fan! The reason wasn't so obvious at first but luckily the solution wasn't very hard to implement, it was just a matter of finding it.

Welcome document.domain

Say we have a script at http://foo.mysite.com that needs to communicate with another friendly script at http://bar.mysite.com through means of an iFrame or something. By default your browsers' security won't let them because the full domains aren't the same same (including the sub-domain).

In order to allow both sub-domains to communicate together you must set the document.domain property in JavaScript to the same domain name. So in our case we would need to set the document.domain property on both sites to mysite.com which is the common domain between the two.

 
document.domain="mysite.com";

You can't however set both to foo.mysite.com or bar.mysite.com and you cannot set both of them to another domain such as mysite1.com or anything else. That's the limitation.

Hint: You could use this technique to share cookies across similar domains as well. When setting a cookie you can set it for the parent domain to be accessible from multiple sub-domains.

Be aware that setting this property incorrectly can compromise the security of your site. It is suggested to determine the value by the server, do not set this property by a value determined by the client.

Share/Save/Bookmark