Category Archives: Apple

Firefox Extensions: Greasemonkey

Apple is publicizing another iTunes contest. Like before, they are giving away a prize for every 100,000 songs download or entries submitted. The latter is the typical alternative that all such contests require to claim that they aren’t a lottery or another form of gambling.

In this case, the free entry form allows up to 25 entries per day. Apple makes it a little tedious by clearing the form when you go Back to it, so you have to re-enter a ton of personal information each time. They also include a captcha image to prevent automated submissions. That’s all cool, but I’d like it to be just a little easier for me to enter for the free iPod goodness.

Among other things, I’m an experienced Javascript programmer, so I know there should be way to automate the form filling a little bit. Enter Greasemonkey. Greasemonkey is a Firefox extension which allows you to customize webpages on the fly. You create a short text file which gives Greasemonkey some URLs to edit and a bit of Javascript to execute on each webpage.

After you install Greasemonkey, you can find some user script you want to install somewhere on the web and right click to “Install User Script…”. Be careful, of course, because user scripts are NOT SAFE! You need to either understand the code you are installing or trust the person who wrote the script.

I created a Greasemonkey user script that fills in some personal information in the iTunes contest form and sets the cursor in the captcha text field. Here’s the code (note that you will have to edit it to insert your own info before using it):

// ==UserScript==
// @name           Apple's iTunes 1 billion contest helper
// @namespace      http://www.chrisdolan.net
// @description    Prefill some data in a contest entry form
// @include        https://phobos.apple.com/WebObjects/MZFinance.woa/wa/billionSongAlternateEntryForm*
// @include        http://www.apple.com/itunes/1billion/entryform/
// ==/UserScript==

var focus_field = "7.1.49";
var data = {};
data["7.1.5.3"] = "Chris";
data["7.1.5.7"] = "Dolan";
data["7.1.9"] = "1234 Main St";
data["7.1.17"] = "Madison";
data["7.1.19.0.3"] = "WI";
data["7.1.21.0.3"] = "537XX";
data["7.1.25"] = "United States";
data["7.1.29"] = "608";
data["7.1.31"] = "555-1212";
data["7.1.35"] = "January";
data["7.1.36"] = "1";
data["7.1.37"] = "1970";
data["7.1.41"] = "chris@example.com";
data["7.1.45"] = "chris@example.com";

var form = document.forms[0];
for (var i=0; i<form.elements.length; i++) {
   var e = form.elements[i];
   if (e.name == focus_field)
      e.focus();
   var val = data[e.name];
   if (val == null)
      continue;
   if (e.type == "text") {
      e.value = val;
   } else if (e.type.indexOf("select") == 0) {
      for (var j=0; j<e.options.length; j++)
         if (e.options[j].text == val) {
            e.selectedIndex = j;
            break;
         }
   } else
      alert("failed to set "+e.name+" = "+val);
}

Save that as a text file called something like foo.user.js and edit it. In Firefox, drag the folder containing that script into the browser pane. You should see a directory listing for a file:// url. Right click on the user.js file and select “Install User Script…”. Then browse to the contest form. Your data should be prefilled.

If you need to edit the script, use “Tools > Manage User Scripts…” to uninstall the current version and install the edited version by the same steps as above.

Disclaimer: I do not feel that this is cheating on Apple’s contest at all. This script is just a helper similar to form auto-fill. You still have to fill in the captcha manually. This just takes a little of the tedium out of form filling.

iTunes Music Store contest

Apple is currently having a contest counting down to 500,000,000 iTunes songs sold. To publicize the contest, they exposed an XML representation of the current count of songs sold. Just out of curiosity, I wrote a Perl program that uses that count information to extrapolate the next winning times. Not too hard considering the simple XML structure:

<?xml version="1.0" encoding="utf-8"?>
<root>
<count name="curCount" timestamp="Thu, 07 Jul 2005 23:16:00 GMT">485066974</count>
<count name="preCount" timestamp="Thu, 07 Jul 2005 23:11:00 GMT">485060489</count>
</root>

The problem is made easier by a few CPAN libraries.

  • LWP::Simple – retrieves files via HTTP
  • CAM::XML – an XML parser and DOM representation
  • DateTime::Format::Strptime – a date/time parser

So, here are my extrapolations followed by the source code. It looks like there will be a winner about once an hour. Of course, that assumes that the purchase rate is typical right now (a Thursday evening).

Retrieving http://www.apple.com/itunes/external_counter.xml...
Rate: 23.19 songs/sec
Predicted count right now: 485173522 (Thu Jul  7 19:35:14 2005 CDT)
485,200,000   Thu Jul  7 19:54:15 2005 CDT
485,300,000   Thu Jul  7 21:06:07 2005 CDT
485,400,000   Thu Jul  7 22:17:58 2005 CDT
485,500,000   Thu Jul  7 23:29:50 2005 CDT
485,600,000   Fri Jul  8 00:41:41 2005 CDT
485,700,000   Fri Jul  8 01:53:33 2005 CDT
485,800,000   Fri Jul  8 03:05:25 2005 CDT
485,900,000   Fri Jul  8 04:17:16 2005 CDT
486,000,000   Fri Jul  8 05:29:08 2005 CDT
486,100,000   Fri Jul  8 06:40:59 2005 CDT
486,200,000   Fri Jul  8 07:52:51 2005 CDT
486,300,000   Fri Jul  8 09:04:43 2005 CDT
486,400,000   Fri Jul  8 10:16:34 2005 CDT
486,500,000   Fri Jul  8 11:28:26 2005 CDT
486,600,000   Fri Jul  8 12:40:17 2005 CDT
486,700,000   Fri Jul  8 13:52:09 2005 CDT
486,800,000   Fri Jul  8 15:04:00 2005 CDT
486,900,000   Fri Jul  8 16:15:52 2005 CDT
487,000,000   Fri Jul  8 17:27:44 2005 CDT
487,100,000   Fri Jul  8 18:39:35 2005 CDT
487,200,000   Fri Jul  8 19:51:27 2005 CDT
487,300,000   Fri Jul  8 21:03:18 2005 CDT
487,400,000   Fri Jul  8 22:15:10 2005 CDT
487,500,000   Fri Jul  8 23:27:02 2005 CDT
487,600,000   Sat Jul  9 00:38:53 2005 CDT
487,700,000   Sat Jul  9 01:50:45 2005 CDT
487,800,000   Sat Jul  9 03:02:36 2005 CDT
487,900,000   Sat Jul  9 04:14:28 2005 CDT
488,000,000   Sat Jul  9 05:26:19 2005 CDT
488,100,000   Sat Jul  9 06:38:11 2005 CDT
488,200,000   Sat Jul  9 07:50:03 2005 CDT
488,300,000   Sat Jul  9 09:01:54 2005 CDT
488,400,000   Sat Jul  9 10:13:46 2005 CDT
488,500,000   Sat Jul  9 11:25:37 2005 CDT
488,600,000   Sat Jul  9 12:37:29 2005 CDT
488,700,000   Sat Jul  9 13:49:21 2005 CDT
488,800,000   Sat Jul  9 15:01:12 2005 CDT
488,900,000   Sat Jul  9 16:13:04 2005 CDT
489,000,000   Sat Jul  9 17:24:55 2005 CDT
489,100,000   Sat Jul  9 18:36:47 2005 CDT
489,200,000   Sat Jul  9 19:48:38 2005 CDT
489,300,000   Sat Jul  9 21:00:30 2005 CDT
489,400,000   Sat Jul  9 22:12:22 2005 CDT
489,500,000   Sat Jul  9 23:24:13 2005 CDT
489,600,000   Sun Jul 10 00:36:05 2005 CDT
489,700,000   Sun Jul 10 01:47:56 2005 CDT
489,800,000   Sun Jul 10 02:59:48 2005 CDT
489,900,000   Sun Jul 10 04:11:40 2005 CDT
490,000,000   Sun Jul 10 05:23:31 2005 CDT
490,100,000   Sun Jul 10 06:35:23 2005 CDT
490,200,000   Sun Jul 10 07:47:14 2005 CDT
490,300,000   Sun Jul 10 08:59:06 2005 CDT
490,400,000   Sun Jul 10 10:10:57 2005 CDT
490,500,000   Sun Jul 10 11:22:49 2005 CDT
490,600,000   Sun Jul 10 12:34:41 2005 CDT
490,700,000   Sun Jul 10 13:46:32 2005 CDT
490,800,000   Sun Jul 10 14:58:24 2005 CDT
490,900,000   Sun Jul 10 16:10:15 2005 CDT
491,000,000   Sun Jul 10 17:22:07 2005 CDT
491,100,000   Sun Jul 10 18:33:59 2005 CDT
491,200,000   Sun Jul 10 19:45:50 2005 CDT
491,300,000   Sun Jul 10 20:57:42 2005 CDT
491,400,000   Sun Jul 10 22:09:33 2005 CDT
491,500,000   Sun Jul 10 23:21:25 2005 CDT
491,600,000   Mon Jul 11 00:33:16 2005 CDT
491,700,000   Mon Jul 11 01:45:08 2005 CDT
491,800,000   Mon Jul 11 02:57:00 2005 CDT
491,900,000   Mon Jul 11 04:08:51 2005 CDT
492,000,000   Mon Jul 11 05:20:43 2005 CDT
492,100,000   Mon Jul 11 06:32:34 2005 CDT
492,200,000   Mon Jul 11 07:44:26 2005 CDT
492,300,000   Mon Jul 11 08:56:18 2005 CDT
492,400,000   Mon Jul 11 10:08:09 2005 CDT
492,500,000   Mon Jul 11 11:20:01 2005 CDT
492,600,000   Mon Jul 11 12:31:52 2005 CDT
492,700,000   Mon Jul 11 13:43:44 2005 CDT
492,800,000   Mon Jul 11 14:55:35 2005 CDT
492,900,000   Mon Jul 11 16:07:27 2005 CDT
493,000,000   Mon Jul 11 17:19:19 2005 CDT
493,100,000   Mon Jul 11 18:31:10 2005 CDT
493,200,000   Mon Jul 11 19:43:02 2005 CDT
493,300,000   Mon Jul 11 20:54:53 2005 CDT
493,400,000   Mon Jul 11 22:06:45 2005 CDT
493,500,000   Mon Jul 11 23:18:37 2005 CDT
493,600,000   Tue Jul 12 00:30:28 2005 CDT
493,700,000   Tue Jul 12 01:42:20 2005 CDT
493,800,000   Tue Jul 12 02:54:11 2005 CDT
493,900,000   Tue Jul 12 04:06:03 2005 CDT
494,000,000   Tue Jul 12 05:17:54 2005 CDT
494,100,000   Tue Jul 12 06:29:46 2005 CDT
494,200,000   Tue Jul 12 07:41:38 2005 CDT
494,300,000   Tue Jul 12 08:53:29 2005 CDT
494,400,000   Tue Jul 12 10:05:21 2005 CDT
494,500,000   Tue Jul 12 11:17:12 2005 CDT
494,600,000   Tue Jul 12 12:29:04 2005 CDT
494,700,000   Tue Jul 12 13:40:56 2005 CDT
494,800,000   Tue Jul 12 14:52:47 2005 CDT
494,900,000   Tue Jul 12 16:04:39 2005 CDT
495,000,000   Tue Jul 12 17:16:30 2005 CDT
495,100,000   Tue Jul 12 18:28:22 2005 CDT
495,200,000   Tue Jul 12 19:40:13 2005 CDT
495,300,000   Tue Jul 12 20:52:05 2005 CDT
495,400,000   Tue Jul 12 22:03:57 2005 CDT
495,500,000   Tue Jul 12 23:15:48 2005 CDT
495,600,000   Wed Jul 13 00:27:40 2005 CDT
495,700,000   Wed Jul 13 01:39:31 2005 CDT
495,800,000   Wed Jul 13 02:51:23 2005 CDT
495,900,000   Wed Jul 13 04:03:15 2005 CDT
496,000,000   Wed Jul 13 05:15:06 2005 CDT
496,100,000   Wed Jul 13 06:26:58 2005 CDT
496,200,000   Wed Jul 13 07:38:49 2005 CDT
496,300,000   Wed Jul 13 08:50:41 2005 CDT
496,400,000   Wed Jul 13 10:02:32 2005 CDT
496,500,000   Wed Jul 13 11:14:24 2005 CDT
496,600,000   Wed Jul 13 12:26:16 2005 CDT
496,700,000   Wed Jul 13 13:38:07 2005 CDT
496,800,000   Wed Jul 13 14:49:59 2005 CDT
496,900,000   Wed Jul 13 16:01:50 2005 CDT
497,000,000   Wed Jul 13 17:13:42 2005 CDT
497,100,000   Wed Jul 13 18:25:34 2005 CDT
497,200,000   Wed Jul 13 19:37:25 2005 CDT
497,300,000   Wed Jul 13 20:49:17 2005 CDT
497,400,000   Wed Jul 13 22:01:08 2005 CDT
497,500,000   Wed Jul 13 23:13:00 2005 CDT
497,600,000   Thu Jul 14 00:24:51 2005 CDT
497,700,000   Thu Jul 14 01:36:43 2005 CDT
497,800,000   Thu Jul 14 02:48:35 2005 CDT
497,900,000   Thu Jul 14 04:00:26 2005 CDT
498,000,000   Thu Jul 14 05:12:18 2005 CDT
498,100,000   Thu Jul 14 06:24:09 2005 CDT
498,200,000   Thu Jul 14 07:36:01 2005 CDT
498,300,000   Thu Jul 14 08:47:53 2005 CDT
498,400,000   Thu Jul 14 09:59:44 2005 CDT
498,500,000   Thu Jul 14 11:11:36 2005 CDT
498,600,000   Thu Jul 14 12:23:27 2005 CDT
498,700,000   Thu Jul 14 13:35:19 2005 CDT
498,800,000   Thu Jul 14 14:47:10 2005 CDT
498,900,000   Thu Jul 14 15:59:02 2005 CDT
499,000,000   Thu Jul 14 17:10:54 2005 CDT
499,100,000   Thu Jul 14 18:22:45 2005 CDT
499,200,000   Thu Jul 14 19:34:37 2005 CDT
499,300,000   Thu Jul 14 20:46:28 2005 CDT
499,400,000   Thu Jul 14 21:58:20 2005 CDT
499,500,000   Thu Jul 14 23:10:12 2005 CDT
499,600,000   Fri Jul 15 00:22:03 2005 CDT
499,700,000   Fri Jul 15 01:33:55 2005 CDT
499,800,000   Fri Jul 15 02:45:46 2005 CDT
499,900,000   Fri Jul 15 03:57:38 2005 CDT
500,000,000   Fri Jul 15 05:09:29 2005 CDT

And the source:

#!/usr/bin/perl -w

use strict;
use LWP::Simple qw(get);
use CAM::XML;
use DateTime::Format::Strptime;

# Constants
my $url = "http://www.apple.com/itunes/external_counter.xml";

# Fetch, parse and extract tags from the XML
print "Retrieving $url...\n";
my $xmlstr = get($url) || die "Failed to retrive the XML";
my $xml = CAM::XML->parse(-string => $xmlstr) || die "Failed to parse the XML";
my @nodes = $xml->getNodes(-tag => "count");
die "Not enough data in the XML\n" unless (@nodes >= 2);

# Parse the times and counts out of the XML data
my $parser = DateTime::Format::Strptime->new(pattern => '%a, %d %b %Y %H:%M:%S %Z');
my ($count1, $count2, $time1, $time2);
foreach my $node (@nodes)
{
   my $timestr = $node->getAttribute("timestamp");
   my $timestamp = $parser->parse_datetime($timestr)->epoch;
   my $count = $node->getInnerText();
   if ($node->getAttribute("name") =~ /cur/)
   {
      $time2 = $timestamp;
      $count2 = $count;
   }
   else
   {
      $time1 = $timestamp;
      $count1 = $count;
   }
}
die "Failed to understand the XML\n" unless ($count1 && $count2 && $time1 && $time2);

# Compute the songs per second
my $rate = ($count2-$count1)/($time2-$time1);
printf "    Rate: %.2f songs/sec\n", $rate;

# Extrapolate to now
my $now = int($rate * (time()-$time2) + $count2);
print "    Predicted count right now: $now (".localtime()." CDT)\n";

# Extrapolate to other prize times
for (my $c=485000000; $c<=500000000; $c+=100000)
{
   next if ($c < $count2); # these have already passed...
   my $t = ($c-$count2)/$rate + $time2;
   # Insert commas
   my $cstr = $c;
   1 while ($cstr =~ s/(\d)(\d{3}(?:,.*|))$/$1,$2/);
   print "    $cstr   ".localtime($t)." CDT\n";
}

Working with UTF-8 on OSX

One of my recent projects has been to port a Clotho application to Japanese. We chose this opportunity to convert the code base fully to Unicode. The other obvious choice would have been to port to Shift-JIS, a popular encoding for Japanese characters. However, we decided that this latter choice was not forward-looking enough. Choosing Unicode, specifically the UTF-8 encoding, will allow us to port to other languages more easily.

This article is an accumulation of my learnings configuring my Mac to default to UTF-8 wherever possible. Most Mac OS X apps default to ISO-8859-1 (aka Latin-1) in the English locale that I use. Fortunately, most of the common utilities have preferences to change that default, although the prefs are different for each App.

My Unix command line setup is still based on the default “C” locale (aka ASCII). In a future post I hope to explore switching that to UTF-8 as well.

General

Under OSX, Safari controls the general encoding prefs for the whole system.

  • open Safari Preferences
  • select Appearance tab
  • change Default Encoding to “Unicode (UTF-8)”

Firefox

  • open Firefox Preferences
  • select General tab
  • click Languages button
  • change Default Character Encoding to “Unicode (UTF-8)”

TextEdit

  • open TextEdit Preferences
  • change the Open pulldown from “Automatic” to “Unicode (UTF-8)”
  • change the Save pulldown from “Automatic” to “Unicode (UTF-8)”

When in doubt, open files with Cmd-O instead of double-clicking them

BBEdit

Warning! BBEdit 6.5 has a bug that files opened via drag or double-click ignoring the Open settings. When working with non-ASCII files, use Cmd-O. I have not tested any BBEdit versions newer than v6.5

  • open BBEdit Preferences
  • select Text Files: Opening
  • check “Translate Line Breaks”
  • set “Interpret File Contents as:” to “UTF-8”
  • check “Warn of Malformed UTF-8 Files”
  • select Text Files: Saving
  • set “Default Line Breaks:” to “Unix”

Terminal

  • open Terminal Window Settings
  • select the Display options
  • check “Wide glyphs for Japanese/Chinese/etc.”
  • check “Wide glyphs count as 2 columns”
  • change “Character Set Encoding:” to “Unicode (UTF-8)”
  • click Use Settings as Defaults

Flash

(this is for Flash MX 2004)

  • open Flash Preferences
  • select the ActionScript tab
  • change Open/Import to “UTF-8”
  • change Save/Export to “UTF-8”

Warning! Flash does NOT obey this preference for files created outside of Flash! Flash only interprets files as Unicode if they have a Byte-Order Mark (BOM). However, BOMs are a pain in plain text files since they break some editors.

As a workaround we recommend putting all non-ASCII dynamic text in external XML files instead of directly in the ActionScript.

Emacs

Upgrade to v21.3. This is newer than the version that is supplied by Apple for either 10.3 or 10.4. Fink provides this version (only in unstable as of this writing).

Add the following to your ~/.emacs file:

(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)