Techniques for Clean Markup

It’s exceedingly rare that I read an article on code techniques and agree with absolutely everything that’s being espoused, but this article by Chris Coyier for Smashing Magazine is just that. In my opinion, this is a must read for all web developers that have to work in and around X/HTML code. My personal favorite: Eliminate unnecessary divs.

Sometime during the learning stages of Web design, people learn how to wrap elements in a div so that they can target them with CSS and apply styling. This leads to a proliferation of the div element and to their being used far too liberally in unnecessary places.

Oh yeah. And the example provided could not be more illustrative.

The others are spot on, as well, but this is my favorite because it’s less obvious and the single biggest source of code bloat, in my experience.

PHP and the Negative Lookahead

I am, quite unabashedly, a sucker for a good regex question. I dig regular expressions. I love their power, their flexibility and their precision. Often I’ll use a regex even when I don’t have to because I can be absolutely precise about what I want even at the expense of a millisecond or two.

Being the sucker that I am, I was thoroughly hooked when @benrasmusen asked for regex help on Twitter yesterday.

He had a set of text that looked something like this:

<p>With more than 20 years’ experience recording, mixing, and mastering, I have an in-depth understanding of the mindsets involved in each step of a project. The focus of the mixer is the balances of the individual elements and how they relate to create a song. By contrast, the mastering engineer focuses on the song as a whole.</p>
<p>In mixing numerous songs for an album, the mixing engineer typically has a hard time creating consistency across all the songs. But the mastering engineer seeks to maintain energy levels and sonic flow among all the songs (except when not appropriate), creating a unified signature for the record. Those involved in recording a project often become emotionally attached to it, whereas the mastering engineer can provide an objective ear.</p>

He also had a set of terms that he wanted to find in that text so that he could replace each instance of the term with markup that injected a link around the text. For example, the word “mastering” might be replaced with:

<a href="#" title="A link to information about mastering">mastering</a>

The problem he was having was that the title attribute value applied to the injected link occasionally contained a word that matched a term that would be replaced in a future iteration. For example, in his case, the title of the link around “mastering” included the word “mix”. “Mix” was another term that had to be replaced with its own link and that happened after the replacement of “mastering”. Result: the instance of “mix” in the title attribute of one link was being replaced with another and he ended up with nested links – a new link nested right within the title attribute of another.

Oy.

Over the years, I think I’ve become a competent user of regex and perhaps even proficient. I am not, however, a guru. There are a number of concepts that elude me on a practical level, though I understand the theory well enough. One of these concepts is the lookahead and another is its sister concept, the lookbehind.

In this case, I knew enough to know that a lookahead (in this case, a negative lookahead) was needed, but not enough to know exactly how to do it. So I looked. And now I’m documenting for my own later reference. What he needed was this regex:

$regex = '/\b' . $term . '\b(?![^<]*>)/ig'

This finds all of the word-delimited instances of the replacement term that are not followed by zero or more instances any character other than a “<” and is, in turn, followed by a “>”. In other words, all of the instances of a replacement term that are not followed at some point, but before opening a new tag, by a “>”.

It’s not perfect, of course, but it’s workable, I think.

Redux: Synchronizing Firefox Through Dropbox

For the most part, the Dropbox-enabled synchronization of Firefox that I wrote about a couple of months ago has been reasonably solid. A few hiccups and a few annoyances, but nothing serious or even significant enough to warrant a change of approach. This morning, though, that changed and I needed to re-evaluate my synchronization strategy.

Yesterday.

Until today, I’ve been running something of a kitchen sink mechanism. You can read the full post for the details, but I was sharing my entire Firefox profile across multiple systems and platforms using Dropbox activity. Bookmarks, extensions, cookies, history and everything else. The transparent and immediate way that Dropbox works made this possible.

Over the last couple of months of using this method across one Mac client, one Linux client and two lesser used Windows clients, I’ve been bothered by several side effects:

  • Sharing profiles is chatty. Really chatty. I noticed this immediately and even mentioned it in my original post, but damn. Sharing an entire profile means a lot of Dropbox activity. Turning off system tray notifications in the preferences helped, but I kid you not when I tell you that the little blue animated icon overlay never stopped spinning. Unless Firefox wasn’t running at all, that thing was spinning like a madman.
  • When I move from machine to machine, I’m not as diligent as I should be about quitting Firefox on the machine I’m leaving. Because of this, I assume, I ended up with a lot conflicted files in my Dropbox-mounted, shared profile directory after a while. This isn’t really a problem, as far as I know, but I hate clutter.
  • Virtually every time I launched Firefox, I got the prompt that tells me my last session quit unexpectedly and asks whether I want to start a new session or restore my last. Because of the difference between how Mac and Windows/Linux order OK/Cancel buttons (even when the buttons don’t actually say OK or Cancel), I’d often find myself hitting the wrong one. Remember, I work in all platforms.

All of these actions by both applications, of course, are absolutely correct. Both Dropbox and Firefox were behaving exactly as expected, but the necessary side effects of the architecture I had concocted were still annoying.

Today.

This morning things got a bit more dire. Firefox simply wouldn’t launch while I was at work on my Linux machine. No matter how I tried – command line, Applications menu, Gnome Do, etc. – Firefox just wouldn’t start. There was no error or indication of an issue, Firefox just, well, did nothing at all. When I launched Firefox into the Profile Manager, though, I was able to select the default profile rather than my Dropbox-ified profile and run Firefox happily.

Knowing that the problem was in my shared profile created a level of severity that I needed to address.

Fixed.

Now that simple annoyance had turned to need, I set about defining a less brutish synchronization mechanism. What I really want synchronized across all of my machines – the profile elements that are most needed and yet the biggest pain to synchronize manually – are my bookmarks, my extensions and my Greasemonkey scripts so I decided to apply a more targeted solution.

Leaving my shared profile directory in place, I created a new Dropbox directory outside of my shared profile directory. To this directory, I copied what I wanted to synchronize from my Dropbox-shared profile. Then, in my default profile – the profile that is local to each machine rather than shared en masse via Dropbox – I created symbolic links to the bits I just copied.

Here’s what it looks like from the shell:


$ mkdir ~/Dropbox/Application Support/firefox/profile-share
$ cd ~/Dropbox/Application Support/firefox/profile-share
$ cp ~/Dropbox/Application Support/firefox/profiles/wg3x0vhj.dropbox/bookmarks.html .
$ cp ~/Dropbox/Application Support/firefox/profiles/wg3x0vhj.dropbox/places.sqlite .
$ cp -r ~/Dropbox/Application Support/firefox/profiles/wg3x0vhj.dropbox/extensions/ .
$ cp -r ~/Dropbox/Application Support/firefox/profiles/wg3x0vhj.dropbox/gm_scripts/ .
$ cd ~/.mozilla/firefox/erbbyfam.default
$ mv bookmarks.html bookmarks.html.orig
$ ln -s ~/Dropbox/Application Support/firefox/profile-share/bookmarks.html bookmarks.html
$ mv places.sqlite places.sqlite.orig
$ ln -s ~/Dropbox/Application Support/firefox/profile-share/places.sqlite places.sqlite
$ mv extensions extensions.orig
$ ln -s ~/Dropbox/Application Support/firefox/profile-share/extensions extensions
$ mv gm_scripts gm_scripts.orig
$ ln -s ~/Dropbox/Application Support/firefox/profile-share/gm_scripts gm_scripts

Details.

Although I thought I knew which files and directories I needed to create my targeted solution, I spent a little time verifying just to be sure. Here are the guts of what I wanted synchronized:

  • In Firefox 3, bookmarks are stored in profile root>/places.sqlite.
  • My application launchers (Quicksilver, Gnome Do & Launchy) key off of profile root>/bookmarks.html, so I needed to synchronize that, too.
  • Extensions are stored in profile root>/extensions/.
  • Greasemonkey scripts are stored in profile root>/gm_scripts/.

The nitty-gritty is pretty readable in the shell syntax above, but here’s the high level:

  1. I created a new directory so I could keep my targeted synchronizations independent of my complete profile synchronization.
  2. From my shared profile, I copied each resource into my new directory.
  3. In my local, default Firefox profile, I renamed its copy of each resource by appending .orig.
  4. In that same local profile, I created a symbolic link for each resource to the shared version in my new directory.
  5. I restarted Firefox and my default profile looked like my old profile in all of the ways that matter to me.

Now.

Hopefully this targeted solution will be much less flaky. Of course, my browsing history, form input history, cookies, etc. won’t be synchronized, but I think I can live without that just fine. The critical path for me, I’m pretty sure, is in the things I use and update frequently: bookmarks, Greasemonkey scripts and extensions.

Epilogue

Once I got home, I logged in and gave Dropbox time to sync up with the Mac. Once sync’d, I edited the Mac’s Firefox default profile and created the same symbolic links I’d created on my Linux machine. After restarting, the default profile loaded up beautifully here too.

I’ll keep an eye over the next few weeks, but hopefully this will be every bit as effective as my kitchen sink solution, but without the annoyances.

Extended Tech Tips for the Basic Computer User

This morning I read David Pogue’s excellent article offering tips for average computer users. I like to think of myself as a power user, but there’s some really good stuff in there and I learned a few things. Nonetheless, a couple of his tips either didn’t go far enough to suit me or I thought they could be extended slightly to add usability without increasing complexity. I haven’t included all of David’s tips here, but only those that I wanted to extend. Read his article for all of his tips. You won’t be sorry you did.

David’s tips that I chose to extend are in the primary list below. My extensions are italicized in a nested (read: indented) list.

  • You can double-click a word to highlight it in any document, e-mail or Web page.
    • If you’ve highlighted editable text (e.g. in an email, form field, etc.), you can just start typing. The highlighted text will be entirely replaced by what you’re typing. You don’t have to hit Delete or Backspace to explicitly clear the text before typing.
    • To copy the highlighted text, save a few clicks by using the Control+C keyboard shortcut rather than right-clicking and selecting the “Copy” menu item. To paste the text, use the Control+V shortcut.
  • When you get an e-mail message from eBay or your bank, claiming that you have an account problem or a question from a buyer, it’s probably a “phishing scam” intended to trick you into typing your password. Don’t click the link in the message. If in doubt, go into your browser and type “www.ebay.com” (or whatever) manually.
    • A helpful “trick” is to simply mouseover the link first (but don’t click it!). The destination will appear in the status bar of your browser (usually in the lower left corner). If the destination is anything other than what you’d expect then beware.
  • You can open the Start menu by tapping the key with the Windows logo on it.
    • You can open Windows Explorer by pressing Windows+E
  • You generally can’t send someone more than a couple of full-size digital photos as an e-mail attachment; those files are too big, and they’ll bounce back to you. (Instead, use iPhoto or Picasa–photo-organizing programs that can automatically scale down photos in the process of e-mailing them.)
    • Better yet, don’t send more than one or two photos by email at all. Upload them to Picasa, Flickr or some other online repository and send a link in the email message. Your recipients will thank you.

Mormons: Stop Baptisms of Dead Jews

This isn’t exactly breaking news; it’s been going on for far too long. Nonetheless the opening sentence caught me by surprise.

Holocaust survivors said Monday they are through trying to negotiate with the Mormon church over posthumous baptisms of Jews killed in Nazi concentration camps, saying the church has repeatedly violated a 13-year-old agreement barring the practice.

They were negotiating? Negotiating?! I hope that was just an unfortunate choice of words because this might be the very reason that the term “non-negotiable” was created. Negotiation should start and end with this: Stop being crass, self-righteous, insensitive jackasses. And do it right fucking now.