But who cares?
Note that though all modern browsers are capable of handling compressed data, there seems to be a small fraction of users for whom even the newest browser releases don't send an "Accept-Encoding: gzip" header, and can't handle compressed data if it is sent to them. I've seen this both for IE and Firefox users. I don't know why it is. Perhaps a missing library file on their computers. I've only seen it for Windows users, but that may just be because the vast majority of users are Windows users. In any case, you cannot just send compressed data to everyone. You must heed the "Accept-Encoding" headers in the request.
Apache has mod_deflate which can automatically compress anything sent to the browser, including the output of CGI programs. But I found this unattractive, because it runs the compression algorithm every time a request is made. I just wanted to compress the files once, store them on disk, and have the webserver serve them up to any browser that can take them. Of course, the browser still has to uncompress the file, but it's fast and I'm willing to assume that user's computers have plenty of extra CPU.
There is also mod_gzip, which I believe can be configured to serve up precompressed files. It is certainly much more well-tested than what I did, but I didn't find any good documentation for it and it's not clear to me whether versions exist for Apache 2.2.
So here's the setup I am using. It requires the Apache http server, mod_rewrite and mod_header.
These commands can alternately be put in an appropriate <Directory> block in the apache config file.
In some cases you may need an "Options +FollowSymLinks command before the first block, because rewrites are the moral equivalent to symbolic links.
The version above assumes that the directory is under the server's document root and is unaliased. If the directory is accessed via an alias (including ~username sorts of things), then things get a bit more complex since it won't automatically reconstruct the alias in the redirect URL. Normally the "RewriteBase" command would be used, but I don't think it works inside "<FilesMatch>" directives. Instead you have to change the "RewriteRule" to something like:
Remember that you are rewriting the pathname of the requested file into the URL to redirect to, so your rule has to convert apples to oranges in situations where they don't happen to be the same.RewriteRule /home/mylogin/public_html/mydir/(.*).js$ /~mylogin/mydir/$1\.js.gz [L]
I'm not sure that the same thing couldn't be achieved more cleanly using Multiviews and Apache's internal content negotiation mechanism, but I haven't been able to manage that.
In my early tests, I just watched the file sizes on the http_access log to see if the the compressed or uncompressed file was being delivered to the browser. But to do really thorough testing, I needed to be able to control the "Accept-Encoding" headers being sent in requests, and see all headers in responses, so I wrote a little Perl program to send requests and display responses using libwww.
Another widely used tool is Dean Edward's Packer tool. It isn't richly documented and I haven't figured out exactly what it's strategy is, but it seems somewhat similar to JSMin. I did my tests using the perl version of the packer2 tool.
So this is all very clever, but it all seemed over-complex to me. What if we stuck more to the KISS (Keep It Simple, Stupid) philosophy? What if you just strip comments, leading and trailing spaces, and blank lines. That's 100% safe, and very easy to write.
Size Gzipped Size Original File 115,547 bytes 100% 34,478 bytes 30% KISS 79,812 bytes 69% 22,911 bytes 20% JSMin 72,462 bytes 63% 21,835 bytes 19% Packer2 71,434 bytes 62% 21,670 bytes 19% Dojo ShrinkSafe 74,486 bytes 64% 22,000 bytes 19% YUI Compressor 66,415 bytes 57% 20,958 bytes 18%
Note that your results are likely to be significantly different, depending on your coding style.
We see that the smarter algorithms do gain a decent amount of extra compression, though the trivial steps of discarding comments and leading and trailing white space really account for most of their compression. The YUI Compressor seemed to work a fair bit better for me than the others.
In the end, after gzipping, the fancy compressors only gain you 2% more compression than the KISS approach. They are much more complex, they make the production code much harder to debug, and there is some slight risk that they might introduce bugs. 2% might be worth that to you, but not to me. I decided to stick with the KISS approach.
Of course, they do a heck of a lot more to obfuscate the code, if you value that. I mostly find that annoying, because, on occasion, I have to debug based on the production code, and because I don't believe that obfuscation is an effective way to protect your code. In the unlikely event that your code is valuable enough to be worth stealing, people who want it will read through any obfuscation.
All configuration information is embedded in the program. There are no command line arguments.
The first few lines give the full path name of the "js" and "jsz" directories. The next few lines define which sets of files in the source directory are to be concatinated together, and what they are to be called in the destination directory. So, for instance, we create jsz/front.js and jsz/front.js.gz from js/browser.js, js/button.js, js/http.js, js/draw_html.js and js/play.js,
Note that I leave /*...*/ type comments in the code. I use these for copyright messages and // comments for everything else.
Not counting configuration data, it's about 20 lines of code. Pretty simple, and a very safe bet that it isn't going to introduce any errors into the code.
Another speedup that can be done is to set the expiration headers of the big files far into the future, so that browsers will just use them from cache without even asking the server if they have changed. This can cause problems when you actually do change the files, but those can be worked around without too much difficulty. My problem was that I couldn't actually convince myself that any of the major browsers were actually heeding the expiration date I set in my tests. They still seemed to be making conditional GET requests for the unexpired files most of the time. I need to research this more.