1 0

The Mixed Bag that is Windows Phone 7

I’d like to preface this review by mentioning two things: firstly, despite a long list of things I dislike about Windows Phone 7, this is overall quite a positive review. While it might not be perfect for me right now (sorry to give away the twist), Windows Phone will serve a niche, and with speedy updates may even become competitive in the iPhone- and Android-saturated smartphone market.

Secondly, the only reason I’m reviewing Windows Phone 7 is because Telstra were kind enough to give me a free HTC Mozart. I’d like to assume that this is because I’m internet famous and an all-round awesome person, but it’s more likely that they thought this long-term Mac and iPhone using, incredibly cynical and generally grumpy guy would be a good critic of an ‘exciting’ new device from a company that is only slightly more interesting than the soles of my feet (you can interpret that as a comment about Telstra, HTC, or Microsoft, it’s your call). Or maybe they drew my name from a hat.

With that out of the way, let’s attempt to piece together a review of Windows Phone 7 without resorting to dropping a “Microsoft sux” and a few “developers, developers”. Keep in mind that this is in no way an exhaustive review of Windows Phone 7 – there are plenty of professional reviewers to do that kind of thing. This is just me, using the phone, and telling you what I liked and didn’t like. (more…)

Leave a Comment

Big Points to Zipcar for Proper Use of Social Media

June 15, 2010

I made the wonderfully intelligent mistake of leaving my TomTom GPS in a Zipcar on my recent trip to San Francisco, only realizing this once I got back to Australia. I whined about this on Twitter and, completely unprompted, I received the following email from Zipcar – they had seen my tweet, matched up my account info, and checked the lost and found for the car I had to see if anything had been turned in.

This is how brands should be using social media. None of this “send us your account information and we’ll see what we can do” crap. Zipcar were proactive in seeing my issue, and did all they could to help (despite the issue being entirely my fault). They didn’t even reply to me on Twitter; This was just to satisfy a customer, not for the publicity.

Zipcar: great job, and you’re sitting very high in my books now.

From: “Lien Bui (Zipcar SF)” <redacted@zipcar.com>
Date: 14 June 2010 9:58:21 PM AEST
To: bck@bradkellett.com
Subject: Re: Zipcar
Reply-To: “Lien Bui (Zipcar SF)”

Dear Bradley,

I wanted to follow up on your reservation with Zipcar Iams regarding your lost item. I have followed up but unfortunately did not see any reports regarding your lost item. I understand your frustration regarding this and will make sure to let you know should we find it.

In the future, if you realize you’ve left an item in a Zipcar you can simply return to the Zipcar up to 3 hours past the end of your reservation with no additional charge. As long as someone else hasn’t taken the car yet, the vehicle will allow you to enter with your Zipcard. Just don’t drive it or you will be charged.

You can also use the online Lost & Found to see if any members have reported this.

Again, I apologize for the inconvenience, and if I see or hear anything, I will be sure to notify you of any updates.

Regards,

Lien Bui (Zipcar SF)

zipcar

Leave a Comment

PlaceWidget and Cross-Domain iFrame Javascript

One of my latest projects is PlaceWidget, a service that lets you embed a Foursquare widget into any website (now with a WordPress plugin and a Facebook App). When I kicked off development of PlaceWidget, I had three goals in mind:

  1. Make it as easy as possible to configure
  2. Make it as easy as possible to add to a site
  3. Make it look exactly the same regardless of the user’s website

Task one was, I believe, achieved with the simple three-step configuration process on the PlaceWidget website, but task two and three seemed to be at odds with each other. The best way to isolate the widget from the user’s website CSS and Javascript is to have it be embedded via an iFrame, but this presented a problem since the widget content is not a fixed height. I needed a way to resize the iFrame on the user’s site based on the content inside of it, but the browser security model gets in the way of this – Javascript loaded from different domains cannot communicate with one another. Facebook gets around this problem my having the user upload a cross domain receiver file to their site, but this conflicts with my task two (I wanted adding the widget to involve adding just a single line of code where the user wanted the widget to appear).

What I came up with was a solution to trick the browser into letting Javascript inside the iFrame communicate with Javascript on the user’s site, so the iFrame could report it’s loaded height, and the parent page could resize the iFrame to fit.

Instead of the user adding an iFrame to their page, I have the user embed a Javascript link tag pointing to a location on http://placewidget.com. Inside this Javascipt file, which is dynamically created on request, is the content that would have been inside the iFrame as a string value. The rest of the code in this JS file then creates an iFrame on the user’s page without an HREF, grabs a handle to the iFrame’s document and builds the content inside. Once that’s complete, the Javascript then reads the height of the content and resizes the iFrame – it can do this because an iFrame with no HREF is deemed as being on the same domain as the parent page.

There are a few caveats to this approach:

  1. There are three different ways to get a handle to the iFrame’s document object depending on what browser you’re in
  2. You have to open and close the iFrame’s document once before you can write to it (see below)
  3. If you include a style tag in your dynamically generated content, you must insert a ‘standard’ element like a BR or a DIV before it, otherwise Internet Explorer will not apply the styles

The code below shows how you can create an iFrame and get a valid, writable handle to its document object.

  1. // Create an iFrame and add it to the document
  2. // You’d likely place it somewhere other than the bottom of the body
  3. var iframe = document.createElement(‘iframe’);
  4. document.body.appendChild(iframe);
  5.  
  6. // Find the iFrame’s document object depending on browser
  7. iframe.doc = null;
  8. if(iframe.contentDocument)
  9.     // Firefox, Opera
  10.     iframe.doc = iframe.contentDocument;
  11. else if(iframe.contentWindow)
  12.     // Internet Explorer
  13.     iframe.doc = iframe.contentWindow.document;
  14. else if(iframe.document)
  15.     // Safari, maybe others?
  16.     iframe.doc = iframe.document;
  17.  
  18. if(iframe.doc == null)
  19.     // Something sucky has happened and you can’t see the document
  20.  
  21. // Open and close the document to make it valid and writable
  22. iframe.doc.open();
  23. iframe.doc.close();
  24.  
  25. // Now you can add content as normal
  26. // Make sure you create your elements in the context of the iFrame
  27. var div = iframe.doc.createElement(‘div’);
  28. div.innerText = "This is pretty neat";
  29.  
  30. // Body is implicit, but you should probably create it properly anyway
  31. iframe.doc.body.appendChild(div);
Leave a Comment

Using Google Voice in Australia

January 15, 2010

Here is an easy way to use Google Voice for voicemail transcription in Australia, with a bit of help from Skype:

  1. Sign up for a new Skype account (it’s best you don’t use your primary one, since you won’t be able to receive calls on it anymore)
  2. Add an Australian Online Number to this account through the Skype website
  3. Add some Skype Out credit to the account, or sign up for a subscription that includes calls to the US
  4. Open the Skype application and turn on call forwarding for unanswered calls for this account. Set your Google Voice number as the number to forward to, and set it to forward after 0 seconds (this just tells Skype to forward right away)
  5. Open Google Voice in your browser, click Settings in the top right, then click the Calls tab. From here, turn on Do Not Disturb mode. This is a crucial step, as it makes Google instantly pick up your call for voicemail
  6. Set your Australian phone to forward unanswered calls to your Skype Online Number. The online number will then forward to Google Voice which will pick up the call for voicemail.

Skype occasionally messes up the called ID on forwarded calls, but it get’s it right 99% of the time. With this method you can now get free voicemail transcription to your email, or use the built-in Google Voice application on Android phones. If you have a Google Voice invite but don’t know how to activate it from outside the US, the easiest way is to get a US Skype Online Number and give that to Google in order to activate your account.

Leave a Comment

Javascript/CSS Minification Script

I’ve been using a little bash script I wrote that will automatically minify any Javascript and CSS files recursively in a directory with the YUI Compressor, and I thought I’d share it with the world. With the script, you can chose to minify just Javascript (with the -j option), just CSS (with the -c option), delete any existing minifed files (with the -d option), or any combination of the three. Files will be saved with a .min.js or .min.css extension (for example, test.js will become test.min.js). All you need to do is put the YUI Compressor JAR file somewhere within the current working directory, and the script will find it.

Usage is simple:

$ ./minify.sh -dcj Deleting existing minified files... Minifying JavaScript... Processing: ./test.js Minifying CSS... Processing: ./test.css Done.

You can download the latest version of the script from my GitHub repository.

Leave a Comment

Limit App Engine to Minified JS and CSS

Call me paranoid or call me efficient, but I don’t like non-minified CSS and Javascript accessible in a production environment. It’s one thing setting up your web application to use minified files when it’s rolled out , but I’d prefer that the originals aren’t available at all – and yes, part of that is me not wanting to make it too easy for people to snoop through my code (though I do release a lot as open source anyway).

In any event, this proved a little tricky when using Google App Engine for LocationFu. Google’s deploy process has the option of excluding certain application files from being sent up, but it can’t exclude any files that sit within a directory marked as a static path. The solution is simple, but took me a while to figure out. Previously, I had my static path set up like so in my app.yaml file (note that this is for Python App Engine):

- url: /static
static_dir: static

That will map any file starting with /static into the static directory in the app tree. Instead, you can map individual files, and limit the extensions you want to serve:

- url: /static/(.*?)
static_files: static/\1
upload: static/(.*\.(gif|png|jpg|min\.css|min\.js))

That tells App Engine to grab the file name requested and map it into the static directory, while the upload parameter includes a regex to match only selected file extensions (.min.js, but not just .js, for example). This won’t stop your app from serving any file while running on the local development server, but when pushed to production, your JS and CSS files will only be accessible in minified form.

Leave a Comment

MooTools Idle Tracker Class

In web applications, there are times when you may want to track activity of a user within your page – perhaps your project includes a realtime component that you want to pause when the user isn’t actually working with the page, for example. This is pretty easy to take care of, but there are a few little quirks, so to simplify it all I’ve released a MooTools class to take care of it for you.

The class is super simple to use, all that is required is for it to be instantiated and it’ll get on with it. You can specify how many seconds of no activity on your page before the user is classed as idle, as well as a couple of more technical options, then just attach a function to the onIdle and onIdleReturn events of the class. For example:

  1. var tracker = new IdleTracker({
  2.     idleTime: 2,
  3.     onIdle: function() {
  4.         // User is idle
  5.     },
  6.     onIdleReturn: function() {
  7.         // User has returned
  8.     }
  9. });

You can get full details and download the class from my GitHub account.

Leave a Comment

Chrome OS Booting on my Original eeePC

November 20, 2009

Booting and running the Google Chrome OS (which I compiled from the open source Chromium OS source code) on my ancient original Asus eeePC. The 404s are because it takes a while to connect to my wireless internet. Note the awesome graphics bugs, which thankfully don’t happen too often.

Leave a Comment

Facebook’s New Photo Uploader – a Plugin Fail

November 19, 2009

I have the utmost respect for the engineers at Facebook. They are a very talented bunch, and the few of them I’ve had the pleasure to meet in person have even been nice folks to boot. Generally, they come up with very innovative and progressive engineering solutions to the huge problems they face, but their new photo uploader is one decision I completely disagree with.

The new Facebook in-browser photo uploader  is detailed well in a post from their engineering blog (along with a slightly more detailed look at security), but in a nutshell, it involves installing a browser plugin to handle file uploads in a separate thread. This plugin also creates Javascript APIs to gain full filesystem access to your computer, and sets up a small web server to allow Facebook to ping back to your local machine to check the status of your uploads. The primary reason to use this plugin-based approach is to allow a user to begin uploading photos and have them continue in the background while continuing to browse around the site.

This is a noble thought, but I really think it’s just engineering for engineering sake. The multitude of issues surrounding creating and maintaining a plugin of this nature far outweigh the advantage of allowing background uploads. And if background uploads are really that important to them, they could have come up with a smart solution to make use of their current AJAX page loading process. This process, currently in place, allows certain browsers like Firefox to surf around Facebook without re-loading the whole page, so technically a simple Flash uploader could continue as part of the original parent page. It’s called progressive enhancement folks – the browsers that don’t support Facebook’s AJAX page loads can just use a normal uploading process.

And apart from all of that, what is stopping users from opening a new tab or window to continue browsing? Seems to work well for every other site out there.

Yes Facebook, your current photo uploader is awful, but let’s not create a huge security hole by exposing filesystem access to Javascript and running a little web server on everyone’s machine. I really don’t want to have to trust you to maintain security on your plugin (on three platforms, even), just so I can upload a photo.

Leave a Comment

Use Firefox Betas on the Mac without Overriding Other Versions

November 12, 2009

On Windows, the Firefox installer (quite considerately) won’t override existing non-beta versions of the browser with a beta version (like the recently released Firefox 3.6 betas). Though it doesn’t explicitly say so, Mac OS X users can have a similar behavior, which by the surprised faces I see when  I tell people this, many people don’t know about.

It’s simple:

  1. Grab a Firefox beta download, then mount the disk image
  2. Instead of dragging the Firefox app to your Applications folder, drag it to your desktop temporarily
  3. Highlight the app on your desktop, hit the return key, and give it a new name (like “Firefox 3.6 Beta”)
  4. Drag the newly renamed app to your Applications folder

You can then run these two Firefox versions with little interaction between the two. I say ‘little’ because they will share the same profile (where your bookmarks, extensions and the like are kept), and so cannot be run at the same time. This has the plus side of meaning you won’t have to re-import bookmarks and such, but you will have to sit through a harmless extension compatibility check whenever you start a different version of Firefox than was last run.

Leave a Comment