Archive for the 'tools' Category

Anaconda Limousine: the guitar parts

Sunday, June 17th, 2012

I'm part of a band that has an album out now. I know, right? (links: excuse-for-a-site, amazon, itunes).

I wanted to put up all the songs on the site, but seems like there's a little dissonance in the band whether this is a good idea. Plan B: 30s samples. Like the bigwigs do on Amazon and iTunes.

But while their samples are random, a band can do a better job in picking parts that are representative of the overall sound. I though - let me pick my solo stuff only as an exercise. So there: Anaconda Limousine: the guitar parts.

I wanted to use command-line ffmpeg, of course, because all music software is like Photoshop to me, just can't figure out what's going on with so much UI. Turned out I needed sox too.

And then I want to use HTML5 Audio to play the samples.

I thought: an audio sprite would be a good idea, put all samples in one file, then JS can update the UI depending on which sample is playing. And I thought might be neat to have the JS turn the volume up and down to fade in/out the samples, like iTunes does. Turns out sox is doing this so nicely, that I let it do it.

Samples

I started by listening to the songs and taking notes with song #, start and end.

var slices = [
  {song: 1,  start:   8, end:  21},
  {song: 1,  start: 301, end: 323}, // from 3:01 to 3:23
  {song: 1,  start: 405, end:   0}, // 0 means till the end
  {song: 2,  start:   0, end:  30},
  {song: 2,  start: 305, end: 318},
  {song: 2,  start: 330, end:   0},
  {song: 3,  start:   0, end:  20},
  {song: 3,  start: 333, end:   0},
  {song: 4,  start: 303, end:   0},
  {song: 5,  start:   0, end:  20},
  {song: 5,  start: 300, end: 333},
  {song: 7,  start:   0, end:  20},
  {song: 7,  start: 340, end:   0},
  {song: 8,  start:   0, end:  25},
  {song: 8,  start: 313, end:   0},
  {song: 9,  start: 155, end: 239},
  {song: 9,  start: 350, end:   0}
];

Start 0 means start from the beginning of the song, end 0 means go to the end.

The time format is optimized for easy typing (I was walking, typing in Notes app on the iPhone). Turned out I need to convert the times to seconds:

function secs(num) {
  if (num <= 60) {
    return 1 * num
  }
  num += '';
  return num[0] * 60 + num[1] * 10 + num[2] * 1;
}

And I need album meta data too, with name of the song, filename and duration:

 
var songs = [
  {name: "Virus",     fname: "01-virus",     duration: 436},
  {name: "Yesterday", fname: "02-yesterday", duration: 346},
  {name: "All for you", fname: "03-all4u",   duration: 404},
  {name: "Damage",    fname: "04-damage",    duration: 333},
  {name: "Everyday",  fname: "05-everyday",  duration: 444},
  {name: "Girl of mine", fname: "06-girlomine", duration: 338},
  {name: "Fool on the hill", fname: "07-fool",  duration: 413},
  {name: "Faultline", fname: "08-faultline", duration: 347},
  {name: "Parting is such sweet sorrow", 
                      fname: "09-parting",   duration: 420}
];

Ripping the album

In the interest of quality I wanted to work with WAV and then encode in m4a, ogg and mp3.

On TuneCore.com there's a nice step-by-step instruction how to rip a CD to WAV in iTunes, because it uses mp3 by default.

So then I had the files:

01-virus.wav
02-yesterday.wav
03-all4u.wav
04-damage.wav
05-everyday.wav
06-girl.wav
07-fool.wav
08-faultline.wav
09-parting.wav

Slicing and fading

I used ffmpeg to do the slicing, like for example the first sample:

$ ffmpeg -i 01-virus.wav -ss 5 -t 20 ff-0.wav

-ss is start time and -t is duration. As you see instead of starting at 8 seconds (as described in the samples array) I start earlier. This is to give some more music for fade in/out.

For fade in/out I used sox. I didn't know this awesome command line tool existed, but found out while searching how to combine wav files (I know for mp3 you can just `cat`)

I don't want to fade in when the sample starts at the beginning. Or fade out then the sample happens to end at the end of the song. sox takes fade-in duration (or 0 for no fade in), stop time (0 for till the end of the file/sample) and fade out duration (again 0 is no fade out)

I used 3 secods fade in, 4 fade out. The sox commands are one of:

$ sox in.wav out.wav fade 3 0 0
$ sox in.wav out.wav fade 3 0 4
$ sox in.wav out.wav fade 0 0 4

And since I have all the configs in JS, JS makes perfect sense to generate the commands. Hope you can make sense of the comments:

var fadein = 3;
var fadeout = 4;
var merge = ['sox'];
slices.forEach(function(s, index){
  var ff = ['ffmpeg -i'];
  ff.push(songs[s.song - 1].fname  + '.wav'); // in file
  ff.push('-ss');
  ff.push(s.start ? secs(s.start) - fadein : 0); // start of the slice
  ff.push('-t');
  ff.push(!s.end ? 
      1000 : 
      secs(s.end) - secs(s.start) + fadein + fadeout); // end slice
  ff.push('ff-' + index  + '.wav'); // out file
  
  var sox = ['sox'];
  sox.push('ff-' + index  + '.wav'); // in file
  sox.push('s-' + index  + '.wav'); // out file
  sox.push('fade');
  sox.push(s.start ? fadein : 0); // fade in, unless it;s the beginning of the song
  sox.push(0); // till the end of the slice
  sox.push(s.end ? fadeout : 0); // fade out unless it's The End
    
  console.log(ff.join(' '));
  console.log(sox.join(' '));
  
  merge.push('s-' + index  + '.wav');
});
 
merge.push('_.wav');
console.log(merge.join(' '));

Running this gives me a bunch of commands:

ffmpeg -i 01-virus.wav -ss 5 -t 20 ff-0.wav
sox ff-0.wav s-0.wav fade 3 0 4
ffmpeg -i 01-virus.wav -ss 178 -t 29 ff-1.wav
sox ff-1.wav s-1.wav fade 3 0 4
[....]

sox s-0.wav s-1.wav s-2.wav s-3.wav [...] s-16.wav _.wav

2 for each sample (slice and fade) and one last one to merge all faded results. Nothing left to do but run the generated commands.

Save for Web

Final step is to convert the result _.wav to a web-ready format: m4a, ogg or mp3:

$ ffmpeg -i _.wav _.m4a
$ ffmpeg -i _.wav _.mp3
$ ffmpeg -i _.wav -acodec vorbis -aq 60 -strict experimental _.ogg

Turn it up!

Enjoy Anaconda Limousine: The Guitar Parts (ogg, m4a or mp3) with all samples in one file.

And come back later for the JS player part.

See you!

 

HTTPWatch automation with JavaScript

Sunday, April 10th, 2011

Background

I gave this short presentation at the recent Yahoo FE summit's open mic, here are the slides and some notes.

Slides and screencast vid

Screencast to see the thing in motion:

Notes

Here's the transcript of the slides as produced by slideshare.net. I've added some notes here and there to make it more readable when the presenter is missing.

JavaScript shell scripting - Presentation Transcript

  1. JavaScript is everywhere #42:
          C:> WSH
  2. Stoyan

    I do programming.

  3. Programming

    There are many options to choose from when you decide top practice the
    art and craft of programming.

  4. JavaScript

    ... is a very good one. Simply because JavaScript...

  5. ... is everywhere
  6. On the server

    node.js, asp.net

  7. Mobile

    html5, phonegap, titanium

  8. Desktop

    XULRunner lets you create cross-OS GUI apps

  9. Browser extensions

    FF, Chrome, bookmarklets...

  10. Photoshop

    yep, that too
    Several Adobe products actually let you script common/uncommon/programmable tasks

  11. Form validation too!

    (this was supposed to be funny)

  12. Shell scripting

    let's talk about shell scripting with JavaScript

  13. In Windows
  14. WSH: Windows Scripting Host

    All reasonable Windows machines (at least as old as Win2000) have this Windows Scripting Host in there.
    You can write VBScript or JavaScript (OK, JScript) to ... well, script.
    How does it work?

  15. C:>edit hello.js

    You create a file.

  16. var a = "Hello",
        b = " WSH!",
        c = a + b;
            
    WScript.Echo(c);

    Put any old JavaScript in there and print out some results

  17. C:>cscript hello.js
    Hello WSH!

    And this is how you run it.

    Or this:

    C:>wscript hello.js
  18. Open apps

    In addition to regular sysadmin tasks (copy, write files, move) you can open and script applications too.

  19. var ie = new ActiveXObject("InternetExplorer.Application");
    ie.Visible = true;
    ie.navigate(yahoo.com);

    This is how you open IE and point it to a page.

    Notice something familiar? ActiveXObject - the thing we used in IE before it got native XMLHttpRequest

  20. Firefox?

    Can you also open FF?

    Not really, as it doesn't have COM interface (whatever that is).

    But there's an easy solution

  21. HTTPWatch

    Finally we come to the topic of the talk.

  22. Speed

    Performance is critical for the success of any web app.

    Really, it is.

    When talking about improving speed there are two main steps:

  23. 1. Fix with YSlow

    Take a slow page, run YSlow, do what it says.

    Voila - a fast(er) page.

  24. 2. No regressions

    The second step is to not allow regressions.

    Whatever you fix in step 1 will be slow in the next few months.

    Even less than months the bigger the team or the rate of changes.

    So to prevent regressions, you need to constantly...

  25. Monitor
  26. Set limits

    The simplest way to prevent regressions is to set some limits.

    If you go over the limits, an email is sent, an alarm sounds, panic instills and you've got to fix whatever cause it was.

  27. e.g.
    max 2 scripts
    max 2 styles
    max 9 images
    max 0 redirects
  28. Scripting HTTPWatch

    Watching for violations of the limits manually every day is not a job anyone would want.

    So automating it will help a great way towards employee satisfaction :)

  29. var http = new ActiveXObject("HTTPWatch.Controller"),
        ie = http.IE.New(),
        ff = http.Firefox.New();

    This is how you open IE and FF with HTTPWatch's help.

    FF - yey!

  30. // browser cache
    ie.clearCache();
    
    // show HTTPWatch
    ie.OpenWindow(false);

    Examples of stuff you can do with the HTTPWatch API.

    You can for example hit the page with empty cache and then again with full cache.

    Best of all - these are the real browsers with their sometimes kinky behaviors.
    Actually if you setup several machines for the monitoring (or somehow do multiple IEs)
    you can test with different versions of the browsers. Nothing synthetic!

  31. ie.Record();
    ie.GotoUrl("yahoo.com");
    http.Wait(ie, -1);
    ie.Stop();

    Start monitoring, go to a page, stop monitoring after the page "settles" meaning some time after onload.

    ie.CloseBrowser();
  32. new HTTPWatch()
          http://github.com/stoyan/etc/

    I did this JavaScript thingie to make everything a little easier.

  33. var http = new HTTPWatch(ff);
    http.go(search.yahoo.com);
    http.done();

    Example usage.

  34. [video]
  35. var har = http.toHAR();
    har = eval(( + har + ));
    
    print(har.log.browser.name);
    print(har.log.browser.version);
    print(# requests: );
    print(har.log.entries.length);

    Opening and navigating browsers is cool. But we need some data back.

    HTTPWatch can export a HAR (HTTP Archive) file. I have this toHAR() method.
    It writes the file, than reads and returns it.
    You can than eval() it because it's just a JSON string.
    And you get the data back in convenient JS objects and arrays.

  36. Internet Explorer 6.0.29...
    # requests: 10
    
    Firefox 3.5.6
    # requests: 15

    Result of running the above.

  37. [video]
  38. var comps = http.getComponentsByType();
          
    for (var i in comps) {
      print(i);
      print(comps[i].length);
    }

    Another method I thought would be useful is getComponentsByType()

  39. redirect: 1
    text/html: 3
    image/gif: 4
    image/png: 3
    text/javascript: 1

    Results of the code above.

  40. But wait...

    There's more :)

  41. What about DOM?

    So far we only talked about HTTP traffic inspection - headers and such.

    Good news is that you can also inspect the DOM (in IE only) for any potential red flags.

    For example having the number of DOM elements sharply increase.

  42. [video]
  43. var http = new HTTPWatch();
    http.go(search.yahoo.com);
    
    var d = http.watch.container.document;
    
    print(d.getElementsByTagName(*).length);
    print(d.documentElement.innerHTML);

    That works!

    All your DOM voodoo skillz are suddenly reusable.

  44. require(statz.js);
          
    var doc = http.watch.container.document;
    var html = http.har.log.entries[0].response.content.text;
    
    var out = statz(document, html);
    print(out.join("\n"));

    This is me repurposing two old bookmarklets that gather some interesting stats (one of them was even featured on Ajaxian, remember?).

    It was pretty easy to repurpose the bookmarklets, because it's just JavaScript.

    The stats thingie can inspect both raw HTML that went over the wire, as well as innerHTML that was the result of any additional DOM manipulations.

  45. JS attributes (e.g. onclick): 1207 bytes
    CSS style attributes: 883
    Inline JS: 5243
    Inline CSS: 5015
    All innerHTML: 17283
    # DOM elements: 134
    
    Total size: 14124 bytes
    Content size: 401 bytes
    Content-to-markup ratio: 0.03

    Sample results.

  46. To summarize...
  47. JavaScript
    WSH
    HTTPWatch
    Monitor
    DOM and HTTP
    IE and Firefox
  48. Thanks
          
    phpied.com
 

YSlow/Chrome hacking

Thursday, March 24th, 2011

If you haven't seen it yet, YSlow for Chrome hit the streets couple of weeks ago. (And Google's own PageSpeed did too yesterday. (And there's now DynaTrace for Firefox. (And WebPageTest for Chrome. (What a month for x-browsering (word?) the performance tools! (And the month's not even over yet)))))

BTW, I closed all the parentheses (or else...).

So anyway, I was eavesdropping on a twitter conversation where Sergey (of ShowSlow) was asking for beacons from YSlow for Chrome, more specifically - when will they start working. I thought I should check how my old baby YSlow 2.0 (this presentation is still pretty relevant) is doing in its new environment.

YSlow 2.0

In YSlow 2.0 things are pretty decoupled. Makes it easier to bring to any possible environment or browser. So rules are rules (you can add, remove, tweak them, combine them into rulesets), results are results, presentation is separate, and so are the additional tools, HAR import/export (forthcoming), etc. Only (ideally) small additions are needed to glue the core of YSlow (the linting part) with a new environment.

In Chrome

It's my first time touching anything Chrome-y, but turned out its pretty easy. Just a bit of file system hunting revealed where code for the extension goes.

/Users/[USERNAME]/Library/Application Support/Google/Chrome/Default/Extensions/[WEIRD-EXTENSION-ID]/

e.g.

/Users/stoyanstefanov/Library/Application Support/Google/Chrome/Default/Extensions/ninejjcohidippngpapiilnmkgllmakh/

On Windows:

C:\Documents and Settings\[USERNAME]\Local Settings\Application Data\Google\Chrome\User Data\Default\Extensions\ninejjcohidippngpapiilnmkgllmakh\

In there you can find three JavaScript files. I could be wrong but here's what I think goes in there:

  1. yslow-chrome.js looks like it contains the reusable parts - rules, etc - packaged for Chrome minus the Firefox stuff and bundled into a single file
  2. content.js is small and not too exciting
  3. controller.js is the Chrome-related parts

In controller.js is where we hack.

YSlow events

In YSlow 2.0 we decided to make use of a simple observer pattern implementation and fire events whenever interesting stuff happens. Especially useful since figuring all the data for all resources better be asynchronous.

Once yslow "peels" the page figuring out the components, gets each component data, headers, etc, then runs the lint rules, finally it fires a lintResultsReady event.

("Peeling a page" I heard for the first time from Steve Souders and for what I know he should be credited with this term describing the activity of figuring all components that go into a page)

All we need to do is listen to this event and send the beacon.

YSLOW.util.event.addListener(
  'lintResultReady', 
  function (o) {
    //...b-b-beacon! ...
  }
);

There's a YSLOW.util.sendBeacon() which does precisely that, so we need to call it and we're done.

Preferences

Firefox has a built-in (native) system to manage preferences. You know, the stuff you tweak in about:config. This is where we put the preferences - beacon yes/no, beacon URL, beacon data verbosity.

In Chrome such native preference system probably exists, but YSlow is currently not taking advantage. (Just guessing here.)

Luckily all calls to get preferences are abstracted in YSLOW.util.Preferences.getPref(prefname, defaultvalue). The default value is returned if a better one cannot be found.

So we can just overwrite the getPref() method to return the default value, unless it's a preference we care about, such as the beacon URL:

YSLOW.util.Preference.getPref = function(what, defaultval) {
  switch (what) {
    case 'beaconUrl':
      return 'http://www.phpied.com/beacon.png';
    case 'beaconInfo':
      return 'all'; // or "basic"
    default:
      return defaultval;
  }
};

Integration

As mentioned we'll hack into the controller.js, we don't want to touch the yslow core stuff. The controller.js is just one immediate function and our hack goes right before the last line. (Or even after it, probably doesn't matter)

(function () {
    // ... yslowy stuff ...
 
    // hack start
    // ...
    // hack end
 
    doc.ysview.setSplashView(...
}());

The complete thing is something like:

(function () {
 
    // ... slo, slo ...
 
    // hack start
    YSLOW.util.Preference.getPref = function(what, defaultval) {
      switch (what) {
        case 'beaconUrl':
          return 'http://www.phpied.com/beacon.png';
        case 'beaconInfo':
          return 'all'; // or "basic"
        default:
          return defaultval;
      }
    };
 
    YSLOW.util.event.addListener('lintResultReady', function (o) {
      var con = o.yslowContext,
          result = con.result_set;
      YSLOW.util.sendBeacon(result.url, result.overall_score, con);
    });
    // hack end
 
    doc.ysview.setSplashView(...
}());

Conclusion

So there - you can run YSlow in Chrome and send yourself (or showslow.com) beacons.

You probably don't need that so bad that you can't wait till next YSlow for Chrome ships with this thing working. But here it is.

And hopefully you learned a bit about YSlow internals so that you can start hacking yourself and/or wait till YSlow shows up on github (soon!) and start sending diffs. I personally can't wait.

Shoutout goes out to Marcel and Betty who are doing awesome stuff with YSlow (slides). And looks like even more is to come!

 

Velocity online conference March 2011

Tuesday, March 15th, 2011

Very rough notes from the event that just ended a few minutes ago

======== 1 ========

Tony Gentilcore (google) and Anderson Quach (MSFT) talked about the W3C working group and the specs for measuring performance:

  • Navigation timing spec - HTML document: DNS, handshakes, load events
  • Resource timing - how long JS, CSS, audio, video... take
  • User timing - interactions, such as clicking a link

Challenges: user security, privacy. Small overhead for browsers. Useful actionable data out.

Navigation timing:

  • window.performance - IE9 has no vendor prefix
  • otherwise window.webkitPerformance
  • FF4 probably shipping it too

Properties:

window.performance.timing
  .domainLookup
  .connect
  .domContentLoaded
  .loadEvent
  ....etc

Specs: http://test.w3.org/webperf/specs/NavigationTiming/

Resource Timing spec - upcoming

foreach resource 
  redirect
  fetchStart
  fetchEnd
  ...

User Timing - upcoming

  mark('pagelet')
  mark('something-else')
  ...
  
  >>> getMarks();
  {
    'pagelet': 123456767
  }

Stretch goals:

  • painting times
  • CPUs
  • visibility (is the page in a background tab)
  • allow real yealding setTimeout(0)
  • don't animate faster than the browser paints

mailto: public-web-perf@w3c.org

======== 2 ========

Above the Fold Time: Measuring Web Page Performance Visually
Speaker: Jake Brutlag
Help from: Zoe Abrams, Pat Meenan (webpagetest) Google

What the user perception of page load timing is.

AFT (Above the Fold Timing) algo
AFT: When content above the fold stops changing and reaches its final state

Integrated in WebPageTest.org

Motivation

  • AFT more representative than DOMContentLoaded or onload
  • onload - different in IE and FF, e.g. async scripts block onload or not

Method:

  • video capture
  • AFT cutoff time - upper bound, e.g. 45 sec for DSL
  • split pixels into static and dymanic (ads)
  • AFT = latest change of a static pixel

Magic number - 5 determined empirically
static pixels are those that change less than 5 times

Application - validate other metrics, cross-browser, visual
Limitation - lab only

{I ran webpagetest.org on facebook.com with AFT on.
The results are very close to onload, as expected.
It's a simple page. Results:

http://www.webpagetest.org/result/110315_P9_5MDJ/

}

== break ==

Note from the sponsor StrangeLoop: a downloadable "State of the Union"
A study of top 2000 Alexa retail sites. Findings:

  1. 11.2 sec onload avg
  2. top 100 sites slower than the lower ranked
  3. those using CDNs are not faster
  4. failed to implement the basics - YSlow stuff

======== 3 ========

Next-gen YSlow
Betty Tso and Marcel Duran, Yahoo!

YSlow for Chrome last week
Not as accurate as Firefox cause headers, timing come from:
1. Crawl DOM
2. x-domain requests

Announcing YSlow bookmarklet version
for mobile and all other browsers to be released early april
YSlow now on Git?

Lots coming up, e.g:

  • Multi-language support
  • HAR-to-YSlow
  • YSlow export to JDrop

Bookmarket works like:
1. crawl DOM
2. Use YQL to get headers

     SELECT * FROM data.headers WHERE
      url IN ('http://...', 'http://...')

======== 4 ========

Visibility into Mobile Performance
Steve Souders, Google

Demo #1: http://blaze.io/mobile/
WebPageTest.org framework connected to iPhone and Android apps
Mobile waterfalls
Demo #2: pcapperf
1. setup wifi hotspot on a pc/mac
2. tcpdump to capture .pcap file
3. upload .pcap to http://pcapperf.appspot.com app
4. observe waterfall via pcap2har and harviewer
5. run pagespeed on the har
Demo #3: JDrop
http://jdrop.org
Upload JSON so you can run something on mobile and view/analyze results in desktop's large screen
Works with the bookmarklet tools part of Steve's package of mobile meta-tool

 

CSS minifiers comparison

Saturday, October 30th, 2010

Last year I compared some CSS minifiers, namely YUICompressor, CSSTidy (with "small" vs. "safe" settings), PHP PEAR's CSS lib and Minify (detailed results). Now that I've done some work on the YUICompressor and since there's a new kid on the block from Microsoft I thought I should give it another go.

I only compared CSSTidy with best compression, YUICompressor and MS' Ajaxmin. The verdict is still the same - CSSTidy looks the best, but there really isn't that big of a difference between the three (and virtually no difference between YUIC and Ajaxmin), especially if you take into account gzipping.

Still on average you get about 35% size reduction when you minify and 80% when you gzip the minified CSS. Meaning when all is done your users only download 19-20% of what they would normally do if you don't perform these simple optimizations.

Experiment

Source CSS files came from csszengarden.com - 213 CSS files.

For the YUICompressor I used the JS verison, on windows with WSH, described in the previous post.

For CSSTidy I used exactly the same code as the last year.

Lastly, Ajaxmin I ran with the default options:

$ AjaxMin.exe in.css -out out.css

Results

CSSTidy wins with bringing the size down to 63.29% of the original on avegare. The other two follow closely with 66.47% (ajaxmin) and 66.41% (YUI). After gzipping: 19.44% (tidy), 19.62% (ajaxmin) and 19.63% (yui). In other words YUI's cssmin performs ever so slightly better that Ajaxmin by 0.06% before gzipping, but after gzipping Ajaxmin wins with 0.01%.

Overall the results show that you shouldn't sweat too much over the choice of CSS minifier. They are all pretty close and you should pick the one that is most convenient for your own use and build process.

CSSTidy and Ajaxmin have parsers, so theoretically they have a much better chance. YUI's cssmin is a bunch of regular expressions and no proper parsing. In reality they are all performing really similarly. Part of the reason is that CSS is not as minifiable as JavaScript as most of it is long selectors and long prperty names (background-image, text-decoration, -webkit-transform-origin, anyone?), which are not renamed to shorter names the way JS variables are.

One random observation (and hint for Ajaxmin folks) is that cssmin strips selectors with empty rulesets while Ajaxmin does not. E.g. #selector {} is totally gone in cssmin but stays in ajaxmin.

Detailed results

File Original cssminjs CSSTidy-small ajaxmin
001.css
gzipped
4832b 100.00%
1781b   36.86%
2650b   54.84%
888b   18.38%
2586b   53.52%
902b   18.67%
2653b   54.90%
892b   18.46%
002.css
gzipped
5242b 100.00%
1936b   36.93%
2651b   50.57%
893b   17.04%
2605b   49.69%
898b   17.13%
2651b   50.57%
893b   17.04%
003.css
gzipped
4964b 100.00%
1856b   37.39%
2734b   55.08%
996b   20.06%
2614b   52.66%
979b   19.72%
2733b   55.06%
992b   19.98%
004.css
gzipped
3854b 100.00%
1285b   33.34%
2370b   61.49%
748b   19.41%
2335b   60.59%
752b   19.51%
2370b   61.49%
748b   19.41%
005.css
gzipped
4648b 100.00%
1616b   34.77%
2652b   57.06%
882b   18.98%
2604b   56.02%
882b   18.98%
2655b   57.12%
884b   19.02%
006.css
gzipped
4319b 100.00%
1435b   33.23%
2689b   62.26%
849b   19.66%
2614b   60.52%
863b   19.98%
2688b   62.24%
850b   19.68%
007.css
gzipped
5412b 100.00%
1557b   28.77%
3694b   68.26%
997b   18.42%
3459b   63.91%
990b   18.29%
3690b   68.18%
993b   18.35%
008.css
gzipped
2143b 100.00%
961b   44.84%
1260b   58.80%
557b   25.99%
1194b   55.72%
553b   25.80%
1260b   58.80%
557b   25.99%
009.css
gzipped
5525b 100.00%
1515b   27.42%
3490b   63.17%
960b   17.38%
3237b   58.59%
946b   17.12%
3490b   63.17%
960b   17.38%
010.css
gzipped
4148b 100.00%
1474b   35.54%
2301b   55.47%
868b   20.93%
2172b   52.36%
853b   20.56%
2307b   55.62%
867b   20.90%
011.css
gzipped
6021b 100.00%
1612b   26.77%
3540b   58.79%
1029b   17.09%
3425b   56.88%
1018b   16.91%
3536b   58.73%
1030b   17.11%
012.css
gzipped
9250b 100.00%
3113b   33.65%
4914b   53.12%
1421b   15.36%
4882b   52.78%
1410b   15.24%
4925b   53.24%
1426b   15.42%
013.css
gzipped
5846b 100.00%
1811b   30.98%
3731b   63.82%
1030b   17.62%
3643b   62.32%
1013b   17.33%
3734b   63.87%
1031b   17.64%
014.css
gzipped
6010b 100.00%
1518b   25.26%
4355b   72.46%
1063b   17.69%
4332b   72.08%
1083b   18.02%
4358b   72.51%
1064b   17.70%
015.css
gzipped
6337b 100.00%
1875b   29.59%
3918b   61.83%
1109b   17.50%
3562b   56.21%
1081b   17.06%
3936b   62.11%
1113b   17.56%
016.css
gzipped
6934b 100.00%
1629b   23.49%
4978b   71.79%
1179b   17.00%
4168b   60.11%
1175b   16.95%
4982b   71.85%
1181b   17.03%
017.css
gzipped
4964b 100.00%
1626b   32.76%
3266b   65.79%
1101b   22.18%
3245b   65.37%
1110b   22.36%
3266b   65.79%
1101b   22.18%
018.css
gzipped
4000b 100.00%
1424b   35.60%
2521b   63.03%
859b   21.48%
2467b   61.68%
862b   21.55%
2519b   62.98%
856b   21.40%
019.css
gzipped
5265b 100.00%
1469b   27.90%
3376b   64.12%
869b   16.51%
3268b   62.07%
879b   16.70%
3406b   64.69%
886b   16.83%
020.css
gzipped
7262b 100.00%
1844b   25.39%
5668b   78.05%
1416b   19.50%
5654b   77.86%
1393b   19.18%
5668b   78.05%
1414b   19.47%
021.css
gzipped
7118b 100.00%
1849b   25.98%
5602b   78.70%
1350b   18.97%
5129b   72.06%
1347b   18.92%
5602b   78.70%
1350b   18.97%
022.css
gzipped
7235b 100.00%
1888b   26.10%
5065b   70.01%
1172b   16.20%
4789b   66.19%
1161b   16.05%
5065b   70.01%
1172b   16.20%
023.css
gzipped
5192b 100.00%
1558b   30.01%
3648b   70.26%
1070b   20.61%
3613b   69.59%
1074b   20.69%
3648b   70.26%
1065b   20.51%
024.css
gzipped
4537b 100.00%
1434b   31.61%
3056b   67.36%
950b   20.94%
2901b   63.94%
936b   20.63%
3062b   67.49%
951b   20.96%
025.css
gzipped
10006b 100.00%
2376b   23.75%
7025b   70.21%
1614b   16.13%
6685b   66.81%
1595b   15.94%
7031b   70.27%
1615b   16.14%
026.css
gzipped
4246b 100.00%
1256b   29.58%
2863b   67.43%
829b   19.52%
2799b   65.92%
826b   19.45%
2863b   67.43%
829b   19.52%
027.css
gzipped
5624b 100.00%
2298b   40.86%
2651b   47.14%
942b   16.75%
2559b   45.50%
941b   16.73%
2651b   47.14%
942b   16.75%
028.css
gzipped
5913b 100.00%
1595b   26.97%
3557b   60.16%
932b   15.76%
2829b   47.84%
849b   14.36%
3563b   60.26%
932b   15.76%
029.css
gzipped
5216b 100.00%
1591b   30.50%
3980b   76.30%
1098b   21.05%
3637b   69.73%
1060b   20.32%
3980b   76.30%
1098b   21.05%
030.css
gzipped
3810b 100.00%
1350b   35.43%
2425b   63.65%
838b   21.99%
2455b   64.44%
839b   22.02%
2423b   63.60%
837b   21.97%
031.css
gzipped
4448b 100.00%
1423b   31.99%
2556b   57.46%
911b   20.48%
2557b   57.49%
905b   20.35%
2556b   57.46%
908b   20.41%
032.css
gzipped
5243b 100.00%
1474b   28.11%
3991b   76.12%
985b   18.79%
3813b   72.73%
992b   18.92%
3984b   75.99%
980b   18.69%
033.css
gzipped
6091b 100.00%
1817b   29.83%
4442b   72.93%
1302b   21.38%
4208b   69.09%
1263b   20.74%
4498b   73.85%
1314b   21.57%
034.css
gzipped
6319b 100.00%
1555b   24.61%
4913b   77.75%
1090b   17.25%
4879b   77.21%
1092b   17.28%
4916b   77.80%
1090b   17.25%
035.css
gzipped
10877b 100.00%
2320b   21.33%
6718b   61.76%
1595b   14.66%
6192b   56.93%
1508b   13.86%
6718b   61.76%
1595b   14.66%
036.css
gzipped
7117b 100.00%
2122b   29.82%
4229b   59.42%
1181b   16.59%
4035b   56.70%
1153b   16.20%
4229b   59.42%
1181b   16.59%
037.css
gzipped
7738b 100.00%
2210b   28.56%
4624b   59.76%
1166b   15.07%
4417b   57.08%
1168b   15.09%
4627b   59.80%
1166b   15.07%
038.css
gzipped
3459b 100.00%
1147b   33.16%
2173b   62.82%
680b   19.66%
2181b   63.05%
681b   19.69%
2173b   62.82%
676b   19.54%
039.css
gzipped
6885b 100.00%
2129b   30.92%
3821b   55.50%
1115b   16.19%
3595b   52.21%
1093b   15.88%
3836b   55.72%
1117b   16.22%
040.css
gzipped
6772b 100.00%
1699b   25.09%
4580b   67.63%
1132b   16.72%
4542b   67.07%
1119b   16.52%
4580b   67.63%
1132b   16.72%
041.css
gzipped
4398b 100.00%
1803b   41.00%
2000b   45.48%
764b   17.37%
1973b   44.86%
754b   17.14%
2002b   45.52%
762b   17.33%
042.css
gzipped
14460b 100.00%
2568b   17.76%
5211b   36.04%
1466b   10.14%
4916b   34.00%
1449b   10.02%
5245b   36.27%
1471b   10.17%
043.css
gzipped
10762b 100.00%
2326b   21.61%
6929b   64.38%
1502b   13.96%
6241b   57.99%
1484b   13.79%
6929b   64.38%
1502b   13.96%
044.css
gzipped
7479b 100.00%
2112b   28.24%
4953b   66.23%
1311b   17.53%
4696b   62.79%
1286b   17.19%
4916b   65.73%
1293b   17.29%
045.css
gzipped
4470b 100.00%
1310b   29.31%
2930b   65.55%
847b   18.95%
2680b   59.96%
841b   18.81%
2933b   65.62%
848b   18.97%
046.css
gzipped
4416b 100.00%
1575b   35.67%
2480b   56.16%
822b   18.61%
2353b   53.28%
817b   18.50%
2480b   56.16%
822b   18.61%
047.css
gzipped
3467b 100.00%
1354b   39.05%
1992b   57.46%
796b   22.96%
2015b   58.12%
799b   23.05%
1992b   57.46%
796b   22.96%
048.css
gzipped
4600b 100.00%
1512b   32.87%
3339b   72.59%
1077b   23.41%
3308b   71.91%
1086b   23.61%
3344b   72.70%
1079b   23.46%
049.css
gzipped
6217b 100.00%
1742b   28.02%
3898b   62.70%
977b   15.71%
3833b   61.65%
977b   15.71%
3901b   62.75%
978b   15.73%
050.css
gzipped
8504b 100.00%
2056b   24.18%
6800b   79.96%
1535b   18.05%
5865b   68.97%
1481b   17.42%
6803b   80.00%
1536b   18.06%
051.css
gzipped
8915b 100.00%
2047b   22.96%
5783b   64.87%
1141b   12.80%
4829b   54.17%
1106b   12.41%
5837b   65.47%
1148b   12.88%
052.css
gzipped
9312b 100.00%
2319b   24.90%
6234b   66.95%
1461b   15.69%
5835b   62.66%
1451b   15.58%
6232b   66.92%
1462b   15.70%
053.css
gzipped
5882b 100.00%
1563b   26.57%
4416b   75.08%
1056b   17.95%
4242b   72.12%
1059b   18.00%
4416b   75.08%
1056b   17.95%
054.css
gzipped
7709b 100.00%
1989b   25.80%
5604b   72.69%
1406b   18.24%
5259b   68.22%
1371b   17.78%
5604b   72.69%
1406b   18.24%
055.css
gzipped
4890b 100.00%
1486b   30.39%
2983b   61.00%
948b   19.39%
2755b   56.34%
919b   18.79%
2983b   61.00%
948b   19.39%
056.css
gzipped
4770b 100.00%
1185b   24.84%
3630b   76.10%
1034b   21.68%
3241b   67.95%
984b   20.63%
3636b   76.23%
1034b   21.68%
057.css
gzipped
12063b 100.00%
3047b   25.26%
10382b   86.06%
2483b   20.58%
9230b   76.51%
2178b   18.06%
9505b   78.79%
2213b   18.35%
058.css
gzipped
8721b 100.00%
1936b   22.20%
6070b   69.60%
1329b   15.24%
5102b   58.50%
1328b   15.23%
6073b   69.64%
1328b   15.23%
059.css
gzipped
6660b 100.00%
1934b   29.04%
4879b   73.26%
1296b   19.46%
4321b   64.88%
1259b   18.90%
4888b   73.39%
1296b   19.46%
060.css
gzipped
4691b 100.00%
1566b   33.38%
2823b   60.18%
922b   19.65%
2629b   56.04%
919b   19.59%
2843b   60.61%
933b   19.89%
061.css
gzipped
4299b 100.00%
1327b   30.87%
2813b   65.43%
850b   19.77%
2803b   65.20%
857b   19.93%
2813b   65.43%
850b   19.77%
062.css
gzipped
15125b 100.00%
2757b   18.23%
11437b   75.62%
2010b   13.29%
10754b   71.10%
1939b   12.82%
11506b   76.07%
2015b   13.32%
063.css
gzipped
6751b 100.00%
2116b   31.34%
4213b   62.41%
1261b   18.68%
4164b   61.68%
1247b   18.47%
4213b   62.41%
1261b   18.68%
064.css
gzipped
5630b 100.00%
1772b   31.47%
4038b   71.72%
1209b   21.47%
3846b   68.31%
1195b   21.23%
4041b   71.78%
1207b   21.44%
065.css
gzipped
5630b 100.00%
1713b   30.43%
4104b   72.90%
1218b   21.63%
3970b   70.52%
1209b   21.47%
4110b   73.00%
1217b   21.62%
066.css
gzipped
5142b 100.00%
1533b   29.81%
3418b   66.47%
938b   18.24%
3088b   60.05%
915b   17.79%
3418b   66.47%
938b   18.24%
067.css
gzipped
4859b 100.00%
1605b   33.03%
3439b   70.78%
1059b   21.79%
3291b   67.73%
1034b   21.28%
3439b   70.78%
1059b   21.79%
068.css
gzipped
9506b 100.00%
2188b   23.02%
4220b   44.39%
1215b   12.78%
4160b   43.76%
1194b   12.56%
4220b   44.39%
1209b   12.72%
069.css
gzipped
4682b 100.00%
1494b   31.91%
3374b   72.06%
1064b   22.73%
3419b   73.02%
1069b   22.83%
3374b   72.06%
1064b   22.73%
070.css
gzipped
5300b 100.00%
1561b   29.45%
3621b   68.32%
1042b   19.66%
3530b   66.60%
1039b   19.60%
3623b   68.36%
1042b   19.66%
071.css
gzipped
4198b 100.00%
1314b   31.30%
2898b   69.03%
897b   21.37%
2752b   65.56%
866b   20.63%
2898b   69.03%
897b   21.37%
072.css
gzipped
5029b 100.00%
1636b   32.53%
3666b   72.90%
1137b   22.61%
3399b   67.59%
1095b   21.77%
3664b   72.86%
1134b   22.55%
073.css
gzipped
3409b 100.00%
1207b   35.41%
2480b   72.75%
838b   24.58%
2417b   70.90%
844b   24.76%
2480b   72.75%
838b   24.58%
074.css
gzipped
5875b 100.00%
1383b   23.54%
3817b   64.97%
879b   14.96%
3347b   56.97%
848b   14.43%
3859b   65.69%
886b   15.08%
075.css
gzipped
6956b 100.00%
1986b   28.55%
3748b   53.88%
1086b   15.61%
3599b   51.74%
1091b   15.68%
3754b   53.97%
1087b   15.63%
076.css
gzipped
6064b 100.00%
1508b   24.87%
4295b   70.83%
1094b   18.04%
4260b   70.25%
1075b   17.73%
4295b   70.83%
1094b   18.04%
077.css
gzipped
6255b 100.00%
1553b   24.83%
4410b   70.50%
1077b   17.22%
3982b   63.66%
1061b   16.96%
4417b   70.62%
1074b   17.17%
078.css
gzipped
6209b 100.00%
1543b   24.85%
3960b   63.78%
945b   15.22%
3543b   57.06%
921b   14.83%
3960b   63.78%
945b   15.22%
079.css
gzipped
2119b 100.00%
1000b   47.19%
1380b   65.13%
627b   29.59%
1399b   66.02%
629b   29.68%
1381b   65.17%
629b   29.68%
080.css
gzipped
5033b 100.00%
1414b   28.09%
3548b   70.49%
957b   19.01%
3351b   66.58%
949b   18.86%
3560b   70.73%
961b   19.09%
081.css
gzipped
8661b 100.00%
1852b   21.38%
6091b   70.33%
1266b   14.62%
5216b   60.22%
1247b   14.40%
6109b   70.53%
1271b   14.67%
082.css
gzipped
5389b 100.00%
1763b   32.71%
3370b   62.53%
1089b   20.21%
3214b   59.64%
1072b   19.89%
3370b   62.53%
1088b   20.19%
083.css
gzipped
6045b 100.00%
1741b   28.80%
4200b   69.48%
1128b   18.66%
4110b   67.99%
1126b   18.63%
4199b   69.46%
1128b   18.66%
084.css
gzipped
4865b 100.00%
1593b   32.74%
3453b   70.98%
921b   18.93%
3273b   67.28%
923b   18.97%
3453b   70.98%
921b   18.93%
085.css
gzipped
4434b 100.00%
1433b   32.32%
3065b   69.12%
956b   21.56%
2843b   64.12%
928b   20.93%
3068b   69.19%
956b   21.56%
086.css
gzipped
7007b 100.00%
1727b   24.65%
2806b   40.05%
947b   13.52%
2792b   39.85%
945b   13.49%
2806b   40.05%
942b   13.44%
087.css
gzipped
7422b 100.00%
1839b   24.78%
3157b   42.54%
996b   13.42%
3158b   42.55%
986b   13.28%
3157b   42.54%
990b   13.34%
088.css
gzipped
13936b 100.00%
3351b   24.05%
7685b   55.14%
1671b   11.99%
6673b   47.88%
1623b   11.65%
7697b   55.23%
1675b   12.02%
089.css
gzipped
4766b 100.00%
1511b   31.70%
3609b   75.72%
1068b   22.41%
3513b   73.71%
1041b   21.84%
3609b   75.72%
1068b   22.41%
090.css
gzipped
5986b 100.00%
1792b   29.94%
4104b   68.56%
1246b   20.82%
3953b   66.04%
1217b   20.33%
4095b   68.41%
1238b   20.68%
091.css
gzipped
3738b 100.00%
1288b   34.46%
2446b   65.44%
902b   24.13%
2399b   64.18%
901b   24.10%
2449b   65.52%
896b   23.97%
092.css
gzipped
4246b 100.00%
1474b   34.72%
2770b   65.24%
997b   23.48%
2761b   65.03%
1001b   23.58%
2818b   66.37%
1001b   23.58%
093.css
gzipped
5169b 100.00%
1572b   30.41%
3376b   65.31%
1012b   19.58%
3081b   59.61%
995b   19.25%
3399b   65.76%
1019b   19.71%
094.css
gzipped
6590b 100.00%
1547b   23.47%
4424b   67.13%
1063b   16.13%
3680b   55.84%
1023b   15.52%
4437b   67.33%
1067b   16.19%
095.css
gzipped
3881b 100.00%
1380b   35.56%
2672b   68.85%
975b   25.12%
2686b   69.21%
972b   25.05%
2672b   68.85%
973b   25.07%
096.css
gzipped
7719b 100.00%
1317b   17.06%
5451b   70.62%
921b   11.93%
3416b   44.25%
833b   10.79%
5451b   70.62%
921b   11.93%
097.css
gzipped
6287b 100.00%
1729b   27.50%
4503b   71.62%
1236b   19.66%
4379b   69.65%
1236b   19.66%
4501b   71.59%
1228b   19.53%
098.css
gzipped
3877b 100.00%
1229b   31.70%
2406b   62.06%
790b   20.38%
2360b   60.87%
800b   20.63%
2406b   62.06%
790b   20.38%
099.css
gzipped
9849b 100.00%
2592b   26.32%
6261b   63.57%
1774b   18.01%
6358b   64.55%
1778b   18.05%
6291b   63.87%
1775b   18.02%
100.css
gzipped
4140b 100.00%
1621b   39.15%
2823b   68.19%
1069b   25.82%
2853b   68.91%
1058b   25.56%
2817b   68.04%
1056b   25.51%
101.css
gzipped
6222b 100.00%
1359b   21.84%
4269b   68.61%
963b   15.48%
4077b   65.53%
953b   15.32%
4264b   68.53%
967b   15.54%
102.css
gzipped
4665b 100.00%
1642b   35.20%
3341b   71.62%
1123b   24.07%
3348b   71.77%
1126b   24.14%
3347b   71.75%
1123b   24.07%
103.css
gzipped
6615b 100.00%
1987b   30.04%
4916b   74.32%
1400b   21.16%
4821b   72.88%
1397b   21.12%
4915b   74.30%
1399b   21.15%
104.css
gzipped
3736b 100.00%
1222b   32.71%
2690b   72.00%
838b   22.43%
2586b   69.22%
827b   22.14%
2690b   72.00%
838b   22.43%
105.css
gzipped
4227b 100.00%
1353b   32.01%
3033b   71.75%
952b   22.52%
2776b   65.67%
947b   22.40%
3040b   71.92%
960b   22.71%
106.css
gzipped
4869b 100.00%
1912b   39.27%
2402b   49.33%
794b   16.31%
2372b   48.72%
800b   16.43%
2402b   49.33%
793b   16.29%
107.css
gzipped
4464b 100.00%
1186b   26.57%
2784b   62.37%
718b   16.08%
2232b   50.00%
705b   15.79%
2886b   64.65%
793b   17.76%
109.css
gzipped
4860b 100.00%
1489b   30.64%
3581b   73.68%
1095b   22.53%
3296b   67.82%
1082b   22.26%
3584b   73.74%
1088b   22.39%
110.css
gzipped
4977b 100.00%
1802b   36.21%
3105b   62.39%
1008b   20.25%
3019b   60.66%
986b   19.81%
3106b   62.41%
1007b   20.23%
111.css
gzipped
6508b 100.00%
1686b   25.91%
4792b   73.63%
1211b   18.61%
4422b   67.95%
1205b   18.52%
4800b   73.76%
1213b   18.64%
112.css
gzipped
4878b 100.00%
1472b   30.18%
3305b   67.75%
965b   19.78%
3240b   66.42%
968b   19.84%
3305b   67.75%
965b   19.78%
113.css
gzipped
4709b 100.00%
1358b   28.84%
3008b   63.88%
850b   18.05%
2730b   57.97%
840b   17.84%
3008b   63.88%
850b   18.05%
114.css
gzipped
4357b 100.00%
1436b   32.96%
2956b   67.84%
966b   22.17%
2862b   65.69%
963b   22.10%
2952b   67.75%
958b   21.99%
115.css
gzipped
8194b 100.00%
1770b   21.60%
5773b   70.45%
1329b   16.22%
5246b   64.02%
1290b   15.74%
5818b   71.00%
1339b   16.34%
116.css
gzipped
4416b 100.00%
1489b   33.72%
2923b   66.19%
969b   21.94%
2800b   63.41%
967b   21.90%
2923b   66.19%
969b   21.94%
117.css
gzipped
6959b 100.00%
1730b   24.86%
4451b   63.96%
1101b   15.82%
3975b   57.12%
1049b   15.07%
4451b   63.96%
1101b   15.82%
118.css
gzipped
7893b 100.00%
2026b   25.67%
5012b   63.50%
1217b   15.42%
4803b   60.85%
1160b   14.70%
5096b   64.56%
1221b   15.47%
119.css
gzipped
5923b 100.00%
1836b   31.00%
4205b   70.99%
1294b   21.85%
4146b   70.00%
1285b   21.70%
4217b   71.20%
1296b   21.88%
120.css
gzipped
4776b 100.00%
1524b   31.91%
2648b   55.44%
867b   18.15%
2549b   53.37%
865b   18.11%
2656b   55.61%
871b   18.24%
121.css
gzipped
4208b 100.00%
1570b   37.31%
2133b   50.69%
809b   19.23%
2144b   50.95%
808b   19.20%
2139b   50.83%
809b   19.23%
122.css
gzipped
7929b 100.00%
2763b   34.85%
4240b   53.47%
1314b   16.57%
4182b   52.74%
1270b   16.02%
4220b   53.22%
1294b   16.32%
123.css
gzipped
6057b 100.00%
1707b   28.18%
4307b   71.11%
1223b   20.19%
4284b   70.73%
1209b   19.96%
4310b   71.16%
1225b   20.22%
124.css
gzipped
5284b 100.00%
1585b   30.00%
3533b   66.86%
1035b   19.59%
3401b   64.36%
1032b   19.53%
3539b   66.98%
1034b   19.57%
125.css
gzipped
4197b 100.00%
1530b   36.45%
2941b   70.07%
1045b   24.90%
2935b   69.93%
1032b   24.59%
2939b   70.03%
1047b   24.95%
126.css
gzipped
4727b 100.00%
1641b   34.72%
3132b   66.26%
1088b   23.02%
2870b   60.72%
1021b   21.60%
3110b   65.79%
1073b   22.70%
127.css
gzipped
5482b 100.00%
1577b   28.77%
3739b   68.21%
1125b   20.52%
3640b   66.40%
1141b   20.81%
3739b   68.21%
1125b   20.52%
128.css
gzipped
4777b 100.00%
1777b   37.20%
2856b   59.79%
984b   20.60%
2838b   59.41%
976b   20.43%
2859b   59.85%
986b   20.64%
130.css
gzipped
5115b 100.00%
1587b   31.03%
3650b   71.36%
1033b   20.20%
3503b   68.48%
1035b   20.23%
3674b   71.83%
1041b   20.35%
131.css
gzipped
4886b 100.00%
1533b   31.38%
3537b   72.39%
1064b   21.78%
3475b   71.12%
1049b   21.47%
3543b   72.51%
1065b   21.80%
132.css
gzipped
3946b 100.00%
1212b   30.71%
2688b   68.12%
805b   20.40%
2660b   67.41%
801b   20.30%
2688b   68.12%
805b   20.40%
133.css
gzipped
4759b 100.00%
1556b   32.70%
3499b   73.52%
1115b   23.43%
3424b   71.95%
1125b   23.64%
3499b   73.52%
1112b   23.37%
134.css
gzipped
4887b 100.00%
1641b   33.58%
3945b   80.72%
1257b   25.72%
3885b   79.50%
1259b   25.76%
3945b   80.72%
1257b   25.72%
135.css
gzipped
5021b 100.00%
1650b   32.86%
3512b   69.95%
1168b   23.26%
3531b   70.32%
1157b   23.04%
3524b   70.19%
1158b   23.06%
136.css
gzipped
4496b 100.00%
1333b   29.65%
2903b   64.57%
841b   18.71%
2797b   62.21%
825b   18.35%
2903b   64.57%
841b   18.71%
137.css
gzipped
6234b 100.00%
1660b   26.63%
4097b   65.72%
1038b   16.65%
3640b   58.39%
1028b   16.49%
4102b   65.80%
1041b   16.70%
138.css
gzipped
5842b 100.00%
1307b   22.37%
4494b   76.93%
913b   15.63%
3129b   53.56%
882b   15.10%
4494b   76.93%
913b   15.63%
139.css
gzipped
3556b 100.00%
1090b   30.65%
2498b   70.25%
669b   18.81%
2254b   63.39%
657b   18.48%
2501b   70.33%
670b   18.84%
140.css
gzipped
5025b 100.00%
1475b   29.35%
3936b   78.33%
1091b   21.71%
3882b   77.25%
1091b   21.71%
3942b   78.45%
1091b   21.71%
141.css
gzipped
3780b 100.00%
1480b   39.15%
2582b   68.31%
977b   25.85%
2446b   64.71%
966b   25.56%
2577b   68.17%
975b   25.79%
142.css
gzipped
4708b 100.00%
1610b   34.20%
3389b   71.98%
1115b   23.68%
3335b   70.84%
1103b   23.43%
3389b   71.98%
1115b   23.68%
143.css
gzipped
4608b 100.00%
1516b   32.90%
3009b   65.30%
1038b   22.53%
3037b   65.91%
1036b   22.48%
3009b   65.30%
1038b   22.53%
144.css
gzipped
4323b 100.00%
1479b   34.21%
2704b   62.55%
945b   21.86%
2705b   62.57%
947b   21.91%
2700b   62.46%
944b   21.84%
145.css
gzipped
4813b 100.00%
1589b   33.01%
3083b   64.06%
1047b   21.75%
2922b   60.71%
1021b   21.21%
3072b   63.83%
1032b   21.44%
146.css
gzipped
4363b 100.00%
1542b   35.34%
3040b   69.68%
960b   22.00%
2782b   63.76%
959b   21.98%
3040b   69.68%
954b   21.87%
147.css
gzipped
5672b 100.00%
1658b   29.23%
4430b   78.10%
1197b   21.10%
3984b   70.24%
1181b   20.82%
4456b   78.56%
1204b   21.23%
148.css
gzipped
9479b 100.00%
2701b   28.49%
5040b   53.17%
1183b   12.48%
4695b   49.53%
1159b   12.23%
5040b   53.17%
1183b   12.48%
149.css
gzipped
5250b 100.00%
1545b   29.43%
3344b   63.70%
1035b   19.71%
3298b   62.82%
1044b   19.89%
3347b   63.75%
1036b   19.73%
150.css
gzipped
6514b 100.00%
1571b   24.12%
4541b   69.71%
1065b   16.35%
4437b   68.11%
1051b   16.13%
4577b   70.26%
1072b   16.46%
151.css
gzipped
5122b 100.00%
1539b   30.05%
3829b   74.76%
1148b   22.41%
3681b   71.87%
1120b   21.87%
3818b   74.54%
1140b   22.26%
152.css
gzipped
4885b 100.00%
1487b   30.44%
3600b   73.69%
1014b   20.76%
3515b   71.95%
1024b   20.96%
3604b   73.78%
1015b   20.78%
153.css
gzipped
4889b 100.00%
1527b   31.23%
3625b   74.15%
1134b   23.19%
3671b   75.09%
1151b   23.54%
3625b   74.15%
1134b   23.19%
154.css
gzipped
5709b 100.00%
1690b   29.60%
4435b   77.68%
1210b   21.19%
3989b   69.87%
1192b   20.88%
4461b   78.14%
1216b   21.30%
155.css
gzipped
3680b 100.00%
1403b   38.13%
2308b   62.72%
829b   22.53%
2307b   62.69%
835b   22.69%
2314b   62.88%
828b   22.50%
156.css
gzipped
4838b 100.00%
1615b   33.38%
3458b   71.48%
1169b   24.16%
3478b   71.89%
1180b   24.39%
3458b   71.48%
1169b   24.16%
157.css
gzipped
3895b 100.00%
1460b   37.48%
2582b   66.29%
795b   20.41%
2464b   63.26%
797b   20.46%
2582b   66.29%
795b   20.41%
158.css
gzipped
4248b 100.00%
1563b   36.79%
2789b   65.65%
1028b   24.20%
2813b   66.22%
1027b   24.18%
2789b   65.65%
1028b   24.20%
159.css
gzipped
5431b 100.00%
1582b   29.13%
3280b   60.39%
1067b   19.65%
3237b   59.60%
1061b   19.54%
3283b   60.45%
1068b   19.66%
160.css
gzipped
4843b 100.00%
1511b   31.20%
3914b   80.82%
1094b   22.59%
3776b   77.97%
1098b   22.67%
3914b   80.82%
1094b   22.59%
161.css
gzipped
4287b 100.00%
1718b   40.07%
2551b   59.51%
1048b   24.45%
2530b   59.02%
1038b   24.21%
2551b   59.51%
1048b   24.45%
162.css
gzipped
7440b 100.00%
2325b   31.25%
4890b   65.73%
1304b   17.53%
4402b   59.17%
1273b   17.11%
4888b   65.70%
1303b   17.51%
163.css
gzipped
6034b 100.00%
1476b   24.46%
4243b   70.32%
1064b   17.63%
3734b   61.88%
1045b   17.32%
4246b   70.37%
1063b   17.62%
164.css
gzipped
3407b 100.00%
1296b   38.04%
2144b   62.93%
761b   22.34%
2023b   59.38%
749b   21.98%
2144b   62.93%
760b   22.31%
165.css
gzipped
4405b 100.00%
1558b   35.37%
3000b   68.10%
1048b   23.79%
3022b   68.60%
1038b   23.56%
3015b   68.44%
1048b   23.79%
166.css
gzipped
6165b 100.00%
1777b   28.82%
4624b   75.00%
1244b   20.18%
4351b   70.58%
1202b   19.50%
4624b   75.00%
1244b   20.18%
167.css
gzipped
5514b 100.00%
1524b   27.64%
3574b   64.82%
1028b   18.64%
3497b   63.42%
1025b   18.59%
3646b   66.12%
1028b   18.64%
168.css
gzipped
3852b 100.00%
1381b   35.85%
2389b   62.02%
812b   21.08%
2407b   62.49%
813b   21.11%
2389b   62.02%
812b   21.08%
169.css
gzipped
6189b 100.00%
1595b   25.77%
4575b   73.92%
1189b   19.21%
4114b   66.47%
1167b   18.86%
4584b   74.07%
1189b   19.21%
170.css
gzipped
4497b 100.00%
1665b   37.02%
3389b   75.36%
1143b   25.42%
3332b   74.09%
1132b   25.17%
3391b   75.41%
1142b   25.39%
171.css
gzipped
4718b 100.00%
1579b   33.47%
2978b   63.12%
920b   19.50%
2828b   59.94%
918b   19.46%
2976b   63.08%
922b   19.54%
172.css
gzipped
7255b 100.00%
1925b   26.53%
4936b   68.04%
1186b   16.35%
4808b   66.27%
1176b   16.21%
4945b   68.16%
1189b   16.39%
173.css
gzipped
3981b 100.00%
1376b   34.56%
3007b   75.53%
955b   23.99%
2983b   74.93%
961b   24.14%
3007b   75.53%
952b   23.91%
174.css
gzipped
4172b 100.00%
1344b   32.21%
3024b   72.48%
953b   22.84%
3012b   72.20%
937b   22.46%
3024b   72.48%
940b   22.53%
175.css
gzipped
4592b 100.00%
1328b   28.92%
2748b   59.84%
909b   19.80%
2638b   57.45%
914b   19.90%
2753b   59.95%
913b   19.88%
176.css
gzipped
3231b 100.00%
1252b   38.75%
2387b   73.88%
854b   26.43%
2362b   73.10%
856b   26.49%
2390b   73.97%
857b   26.52%
177.css
gzipped
6261b 100.00%
1669b   26.66%
3842b   61.36%
1080b   17.25%
3738b   59.70%
1082b   17.28%
3843b   61.38%
1092b   17.44%
178.css
gzipped
5249b 100.00%
1673b   31.87%
3111b   59.27%
1017b   19.38%
2930b   55.82%
1012b   19.28%
3117b   59.38%
1019b   19.41%
179.css
gzipped
5853b 100.00%
1606b   27.44%
3597b   61.46%
1028b   17.56%
3420b   58.43%
1010b   17.26%
3597b   61.46%
1028b   17.56%
180.css
gzipped
4566b 100.00%
1247b   27.31%
2888b   63.25%
793b   17.37%
2230b   48.84%
749b   16.40%
2894b   63.38%
792b   17.35%
181.css
gzipped
4420b 100.00%
1325b   29.98%
3146b   71.18%
951b   21.52%
3002b   67.92%
950b   21.49%
3146b   71.18%
946b   21.40%
182.css
gzipped
4588b 100.00%
1466b   31.95%
3027b   65.98%
900b   19.62%
2906b   63.34%
898b   19.57%
3027b   65.98%
900b   19.62%
184.css
gzipped
5426b 100.00%
1749b   32.23%
4127b   76.06%
1290b   23.77%
4171b   76.87%
1302b   24.00%
4127b   76.06%
1290b   23.77%
185.css
gzipped
4632b 100.00%
1389b   29.99%
3325b   71.78%
952b   20.55%
3326b   71.80%
958b   20.68%
3320b   71.68%
951b   20.53%
186.css
gzipped
3932b 100.00%
1319b   33.55%
2768b   70.40%
935b   23.78%
2736b   69.58%
928b   23.60%
2786b   70.85%
939b   23.88%
187.css
gzipped
5687b 100.00%
1509b   26.53%
3138b   55.18%
936b   16.46%
2879b   50.62%
908b   15.97%
3138b   55.18%
936b   16.46%
188.css
gzipped
5893b 100.00%
1750b   29.70%
3633b   61.65%
1105b   18.75%
3566b   60.51%
1126b   19.11%
3632b   61.63%
1105b   18.75%
189.css
gzipped
6378b 100.00%
1804b   28.28%
3714b   58.23%
1155b   18.11%
3607b   56.55%
1147b   17.98%
3717b   58.28%
1154b   18.09%
190.css
gzipped
4385b 100.00%
1417b   32.31%
2598b   59.25%
920b   20.98%
2443b   55.71%
920b   20.98%
2604b   59.38%
921b   21.00%
191.css
gzipped
5072b 100.00%
1569b   30.93%
3923b   77.35%
1157b   22.81%
3799b   74.90%
1147b   22.61%
3926b   77.41%
1159b   22.85%
192.css
gzipped
5042b 100.00%
1587b   31.48%
3236b   64.18%
1020b   20.23%
3225b   63.96%
1014b   20.11%
3246b   64.38%
1013b   20.09%
193.css
gzipped
4411b 100.00%
1764b   39.99%
2461b   55.79%
872b   19.77%
2485b   56.34%
873b   19.79%
2465b   55.88%
874b   19.81%
194.css
gzipped
6109b 100.00%
1612b   26.39%
4162b   68.13%
1145b   18.74%
4031b   65.98%
1157b   18.94%
4162b   68.13%
1145b   18.74%
195.css
gzipped
4990b 100.00%
1484b   29.74%
3589b   71.92%
1038b   20.80%
3595b   72.04%
1041b   20.86%
3592b   71.98%
1039b   20.82%
196.css
gzipped
4859b 100.00%
1537b   31.63%
3569b   73.45%
1152b   23.71%
3523b   72.50%
1151b   23.69%
3578b   73.64%
1152b   23.71%
197.css
gzipped
6709b 100.00%
1769b   26.37%
4534b   67.58%
1179b   17.57%
4136b   61.65%
1162b   17.32%
4539b   67.66%
1177b   17.54%
198.css
gzipped
4996b 100.00%
1476b   29.54%
3214b   64.33%
975b   19.52%
3078b   61.61%
959b   19.20%
3211b   64.27%
976b   19.54%
199.css
gzipped
4128b 100.00%
1354b   32.80%
2941b   71.25%
914b   22.14%
2752b   66.67%
917b   22.21%
2987b   72.36%
923b   22.36%
200.css
gzipped
5029b 100.00%
1491b   29.65%
3714b   73.85%
1027b   20.42%
3695b   73.47%
1020b   20.28%
3723b   74.03%
1028b   20.44%
201.css
gzipped
5752b 100.00%
1500b   26.08%
3723b   64.73%
973b   16.92%
3463b   60.21%
956b   16.62%
3723b   64.73%
973b   16.92%
202.css
gzipped
5689b 100.00%
1633b   28.70%
4116b   72.35%
1214b   21.34%
3944b   69.33%
1179b   20.72%
4124b   72.49%
1214b   21.34%
203.css
gzipped
3777b 100.00%
1359b   35.98%
2643b   69.98%
978b   25.89%
2649b   70.14%
968b   25.63%
2642b   69.95%
977b   25.87%
204.css
gzipped
11101b 100.00%
1499b   13.50%
4480b   40.36%
1054b   9.49%
4033b   36.33%
1063b   9.58%
4479b   40.35%
1055b   9.50%
205.css
gzipped
4048b 100.00%
1413b   34.91%
2840b   70.16%
967b   23.89%
2862b   70.70%
977b   24.14%
2840b   70.16%
965b   23.84%
206.css
gzipped
4565b 100.00%
1270b   27.82%
3190b   69.88%
883b   19.34%
3083b   67.54%
882b   19.32%
3190b   69.88%
883b   19.34%
207.css
gzipped
3056b 100.00%
1203b   39.37%
2149b   70.32%
798b   26.11%
2113b   69.14%
798b   26.11%
2152b   70.42%
798b   26.11%
208.css
gzipped
4805b 100.00%
1462b   30.43%
3553b   73.94%
1041b   21.66%
3477b   72.36%
1029b   21.42%
3559b   74.07%
1042b   21.69%
209.css
gzipped
7164b 100.00%
1795b   25.06%
4765b   66.51%
1187b   16.57%
4461b   62.27%
1187b   16.57%
4765b   66.51%
1187b   16.57%
210.css
gzipped
6601b 100.00%
1934b   29.30%
4236b   64.17%
1279b   19.38%
4235b   64.16%
1270b   19.24%
4234b   64.14%
1278b   19.36%
211.css
gzipped
5181b 100.00%
1498b   28.91%
3725b   71.90%
1052b   20.30%
3679b   71.01%
1040b   20.07%
3768b   72.73%
1053b   20.32%
212.css
gzipped
5857b 100.00%
2239b   38.23%
4043b   69.03%
1578b   26.94%
4030b   68.81%
1566b   26.74%
4040b   68.98%
1571b   26.82%
213.css
gzipped
7701b 100.00%
1768b   22.96%
5827b   75.67%
1307b   16.97%
5293b   68.73%
1268b   16.47%
5879b   76.34%
1337b   17.36%
Avg size after min 100% 66.41% 63.29% 66.47%
Avg size gzip+min 30.36% 19.63% 19.44% 19.62%
 

Web Testing Framework

Sunday, June 20th, 2010

There's a new version and now hosted on AMO (addons.mozilla.org). Get the new version there and it will take care of auto-updates in the future.

WFT

Web Testing Framework (WTF) is an extension to YSlow that tests for the following shady web dev practices:

  • Use of <blink>
  • Use of <marquee>
  • Use of <font>
  • Missing doctype
  • Use of spacer GIFs
  • Use of <a href="#"..> and <a href="javascript:...>

Gimme!

Note that this extension requires Firebug and YSlow2

Help

Please report any bugs here and also suggestions for more checks for what you think is bad practice (and is technically possible to test by a tool, as opposed to a human)

Thanks!

Thanks to Ryan Grove for inspiration with the naming of the new tool :)

Motivation

The motivation is mainly to demonstrate how easy it is to create new extensions and new checks to YSlow. They don't even need to be performance-related.

There are two basic concepts. 1. A rule is a type of check like "use gzip". 2. The pool of available rules can be combined into rulesets. The API is therefore just:

YSLOW.registerRule({...});
YSLOW.registerRuleset({...});

The first takes an object containing name, id, info about the new check and a lint() function that performs the check. The second accepts a config object - which rules go into the ruleset.

Check this file - that's all it takes to do a new extension.

 

YUI CSS min – part 3 – hacks

Friday, May 21st, 2010

The previous parts are here (building and testing) and here (what gets minified). Now let's see how YUI CSS min handles CSS hacks.

As you know CSS hacks often use errors in CSS parsers in browsers to target specific browser versions and supply additional rules to work around other issues in said browsers. That makes any CSS tool's job slightly more challenging. Not only does the tool have to avoid repeating the browsers errors, but also has to understand what browsers got wrong and support it too. Fun stuff. Isn't it a joy being a web developer?

So here are some hacks that are tested to work with the YUICopmpressor's CSS min.

Underscore/star hack

The simplest ever hack to target IE6 and IE7. In the example below normal browsers see 1px dropping _width and *width as invalid, IE7 ignores the *, drops the _width as invalid and sees 3pt, IE6 ignores the _ and sees _width as width, so it sees 2em.

CSS min doesn't parse and doesn't understand CSS properties, so it accepts pretty much any property.

Before:

#element {
    width: 1px;
    *width: 3pt;
    _width: 2em;
}

After:

#element{width:1px;*width:3pt;_width:2em}

Child selector hack

CSS min strips comments but there is this child selector hack people use to hide declarations from IE7 and below.

CSS min retains empty comments that immediately follow > (thanks go out to Chris Burroughs)

Before:

html >/**/ body p {
    color: blue; 
}

After:

html>/**/body p{color:blue}

IE5/Mac hack

This hack targets IE5/Mac, if anyone still worries about this browser. The hack is retained after minification, only it's minified.

Before:

/* Ignore the next rule in IE mac \*/
.selector {
   color: khaki;
}
/* Stop ignoring in IE mac */

After:

/*\*/.selector{color:khaki}/**/

Box model hack

This hack uses valid CSS and there's no special use of comments so it's retained.

Before:

#elem { 
    width: 100px; /* IE */
    voice-family: "\"}\""; 
    voice-family:inherit;
    width: 200px; /* others */
}
html>body #elem {
    width: 200px; /* others */
}

After:

#elem{width:100px;voice-family:"\"}\"";voice-family:inherit;width:200px}html>body #elem{width:200px}

Seems like the code highlighter chokes here though. It ain't easy :)

That's all, folks!

Thanks and please, feel free to suggest improvements and report bugs. Also play with the web UI of the JS-version here to see for yourself what it does to your code.

 

YUI CSS Min – part 2

Thursday, May 20th, 2010

The first part is here. It was more about building the YUICompressor, writing and running test cases. Now let's see what the compressor does exactly to your CSS.

BTW, you can play with the web UI to see for yourself how the minifier works.

Stripping comments and white space

This is the bare minimum a minifier can do. And when it comes to CSS, this is also the place where ther biggest improvement comes from. In JS for example you can rename variables and save bytes, but in CSS the possibilities are more limited. No shorter way to say text-decoration, unfortunately.

So before:

/***** 
  classmates stuff
*****/
.classmates {
    /* after 10 years */
    weight: considerable;
}

After:

.classmates{weight:considerable}

Special comments

Stripping comments is nice but not always ok. Sometimes you need to retain copyright information. Use ! at the beginning of the comment to mark the comment as special.

Before:

/*!
  (c) copyright copyleft
*/
.classmates {
    /* after 10 years */
    weight: considerable;
}

After:

/*!
  (c) copyright copyleft
*/.classmates{weight:considerable}

Thanks to the charmingly insisting Billy Hoffman and the valid case he presented, the bang (!) itself is preserved too. This way you can safely double minify. Also lint tools (such as Zoompf, YSlow and PageSpeed) can see the ! and conclude that this comment is there intentionally, not because you forgot to minify.

Striping last semi-colon

The last semi-colon in a declaration block is out. So keep it in your source for maintenance purposes and let the minifier take care of stripping it out.

Before:

a {
  one: 1;
  two: 2;
}

After:

a{one:1;two:2}

Extra semi-colons

One semi-colon is all you need, so the minifier will strip an accidentally added one.

Before:

p :link { 
  ba: zinga;;;
  foo: bar;;;
}

After:

p :link{ba:zinga;foo:bar}

No empty declarations

Empty declaration blocks don't do anything, so why send them over the net?

Before:

.empty { ;}

After:

(nothing...)

Zero values

A zero is a zero. Zero pixels or % or centimeters or whatever, it's still zero. Also sometimes (when everything is a zero) you need just one zero instead of four, or three or two.

Before:

a { 
  margin: 0px 0pt 0em 0%;
  background-position: 0 0ex;
  padding: 0in 0cm 0mm 0pc
}

After:

a{margin:0;background-position:0 0;padding:0}

Floats

For values such as 0.something, the 0 is not needed.

Before:

::selection { 
  margin: 0.6px 0.333pt 1.2em 8.8cm;
}

After:

::selection{margin:.6px .333pt 1.2em 8.8cm}

Colors values

RGB color values are nice, but not the most concise form. Make them hex. Also AABBCC hex can be the shorter ABC. But don't touch RGBA and don't touch the IE filter values in quotes.

Before:

.color {
  me: rgb(123, 123, 123);
  impressed: #ffeedd;
  background: none repeat scroll 0 0 rgb(255, 0,0);
}

After:

.color{me:#7b7b7b;impressed:#fed;background:none repeat scroll 0 0 #f00}

Before:

.cantouch {
  alpha: rgba(1, 2, 3, 4);
  filter: chroma(color="#FFFFFF");
}

After (no color minification) :

.cantouch{alpha:rgba(1,2,3,4);filter:chroma(color="#FFFFFF")}

Single charsets

Only one charset is allowed per stylesheet. So, if there's more than one, strip it. It may happen when merging several stylesheets into one.

Before:

@charset "utf-8";
#foo {
  border-width: 1px;
}
 
/* second css, merged */
@charset "another one";
#bar {
  border-width: 10px;
}

After:

@charset "utf-8";#foo{border-width:1px}#bar{border-width:10px}

Alpha opacity

There's a shorter way to write opacity filter for IE.

So before:

code {
   -ms-filter: "PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80)"; /* IE 8 */
   filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);       /* IE 4-7 */
}

After:

code{-ms-filter:"alpha(opacity=80)";filter:alpha(opacity=80)}

There are more filters that could be shorten besides the opacity, but MSDN suggests the longer syntax should be used, So a bit more experimentation is needed here...

Thanks!

Whew, sort of a lengthy post. Thank you for reading and coming up next time... hacks :)

If you have ideas, comments, your bug reports are welcome

 

YUICompressor’s CSSMin

Wednesday, March 10th, 2010

Honored to be a part of the YUI project, I am now helping with the maintenance of the CSSMin part of the YUICompressor. My changes are now part of the trunk on github, so I'm official. Next on the agenda is documenting the thing, so that's what I'll try to do here, maybe in a few posts. You know, divide and conquer.

PHP, Java and a JavaScript port

Originally written in PHP by Isaac Schlueter and ported to Java by Julien Lecomte, CSSMin got a JavaScript port by yours truly some time ago. Because, after all, JavaScript is the language of the web, isn't it?

You can play with the latest git version of the JS port online here.

I'm also happy to report that the JS port is now used in PageSpeed and YSlow (as you probably know Firefox extensions are written in JavaScript)

Page Speed

YSlow

Building

If you want to play on your own with the source version of YUICompressor without waiting for the next release, you can build it like so:

  1. Checkout or download the code from http://github.com/yui/yuicompressor/
  2. Navigate to the root yuicompressor/ directory
  3. Type ant and hit enter

In order for this to work you need a somewhat recent Java SDK installed and also Ant running. (On the Mac, just do port install apache-ant to get Ant)

This is for the Java version, the JS version needs no building, of course.

Tests

There's a bunch of new tests now (and if you want to contribute to the project, you can always write more tests and test cases for any bugs), you can run them with the suite script that Isaac wrote:

  1. cd tests/
  2. ./suite.sh

One thing I added (and loved it) is to run the tests using the JS port as well. Since the JS min part is using Mozilla's Rhino (slightly modified), Rhino is part of the code. So I'm using this already available JavaScript interpreter to run the JS port. Convenient.

The procedure to write new tests is simple:

  1. Create source CSS file in the tests/ directory, e.g. new-test.css
  2. Create a new file with the expected result and name it with a .min extension, e.g. new-test.css.min

You can use the handy-dandy online version to help with the tests creation.

Next time

With those details out of the way, the next time I'll talk more about the different things that CSSMin does to your CSS code. Thanks for reading!

 

One-click Minifier Gadget (OMG) – initial checkin

Sunday, January 31st, 2010

So I've been thinking and talking to folks about this idea of having one-stop shop for all your minification needs. Minification of JS and CSS as well as image optimization helps site performance by reducing download sizes. This is good. But not a lot of people do it.

People don't do it, because it's a PITA :) It's simple enough, but with deadlines upon you and all that, you don't want to do an extra step. That's why having a build process helps, by automating this. But setting up a build process is yet another PITA. So it goes.

So my idea was to help busy designers and developers, that wouldn't invest their time researching which minifiers are good, downloading setting up, learning about the 10+ PNG optimization tools... That's how the the idea for the one-click OMG tool came about. (One-drag is more appropriate, come to think of it...) One tool that runs on all operating systems - Win, Mac, Linux - and delivers all minification and optimization tools you need as one package.

Running

Running the tool is as simple as drag/dropping a bunch of files and directories. Here I've dropped "wordpress" directory. The tool recursively looks into the dropped files for things it can optimize. More information here.

OMG screenshot

Download

Version 0.0.1 is here. It doesn't do image optimization, only JS and CSS minification, but please feel free to download and give it a shot. Unzip the package for your OS and run omg.exe (Windows), OMG.app (Mac), or the omg binary (Linux)

Open source

The code is on GitHub. Fork and enjoy.

The developer's notes are there too - how to setup, run, package. Also a list of todos if you want to help.

Next?

This is just a preliminary version. Feel free to join, comment, suggest. Hate the name? Say so :)

Personally, looks like my plate is very full for the next moth or two, so I probably won't be actively working on the tool. I hope though the foundation is good enough and relatively documented, should be easy to pick up if anyone's interested in contributing.

Built with XUL

This has been a learning experience for me with XULRunner. I loved it. I love the idea of being able to create cross-OS desktop apps with JavaScript alone.

Behind the scenes, I'm using my JavaScript port of YUICompressor's CSSmin and Doug Crockford's JSMin. JSMin should be replaced with YUICompressor (or Google closure compiler) in the next release.

 

Big list of image optimization tools

Saturday, December 12th, 2009

2010 update:
Lo, the Web Performance Advent Calendar hath moved

Dec 12 This post is part of the 2009 performance advent calendar experiment (12 articles down, 12 more to go). Stay tuned for the articles to come.

Let's continue the topic of reducing file sizes started with the previous post and talk about making images smaller.

Engineer's guide to smaller images

Just to set the frame of the discussion - this is not about using Photoshop or setting the quality of the JPEGs and so on. I realize that we, web developers, wear many hats - we're designers, client/server coders, Apache/Linux admins, database heros. But this post is not about using image programs and assumes that you or your designer has already created the images to be used on the site with the appropriate colors, quality and so on.

Now, repeat after me: you should never take an image from the designer and put it up on the web.

Most often this image is bigger than it should be. It's not the designer's fault, it's usually the software used to produce the image.

You shouldn't put an image up on your server before running it through a few tools. These tools are free, open source, cross-platform and can be run on the command line, hence scripted and run in batches over a large number of files - by you, or even better, automatically by a build deployment process. It's OK to batch-run those files without human intervention, because these tools simply optimize the files, they don't change the pixel information, so the "after" images look exactly like "before", only smaller.

Selecting the right file format

The first step towards leaner images is to select the correct file format. There are three options:

  1. JPEG for photos. Photos contain millions of colors and smooth transitions of colors. Blue skies, clouds, sunset, your dog, lolcats - all photos.
  2. GIF is for the occasional "loading..." progress animation. This is it, no other uses for GIFs.
  3. PNG is for everything that's not a photo or an animation. That includes all icons, graphs, buttons, gradients, and what not. Any image with sharp transitions of colors. Think (but don't use for) text. Sharp transitions become "dirty" in JPEG.

PNG is an interesting topic for a follow up post, for now let's stop here. If it's not a photo, it should be PNG. An edge case is a screenshot for example. Depends on what's on the screen of course, but most often JPEG will give a smaller size if you can live with the artifacts around the sharp edges (like text).

Optimizing GIFs

Unfortunately many people still use GIFs even for non-animated images. That's a mistake. PNG is a superior format and yields smaller file sizes.

People still use GIFs because they think either that a/ GIF is smaller than PNG or b/ there's lack of support for PNG in browsers. These are misconceptions and I'll talk more about them tomorrow.

So, the way to optimize a GIF is to convert it to PNG.

You can use many tools to turn your GIFs to PNGs, including ImageMagick and OptiPNG.

# option 1: ImageMagick (if you know the filename)
$ convert logo.gif logo.png

# option 2: ImageMagick again (if you just convert all files in a directory)
$ mogrify -format png *.gif

# option 3: OptiPNG
$ optipng *.gif

These are just some of the options, I'm sure there are others.

After the conversion you can optimize the new PNGs like all other PNGs

Optimizing PNGs

There are various ways to write a PNG file. Unfortunately not all image editing programs do a good job at writing PNGs for the minimal file size.

Luckily, to fill the void, there's a great number of tools that excel in writing small PNGs. There are different ways to optimize a PNG:

  1. Stripping out "chunks" - PNG is an extensible format. Extensions come in the form of chunks and most chunks are not needed for the web.
  2. Reducing the number of colors and switching between PNG types - truecolor PNG, grayscale, palette...
  3. Chosing the best "filter". Filters are a pre-compression step. You can compress any type of file, but when you know the file is an image, you can do better. Filters are for this purpose.
  4. Optimizing the actual DEFLATE compression algorithm

Different tools specialize in one or more of these areas. So the more tools you run, the better the results will be. But you have to run at least one tool, always. You'll be surprised how unoptimized are most PNGs coming from common commercial image programs.

So, to optimize a PNG you shoul run as many of the following programs as possible:

# optipng (skip -o7 to run faster)
$ optipng -o7 my.png

# pngcrush (skip -brute to run faster)
$ pngcrush -rem alla -brute -reduce my.png my.png.temp
$ mv my.png.temp my.png

# pngout - closed source, non-windows binaries here
# (add parameter -s2 to run faster)
$ pngout my.png

# advpng (use -z2 to run faster)
$ advpng -z4 my.png

# deflopt - windows only
$ deflopt my.png

Other tools to note include PNGrewrite, PNGNQ and PNGquant, but they are limited because they deal only with PNG8 (256 colors) files. PNGNQ and PNGQuant are actually converters from truecolor to PNG8, so they are not guaranteed to be lossless. PNGreqwrute is safe to use, it will just silently fail if the file has more than 256 colors, so there's nothing to lose.

Oh, and another, excellent tool - PNGOptimizer, windows-only has both command line interface and a GUI.

PNGSlim for the hardcore PNG optimization

If you're really serious about optimizing your PNGs, the tool is called PNGSlim. It's a Windows-only batch file that runs pretty much all tools above and runs them (especially PNGOut) with all kinds of parameters, hundreds of times. So it can take a while to run.

Optimizing JPEGs

JPEG is a lossy format (you lose information every time you save it, even if you choose 100% quality), but there are some operations that can be done losslessly - such as tweaking comments and meta information, cropping, rotating to 90, 180, 270 degrees. The tool that does this magic is called JPEGTran and is likely already on your unix/linux box. If not - here's how to install it (for Windows - get the .exe here)

So to optimize a JPEG losslessly you remove the meta information and optimize the so called Huffman tables. For bigger JPEGs (bigger than 10K) you can also convert the image to progressive coding.

# strip meta and optimize
$ jpegtran -copy none source.jpg > destination.jpg

# strip meta and convert to progressive coding
$ jpegtran -copy none -progressive source.jpg > destination.jpg

# keep all meta but still optimize
$ jpegtran -copy all source.jpg > destination.jpg

Important note on stripping meta

Only strip meta information from images you own the rights for and have permission. Otherwise you're committing a crime. Photographers put important copyright information in meta markers.

Optimizing GIF animations

Remember - no GIFs other than animations. For animations, run GIFsicle (pronounced "yo' mama" :) ) berfore you put them up:

# GIFSicle
$ gifsicle -O2 source.gif > destination.gif 

More tools?

These are the core tools for image optimization. There's a number of wrapper tools that are more or less UIs on top of these, because there are people who don't like consoles (really?!). I'll list the few that I can think of, please comment if you know of others, especially for windows. It's nice to give nice UIs to give to designers so they can drag-drop optimize images too.

  • smush.it, created by yours truly and Nicole Sullivan, now part of YSlow - runs pngcrush, jpegtran, gifsicle
  • PageSpeed runs optipng, jpegtran
  • PunyPNG - originally inspired by smush.it, but more advanced
  • ImageOptim - Nice easy UI for Mac, runs most of the tools above (hope your company firewall doesn't block the site because of the domain name ;) )
  • PNGSquash another UI for Mac, runs advpng, pngcrush, optipng
  • PNG Monster is for Windows, runs many PNG tools, you can drag/drop on it
  • IrfanView - my favorite image viewer for Windows has a plugin to use PNGOut
  • WP-Smushit is a WordPress plugin by Alex Dunae which sends all your image uploads to smush.it for optimization. Talk about easy!

More reading

Series of articles on YUIBlog:

presentations:

and more:

Thanks!

Thank you for reading. Now you have a whole lot of tools/toys to install and play with. Image optimization is an easy way to improve performance, it's just running a bunch of tools. You don't need to worry that the quality will suffer (so the designer won't be disappointed in you :) ). So you can only win. You may win quite a bit, you may win just a little (anywhere between 5 to 30% savings is what I've seen on random live sites when I was working on and testing smush.it). The thing is you'll almost always win something.

And because it's human to forget to optimize the images before you push them live, do take the time to setup the optimization step as part of the automated deployment process.

And to summarize once again the steps of what this automated process would be:

  1. Convert GIFs to PNG. Then relax and take a deep breath (instead of flaming the unfortunate soul who created the GIFs) while you string replace "gif" with "png" in all your styleshets
  2. Run PNGs through optipng, pngcrush, pngout, any or all of the tools listed above
  3. Run JPEGtran
  4. Run GIFsicle
 

Reducing the payload: compression, minification, 204s

Friday, December 11th, 2009

2010 update:
Lo, the Web Performance Advent Calendar hath moved

Dec 11 This post is part of the 2009 performance advent calendar experiment. Stay tuned for the next articles.

After removing all the extra HTTP requests you possibly can from your waterfall, it's time to make sure that those that are left are as small as they can be. Not only this makes your pages load faster, but it also helps you save on the bandwidth bill. Your weapons for fighting overweight component include: compression and minification of text-based files such as scripts and styles, recompression of some downloadable files, and zero-body components. (A follow-up post will talk about optimizing images.)

Gzipping plain text components

Hands down the easiest and at the same time quite effective optimization - turning on gzipping for all plain text components. It's almost a crime if you don't do it. Doesn't "cost" any development time, just a simple flip of a switch in Apache configuration. And the results could be surprisingly pleasant.

When Bill Scott joined Netflix, he noticed that gzip is not on. So they turned it on. And here's the result - the day they enabled it, the outbound traffic pretty much dropped in half (slides)

Netflix traffic after turning on gzipping

Gzip FAQ

How much improvement can you expect from gzip?
On average - 70% reduction of the file size!
Any drawbacks?
Well, there's a certain cost associated with the server compressing the response and the browser uncompressing it, but it's negligible compared to the benefits you get
Any browser quirks?
Sure, IE6, of course. But only in IE6 service pack 1 and fixed for after that. You can boldly ignore this edge case, but if you're extra paranoid you can disable gzip for this user agent
How to tell if it's on?
Run YSlow/PageSpeed and they'll will warn you if it's not on. If you don't have any of those tools just look at the HTTP headers with any other tool, e.g. Firebug, webpagetest.org. You should see the header:

Content-Encoding: gzip

provided, of course, that your browser claimed it supports compression by sending the header:

Accept-Encoding: gzip, deflate
What types of components should you gzip?
All text components:

  • javascripts
  • css
  • plain text
  • html, xml, including any other XML-based format such as SVG, also IE's .htc
  • JSON responses from web service calls
  • anything that's not a binary file...

You should also gzip @font-files like EOT, TTF, OTF, with the exception of WOFF. Average about 40% to be won there with font files.

How-to turn on gzipping

Ideally you need control over the Apache configuration. If not full control, at least most hosting providers will offer you ability to tweak configuration via .htaccess. If your host doesn't, well, change the host.

So just add this to .htaccess:

AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml application/javascript application/json

If you're on Apache before version 2 or your unfriendly host don't allow any access to configuration, not all is lost. You can make PHP do the gzipping for you. It's not ideal but the gzip benefits are so pronounced that it's worth the try. This article describes a number of different options for gzipping when dealing with uncooperative hosts.

Rezipping

As Billy Hoffman discovered, there's potential for file size reduction with common downloadable files, which are actually zip files in disguise. Such files include:

  • Newer MS Office documents - DOCX, XLSX, PPTX
  • Open Office documents - ODT, ODP, ODS
  • JARs (Java Applets, anyone?)
  • XPI Firefox extensions
  • XAP - Silverlight applications

These ZIP files in disguise are usually not compressed with the maximum compression. If you allow such downloads from your website, consider recompressing them beforehand with maximum compression.

There could be anywhere from 1 to 30% size reduction to be won, definitely worth the try, especially since you can do it all on the command line, as part of the build process, etc. (re)Compress once, save bandwidth and offer faster downloads every time ;)

15% uncompressed traffic

Tony Gentilcore of Google reported his findings that a significant chunk of their traffic is still sent uncompressed. Digging into it he realized there's a number of anti-virus software and firewalls that will mingle with the browser's Accept-Encoding header changing into the likes of:

Accept-Encoding: xxxx, deflxxx
Accept-Enxoding: gzip, deflate

Since this is an invalid header, the server will decide that the browser doesn't support gzip and send uncompressed response. And why would the retarded anti-virus program do it? Because it doesn't want to deal with decompression in order to examine the content. Probably not to slow down the experience? In doing so it actually hurts the user to a greater extend.

So compression is important, but unfortunately it's not always present. That's why minification helps - not only because compressing minified responses is even smaller, but because sometimes there is no compression despite your best efforts.

Minification

Minification means striping extra code from your programs that is not essential for execution. The code in question is comments, whitespace, etc from styles and scripts, but also renaming variables with shorter names, and various other optimizations.

This is best done with a tool, of course, and luckily there a number of tools to help.

Minifying JavaScript

Some of the tools to minify JavaScript include:

How much size reduction can you expect from minification? To answer that I ran jQuery 1.3.2. through all the tools mentioned above (using hosted versions) and compared the sizes before/after and with/without gzipping the result of minification.

The table below lists the results. All the % figures are % of the original, so smaller is better. 29% means the file was reduced to 29% of its original version, or a saving of 71%

File original size size, gzipped % of original gzip, % of original
original 120619 35088 100.00% 29.09%
closure-advanced 49638 17583 41.15% 14.58%
closure 55320 18657 45.86% 15.47%
jsmin 73690 21198 61.09% 17.57%
packer 39246 18659 32.54% 15.47%
shrinksafe 69516 22105 57.63% 18.33%
yui 57256 19677 47.47% 16.31%

As you can see gzipping alone gives you about 70% savings, minification alone cuts script sizes with more than half and both combined (minifying then gzipping) can make your scripts 85% leaner. Verdict: do it. The concrete tool you use probably doesn't really matter all that much, pick anything you're comfortable with to run before deployment (or best, automatically during a build process)

Minifying CSS

In addition to the usual stripping of comments and whitespaces, more advanced CSS minification could include for example:

// before
#mhm {padding: 0px 0px 0px 0px;}
// after
#mhm{padding:0}

// before
#ha{background: #ff00ff;}
// after 
#ha{background:#f0f}
//...

A CSS minifier is much less powerful than a JS minifier, it cannot rename properties or reorganize them, because the order matters and for example text-decoration:underline cannot get any shorter than that.

There's not a lot of CSS minifiers, but here's a few I tested:

  • YUI compressor - yes, the same YUI compressor that does JavaScript minification. I've actually ported the CSS minification part of it to JavaScript (it's in Java otherwise) some time ago. There's even an online form you can paste into to test. The CSS minifier is regular expression based
  • Minify is a PHP based JS/CSS minification utility started by Ryan Grove. The CSS minifier part is also with regular expressions, I have the feeling it's also based on YUICompressor, at least initially
  • CSSTidy - a parser and an optimizer written in PHP, but also with C version for desktop executable. There's also a hosted version. It's probably the most advanced optimizer in the list, being a parser it has a deeper understanding of the structure of the styleshets
  • HTML_CSS from PEAR - not exactly an optimizer but more of a general purpose library for creating and updating stylesheets server-side in PHP. It can be used as a minifier, by simply reading, then printing the parsed structure, which strips spaces and comments as a side effect.

Trying to get an average figure of the potential benefits, I ran these tools on all stylesheets from csszengarden.com, collected simply like:

<?php
$urlt = "http://csszengarden.com/%s/%s.css";
for ($i = 1; $i < 214; $i++) {
  $id = str_pad($i, 3, "0", STR_PAD_LEFT);
  $url = sprintf($urlt, $id, $id);
  file_put_contents("$id.css", file_get_contents($url));
}
?>

3 files gave a 404, so I ran the tools above on the rest 210 files. CSSTidy ran twice - once with its safest settings (which even keep comments in) and then with the most aggressive. The "safe" way to use CSSTidy is like so:

<?php
// dependencies, instance
include 'class.csstidy.php';
$css = new csstidy();
 
// options
$css->set_cfg('preserve_css',true);
$css->load_template('high_compression');
 
// parse
$css->parse($source_css_code);
 
// result
$min = $css->print->plain();
?>

The aggressive minification is the same only without setting the preserve_css option.

Running Minify is simple:

<?php
// dependencies, instance
require 'CSS.php';
$minifier = new Minify_CSS();
 
// minify in one shot
$min = $minifier->minify($source_css_string_or_url);

As for PEAR::HTML_CSS, since it's not a minifier, you only need to parse the input and print the output.

<?php
require 'HTML/CSS.php';
 
$options = array(
    'xhtml' => false,
    'tab' => 0,
    'oneline' => true,
    'groupsfirst' => false,
    'allowduplicates' => true,
);
 
$css = new HTML_CSS($options);
$css->parseFile($input_filename);
$css->toFile($output_filename);
// ... or alternatively if you want the result as a string
// $minified = $css->toString();

So I ran those tools on the CSSZenGarden 200+ files and the full table of results is here, below are just the averages:

  Original YUI Minify CSSTidy-safe CSSTidy-small PEAR
raw 100% 68.18% 68.66% 84.44% 63.29% 74.60%
gzipped 30.36% 19.89% 20.74% 28.36% 19.44% 20.20%

Again, the numbers are percentage of the original, so smaller is better. As you can see, on average gzip alone gives you 70% size reduction. The minification is not so successful as with JavaScript. Here even the best tool cannot reach 40% reduction (for JS it was usually over 50%). But nevertheless, gzip+minification on average gives you a reduction of 80% or more. Verdict: do it!

An important note here is that in CSS we deal with a lot of hacks. Since the browsers have parsing issues (which is what hacks often exploit), what about a poor minifier? How safe are the minifiers? Well, that's a subject for a separate study, but I know I can at least trust the YUICompressor, after all it's used by hundreds of Yahoo! developers daily and probably thousands non-Yahoos around the world. PEAR's HTML_CSS library also looks pretty safe because it has a simple parser that seems to tolerate all kinds of hacks. CSSTidy also claims to tolerate a lot of hacks, but given that the last version is two years old (maybe new hacks have surfaced meanwhile) and the fact that it's the most intelligent optimizer (knows about values, colors and so on) it should be approached with care.

204

Let's wrap up this lengthy posting with an honorable mention of the 204 No Content response (blogged before). It's the world's smallest componet, the one that has no body and a Content-Length of 0.

Often people use 1x1 GIFs for logging and tracking purposes and other types of requests that don't need a response. If you do this, you can return a 204 status code and no response body, only headers. Look no further that Google search results with your HTTP sniffer ON to see examples of 204 responses.

The way to send a 204 response from PHP is simply:

header("HTTP/1.0 204 No Content");

A 204 response saves just a little bit but, hey, every little bit helps.

And remember the mantra: every extra bit is a disservice to the user :)

Thank you for reading!

Stay tuned for the next article continuing the topic of reducing the component sizes as much as possible.

 

Performance tools

Wednesday, December 2nd, 2009

2010 update:
Lo, the Web Performance Advent Calendar hath moved

Dec 2 This is the second in the series of performance articles as part of my 2009 performance advent calendar experiment. Stay tuned for the next articles.

While theoretically you can speed up your site by just blindly following advice from this blog and other sources, it is much better to understand what's going on on the page and what you're dealing with. That's where the tools come in. Some tools give you insight about the network activities going on between the server and the browser (packet sniffers), some help you benchmark code execution on the client (profilers), some even give recommendations specific to performance improvements. You should aim at mastering as many of the tools as possible, because there's no single one that is The Tool. And that's not a bad thing, it's normal, because performance optimization is a multi-discipline activity touching a lot of different aspects of the the development process.

YSlow

(Full disclaimer: I helped with YSlow development and I was the architect of YSlow 2.0 so this fact may or may not have something to do with why YSlow is the first in the list. Plus, this is an unordered list.)

YSlow is an extension to Firebug, that:

  • inspects the DOM
  • hooks into the Net panel to listen to network activity and discover components that are not part of the DOM

Then the tool looks at the page and the components and tries to figure out how closely they match with Yahoo!'s performance best practices. Then you're given a list of findings with some advice and links for more information on how to improve.

PageSpeed

page speed screenshot

PageSpeed inspects the page and the components and checks it against conformance with Google's performance best practices.

In addition to that, PageSpeed has some quite advanced features like the Activity Panel which shows more detailed information on the page's, well, activity - such as the browser paint events, javascript parsing, execution, DNS lookups and so on. PageSpeed also tells you how many (and which) JavaScript functions were never called before onload so you can take some hints to lazy-load some of the JavaScript payload (after analysing, of course that the code is not needed in other browsers or other page use cases). Same with CSS - PageSpeed gives you a list of unused selectors so you can check whether you have leftovers from previous versions of the page.

MSFast

MSFast (from MySpace) inspects the page and helps answer many questions left open by YSlow and PageSpeed, such as:

  • What's going on with IE?
  • What's the memory and CPU footprint of your code?
  • How does the page looks like (as in screenshots) while it's being loaded so you can see what the people with slower connections have to experience

PageTest

AOL's PageTest is an IE plugin but also a hosted service which is a great way to show your boss/client performance details without inconveniencing them with challenging download and installation activities. PageTest gives you a waterfall view of the page load and a checklist of things to improve, plus some screenshots of interesting moments during load and even a video - an excellent view of how the page looks like in slow speeds. The hosted service can show you the dial-up experience in 4 different places in the world.

DynaTrace's Ajax Edition

dynatrace screenshot

Dynatrace Ajax is a very detailed lower level tool that not only shows the waterfall of components downloads but also the rendering time, CPU consumption, JavaScript parsing and execution. The screenshot above is just the tip of the iceberg of the tool's plethora of views and insights. It's highly recommended. (free, registrationware)

Packet sniffers

A good packet sniffer is indispensable for inspecting the HTTP traffic and figuring out how the page loads and what the request/responses and their headers look like. Here's a list of recommended sniffers, each with something good on top of the others:

  • IBM PageDetailer - a mature tool, somewhat simple which makes it a good start, requires registration to download
  • Fiddler - very powerful, extensible
  • HTTPWatch - (paid, but with a free version) integrates into the browser (both IE and FF) as a panel - very convenient to use. Extensible.
  • Microsoft Visual Round Trip analyser (and an excellent writeup)- goes even lower into the packet level of the requests and draws a different view of the waterfall, one that visualizes the TCP packets and the TCP slow start. It also gives recommendations for performance improvements. Built on top of NetMon (Microsoft Network Monitor) to present the data in a more useful and friendly way.
  • Charles proxy - the only non-windows tool in the list is an excellent packet sniffer for Mac

Time for a little rant - a more detailed view into the HTTP chunks is something that I think is important (will blog about it as part of this series) and missing from the current tools. HTTPWatch is the only tool that at least tells you the number of chunks and Fiddler prompts you to de-chunk HTTP responses when inspecting the body, which gives you a hint that the response was chunked. I hope to see more in that area, hopefully soon.

Thanks for reading

That concludes day 2 of the performance advent calendar. Hope you'll have fun installing and playing with new toys!

Did I miss a tool that should've been in the list? Let me know.

 

Installing PHP and Apache on Mac OSX – that was (pretty) easy

Saturday, March 7th, 2009

This posts is one of those "note to self" kinda posts. I just finished installing PHP and Apache on my Mac OS 10.5.6 and though I should document the experience should I (or you) need to do it again.

It could already be there

The default OS install came with goodies like ruby and php already there. So I could use php on the command line already. But it wasn't "hooked" to Apache for proper web development.

Also turns out Mac comes with some version of Apache, looks like it's disabled by default, but if it isn't, disable it from System Preferences / Sharing / Web Sharing.

Now let's start fresh with PHP5 and Apache 2, ignoring the PHP that's already there.

Mac ports prerequisite

Mac ports makes installing many software packages a breeze on the Mac. If you don't have it already, do set up Mac ports first.

Ready? Set? Go

  1. Log into the mac ports prompt:
    $ sudo port
    Password:
    MacPorts 1.700
    Entering interactive mode... ("help" for help, "quit" to quit)
    [Users/stoyan] >
  2. inside the Mac ports prompt simply do:
    [Users/stoyan] > install php5
  3. This is it! Give it a bit of time to pull out all dependencies, including Apache2. The rest is just some configuration...
  4. Start Apache and make it start when you power on the computer next time:
    $ sudo launchctl load -w /Library/LaunchDaemons/org.macports.apache2.plist
  5. Test that Apache runs fine by pointing your browser to http://localhost/. You should see a page that says "It works!"
  6. Tell Apache that PHP exists:
    $ sudo /opt/local/apache2/bin/apxs -a -e -n "php5" libphp5.so
    [activating module `php5' in /opt/local/apache2/conf/httpd.conf]
    
  7. Create a php.ini file (PHP configuration) by copying the default .ini
    sudo cp /opt/local/etc/php.ini-dist /opt/local/etc/php.ini
  8. If you need Apache stuff, like config files, error/access logs, htdocs... look around /opt/local/apache2. The web root for example is /opt/local/apache2/htdocs. I found it kinda convoluted so decided to move the web root to my home directory. So next two steps are optional.
  9. I'll store all web apps and pages and scripts in a directory called /localhost in my home directory:
    mkdir ~/localhost
  10. Time to edit the Apache config to tell it about the new location of the web root. Open httpd.conf
    sudo vi /opt/local/apache2/conf/httpd.conf

    Search for "DocumentRoot" and replace the current value /opt/local... with /Users/[your username]/localhost.
    The result would be like:

    DocumentRoot "/Users/stoyan/localhost"

    Then do the same further down in the config where it says <Directory..., so it should read:

    #
    # This should be changed to whatever you set DocumentRoot to.
    #
    <Directory "/Users/stoyan/localhost">
    
  11. Apache also need to know that files that end with .php will be handled by the php module, so edit httpd.conf (the same one from the previous step). Search for "php" and you'll find:
    LoadModule php5_module        modules/libphp5.so

    Add one more line so it looks like:

    LoadModule php5_module        modules/libphp5.so
    AddHandler application/x-httpd-php .php
  12. restart Apache (see below) and you're all done

start/stop/restart Apache

Here's how you start/stop/restart Apache:

  • $ sudo /opt/local/etc/LaunchDaemons/org.macports.apache2/apache2.wrapper start
  • $ sudo /opt/local/etc/LaunchDaemons/org.macports.apache2/apache2.wrapper stop
  • $ sudo /opt/local/etc/LaunchDaemons/org.macports.apache2/apache2.wrapper restart

Or to stop and disable starting up every time you power on:

$ sudo launchctl unload -w /Library/LaunchDaemons/org.macports.apache2.plist

Verify that all is good

You can check that all is good by creating a PHP info script.

echo "<?php phpinfo(); ?>" > ~/localhost/test.php

Now point your browser to http://localhost/test.php. It should give you the php info page (a looong page of PHP-related information)

 

JSLint on Mac + TextMate integration

Saturday, February 21st, 2009

UPDATE: Ryan Grove has a better script to display the JSLint results. So basically follow the instructions here until you get to Step 2, point 5 (where you write the command to run JSLint). Then head over to Ryan's blog post to get the better script.

JSLint is an indispensable tool if you're serious about your JavaScript code quality. You can run it online for curiosity but for real development it has to be part of your coding environment and just a click/keystroke away.

While on PC I integrated JSLint with my text editor of choice - TextPad - and shared here. Now, ladies and gentlemen...[drum roll] I give you...[bzfghgang!] JSLint on the Mac!

Prerequisite: get Rhino running on your OSX

Don't worry, it's pretty straightforward, described here

Step 1: get JSLint

The Rhino version of JSLint is here. It's just one JS file. Find an appropriate place to copy it, I think ~/Library/JSLint is as good as any.

$ mkdir ~/Library/JSLint
$ curl http://jslint.com/rhino/jslint.js > ~/Library/JSLint/jslint.js

Test how it works from the command line:

$ java org.mozilla.javascript.tools.shell.Main ~/Library/JSLint/jslint.js myjavascript.js

Step 2: integrate with TextMate

TextMate extensions work their magic through the so called bundles. Here's what you do.

  1. Select menu: Bundles / Bundle Editor / Edit Commands...
  2. In the list of commands, expand JavaScript
  3. Click the + sign found under the list, select New Command
  4. type the name "jslint"
  5. Replace the contents of the Command(s) text field with
    java org.mozilla.javascript.tools.shell.Main ~/Library/JSLint/jslint.js "$TM_FILEPATH"
  6. In the Input: dropdown select "Entire Document", in the Output: "Show as Tool Tip" or "Show as HTML"
  7. In Activation, click on Key Equivalent and then select a key combination you like, for example Command + L (L for Lint)
  8. And this is it, refer to the screenshot below to compare with what you just did. Close the bundle editor window and you're done

textmate bundle editor

Now test your new shiny tool. Open a javascript file and press Command+L. Here's a sample output:

JSLint results in TextMate

And after fixing the missing semi-colon:

jslint fixed

 

Installing Rhino on Mac

Friday, February 20th, 2009

To quote http://www.mozilla.org/rhino/:

Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users.

Rhino allows you to use JavaScript:

  • on the server-side, so you can ditch RoR, Perl, PH... well, keep PHP :) ... in favor of JavaScript
  • on the command line, so you can shell scripts

Let's see how you can install Rhino on OSX.

Step 1 - download and unzip

Download the binary from the Rhino site and unzip to a temporary directory, say /tmp. On the command-line:

$ curl ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_7R1.zip > /tmp/rhino.zip
$ cd /tmp
$ unzip rhino.zip

Now you have the file /tmp/rhino1_7R1/js.jar

Step 2: move js.jar where Java can find it

Your default Java install (comes "free" with OSX) will look for class libraries in a predefined directory ~/Library/Java/Extensions. This directory may not exists, so create it and move the js.jar there.

$ mkdir ~/Library/Java
$ mkdir ~/Library/Java/Extensions
$ mv /tmp/rhino1_7R1/js.jar ~/Library/Java/Extensions/

Step 3: Done! Now test it

That's all there is, your Rhino install is ready to use. To launch and test the Rhino shell try:

$ java org.mozilla.javascript.tools.shell.Main
Rhino 1.7 release 1 2008 03 06
js> print('hello!')
hello!
js> parseInt('123abc')
123
js> encodeURI('hola LA!')
hola%20LA!
js> for (var i = 0; i < 5; i++)
  > print('i is now ' + i)
i is now 0
i is now 1
i is now 2
i is now 3
i is now 4
js> quit()

Last example - create a script that reads the HTML source of my blog:

$ echo "print(readUrl('http://phpied.com'))" > read.js

now you have a script called read.js, let's run it:

$ java org.mozilla.javascript.tools.shell.Main read.js

Thanks for reading!

And happy JS-scripting!

 

Blog-to-podcast with ffmpeg

Monday, February 16th, 2009

ffmpeg is such an amazing tool, looks like it's for video what ImageMagick is for images. An all-powerful all-formats wicked cool command-line tool.

This blog post is an introduction to some of the MP3 capabilities of ffmpeg. I'll use ffmpeg to transform a blog post into a podcast-ready mp3 file. If you continue to read this longish post, here's what you can expect to see:

  • using PHP DOM
  • ffmpeg to convert to MP3
  • ffmpeg to crop (slice) an MP3
  • glue together several MP3s
  • Mac's say command for TTS (text-to-speech)

PHP DOM to get some blog content

This blog's feed is at http://phpied.com/feed. Let's create a small PHP script to extract the title, date and content of the last blog post. We'll feed these to Mac's say command to read then aloud.

Location of the feed:

$file = 'http://phpied.com/feed/';

Load the feed into a DOM instance:

$dom = new DOMDocument;
$dom->loadXML(file_get_contents($file));

Access the node that contains the first item, i.e. the last blog post

$post = $dom->getElementsByTagName('item')->item(0);

The title and a friendly-formatted date:

$title = $post->getElementsByTagName('title')->item(0)->textContent;
$date = $post->getElementsByTagName('pubDate')->item(0)->textContent;
$date = strtotime($date);
$date = date('F jS, Y', $date);

Get the content:

$ns = 'http://purl.org/rss/1.0/modules/content/';
$content = $post->getElementsByTagNameNS($ns,'encoded')->item(0)->textContent;

Strip out HTML tags and entities:

$content = strip_tags($content);
$content = html_entity_decode($content);

(Since this content will be read aloud, HTML tags and entities will make no sense. Here we cound've done better job by doing something more special for list, using ALT tags to replace images and so on...)

echo $title, "\n\n", $date, "\n\n", $content;

OK, so now let's call the script from the command line and write the output to a file:

$ php feed.php > thepost.txt

Here's the result - thepost.txt

Using Mac's say for text-to-speech

You can make your Mac talk on the command line, like:

$ say test

and it will say the word "test"

The say command can also read text from text files and write to AIFF audio files. Let's read thepost.txt into an audio file.

$ say -f thepost.txt -o thepost.aiff

Since I'll make this look like it's a part of an ongoing series of podcasts, I'll add some music and I also need a greeting and goodbye spoken text. So:
$ say -o welcome.aiff Welcome to phpied.com podcast
$ say -o thatsallfolks.aiff That was all for today, join us next time on... phpied.com

OK, so now I have three AIFF files:

Now I want to add some music before/after the podcast. I took four loops from Garage Band's library. Here they are:

Next?

Now I have a bunch of audio files. All I need to do is merge them, glue them together into one MP3. Glueing MP3 will be as easy as simply concatenating the files, using cat for example and them making a final pass through ffmpeg to correct dates and other meta data, so that the result looks like one single file, and not like a Frankenstein :)

In order for the concatenation to work, you only need to make sure all files are the same format, bitrate, etc.

Let's choose 22050 Hz, mono for the result. This means always add the options:

-ar 22050 -ac 1

to all calls to ffmpeg.

Let's get cracking.

ffmpeg to convert just about anything

The simplest use of ffmpeg is to convert from one file format to another, for example AVI to MPEG, WMV to FLV and what not. This is done like this for example:

$ ffmpeg -i input.avi output.flv

ffmpeg to get file information

It's useful to know what type of file we're dealing with, you can do this simply by omitting the output file:

$ ffmpeg -i input.avi

Let's check out one of the Garage Band loops:

$ ffmpeg -i opener.mp3 
FFmpeg version..... (more ffmpeg information)
Input #0, mp3, from 'opener.mp3':
  Duration: 00:00:12.6, start: 0.000000, bitrate: 191 kb/s
  Stream #0.0: Audio: mp3, 44100 Hz, stereo, 192 kb/s
Must supply at least one output file

Pretty good quality, more than I need. Plus, for some reason Garage Band added silence at the end of the files I exported, so let's cut it off.

ffmpeg to crop files

I want to remove trailing 5 seconds or so of each Garage Band loop. Here goes:

$ ffmpeg -i breaking-news.mp3 -ac 1 -ar 22050 -ss 0 -t 6 breaking-news-ok.mp3
$ ffmpeg -i opener.mp3 -ac 1 -ar 22050 -ss 0 -t 8 opener-ok.mp3
$ ffmpeg -i closer.mp3 -ac 1 -ar 22050 -ss 0 -t 33 closer-ok.mp3
$ ffmpeg -i squeeze-toy.mp3 -ac 1 -ar 22050 -ss 0 -t 2 squeeze-toy-ok.mp3

-i is the input file -ac is the number of channels (1 for mono) -ar is the rate, -ss is start, -t is length.

Now you can see how the meta information for the opener.mp3 has changed:

$ ffmpeg -i opener-ok.mp3 
Input #0, mp3, from 'opener-ok.mp3':
  Duration: 00:00:08.1, start: 0.000000, bitrate: 63 kb/s
  Stream #0.0: Audio: mp3, 22050 Hz, mono, 64 kb/s

Convert AIFF to MP3

Now let's convert the AIFF files from our TTS say command to MP3, keeping the same 22050 mono rate:

$ ffmpeg -i thepost.aiff -ac 1 -ar 22050 thepost.mp3
$ ffmpeg -i welcome.aiff -ac 1 -ar 22050 welcome.mp3
$ ffmpeg -i thatsallfolks.aiff -ac 1 -ar 22050 thatsallfolks.mp3

Here are the new MP3s:

Glue the pieces with cat and ffmpeg

Now, last stage, let's glue all the pieces with cat which simply means append the next file at the end of the previous.

$ cat breaking-news-ok.mp3 welcome.mp3 opener-ok.mp3 thepost.mp3 
            squeeze-toy-ok.mp3 thatsallfolks.mp3 closer-ok.mp3 > pieces.together

Then make these pieces a proper MP3 file

$ ffmpeg -i pieces.together final.mp3

Well, that's all folks, here's the final result:
final.mp3

 

FTP on the command line

Monday, February 16th, 2009

Since I switched to Mac I need to learn a bunch of stuff I was taking for granted for years. Recently I needed to upload a bigger file or two to my server and scp is not the tool for the job. So instead of starting a hunt for a decent free FTP client, I tried the good old command line.

Turns out it's much easier to FTP files from the command line than I would think.

Connecting to the server

$ ftp phpied.com
Connected to phpied.com.
220---------- Welcome to Pure-FTPd [TLS] ----------
220-You are user number 1 of 50 allowed.
220-Local time is now 02:17. Server port: 21.
220-IPv6 connections are also welcome on this server.
220 You will be disconnected after 15 minutes of inactivity.
Name (phpied.com:stoyan): myusername
331 User myusername OK. Password required
Password:

After supplying your credentials you're in and you have an inviting ftp> prompt:

230 OK. Current restricted directory is /
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>

So what do you do here? You can list available commands with
ftp> help

You can navigate with cd (good old "change directory")

ftp> cd www/phpied.com/files
250 OK. Current directory is /www/phpied.com/files

You can list the contents of a remote directory with ls or dir.

And finally you can copy files from your desktop to the remote machine. You don't copy files, you PUT them. You're rewarded with a nice progress indicator.

ftp> put src.zip 
local: src.zip remote: src.zip
229 Entering Extended Passive Mode (|||59237|)
150 Opening BINARY mode data connection for src.zip
 10% |***                                  |   511 KB   64.00 KB/s    01:09 ETA

later...

ftp> put src.zip 
local: src.zip remote: src.zip
229 Entering Extended Passive Mode (|||59237|)
150 Opening BINARY mode data connection for src.zip
 93% |**********************************   |  4607 KB   45.62 KB/s    00:07 ETA

finally...

ftp> put src.zip 
local: src.zip remote: src.zip
229 Entering Extended Passive Mode (|||59237|)
150 Opening BINARY mode data connection for src.zip
100% |*************************************|  4938 KB   44.73 KB/s    00:00 ETA
226 Transfer complete.
5056569 bytes sent in 01:53 (43.55 KB/s)

And then, bye-bye:

ftp> quit
221 Goodbye.
 

Installing JPEGTRAN on a Mac or Unix/Linux

Friday, January 16th, 2009

JPEGtran is cool because it lets you optimize JPEG images losslessly by:

  1. Stripping meta data (meta is sometimes bulky and useless for web display)
  2. Optimizing Huffman tables or
  3. Convert a JPEG to progressive encoding

From my experience 1 is more important than 2 or 3 and 3 gives better results than 2 for images over 10K

Installation

I never had to install jpegtran before because all unix/linux machines I've touched already have it. And on windows you just copy a binary somewhere in your path.

Well, I got this MacBook now and it doesn't have jpegtran so had to figure it out myself. Here's how you can do it, worked for me on Mac OS should work on any unix/linux too.

BTW, jpegtran is part of a package of few tools known as libjpeg, so you'll be installing a few programs not only jpegtran.

  1. Get the source code from here. It's the file called jpegsrc.v6b.tar.gz. Using cURL you can download like:
    curl http://www.ijg.org/files/jpegsrc.v6b.tar.gz > /tmp/libjpeg.tar.gz
  2. Uncompres the package, e.g. tar -xzvf /tmp/libjpeg.tar.gz
  3. go to the directory that contains the uncompressed code, e.g. cd /tmp/jpeg-6b
  4. ./configure
  5. sudo make install

Done.

You can test your shiny new set of tools like this and get some help information about the various options:

> jpegtran -h
> cjpeg -h
> djpeg -h
> rdjpgcom -h
> wrjpgcom -h

You also test by optimizing my book cover from Amazon like:

curl http://ecx.images-amazon.com/images/I/41ckBp3bBUL._SL500_AA240_.jpg > oojs.jpg
jpegtran -copy none -progressive oojs.jpg > oojs-opt.jpg

This gives you 10% smaller file with not a pixel of quality loss. Not bad, eh, for a minute of work, or less.

 

Paint.NET is cool…

Tuesday, December 23rd, 2008

... but doesn't write PNG8 with alpha transparency, unfortunately.

This comment on the YUI blog got me all excited by the possibility of having another designers tool other than Fireworks that creates PNG8 (palette PNGs) with alpha transparency.

Overall Paint.NET is a very simple and friendly program (as a non-designer I'm often intimidated by Photoshop's plethora of features) and I can see how I can use it for my future (rare, I hope) design needs :) It's pretty impressive, free and from what I read, it has a dedicated community, plus a number of plugins (hmm, a smush.it plugin would be nice... one day).

I tried saving a PNG8 with alpha but unfortunately this feature is not (yet!) supported. I created an image with a sold red square and blue gradient that fades to 100% transparency. When saving as 8-bit (in order to create a palette PNG) the semi transparent pixels are all gone. It treats palette PNGs like GIFs (like Photoshop does).

Default (produces truecolor PNG):
png 32

With 8 bit option selected:
png8

 

Installing ExifTool on Dreamhost

Tuesday, December 23rd, 2008

ExifTool looks like a very promising tool to fiddle with all sorts of JPEG metadata (needed for smush.it) but first I had to make sure I can install it on Dreamhost. Although installation didn't go as described on the exiftool site (since I don't have sudo access on Dreamhost), it's still installable and it's actually pretty easy.

  1. ssh to your DH box and go to the directory that will contain the tool, in my case it's my home directory
  2. Download the latest version of the code, e.g.
    wget http://www.sno.phy.queensu.ca/~phil/exiftool/Image-ExifTool-7.59.tar.gz
  3. Uncompress the code archive
    tar -xf Image-ExifTool-7.59.tar.gz
  4. Delete the original archive
    rm Image-ExifTool-7.59.tar.gz
  5. Rename the newly created Image-ExifTool-7.59 to something shorter to type, e.g. "et"
    mv Image-ExifTool-7.59 et
  6. All done! You can now run it from any directory and show help info like
    ~/et/exiftool - h

You can also test the new installation with some of the images found in the exiftool test directory, like so:

$ ~/et/exiftool ~/et/t/images/IPTC-XMP.jpg

ExifTool Version Number         : 7.59
File Name                       : IPTC-XMP.jpg
File Size                       : 20 kB
File Modification Date/Time     : 2005:12:31 13:05:50-08:00
File Type                       : JPEG
MIME Type                       : image/jpeg
JFIF Version                    : 1.02
Exif Byte Order                 : Little-endian (Intel, II)
Image Description               : A witty caption
Make                            : FUJIFILM
Camera Model Name               : FinePix2400Zoom
Orientation                     : Horizontal (normal)
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Software                        : Adobe Photoshop 7.0
Modify Date                     : 2004:02:26 09:36:46
Artist                          : Phil Harvey
Y Cb Cr Positioning             : Co-sited
Copyright                       : Copyright 2004 Phil Harvey
F Number                        : 3.5
Exposure Program                : Program AE
ISO                             : 100
Exif Version                    : 0210
Date/Time Original              : 2001:05:19 18:36:41
Create Date                     : 2001:05:19 18:36:41
Components Configuration        : YCbCr
Compressed Bits Per Pixel       : 1.6
...
... [snip]
...
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:4:4 (1 1)
Aperture                        : 3.5
Image Size                      : 100x80
Shutter Speed                   : 1/64
Focal Length                    : 6.0 mm
Light Value                     : 9.6
 

Smush.it presentations

Sunday, October 5th, 2008

Smush.it is getting more and more buzz all over the internets. Now there's even a song about it! Me and Nicole are pretty busy answering email, but a little slow to document the thing, I though I should at least shed some light on how the tool works by using some of the presentations.

What the tool does can be summarized in these steps:

  • Turn GIFs into PNG8. Results are reported only if there's a saving, the file name then becomes source.gif.png. Smush.it uses imagemagick to do the conversion and then pngcrush to crush the pngs
  • Crush PNGs using pngcrush
  • Strip JPEG metadata and make them progressive, using jpegtran
  • GIF animations: use gifsicle to remove pixels that don't change from one frame to another

This has been documented here on developer.yahoo.com together with the command line tools and options.

So all the tool does is run the appropriate command for each file type. Easy as that ;) All the tools mentioned are free open-source and available on all operating systems, including Windows.

Here are some presentations on slideshare that might explain things a little more:

  1. High-perf web sites - PHP Quebec Montreal, March 2008
  2. 7 mistakes in image optimization - O'Reilly's Velocity, SFO, June 2008
  3. Ajax Experience, Boston, earlier this week. Draft 1, Draft 2. The final and shortest version is below. It doesn't say much but the talk was just 5 minutes and included a demo. It's weird how little you can say in 5 minutes, I mean just "hello, my name, ... welcome to blah, blah..." is 20 seconds
Images - 7 mistakes
View SlideShare presentation or Upload your own. (tags: images optimization)

Happy smushing!

"Smush it! Smush it real good..." - hothardware.com ;)

 

smush.it is a smash hit

Thursday, October 2nd, 2008

Since me and Nicole announced smush.it yesterday at Ajax Experience and thanks to Christian Heilmann posting it on Ajaxian and Yahoo Developer Network, this thing seems to have exploded! It's all over the blogosphere, twitter-sphere and every other sphere.

BTW, Chris never seizes to amaze - he posted the video on Ajaxian at 11:01 am and our talk was from 10:30 to 10:35 am :)

Last night, about 12 hours after the announcement, I checked the directory that stores the results and it had over 10 000 entries. There's one entry for every smush.it run, which means for example every page you smush using the FF extension. If you have 5 images on a page on average, this means over 50 000K smushed images in a day, nice!

We've received great feadback and great suggestions, keep'em coming. People are already making smush.it part of their dev/build process. People are already seeing response time performance improvement.

I'm really looking forward to releasing the official API, open source the code, the command-line version and all the fun stuff. We were just too busy trying to come up with something presentable for the Ajax conference.

The URL again: http://smushit.com

So, what's a smu? ;)

 

ppt to pdf to slideshare via imagemagick

Friday, August 8th, 2008

Some time ago I posted the slides from my Velocity 2008 talk on Slideshare and since the slides have tables, all the tables came out with the wrong font and all messed up and misplaced. Instead of trying to figure out what the heck is wrong with Powerpoint and how to work around it, I thought I could probably convert the ppt to pdf, since Slideshare accepts pdfs too.

Now I'm sure there are probably several tools out there that do PDF-2-PPT, some maybe commercial, but I love imagemagick and the command line, so I had to try it myself. Here's how to do it if ever need to.

  1. Export all slides from PPT into individual PNG images. In powerpont, go File | Save As..., then select PNG. When promted, say that you want all slides, not only the current one.
  2. Rename the first 9 files. The previous step created a number of files like Slide1.PNG, SLide2.PNG....Slide56.PNG. Now you need to rename the first 9 to pad the numbers with a zero, like Slide01.PNG and so on, this will help with the sorting.
  3. convert Slide*.PNG show.pdf

And that's that.

Now this has some drawbacks: the links are no longer links in the PDF, the file size is pretty big and the font is kinda bad, but hey, it worked for me.

 

HttpFox

Friday, July 25th, 2008

HTTPFox is an interesting Firefox extension for monitoring the HTTP traffic, obviously inspired by the IE-only commercial HttpWatch.

HTTPFox shows some stuff that are missing from Firebug's Net Panel, such as requests for favicons and such. There's also a little search box that lets you filter the list of components. Pretty cool too is the ability to select and copy the list of components.

Funny today I played with a little export feature in the Net Panel, logged here, demo here.