Resize Subsonic frames

I upgraded Subsonic today and I had to remember the little hack I put in to enable resizing of the playlist frame. This is the second time I've had to look up how to do this, so I'm just going to document it here.

By default, each piece of the Subsonic web interface is a frame, and all the frames are borderless and not resizable.  This is kind of a pain because sometimes I have a long playlist and I want to see all of it at once.  The ideal way to do that would be to just increase the size of the playlist frame - but I can't do that.  Fortunately, fixing this is relatively easy.  You can just modify the frameset markup in the following file:
C:\subsonic\jetty\<number>\webapp\WEB-INF\jsp\index.jsp

For my case, the solution was to change the third frameset tag by increasing turning on frame borders and increasing the border size.  Here's the markup:
<frameset rows="75%,25%" border="5" framespacing="0" frameborder="1">

Using RewriteBase without knowing it

So here's an interesting tidbit that I discovered this afternoon: you can use RewriteBase in a .htaccess file without knowing the actual base URL.  This is extremely useful for writing a portable .htaccess file.

In case you don't know, the RewriteBase directive to Apache's mod_rewrite is used to specify the base path used in relative rewrite rules.  Normally, if you don't specify a full path, mod_rewrite will just rewrite the URL relative to the current directory, i.e. the one where the .htaccess file is.  Unfortunately, this isn't always the right thing to do.  For example, if the .htaccess file is under an aliased directory, then mod_rewrite will try to make the URL relative to the filesystem path rather than the path alias, which won't work.

Turns out that you can account for this in four (relatively) simple lines:

RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*)\1$
RewriteRule ^(.*)$ %2index.php [QSA,L]

All you need to do is substitute in your rewrite target for "index.php" and it "just works".  No changes to server configuration required and no need to edit the RewriteBase for the specific server.

VirtualBox shared folders

Here's a little issue I ran across the other day.  I was setting up a VirtualBox VM with an Ubuntu guest and I wanted to add a shared folder.  Simple, right?  Just install the VirtualBox guest additions, configure the shared folder to mount automatically, and it "just works".

The only problem is the permissions.  By default, VirtualBox mounts the shared folder with an owner of root.vboxsf and permissions of "rwxrwx---".  So if you add your user account to the vboxsf group, you get full access.  Everyone else...not so much.  Of course, this isn't inherently a problem.  However, I wanted the Apache process to have read access to this shared folder because I have a web app that needs to read data off of it.  And I didn't really want to give it write access (which it doesn't need), so just adding it to the vboxsf group wasn't a good option.  What I really needed to do was change the permissions with which the share was mounted.

As far as I can tell, there's no way to get VirtualBox to change the permissions.  At least, I didn't see anything in the guest OS and there's no setting in the management UI.  Fortunately, you can pretty easily bypass the auto-mounting.  Since it's a Linux guest, you can just turn off the auto-mounting in the VirtualBox management console and add a line to your /etc/fstab.

There is one issue, though: you have to make sure the vboxsf kernel module is loaded before the system auto-mounts the share.  If you don't the mounting will fail.  However, forcing the module to load is easily accomplished by adding a vboxsf line to your /etc/modules file.

As for the line in /etc/fstab, this seems to work pretty well for me:
SHARE_NAME   /media/somedir   vboxsf   rw,gid=999,dmode=775,fmode=664   0   0

Tired of Cloud Drive

You know what?  Amazon Cloud Drive is a little bit of a pain.  I think it's time to start moving away from it. I think I'm going to give DropBox another look.

To be clear, I don't plan to stop using Cloud Drive altogether. For one thing, the Kindle integration is actually pretty nice.  And for another, they give Amazon Prime members a pretty decent amount of space for free.  And I find that the desktop Cloud Drive app actually works pretty well.  It's just the mobile app and the website that suck.

The mobile app

For starters, let me voice my new complaint with the mobile app: the semantics of syncing your pictures are rather poorly defined.  Or, rather, they're not defined — at least, not to my knowledge.  By that I mean: what happens when you delete a bunch of files from your Cloud Drive, but not from your phone's main picture storage?  I'll tell you what: the app tries to re-add them to your Cloud Drive. 

This has happened to me two or three times in the last week or two.  A couple of weeks ago I went through my Cloud Drive and deleted some of the pictures that were duplicated, out of focus, etc.  Now, at semi-random intervals, the app wants to re-upload them.

In fairness, this could be due in part to the fact that Amazon's apps all seem to share authentication and it seems to be semi-broken on my phone.  I've had this problem several times recently.  The Kindle app on my Galaxy Nexus will stop synchronizing and the settings will report that it is "Registered to .".  No, that's not a typo, my name is just missing.  And when this happens, I can't authenticate with the Cloud Drive app or the Cloud Player app either — they just reject my credentials.  So far, the only fix I've found is to deregister my device in the Kindle app settings and then re-register it.  That fixes the Kindle app as well as authentication in the other apps. 

The website

I've blogged about the Cloud Drive website and it's useless "share" feature before.  Well, despite that, the other day I decided to give that "share" feature a try.  Hey, maybe it won't really be so bad, right?

Good Lord, it was even worse than I'd imagined.

So my task was to share one or two dozen pictures of my son with my relatives.  Seems simple enough, right?  The pictures are already in my Cloud Drive, so I just need to send out some links to them.  As I noted in the last entry, Cloud Drive doesn't actually support sharing folders or sharing multiple files at once, so the only way to do this is by sharing one file at a time and copying the URL into an e-mail. 

As bad as this is, it turns out it's made even worse by the fact that Cloud Drive is now reloading the file list after you complete the "share" operation.  And to add insult to injury, the reload is really slow.  Granted, I have about 1500 images in that directory, but that shouldn't matter because the reload really doesn't need to happen at all.  I mean, nothing has changed in the folder view.  All that happens is that the UI locks up for a few seconds and then I get a "file has been shared" message.

So this only confirms my opinion that the "share" feature in Cloud Drive is unusable.  I mean, if sharing were just a matter of popping up the "share" dialog that holds the URL, copying it, pasting to another window, and then closing the dialog, that would be one thing.  It would suck, but I could deal with it.  But the brain-dead UI locking up for five seconds after each share just makes it too painful to even try.  I got through maybe half a dozen pictures before giving up in disgust.

Solution - DropBox?

So, clearly, I need another platform for sharing my pictures.  So I looked around and found myself mighily confused.  My requirements were pretty simple.  I wanted something that:

  1. Had a free account tier,
  2. Had an easy way to share by just sending someone a link,
  3. And provided a simple uploading option.

I briefly considered using the Google+ "Photos" feature, but gave up on that when I realized the whole visibility/sharing thing wasn't completely obvious.

For now, I'm trying out DropBox.  I've had a free DropBox account for a couple of years, but never really used it for anything, so that was one less thing to sign up for.  The desktop app is pretty nice and allows me just drag files between Explorer windows and shows an icon of the file's sync status.  And sharing things via the web UI is dead simple, which is exactly what I was looking for.  So we'll see how that goes.  Worst-case scenario, I won't have lost anything.

Quickie TDD with Jasmine and Komodo

I'm currently on my annual "this time I'm really going to start doing test-driven development (or at least something close to it)" kick.  And it seems to be going pretty well, so hopefully this time it will actually stick. 

As I said, I do this every year and testing usually ends up falling by the wayside.  Granted, this is partly just due to a lack of discipline and commitment on my part.  Getting started with a new practice takes effort, and sometimes it's hard to justify that effort when "I've been doing just fine without it for years."  But there's also the matter of the type of projects I work on.  Most of the projects I've worked on have not had an establish test suite or a culture of testing.  Whether it's a work-related project or an old personal project for before I heard the gospel of TDD, the norm has been a sizable, years-old code-base that has few if any tests and isn't really designed to be testable in the first place. 

Getting into TDD with a project like that can be daunting.  Picture PHP code littered with static method calls, system calls, and direct class instantiations at multiple levels.  "You need to inject a mock object?  Yeah, good luck with that."  The classes may be well-defined, but there's not much compartmentalization of responsibilities within them, so inserting test doubles is not always straight-forward.  That leaves you in the unenviable position of either having to rewrite a bunch of existing code to make it unit-testable or set up some real test data and turn the unit test into an integration test.  The first option is obviously preferable, but can be much more risky, especially since you don't already have tests to validate the behavior of the code you need to change.  And while the second approach is certainly better than no tests at all, integration tests are slow to run, cumbersome to set up, and much more prone to breakage when things change.  Faced with a mess like that, it doesn't seem that unreasonable to say, "You know what?  I've got things I need to get done.  I'll get back to those tests later."  But, of course, you never actually do.

This time, I'm doing things a little differently.  For starters, I'm not writing new tests for old code.  I'm just doing the new code.  I'll get to the old code when/if I get around to refactoring it.  That means that I don't have to worry about untestable code, which makes the entire enterprise about a thousand times simpler.  I'm also not working with PHP code for the time being.  I'm trying to do TDD on two projects — one for work that's in JavaScript and a personal one in Python.  For Python, I'm using unittest and mock which, so far, I'm finding to be less of a pain in the neck than PHPUnit.

For the JavaScript project, I'm using Jasmine, which brings me to the title of this post.  Since I'm trying to do TDD (or rather BDD), I quickly tired of alt+TABbing to a browser window and then hitting ctrl+R to reload the page with the test runner.  Sure, it's not a big deal, but it's just one more thing that gets in the way.  I wanted to do what I could do in Python and PHP, which is just hit a hotkey and have Komodo run the test suite right in the same window. 

Turns out, that was actually pretty easy to set up.  I just banged out a quick macro that opens up the Jasmine test runner HTML file in a browser tab or refreshes it if it's already opened.  I bound that to a hotkey and I'm good to go.  Sure, it doesn't use the Komodo IDE testing framework, but that's not the end of the world — I just move it to a split pane and it works out pretty well.  I even added some CSS to the spec runner to make it match my Komodo color scheme.

Here's a screenshot of the side-by-side runner and some (unrelated by public) code:

And here's the macro itself:
(function() {
    var uri = 'file:///path/to/test_runner.html',
        view = ko.views.manager.getViewForURI(uri);
    if (view === null) {
        ko.open.URI(uri, 'browser');
    } else if (view.reload) {
        view.reload();
    } else {
        view.viewPreview();
    }
})();

Ext Direct errors

Note to self: when Ext Direct calls start failing, look in the request headers for error messages.  I'm not sure whether it's Ext itself or just our framework, but for whatever reason, Ext Direct calls seem to more or less swallow server-side errors.

In this particular case, I was experimenting with some of the code that our in-house development framework uses to render maps.  We have OpenLayers on the front-end a custom PHP back-end that we communicate with in part through Ext Direct, which is the handy-dandy AJAX RPC framework that comes packaged with Sencha's ExtJS.

So anyway, I made some changes, reloaded the page, and all my Ext Direct calls were failing.  No meaningful error messages, nothing in the JavaScript console, and the response body was just empty.  So what the heck was happening?  (Yeah, I know, I could have just run the unit tests, since we actually have unit tests for the framework code.  But that didn't occur to me because so much of the application code is missing them and I was just experimenting anyway.  Get off my back!) 

Then I noticed, just by chance, that the request headers in the network tab of Chrome's dev tools looked weird.  In particular, it contained this header:
X-Powered-By:PHP/5.3.21 Missing argument 1 for...

So that's what happened to the error message — it got dumped into the headers.  Not terribly helpful, but good to know.

VI makes so much more sense now

Thanks to this answer from StackOverflow, the key mappings in VI and Vim make soooo much more sense now.  It's all explained by this keyboard layout:

The keyboard layout that was used when VI was created.

The more I use Vim, or the Vim emulation in Komodo, the less sense it makes to use the "escape" key to switch out of insert mode.  I mean, you have to move your hand way away from the home row just to reach it, which is exactly the opposite of how things are supposed to work.  But if you're using this keyboard, then it seems like a completely obvious choice.

Incidentally, after far too many years of suffering with the escape key, I've switched to using the other choices for getting out of insert mode — Ctrl-C and Ctrl-[.  Both do the same thing as the escape key, but require significantly less hand movement and so are faster to type.  Especially Ctrl-C, as most of us are used to using that for copying text to the clipboard.

Incidentally, since I'm using the Vim emulation for Komodo IDE/Edit, I should probably mention that it does support Ctrl-[ out of the box, but not Ctrl-C — that is, naturally, reserved for the "copy" operation.  However, it's easy to change that.  Simply create a new macro with the following code and set the key binding for it to Ctrl-C (you may have to unbind the standard "copy" operation first):

var scimoz = ko.views.manager.currentView.scimoz;
if (scimoz && scimoz.selText.length == 0) {
    ko.commands.doCommand('cmd_vim_cancel');
} else {
    ko.commands.doCommand('cmd_copy');
}

This macro preserves the standard copy behavior by checking if you have any text selected when it executes.  If you do, it does a standard "copy to clipboard".  If you don't, it switches the editor from "insert" mode to "normal" mode.

Project commit in Komodo IDE

So I finally got around to writing a little macro for Komodo IDE that I've been missing for a while.  It has a very simple task - perform a "source control commit" operation on the currently project.  You'd think this would be built in, and it wort of is, but it doesn't work very well. 

In Komodo IDE, the SCC commit action is tied to files and directories.  So if you want to commit an entire directory, you need to select it in the "places" pane (i.e. the file system browser).  And if you want to commit the directory that's currently set as the root of the file view, you either have to go up a level and select it or right-click on an empty spot in the file browser.  So, in other words, it's grossly inconvenient.

Hence this little macro.  Just create a new macro in your toolbox and paste this code into it (note that this doesn't work in Komodo Edit).  Note that this is a little hacky, as some of the SCC initialization seems to be asynchronous and I don't know what (if any) events are fired on completion.  But hey, it works, so close enough.

(function() {
    var curr_project_url =  ko.projects.manager.getCurrentProject().importDirectoryURI,
        fileSvc = Components.classes["@activestate.com/koFileService;1"]
                            .getService(Components.interfaces.koIFileService),
        kodir = fileSvc.getFileFromURI(curr_project_url),
        count = 0;
    
    // HACK: For some reason, the SCC type takes some time to populate.
    // I don't know if there's any event for this, so instead just try it again
    // if it's empty.
    var runner = function() {
        var cid = '',
            sccSvc = undefined;
            
        if (kodir.sccDirType) {
            cid = "@activestate.com/koSCC?type=" + kodir.sccDirType + ";1";
            sccSvc = Components.classes[cid].getService(Components.interfaces.koISCC);
            
            // Get the koISCC service object
            if (!sccSvc || !sccSvc.isFunctional) {
                alert("Didn't get back a functional SCC service. :(");
            } else {
                ko.scc.Commit(sccSvc, [curr_project_url]);
            }
        } else if (count < 10) { // Just in case this never actually works....
            count += 1;
            setTimeout(runner, 100);
        }
    };
    
    runner();
})();

Chrome debugging victory

This post is about a Chrome bug that I fixed a few months ago in new product I was working on. Normally, this would not be noteworthy, but this one was a real mind-bender.

The product I was working on at the time is an add-on to one of my company's services. To give a little background, our bread and butter is high-resolution aerial imagery - think satellite images, but with better resolution and with oblique shots taken from multiple directions in addition to the overheads. Our biggest customers for these fly-overs are county governments, who use the imagery for a variety of purposes. For example, when I was working for a county government, we used Pictometry's images for 911 dispatching, so that dispatchers could give detailed information on a property to police and other first responders.

Anyway, counties will typically order a new fly-over every year or every few years so that their images remain up to date. One of the add-on services we offer with this is a "change finder" service, where we'll analyze the images from two different years and provide the customer with a data set highlighting the structures that have changed. The product I was working on is actually an add-on to that service. It's a web-based application targeted at local assessors that gives them a simple, no-setup-needed way to access their change data and provides a basic workflow for analyzing it and making determinations on property values. So rather than spending an hour driving out to inspect a location, the assessor can use a rich web UI to see that the property now has garage that wasn't there last year and estimate how big it is. Saves them time and keeps the tax rolls up-to-date.

As for the application itself, it's based on our next-generation internal development framework. The back-end is built on PHP and PostgreSQL/Oracle with the front-end being built on Ext JS. It features a dual-pane "before and after" map viewer that's built on OpenLayers and integrates with the customer GIS data. There is a set of search tools that lets the customer narrow down and navigate their data set. In addition, the images are overlaid with outlines to highlight what properties have changed.

But enough exposition - on to the bug! This one was a real head-scratcher. Basically, sometimes the UI would just freak out. All the controls outside the map area would just turn into one solid gray field, effectively rendering them invisible. They still worked, if you knew where to click, but you couldn't see them. They'd come back for a second when you panned the map image, but then disappear when you stopped panning. And it wasn't a transient JavaScript thing - reloading the page didn't help. And the real kicker was that this bug only happened in Chrome (we didn't test other Webkit-based browsers, as Chrome is the only one we officially support).

This bug had been languishing in our queue for weeks. It wasn't consistently reproducible. We found that it happened less if you turned off Chrome's hardware acceleration, but it didn't go away entirely. Sometimes it would happen consistently, whereas other times you could go a full week and never see it. We eventually determined that it was related to the number of images overlayed in the map area, because hiding enough of them made the issue go away. We also found that it happened more at larger window sizes. In retrospect, that was probably because that allows room for more images.

Since the non-map portion of the UI was what was getting messed up, I hypothesized that it might be due do some weird interaction with the styes used by Ext JS. However, that didn't pan out. It turned out that there was nothing remarkable about Ext's styles and every "discovery" I made ended up reducing to "more images in the map". The one insight I was able to glean was that the issue was the number of images visible in the viewport, not just the number present in the map area DOM.

I eventually found the source of this bug through sheer brute force, i.e. process of elimination. I opened up the Chrome developer tools and starting hiding and removing DOM nodes. I started outside the map area and gradually pruned the DOM down to the smallest number of elements that still caused the bug.

It was at that point that I finally found a suspicious CSS rule. It was a CSS3 transform coming from our client-side image viewing component. Apparently the rule was originally put there to trigger hardware acceleration in some browsers. I have no idea why it caused this bug, though - I don't think we ever did figure that out. But it was an easy fix and I was rather proud of myself for having figured it out. I suppose that just goes to show that sometimes persistence is more important than brilliant insight when it comes to bug fixing.

Komodo fullscreen fix

Here's a a quick fix for a Komodo IDE bug that I reported a few weeks ago. 

I noticed a little while back that when you switch to full-screen mode in Komodo, the breadcrumb trail in the status bar disappears.  This seems like abug, as nothing else in the status bar goes away.  Why should the breadbrucms?

Anyway, a quick perusal of the komodo.css file and some tinkering using the Komodo Extension Developer add-on revealed an easy way to fix this.  All you need to do is create a new macro with the following text and set it to run on startup:

document.getElementById('breadcrumbBar').setAttribute('fullscreentoolbar', 'true');

That will add the appropriate attribute to the breadcrumb bar so that the CSS rule for "hide these when full-screen" won't catch it.  The breadcrumb bar is now visible in full-screen mode and seems to be fully functional.