Tuesday, November 11, 2014

Reading MP3/OGG/M4A tags with pure JavaScript

Summary

There aren't many sources in the internet that show you how to read tags (metadata) from music files such as MP3, OGG or even M4A. Not only this, but they do use an external library. I've build an HTML5 player inspired by Foobar2000, called Noisy, and I needed it to be as lightweight as possible, so everything inside I wanted to do by myself and optimize it in time. Unfortunately, this required a lot of specifications reading and many retries to get the things working. Here I'll show you how I did the reading and will try explaining the meaning of the code (by comments). It's far from perfect, but there wasn't much in the internet to help me with that task, so if you have any suggestions you are more than welcome to share them with me. Without further ado, lets begin!

Prequel

All tags we read by getting the file as ArrayBuffer using HTML5 FileReader, so here is a sample of how to do that:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function onFilesSupplied( e )
{
 // Get all the files from the input of type file
 var files = e.files || e.dataTransfer.files,
  file,
  fr = new FileReader();

 // Handle the typed array read by the FileReader,
 // when it's finished loading
 function _handleFileLoad()
 {
  // This is the point where the real tag
  // read is happening - covered later on
  var tags =readTags( this.result );
 }

 // Listen when FileReader finishes reading and
 // pass result to the handler
 fr.addEventListener( 'load', _handleFileLoad );

 // This example uses only the first file to load,
 // but you can loop them all and read them
 file = files[0];

 // Do the actual read, by saying to the reader
 // we want the result as ArrayBuffer
 fr.readAsArrayBuffer( file );
}

This code is made to run on both input of type file or drag and drop events. Noisy is cloud player, so it rarely reads the files from disk, meaning that code is not used a lot. What Noisy uses instead is to make the cloud service return the file directly as ArrayBuffer. So a simple:


1
xhr.responseType = 'arraybuffer';

to the request does the trick, thus no FileReader is required. Unfortunately this works for Dropbox, but not for Google Drive (update: now it works!). That's the reason Noisy doesn't support tag reading when user uses Google Drive. If he uses Mozilla Firefox, though, Noisy will read the tags using audio.mozGetMetadata(), doesn't matter from where the file is coming. All that said, it really doesn't matter how you get your hands on the ArrayBuffer - what really matters is how we read the tags. I've split them in separate code snippets.

MP3 ID3v2


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
readTags( buffer )
{
 // We need only these tags for now. You can check specifications for other ones, if you need them
 toGet = {
  'TPE1': 'artist',
  'TIT2': 'title',
  'TALB': 'album',
  'TYER': 'date'
 };
 len = Object.keys( toGet ).length;
 i = 0;

 // Loop first 1000 bytes. Here I couldn't find better solution to where the tag entries end, so I just loop the first 1KB of data
 while( i < 1000 && len )
 {
  charCode = dv.getUint8( i++ );

  // Loop through all the characters in the configuration object on top and compare letter by letter with current one
  for( tag in toGet )
  {
   tagCode = tag.charCodeAt( 0 );

   // Check for lower case match
   if( charCode == tagCode )
   {
    match = true;
    // loop through all letters to make sure we have found a match
    for( k = 1; k < tag.length; k++ )
    {
     matchCharCode = dv.getUint8( ( i - 1 ) + k );
     tagCode = tag.charCodeAt( k );
     if( matchCharCode != tagCode )
     {
      match = false;
      break;
     }
    }

    // If a match is found get the tag
    if( match )
    {
     // Pattern for tag: TALB 00 00 00 (HEX for length of tag in bytes, grouped
     // by 2 for each char, the second being the first part in unicode, eg. 0415
     // for cyrillic ? is written 15 04 here) 00 00 (unicode flag byte. If 00 then
     // no unicode, else it's the first part of unicode char) (tag itself). All this
     // I found out by myself. Don't know if it's always true
     tagLength = i + 9 + dv.getUint8( i + 6 );
     tagValue.length = 0;
     // Check for *ÿþ symbols and read after them if found. Some tags have this funky
     // charachers upfront and I don't know why and what they mean
     i = ( 255 == dv.getUint8( i + 10 ) && 254 == dv.getUint8( i + 11 ) ) ? i + 12 : i + 9;

     // First byte shows if the tag is encoded in Unicode or not
     matchCharCode = dv.getUint8( i++ );

     // If unicode
     if( matchCharCode )
     {
      nextMatch = ( '00' + dv.getUint8( i - 1 ).toString( 16 ) ).slice( -2 );
      while( i <= tagLength )
      {
       matchCharCode = ( '00' + dv.getUint8( i ).toString( 16 ) ).slice( -2 );
       tagValue.push( '0x'.concat( matchCharCode, nextMatch ) );
       nextMatch = ( '00' + dv.getUint8( i + 1 ).toString( 16 ) ).slice( -2 );
       i += 2;
      }
     }
     else
     {
      i++;
      matchCharCode = ( '00' + dv.getUint8( i - 1 ).toString( 16 ) ).slice( -2 );
      while( i <= tagLength )
      {
       tagValue.push( '0x00' + matchCharCode );
       matchCharCode = ( '00' + dv.getUint8( i++ ).toString( 16 ) ).slice( -2 );
      }
     }

     // Substract last step increase, as next tag comes right after this one
     i--;

     // Save tag's value as we are done reading it
     metadata[toGet[tag]] = String.fromCharCode.apply( null, tagValue );

     // Remove tag name from the object, so we don't loop it again
     delete toGet[tag];
     len = Object.keys( toGet ).length;
    }
   }
  }
 }
}

I do not do ID3v1, but it's very easily implementable if you understand this code. I just decided it's too old to be supported - all modern MP3 files should have ID3v2.

OGG


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
readTags( buffer )
{
 // We need only these tags for now. You can check specifications for other ones, if you need them
 toGet = [
  'artist',
  'title',
  'album',
  'date'
 ];
 len = toGet.length;
 i = 0;

 // Loop first 1000 bytes. Here I couldn't find better solution to where the tag entries end, so I just loop the first 1KB of data
 while( i < 1000 && len )
 {
  charCode = dv.getUint8( i++ );

  for( j = len; j--; )
  {
   tag = toGet[j];
   tagCode = tag.charCodeAt( 0 );

   // Check for lower case match
   if( charCode == tagCode )
   {
    match = true;
    // loop through all letters to make sure we have found a match
    for( k = 1; k < tag.length; k++ )
    {
     matchCharCode = dv.getUint8( ( i - 1 ) + k );
     tagCode = tag.charCodeAt( k );
     if( matchCharCode != tagCode )
     {
      match = false;
      break;
     }
    }

    // If a match is found get the tag
    if( match )
    {
     // Byte before the 00 00 00 shows how many bytes the tag will be, including the "artist=" part,
     // so we read everything from "=" sign till the length is reached. All this
     // I found out by myself. Don't know if it's always true
     tagLength = i - 1 + dv.getUint8( i - 5 );
     tagValue.length = 0;
     i = i + tag.length;
     matchCharCode = dv.getUint8( i++ );
     while( i <= tagLength )
     {
      tagValue.push( matchCharCode );
      matchCharCode = dv.getUint8( i++ );
     }
     str = '';

     for( k = 0; k < tagValue.length; k++ )
     {
      str += '%' + ( '0' + tagValue[k].toString( 16 ) ).slice( -2 );
     }

     // Save tag's value as we are done reading it
     metadata[tag] = decodeURIComponent( str );

     // Remove tag name from the object, so we don't loop it again
     toGet.splice( j, 1 );
     len = toGet.length;
    }
    continue;
   }

   tagCode = tag.toUpperCase().charCodeAt( 0 );

   // Check for uppercase match
   if( charCode == tagCode )
   {
    match = true;
    tag = tag.toUpperCase();
    // loop through all letters to make sure we have found a match
    for( k = 1; k < tag.length; k++ )
    {
     matchCharCode = dv.getUint8( ( i - 1 ) + k );
     tagCode = tag.charCodeAt( k );
     if( matchCharCode != tagCode )
     {
      match = false;
      break;
     }
    }

    // If a match is found get the tag
    if( match )
    {
     // Byte before the 00 00 00 shows how many bytes the tag will be, including the "artist=" part,
     //so we read everything from "=" sign till the length is reachedl
     tagLength = i - 1 + dv.getUint8( i - 5 );
     tagValue.length = 0;
     i = i + tag.length;
     matchCharCode = dv.getUint8( i++ );
     while( i <= tagLength )
     {
      tagValue.push( matchCharCode );
      matchCharCode = dv.getUint8( i++ );
     }
     str = '';

     for( k = 0; k < tagValue.length; k++ )
     {
      str += '%' + ( '0' + tagValue[k].toString( 16 ) ).slice( -2 );
     }

     // Save tag's value as we are done reading it
     metadata[tag.toLowerCase()] = decodeURIComponent( str );

     // Remove tag name from the object, so we don't loop it again
     toGet.splice( j, 1 );
     len = toGet.length;
    }
   }
  }
 }
}

M4A


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
readTags( buffer )
{
 // We need only these tags for now. You can check specifications for other ones, if you need them
 toGet = {
  '©art': 'artist',
  '©nam': 'title',
  '©alb': 'album',
  '©day': 'date'
 };
 len = Object.keys( toGet ).length;
 i = 50000;

 // Loop second 50000 bytes. Here I couldn't find better solution to where 
 // the tag entries end, so I just loop the characters between 50KB and 100KB
 while( i < 100000 && len )
 {
  charCode = dv.getUint8( i++ );

  for( tag in toGet )
  {
   tagCode = tag.charCodeAt( 0 );

   // Check for lower case match
   if( charCode == tagCode )
   {
    match = true;
    // loop through all letters to make sure we have found a match
    for( k = 1; k < tag.length; k++ )
    {
     matchCharCode = dv.getUint8( ( i - 1 ) + k );
     tagCode = tag.charCodeAt( k );
     if( matchCharCode != tagCode )
     {
      match = false;
      break;
     }
    }

    // If a match is found get the tag
    if( match )
    {
     // Pattern: @nam 00 00 00 (byte showing length of tag, starting from next
     // byte) data 00 00 00 (byte showing I don't know what) 00 00 00 00 (text
     // for tag) 00 00 00 (byte showing I don't know what)
     i += 6;
     matchCharCode = dv.getUint8( i );
     tagLength = i + matchCharCode;
     i += 13;
     matchCharCode = dv.getUint8( i );

     tagValue.length = 0;

     matchCharCode = dv.getUint8( i++ );
     while( i <= tagLength && matchCharCode )
     {
      tagValue.push( matchCharCode );
      matchCharCode = dv.getUint8( i++ );
     }
     str = '';

     for( k = 0; k < tagValue.length; k++ )
     {
      str += '%' + ( '0' + tagValue[k].toString( 16 ) ).slice( -2 );
     }

     // Save tag's value as we are done reading it
     metadata[toGet[tag]] = decodeURIComponent( str );

     // Remove tag name from the object, so we don't loop it again
     delete toGet[tag];
     len = Object.keys( toGet ).length;
     continue;
    }
   }

   tagCode = tag.toUpperCase().charCodeAt( 0 );

   // Check for lower case match
   if( charCode == tagCode )
   {
    match = true;
    tag = tag.toUpperCase();
    // loop through all letters to make sure we have found a match
    for( k = 1; k < tag.length; k++ )
    {
     matchCharCode = dv.getUint8( ( i - 1 ) + k );
     tagCode = tag.charCodeAt( k );
     if( matchCharCode != tagCode )
     {
      match = false;
      break;
     }
    }

    // If a match is found get the tag
    if( match )
    {
     // Pattern: @nam 00 00 00 (byte showing length of tag, starting from next
     // byte) data 00 00 00 (byte showing I don't know what) 00 00 00 00 (text
     // for tag) 00 00 00 (byte showing I don't know what)
     i += 6;
     matchCharCode = dv.getUint8( i );
     tagLength = i + matchCharCode;
     i += 13;
     matchCharCode = dv.getUint8( i );

     tagValue.length = 0;

     matchCharCode = dv.getUint8( i++ );
     while( i <= tagLength && matchCharCode )
     {
      tagValue.push( matchCharCode );
      matchCharCode = dv.getUint8( i++ );
     }
     str = '';

     for( k = 0; k < tagValue.length; k++ )
     {
      str += '%' + ( '0' + tagValue[k].toString( 16 ) ).slice( -2 );
     }

     tag = tag.toLowerCase();

     // Save tag's value as we are done reading it
     metadata[toGet[tag]] = decodeURIComponent( str );

     // Remove tag name from the object, so we don't loop it again
     delete toGet[tag];
     len = Object.keys( toGet ).length;
    }
   }
  }
 }
}

I'm sure this code can be optimized a lot, but I'm still keeping it as simple as it can be so it's clear how things work. When I understand all the little details, then I'll merge things and make the code more compact.

Final words

Don't know if this article can help you, but I hope it can add to the end result you are looking for. If you know something that I missed, please do say - I'll be very happy to any optimizations and suggestions.

Monday, August 19, 2013

Replace SSD with Compact Flash card on Acer Aspire One ZG5

Earlier this year (2013) my SSD on AOA110 died. I had to choose what to do next - sell the netbook or fix it. As both things are fairly easy to be done and I love this netbook (in fact I'm currently writing this article on it), I chose to fix it, test it for some time and write on my blog about the results. So here we go.

What you'll need:


  1. Aser Aspire One ZG5 with SSD. Model of the SSD doesn't matter. The only thing that matters is that it should be PATA SSD with ZIF connector.
  2. CF/ZIF Convertor Circuit Board with ZIF Cables
  3. A compact flash card of your choice.
  4. Double-sided tape.
  5. Knowledge of how to tear down your AOA. You can find a video here.
  6. An hour or two, depending on your skills.


End result:

There are two tricky parts here:


  1. The cable you need to use is NOT supplied with this adapter. It's called flipped cable. As far as I know, it's not supplied with any adapter. You have to make it yourself. There are two cables coming with this adapter, so you have 1 mistake margin. I've read a couple of ways to make this cable, but I had another idea. The end result is to flip the pins of the cable so from one of the sides they should face down and the other up. Unfortunately, I didn't took a picture of my cable because it's hard to fit the flipped cable inside the adapter and when I succeeded I didn't wanted to tear it down again. Here is how I did it, though - see that both ends of the cable are a bit thick? That's made because the cable there should make good contact with the board. Since we want to flip the cable there, it'll have double thickness in the end, so we should remove this thick peace of plastic. If you don't (as I did in my first try), you will probably brake some of the buses and you won't be able to plug the cable into the adapter. So remove it and flip the cable exactly where the pins end, so they should not be visible on one of the sides of the cable.
  2. Compact flash card should be marked as fixed drive, not as removable drive, if you want to install Windows XP (for 7/8 I think it's not a problem, but I never tested it). For Linux it doesn't matter. If your CF card is marked as a removable drive, you won't be able to see it in the BIOS of your AOA - that should not worry you - it's still visible and bootable from the boot menu (F12). I bought 16GB 400X Transcend, as I read on the Internet that 90% of the Transcends are marked as fixed drives, but mine was not :( For Sandisks there is an utility for flipping this bit and making fixed disk from removable disk and vice versa. It's called ATCFWCHG and can be downloaded from here. For other brands I don't have any information.


After you make your flip cable and get your CF card, the rest is simple:


  1. Tear down the netbook.
  2. Make space for the adapter (you can look how I did it at the picture below).
  3. Glue the adapter there with the double-sided tape.
  4. Put the netbook back together and test it. Don't forget the ferrite ring on the cable (get the old one from the SSD).


Here is how I fit my adapter inside the AOA.

CF life expectations

I used my Transcend for 3 months (everyday use) and I can feel it slowing down as I did with the original SSD. That's really bad news for me. But the good news is that this is MLC flash card. Sandisk are offering SLC flash cards - they are pricey, but their life should be more than twice the length. Also the larger the capacity of your CF card is, the longer it's lifetime will be. So with 32GB SLC you should start feeling the slowdown not in 3 months, but in at least an year if not more. And that's if you use you netbook as I used mine - 2 to 6 hours each day (more in the weekends). It's up to you ;)

Friday, December 21, 2012

Automated ClixGrid clicking


If you are reading this, you probably know what ClixSense is and it's game - ClixGrid. If you do not know - check it out. Here I'll show you how to automate the clicking on the game (ClixGrid) - there is no captcha here, so you can easily do that with a bit of programming. I made a script in my spare time, so you won't have to program.

Here is a video of how the script is working:



WARNING! USE AT YOUR OWN RISK! I'M NOT RESPONSIBLE IF YOU GET YOUR ACCOUNT SUSPENDED!

Current Version: 20161023 (Changelog below)

How it works:

I use Selenium tests to mimic human actions. All the actions are applied without using the cursor, so you can use your mouse wherever you want - the clicks will still happen on the right places. The script generates random numbers to target the box it'll click. If the box is clicked, the script generates another set of numbers. If not - it clicks it, waits for the ad to complete and closes the popup. Then if there are tries left, it generates numbers again. Selenium doesn't solve the focus problem, though. Using only this script, you will automate the clicking, but you won't be able to use your PC, as you'll loose focus of the advertisements window, which will stop the timer. To solve this, I used a virtual machine - it has it's own focus, so I can use my primary OS as usual :) Because Clixsense can change the elements on the page, the script can stop working. I've included update feature to the script - after all clicking is done, the script opens this blog post, checks for newer version and alerts the user if one is available. It's good to be always up to date, to ensure the right working.

What you'll need:


1. My script.
2. Mozilla Firefox.
3. Selenium IDE addon.
4. Selenium IDE Flow Control extension.
5. (optional, but recommended) VirtualBox or any other virtual machine software.

How to install:

1. (optional) Install VirtualBox.
2. (optional) Install you OS of choice.
3. Install Firefox.
4. Install Selenium IDE addon.
5. Install Selenium IDE Flow Control extension.

How to update:

Just copy the new script over the old one and reload it, if you have loaded it into Selenium.

How to use:

1. Open Firefox.
2. Go to GlixGrid's page.
3. Open Selenium IDE (Ctrl+Alt+S).
4. Open my script from File -> Open (or press Ctrl+O).
5. On ClixGrid's board click on any box, watch the advertisement and close the window. If Firefox blocks the popup, allow all popups from clixsense.com and then continue with the instructions.
6. After one successful click on the grid, the script is ready to be run. Click on one of the play buttons you see on Selenium IDE and the clicking begins :) Watch the first one, just to be sure it's working. Sometimes, on start, Selenium IDE steals the focus from the popup, which stops the counter and the ad never completes, so be sure you watch the first ad and then you can go somewhere till this is done. If you are running the script in a virtual machine, then you can use your host OS as usual, because the script is running in the guest OS, which have it's own focus, so the windows are always focused, no matter the focused window on the host OS ;) This way you get a complete automation without having to watch the ads!

Changelog:
21122012: initial release

22122012:
- Added pause of 1s before clicking on close link. Till now, the period was a random number between 0 and 2s, but when the number was too low, "Stay on this page" dialog appeared, which brakes the script. Now the number is between 1 and 3s, so the problem should be solved.
- Now after version update check, if there is no newer version, the script redirects you to the GlixGrid page, to see your results of the clicking.
- Added check for remaining clicks at the beginning of the script. That way if you run the script for the second time, it won't fail, instead it'll go straight to checking for newer version.
- Removed useless echo command.

20121224:
- Removed onbeforeunload event from the popup. The fix in the previous version helped, but did not solved the problem entirely. Removing onbeforeunload event prevents "Stay on this page" dialog from ever appearing. In general, the dialog appears when you try to close the popup before he successfully reported to the parent site about the completion of the ad. So if there is no dialog to prevent this, the ad won't be counted, which is bad, but the script won't be broken, so he'll run the ad again, till it's counted.
- Fixed version numbering.

20161023:
- Fixed the script as it was not running at all (the id of the Close link in the popup was changed).

Monday, December 3, 2012

Points2Shop & CashPirate Android app download (apk)


p2s_intro by glamurtv

Points2Shop

I have Rena3 tablet, which is a nice piece of Chinese hardware. Good tablet, but as any Chinese thingy it has a horrible support. The latest Android version is 2.3.5 beta, which is bad, as the tablet is dual core with 512MB RAM, more than enough for Android 4.x. Not only I cannot upgrade to latter version of Android, but most of the apps in Google Play are "not compatible" with this device. I've tried many ways to unlock more apps, but I couldn't do it. One of the apps I really needed was Points2Shop. I ended up installing it on my sister's HTC phone and copying it from there to my tablet. It works flawlessly. So if you are like me and you cannot get the app from the market, here is a download link for the apk itself. I'll try to keep it up to date. Have fun!

PS: Here is my referral link, if you are interested in helping me ;)

Version 2.0.1017 (uploaded April 19, 2016 from Nexus 7 2013)
Download link: Points2Shop_com.points2shop.apk

Version 1.1.6 (uploaded September 16, 2015 from Nexus 7 2013)
Download link: com.points2shop.apk

Version 180.4.1 (uploaded March 30, 2015 from Nexus 7 2013)
Download link: com.darksciencemedia.p2s.android.apk

Version: 78.1 (Legacy)
Download link: Points2Shop.apk

CashPirate


CashPirate is an alternative to Poinst2Shop, which in my opinion is MUCH better. They pay for mobile app installations and they pay well. Also, they do instant PayPal redeems for as low as $2.5! You can get that for 3 days in a not well known country like Bulgaria - who knows how fast you can cash out in the USA! And of course they have the typical referral programs (10% for first level, 5% for second), but they also give the guy who registers as a referral a 50% increase in the earnings for the first 1000 coins ($1), which means by the time you normally get $1, you'll have $1.5 if you register as a referral to someone. So if you want to give it a shot, here is my referral code (for 50% bonus): CBVTUG. The coolest part of the app is that they allow multiple accounts under the same IP, so you and everyone in your family can install and use it without any problems, as long as it's on different devices of course. And get that - they can all be referrals to each other! Payments are done pretty fast - mine came for less than a minute after I requested it. They also don't send you any emails, so don't worry about SPAM. Here is a download link to the apk, in case you cannot download it from Google Play (the first link in this paragraph):

Version: 3.2 (updated June 23, 2014 from Nexus 7)
Download link: com.ayet.cashpirate.apk

Useful software

Both Poinst2Shop and CashPirate want you to install software on your device to earn money. Soon you'll run out of resources on the device so it's good idea to cleanup a bit. There are a lot of programs on the market for uninstalling apps, but I think the best way is still to use the Settings -> Apps to uninstall them. The only problem with that is that there is no quick way to access directly that menu. You have to open the drawer, choose Settings, scroll down to Apps and then you can uninstall. Fortunately, there is an app, Manage Applications Shortcut, which is just a shortcut to the integrated app manager in Android. Hope that helps!

Thursday, November 22, 2012

How to gain 100-350MB more free space upon Windows installation



When installing Windows Vista or 7 on a new hard drive, the OS creates a 100MB partition right at the beginning of the drive. If you are installing Windows 8, then the size of that partition is 350MB. I have Acer Aspire One ZG5 with 8GB SSD, which is very small and I have to make custom Windows installation, that use less space. Nevertheless, every MB counts! So if you are like me or you just hate that small partition that the OS creates, let me show you how to trick the setup not to create it. It's really simple - create partitions manually, not through the installer. Here's how:

1. Boot your installation.
2. When the setup wants you to choose partition to install Windows to, hit Shift + F10. This should bring command prompt to you.
3. Type diskpart and press Enter. Diskpart is an utility coming with Windows, so you really shouldn't do anything special to get it. In case you make your own installation, make sure you leave diskpart utility on the setup disk.
4. Type list disk and remember the number of the disk you want to partition. In my case it's 0, so replace 0 with your number from now on.
5. Type select disk 0.
6. Type clean (this will erase all the info on the drive, if any).
7. Now if you want the whole drive to be one partition (as in my case), type create partition primary. If you want to have more partitions, type create partition primary size=n where n is the size of the partition you want in MB. The rest of the partitions you can create again here, or later with the GUI.
8. Type select partition 1.
9. Type format fs=ntfs quick.
10. Probably not needed, but just to be sure type active.
11. Type exit, to exit Diskpart and then exit again, to exit cmd.
12. From the setup choose the partition you've just created and proceed with the install without loosing 100/350MB :)

Wednesday, August 8, 2012

DBSyncer - A tool to synchronize databases through repository


Recently I started cleaning my PC from old, useless code and projects that I had been working on in the past and then I came across DBSyncer - a project intended to sync databases through repositories. Pretty useful if you and your colleagues (or friends) are developing a website or system, and you need a tool to sync the database between all the dev computers. I started developing this project in my spare time back in the beginning of 2011 because I needed such tool at work, where we were constantly  fighting with database synchronization through our subversion. But then in May 2011 I found a better job, where I didn't need this tool anymore, so development halted. Still, the made progress is pretty good - DBSyncer is fully working. I never released it because I wanted to implement a couple of features to it, but never got the time. Now, after more than an year, I'll release it as is - I'm sure there will be people happy to use it. I only replaced deprecated functions in newer PHP versions with working ones. So, what DBSyncer offers?

  • Automated database synchronization
  • Database change revisions
  • Database revert to revision
  • Well documented API
  • GUI

The main part of DBSyncer is it's PHP class - DBSyncer.php (only about 33kb). All other files are optional. If you choose to use only this file, you'll have to make the settings manually. Info about this, how to use DBsyncer and what methods it offers, is provided in the docs. If you want to use the GUI, then you'll need all files from the archive (available to download at project's page in Google Code). GUI can help you with the following things:

  • Install DBSync
  • Add new database
  • Import database
  • Remove database
  • See revision changes for each database
  • Apply revision to database both forward and backwards
  • Add or remove revisions
  • Export database without data (structure only)
  • Make changes to the config

All of these things are provided by DBSyncer.php. The GUI only helps you to manage databases visually. Also note GUI won't sync your databases automatically. If you want that, then you'll need to implement DBSyncer.php in your project and use its methods. See docs for more info.

Main screen
Screen showing database revisions

There is some unfinished work with the GUI - no "please wait" screen when applying revision (probably somewhere else too). If I have time, I'll fix this.

Final words - Although not entirely finnished, DBSyncer is still a good piece of work if it fits your needs. Enjoy!

Wednesday, July 4, 2012

EasyPeasy on Aser Aspire One


Yet another post of the series. This time I tried EasyPeasy on my AOA110. It works really good, with the exception of WiFi - no bundled driver inside the .iso image. Also brightness settings are not working - only fn key combinations can be used to control it. Nevertheless if you want to try it, you'll need:
1. EasyPeasy images.
2. UNetbootin.
3. At lease 1GB flash drive.
Now run UNetbootin and select EasyPeasy's image and write it to your flash drive. Boot the flash drive and have fun :)

Sunday, June 24, 2012

Overclocking Dell Inspiron 1000

Yesterday I was cleaning-up my hard drive (yeah, it happens from time to time :D) and found a screenshot of SetFSB with the clock generator needed for my old Dell Inspiron 1000 (which I sold a couple of months ago) to be overclocked. I decided to publish it here, on my blog, so other people can find it, too. I remember when I was overclocking this laptop, there was absolutely no info about it on the internet! I had to experiment, but eventually I found out that the clock generator is ICS952001AF. Settings FSB to 188MHz allowed me to run the Celeron inside to 2.8GHz from the stock 2.2GHz! I don't have any benchmarks or stress tests, but I remember 2.8GHz was the max stable frequency I got. I used it for half an year without any problems. Inspiron tends to get a little too hot, though. So use on your own risk!

Thursday, May 31, 2012

Microsoft AutoRoute 2001 Portable Launcher


I couple of months ago I dug up Mazda EPC2 Portable Launcher from my archives and shared it with you. Yesterday I dug up my old copy of AutoRoute 2001 and decided to try and make it portable. Since it worked I'll show you what you need to get it working for you, too :) Actually there are 2 tings you'll need - installation CD and my portable launcher (available in the Download section bellow). This time I followed the specifications posted by PortableApps.com platform :) Ok, let's get to the details:

1. Install your Microsoft AutoRoute 2001 on your computer.
2. Extract my portable launcher (download bellow).
3. Copy all the contents of AutoRoute to the App\AutoRoute folder from the extracted archive. You should have AutoRout.exe file in App\AutoRoute folder.
4. Uninstall Microsoft AutoRoute 2001.
5. Run the launcher and see if it's working :) Should be working. I tested this only on Windows 8 DP and CP, both 32-bit and 64-bit, but it should work on everything from XP and above.
6. Enjoy never installing AutoRoute 2001 again (if you still use it) :)

Downloads:
AutoRoute 2001 Portable
AutoRoute 2001 Portable Source

Monday, May 28, 2012

Automated JS/CSS combining/minifying system - JSS Build Environment


Recently I wrote a post about JS compressors and how they compare to each other. Now I've build a system for combining and/or minifying JS and CSS files. This system uses Google's Closure Compiler for JS minification and Google's Closure Stylesheets for CSS minification. Both of them require Java, so if you want to use 'Compress' option, then you'll need to install it and point JSS Build Environment to java.exe. I recommend extracting jPortable to a folder next to this EXE file.


Features:



- JavaScript combine and minify
- CSS combine and minify
- Automatic combine/minify when one of the input files is changed.
- Control over the sequence of combining
- Minimizing to system tray
- Single EXE without installer


Setup:



close-compiler is the folder containing compiler.jar file of Closure Compiler.
closure-stylesheets is the folder containing closure-stylesheets.jar of Closure Stylesheets.
Java folder contains extracted files from jPortable.
JSS Build Environment can run without all of these folders, but you'll only have combine option.

Usage:


To use this app, you need to add files to CSS or JS panels (or both), choose Output file and click Build. It's as simple as that. Of course this only activates combining. No compression is applied. If you want compression, then you have to setup files as described in Setup above and then check Compress checkbox before building.
Now let's talk about Auto Build feature. This is really useful feature when you need to build every time you change something. Auto Build monitors the files and when some file is changed it starts build procedure. Build procedure includes compression if it's checked, but I recommend not checking it, as compression takes time and you'll need to wait a lot just to see let's say your text in red. Combining Auto Build with minimizing to system tray and some balloons makes things look and work really good.


Download:


JSS Build Environment
Source Code (in Delphi XE)

Happy codding ;)

Friday, May 18, 2012

Minimizing Dropbox usage thus maximizing free space

I bet you already know about Dropbox and currently you are searching of a way to increase the available free space. I'm already at the maximum available space - 25.8GB. I've done Getting Started, Sharing staff, DropQuest 1 and 2 and beta photo upload (dough last 500MB never got to me). Referrals are also to the maximum. So right now I'm stuck with ~26GB till Dropbox releases another option for gaining free space :( Then I thought what if I shrink the size of my files instead of increasing my free space? It's a good idea, but I needed a way to make it without archives, as I wanted to be able to open the files with a double click. To answer this I first needed to analyze what type of files do I have and how often I use them. Here is my report:

Music files - MP3 mostly. Everyday use.
Video files - VOB, MOV, AVI, MKV and MP4. Using them from time to time.
Pictures - JPG mostly. Using them from time to time.
Text files - PDF, MHT (HTML site in one file), TXT. Most of the files are used once per year at best.
Programs - lots of EXE and DLL executable files. Using them on daily basis.

After the analysis I found out there is a way to compress 99% of my files and still be able to open them by just double clicking! Of course to do that I needed to change some formats, loose some (unnoticeable) quality and loose a lot of time (most of the time was for research, so you won't lose it as I'm giving you all info in one place), but it was totally worth it! I was able to shrink all my files and add a lot more and still have almost half of my Dropbox free! In numbers, I was able to compress ~35GB to ~15GB!!! That's 20GB saved! And I still have 10GB of free space with all my music and pictures being in a save place :) So, how I did it? Let's get down to the details:

Music files

MP3 is an old format and as such it's not the best. There are other formats which can provide better quality on the same bitrate. Also it's proven that 128 kbps MP3 is enough for most people. Only trained ear can make difference between 128 kbps and 320 kbps MP3. So compressing your MP3 files to 128 kbps will save you a lot disk space (saved me 7GB), but you'll loose some quality (not that you'll notice it). To minimize quality loss, use another file format, newer that MP3! There are OGG, WMA, AC3 and others. I personally used OGG, but WMA is better integrated (almost all MP3 players read it) - you decide. To encode my files into the new format I used MediaCoder with the following settings:

Video files

VOB files are VERY large files! They are one of the main reasons I got 20GB saved :D Other files are small, but not as small as I wanted to! So I searched Internet about the best new (newer almost always is better) codec that have support in players too. I ended up with Google's WebM. It was able to compress 250MB VOB into 32MB WEBM file! Of course there is slight (unnoticeable) quality loss, but when you watch this video say 10-15 years from now, the quality then will be much better and this VOB's quality will look awful with or without this small quality loss. The memories this video brings you are important, not the video quality itself ;) Other files are already compressed, so WEBP was able to slice the size in two without any quality loss! So how do I encode to WebM? Well, the best way I found is using an Add-on for Mozilla Firefox (I'm a Chrome fan, but sometimes Firefox beats the crap out of Chrome) called Firefogg.

Pictures

It turns out WebM has a sister project called WebP for pictures instead of movies :) So I used it. It was able to smack 5MB JPG into 1MB WEBP without any loss! For already compressed JPEGs with size of 1-1.5MB, WebP was able to deliver files with sizes between 100-150KB! Also WebP supports lossless compression with alpha channel, so you can smack your PNG files too :) GIF files are in the works ;) For more on how to compress your images using WebP see my earlier blog post.

Text files

Here I did a bit of cheating - I uploaded as much as I could in Google Docs and compressed text files which I do not use often (for an example my old HTML projects). All the text files were less than 100MB, so not much cheating. But there are some files left, too big to be uploaded to Google Docs. 55MB PDF file! Google Docs allows only 2MB file to be uploaded :( Almost all of my PDF files were larger. Not to mention MHT files. So what I did is I compressed those PDFs with Adobe Acrobat Pro using this tutorial. This sliced 20MB off of this large PDF! And the end result is again PDF! So it just compresses the PDF. The down side is reduced compatibility - older PDF readers won't be able to read it. Who cares - Chrome reads it :D What about MHTs? Well here I had to make a couple of conversions to be able to convert MHT to PDF and then compress it using Acrobat Pro. Chrome cannot open MHT files, but can print everything to PDF file. So I needed this MHT opened in Chrome no matter what! Well, the solution is simple - open it with IE and save it as complete web page. Then open this HTML file with Chrome and print it as PDF. After that crush it with Acrobat Pro and you should have smaller PDF than the MHT. If you are advanced web user like me, you can even open object inspector and remove useless stuff from the web page before printing - this will further reduce the size of the PDF ;) In the end, after the compression, I uploaded again to Google Docs files with sizes smaller than 2MB. End result - 50% less space for text files :)

Programs

This actually is quite simple - use PortableApps.com AppCompactor to compress all of your programs. Backup all programs before use. Set to best compression first. Compress them one by one and test them after compression. If the program doesn't run, copy over the backup and try again with other compression algorithm. In my case all compressed perfectly with the best compression :)


Conclusion

All file types can be opened with popular software. OGG is supported by all popular players, WebM is also supported by them and browsers like Chrome, Firefox and Opera. WebP can be opened with some picture viewers like Picasa and IrfanView and can be opened with the default Windows Photo Viewer by installing a codec supplied by Google. PDFs can be opened with latest version of your PDF reader or by Chrome. So all in all this is a win-win situation - you get more space and you are still able to use your files as before :)

ReactOS on Acer Aspire One (installation without CD)


As I mentioned earlier, I've tried ReactOS on my AOA110, but without any success. Here I'll show you how to install ReactOS without CD. First, there is not an easy way to make ReactOS run on those netbooks. At first I tried 0.3.14 using a flash drive with CDFS partition (some flash drives allow you to have a partition which the BIOS and OS sees as CD), but that failed. Then I tried chaining GRUB with Freeloader, but that failed too. In the end I was able to install ReactOS (56595) by first installing it in a virtual machine and then copying all files to the netbook's SSD and again using GRUB. After loading ReactOS it asked me for a lot of drivers, but I had them prepared and copied to the SSD  The bad new is only WiFi driver installed, but I didn't found any way to scan for WiFi networks. When tried to install video driver (GMA 950) using it's setup program, everything went fine, till next boot - blank screen after ReactOS boot screen. Also installing LAN (Realtek) driver through ReactOS' wizard ended in BSOD.

Drivers tested:
Video - only setup install possible, which crashes the system after reboot
Audio - wizard can't find the driver (XP version)
LAN - wizard finds the driver, but ends in BSOD (both 2000 and XP versions)
WiFi - wizard finds and installs successfully the driver
Web Cam - wizard can't find the driver (XP version)

There ware also some other drivers (ACPI, System device, USB something), but I don't know that they are and where to get drivers for them. ReactOS doesn't have them.

So, if you are still up to it, here is the full procedure to install ReactOS on your PC without using a CD:
1. Get all the drivers and extract them (ROS doesn't support ZIP files). You'll need XP drivers, but 2000/2003 might work as well.
2. You'll need to prepare USB flash drive with WinPE using WinBuilder or use Windows Vista/7/8 USB install drive. You may also use Linux, but if you know how to use it, you won't be reading this :D.
3. After creating the flash drive and ensuring it works, it's time to install ReactOS under virtual machine. I used VirtualBox for the purpose, as it's free.
4. Install VBox, create a virtual machine and a virtual hard drive (make sure it's VHD format), and install ROS. Shutdown the machine after you get to the desktop.
5. It's time to mount the VHD file. At this point it'll be good if you are running Windows 7 or better. If not, you'll need to mount it using VHDMount. Othewise use this procedure - type in CMD the following commands:

diskpart
select vdisk file=path_to_reactos_vhd
attach vdisk
assign

6. Now you should have a new drive in Windows Explorer. Go to your flash drive and create new folder with the name ROS. Copy in it all the contents of the VHD file (the new letter in Windows Explorer).
7. In ROS folder create a new folder called Drivers and copy all the extracted drivers.
8. Download GRUBinst, extract it and copy it to ROS folder.
9. From GRUB folder copy grldr file to ROS folder.
10. In ROS folder create a new file with name menu.lst and open in with Notepad. In in paste the following code and save it:


title ReactOS
root (hd0,0)
kernel /freeldr.sys

11. Get back to the diskpart to detach VHD by typing the following commands:

detach vdisk
exit
exit

12. You are about to install ReactOS on your PC. Just to be sure, you should have a flash drive containing bootable Windows based OS, ROS folder on it containing all the files from ReactOS + GRUB and Drivers folders containing GRUBinst and extracted drivers respectively. If you have all that, you are ready. Plug the drive in your PC and boot from it.
13. Open CMD (if you are using Windows Setup flash drive, wait till you get to HDD format window and press Shift + F10 - this will bring you CMD) and type diskpart.
14. In diskpart you'll need to find the drive number on which you'll install ReactOS. Typically that's 0, so from now on I'll use hd0 as drive, but if yours is another number, replace 0 with the number you need. Also, you'll need to change it in menu.lst file you created earlier. To see the number type list disk.
15. Type the following commands and your ReactOS should be installed. Note in my case C: is the drive I'll be installing ReactOS to and D: is my flash drive letter. Switch the letters as you need (if you need). Also note this procedure will destroy all your data on your HDD/SSD, not just C drive! All partitions will be lost! If you mistake the hard drive number you risk cleaning another drive! USE AT YOU OWN RISK!

select disk 0
clean
create partition primary
select partition 1
format fs=fat32 quick
active
assign
exit
xcopy d:\ROS\*.* c: /e /x /b
d:
cd ROS
cd GRUB
grubinst.exe (hd0)

16. Your ReactOS should be now ready to boot, so reboot the machine.
17. First you should see GRUB manager with only one option - ReactOS. If you don't, then something is wrong with GRUB installation. If you do, press enter and you should see Freeloader's options (ReactOS, debug etc.). If you don't, then there is a problem with chaining GRUB to Freeloader - check your menu.lst to see if the correct HDD number is written.
18. If everything is ok, you should be getting a wizard asking for a specified driver. If you have this driver, try installing it. If not, let the OS try to install some (probably it'll fail). Also note you may get BSOD while installing a driver. ReactOS will ask you for many drivers, so have patience.
19. Browse arround ReactOS and be happy :)

Wednesday, May 16, 2012

Aspire One AOA110/AOA150 BIOS mod - a disaster and a solution

Here I'll tell you how to revert back to the latest official BIOS of Acer Aspire One if you have this kind of trouble with the modded one.

The story:

Ok, a couple of days ago I decided to search for a modded BIOS, for my AOA110, with more options available in the menus. I came across this. I was happy, very happy with this discovery and I rushed to flash it! Flashing (under Windows) went fine. The new BIOS had many new options available in it, just what I wanted! I spent maybe 10 minutes poking around the BIOS - checked every option's meaning. Then I changed a a couple of them, that I thought will give a boost, and rebooted. Windows started loading and suddenly upon welcome screen my Aspire One powered off! First thought - some of the options I changed are making the system unstable. So I decided to go to the BIOS and switch option by option back to it's original state, to see which one is the one to blame. Yeah, right! I entered the BIOS, found the first option I changed and when tried to change it back to it's original state, the netbook powered off again! In fact the fan was spinning at it's best! This means the CPU is working too hard and it's overheating, leading to power off, to prevent it from burning. Ok, I though I'll load BIOS defaults, as this could be done really fast. I did it :) But no result :( Then I noticed the last comment, from mad_max, in the thread I downloaded the modded BIOS - he had exactly the same problem as me! If only had read that earlier... not that would have stopped me from trying :D So, while searching the internet for a solution, the netbook apparently cooled off a bit, as when I run it, I had a bit more time to work before power off. Still not enough to flash it. Then I though - I'll cool it in the fridge! So I did that :) After 3 hours of cooling I run it and this time I was able to open My Computer before power off :D Ok, the time needed to do that is about 40-50 seconds (haven't really measured it), which may be enough to flash the BIOS, but I wouldn't try it yet. I wanted a bit more time, so I froze my AOA110! It worked and I was able to flash it back to it's latest official BIOS version :)

The procedure:

1. Put your netbook to your freezer and leave it there for 30-40 minutes. As zstand from Reddit suggested, "Place the laptop in a large, sealed ziplock bag before placing it in the freezer/fridge. The humidity might cause some damage".
2. Meanwhile download the latest official BIOS and prepare an USB flash drive using this tutorial.
3. When you get your netbook out of the freezer, act as fast as you can - plug the power and the USB drive and flash using the same procedure mentioned in the article for preparing the USB drive.
4. After flashing check if the BIOS is the official or not. If it's not, put the netbook back in the freezer and try again. Sometimes this procedure doesn't work from the first time. If the BIOS is the official, your flashing was successful. Shutdown the netbook and let it warm up naturally.

Update 2014:

Since the links in the forum are dead, I took the liberty to export the modded BIOS from my AOA and share it here. I used Insyde Flash Utility for InsydeH2O Flashit V1.2j IHISI V1.7.2 downloaded from the official website of Acer. I made all the 5 dumps the utility allows, but first (G) is what most people want. Still, I never know, so I attached all of them:
AOA_3309_mod_G.bin /G Save current BIOS to file (from IHISI).
AOA_3309_mod_1.bin /1 Save current BIOS to file (1Mbits from memory)
AOA_3309_mod_2.bin /2 Save current BIOS to file (2Mbits from memory)
AOA_3309_mod_4.bin /4 Save current BIOS to file (4Mbits from memory)
AOA_3309_mod_8.bin /8 Save current BIOS to file (8Mbits from memory)

USE THIS AT YOU OWN RISK!!! I'M NOT RESPONSIBLE IF YOU BRICK YOUR DEVICE!!!

Monday, May 7, 2012

Converting multiple files into WebP under Windows (MultiWebP)

You know of Google's image format WebP? If you do and you want to convert your images to .webp, you'll notice it's a bit hard, as you have to do it image by image. I wrote a little batch file, called MultiWebP, which can automate this for you ;) You can download it bellow. There are two files - test.bat and encode.bat. The first is optional and is used to encode only one file, just to test the settings. The second file is used to encode the whole folder :)

Update: New version is out!
Details for MultiWebP 2.0:
- Only one file! test.bat and encode.bat are now combined in MultiWebP.bat.
- One file means only one line of settings, not two as before.
- Subfolders. MultiWebP now supports subfolders. You can run it on a parent folder and all files will be encoded, even those in subfolders.
- Progress. MultiWebP now shows total files to convert and files converted till now.
- Colors. MultiWebP now shows it's window in different colors, depending on the progress made. This is useful for remote observing (if you lay down on your bed and wait it to finish). Color levels: black - 0 to 32%, blue - 33 to 66%, aqua 67 to 100% and green when all is done.
- Beep. Additionally Windows' beep sound is heard upon all task completed.

Installation:
1. Download WebP package from Google and extract it somewhere. Optional you can download WebP codec to be able to open .webp files with your Windows Photo Viewer.
2. Download my scripts and extract them.
3. Place the two files next to cwebp.exe.

4. Create two shortcuts for test.bat and encode.bat and name them as you like.

5. Open Explorer and type in the address bar %APPDATA%\Microsoft\Windows\SendTo
6. Paste the two shortcuts there. Now you should see both of them in the Send To menu.

Settings:
You should read Readme.txt of WebP and learn about what cwebp.exe parameters are available. Then you should open test.bat/encode.bat, find cwebp.exe and change it's parameters. I've included some by default, so you can start using my scripts without changing settings.

Usage:
1. Create a folder where you'll put cwebp.exe supported images that have common source (shot with the same camera), to ensure settings will be the same for all files.
2. Right click one of the files and send it to test.bat.

3. Change settings for test.bat if you don't like the result and repeat step 2, until you are pleased with the result.
4. After you found your settings, copy them to encode.bat.
6. You are about to convert the whole folder, so make sure you don't have any unsupported by cwebp.exe files as they will be deleted in the end!
7. Right click on the folder and send it to encode.bat.

8. Go take a walk :)

Wednesday, May 2, 2012

JavaScript Compressors Comparison

Recently I got back a bit to my old hobby - writing JS in my spare time. There is something in me that makes me optimize things. I love optimizing, no matter if it'll be a drop in the ocean, a lost time. But no matter how much you optimize, you never can beat a JavaScript compressor. So, I decided to test a couple of JS compressors available for free online use and see which is the best. For this purpose I used two files, from some of my JS projects:

JSOS.js - 97.8 KB (100 227 bytes) of mainly procedure oriented code, written for my JavaScript Operating System 5 years back. The code is a crap. Not one standard is kept while writing. Hell, I didn't even knew there are standards back then. And I didn't knew about compressors neither, so I compressed as much as I could by hand.
portfolio.js - 45.9 KB (47 020 bytes) of mainly object oriented code, written for my portfolio. Code is a bit new (one year old or so), but I still don't like it :D

The compressors I choose to benchmark are:

1. Google's Closure Compiler
2. Yahoo's YUI Compressor
3. Microsoft's Ajax Minifier
4. Dojo's shrinksafe
5. Online Javascript Compression Tool

Ok, lets see how they do on default settings.

1. Closure Compiler
JSOS.js - 88.1 KB (90 220 bytes)
portfolio.js - 39.4 KB (40 374 bytes)
2. YUI Compressor
JSOS.js - 89.8 KB (92 035 bytes)
portfolio.js - 34.5 KB (35 406 bytes)
3. Ajax Minifier
JSOS.js - 88.4 KB (90 537 bytes)
portfolio.js - 33.9 KB (34 734 bytes)
4. shrinksafe
JSOS.js - 93.6 KB (95 885 bytes)
portfolio.js - 36.8 KB (37 767 bytes)
5. Online Javascript Compression Tool
JSOS.js - 89.2 KB (91 358 bytes)
portfolio.js - 34.5 KB (35 381 bytes)

Let's wrap-up these results in graphics:
Or in percents:
As we can see, the "default" winner for crappy procedure oriented code is Google's Closure Compiler with just 317 bytes ahead of Microsoft's Ajax Minifier. For object oriented crappy code, the "default" winner this time is Microsoft's Ajax Minifier, while previous winner, Google's Closure Compiler is at last place.
Now let's see what those compressors can do at their best:


1. Closure Compiler
JSOS.js - 35.5 KB (36 376 bytes)
portfolio.js - 31.5 KB (32 352 bytes)
2. YUI Compressor
JSOS.js - 89.8 KB (92 035 bytes)
portfolio.js - 34.5 KB (35 406 bytes)
3. Ajax Minifier
JSOS.js - 84.7 KB (86 767 bytes)
portfolio.js - 33.2 KB (34 056 bytes)
4. shrinksafe
JSOS.js - 92.3 KB (94 562 bytes)
portfolio.js - 36.1 KB (36 991 bytes)
5. Online Javascript Compression Tool
JSOS.js - 89.2 KB (91 358 bytes)
portfolio.js - 34.5 KB (35 381 bytes)

In graphics:
and in percents:
Yes, I noticed Closure Compiler, too! At first I thought it produced a non working code, but when I tried it - it works as a charm! I don't know how they did it, but they did it :) So apparently the "max" winner for crappy procedure oriented code is Google's Closure Compiler. For crappy object oriented code, the "max" winner is again Google's Closure Compiler with a lead of more that 1k of code. Notice Yahoo's YUI Compressor and Online Javascript Compression Tool - they both have the same numbers as for default test?!?!? Well, their default settings are actually their best settings. That's a bit disappointing, as default settings of some compressors are better that the best settings of YUI Compressor, and I know a lot of people are using it. So I recommend to those people to switch to Closure Compiler.

Let's make an average to name a winner by the normal formula:
1st place = 5 points
2nd place = 4 points
3rd place = 3 points
4th place = 2 points
5th place = 1 point
Results:

Closure Compiler YUI Compressor Ajax Minifier shrinksafe OJSCT
Points 16 10 17 5 13
So the order is:
1. Ajax Minifier
2. Closure Compiler
3. Online Javascript Compression Tool
4. YUI Compressor
5. shrinksafe
You agree with that? Neither do I. This formula is good for other uses. Here we should really find how many bytes are saved, average. Let's see the same table again, but this time with bytes and percents saved.

Closure Compiler YUI Compressor Ajax Minifier shrinksafe OJSCT
Bytes 95172 39612 48400 29289 41016
Percents 32.32 13.45 16.43 9.95 13.93
Now we see who the real winner is:
1. Closure Compiler
2. Ajax Minifier
3. Online Javascript Compression Tool
4. YUI Compressor
5. shrinksafe

So next time you need to shrink JS files, consider Closure Compiler as a good choice :)