<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
   <channel>
      <title>GryphonShafer.com</title>
      <link>http://www.gryphonshafer.com/blog/</link>
      <description>Greetings. This site is just a place I occasionally toss stuff and things about which I&apos;m interested. I&apos;m a serial entrepreneur, software engineer, movie theater owner, book writer/editor/publisher, pastor, geek, father, husband, and human. I think a lot about philosophy, bicycling, flying, sailing, theology, parenting, current events, predictioneering, financial investing, ethics, and other stuff and things.</description>
      <language>en</language>
      <copyright>Copyright 2011</copyright>
      <lastBuildDate>Sat, 23 Apr 2011 15:53:43 -0800</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>Good Story Doesn&apos;t Stale with Time</title>
         <description><![CDATA[It has been in fact not quite but nearly two years since I wrote anything on this little blog. A lot has happened in that time: the Earth completed nearly two orbits around the sun, spinning hundreds of times in the process. On that little ball, I experienced too many stories to recount. (One should not conclude by my failure to mention any given story that I consider it any less important than those mentioned. Like any personal and mostly pointless blog, this is a stream-of-consciousness post, predominately unedited. Certainly not well thought-out.)

Over the past two years, Xander grew from a loving and meek 7-year-old into a loving and risk-taking 9-year-old entrepreneur. <img src="/blog/2011/04/xander_lemonaid.jpg" width="220" style="float: right; margin: 10px; border: 1px solid black" />He opened a lemonade stand on a particularly hot summer day and raked in over $12 an hour. Considering he kept his costs to a minimum by having mommy provide supplies and daddy provide the storefront (an open tent covering), he had good ROI. He pondered new and inventive ways to advertise, and for the rest of the summer was noting the positive and negative market impact of other beverage venues in the area.

Sharalyn and I got a couple years older; maybe, hopefully, a couple years wiser. We decided to expand our family from three to four; and soon (perhaps very soon), our fourth member of the family will arrive on scene. We started a book publishing company, and Sharalyn took over day-to-day operations of the business. Our church started and grew a bit, and we’re participating in several routine ministries. I led an apologetics course at the district camp meeting, and this year will be leading two (one on theology). We’re still living in quiet and scenic Port Orchard, Washington. We’re still making improvements to our home. We’re still doing the church thing. We’re still working, growing, learning; being what we’re called to be.

We are blessed beyond what we deserve. The drama and adventures in our lives seem to be all self-imposed, excitement we elect to encounter rather than crises involuntarily thrown upon us. There’s no such thing as the idyllic life; not totally, not outside of the odd Hallmark TV-movie. Still, as much as I think it possible, we have something close to that life.

So then why would I want to own a movie theater business that has regularly lost money in recent history?

<img src="/blog/2011/04/et.jpg" height="120" style="float: left; margin: 10px; border: 1px solid black" />I remember fondly watching E.T. in the movie theater as a child. There’s a scene near the beginning of the film where the little alien is wandering at night through an old forest with gigantic trees. John Williams swells the symphony and Spielberg casts only enough light to capture blue-gray shapes, daunting even to we who are fully aware trees (unlike those from Tolkien’s Middle Earth) won’t come alive and attack. I remember being so scared I would squeeze shut my eyes and plug my ears, counting in my head to 10 slowly before reengaging the story. I kept going back, watching the movie over and over; and finally, I was able to watch the opening sequence. (There’s a scene much later on when guys in space suits break into the Elliot’s suburban home. That I was never able to watch in the theater.)

<img src="/blog/2011/04/empire.jpg" height="150" style="float: right; margin: 10px; border: 1px solid black" />I remember watching The Empire Strikes Back and wanting so intently to be Luke or Han. The closest thing in our world seemed to be an astronaut, and so began my obsession with NASA and space exploration. That led to my love of astronomy. And that led to my love of physics, science, mathematics, computer science, technology, and eventually into a career as a software engineer.

<img src="/blog/2011/04/last_starfighter.jpg" height="140" style="float: left; margin: 10px; border: 1px solid black" />I remember a scene from the Last Starfighter, just after Alex decides to return to Earth and refuse the call to adventure. Centauri, an alien masquerading as some sort of washed-up talent scout for the Star League, reluctantly takes Alex home, and before leaving him be, tosses him a means to make an interstellar phone call in case Alex changes his mind. Alex, about ready to decline, says he’s just a kid from a trailer park. And in one of the greatest dialog gems of all time, Centauri retorts, “If that’s what you think, then that’s all you’ll ever be.”

That had profound impact on me, that one little and seemingly simple line. It didn’t matter where I came from; it mattered what I did, what I decided to do. If I thought I was just some average (maybe even below-average) guy from a northern desert town, then that’s all I’d ever be. But if I took a leap of faith, if I took risks, if I accepted the call to adventure...

Movies are a medium for communicating story. And as Joseph Campbell and the legion of Campbell-inspired storywriters will tell you, a good story can inspire and instruct better than nearly any other means of instruction. From great story, I witnessed courage, identified a career, and became inspired to develop character. It’s one thing to tell my son to be courageous and to be of strong character. It’s another to let him experience E.T., Empire, and Starfighter and come to his own epiphanies.

Some movies I saw when I was too young and therefore didn’t quite understand. It wasn’t until college before I really began to realize why Lawrence of Arabia is the greatest film ever written. It wasn’t until I matured a bit before I heeded the warning of Citizen Kane or accepted the lesson of never giving up from Rocky. We need the right story at the right time. A great story doesn’t become less valuable because it isn’t in a newly released first-run film. A great story, and thereby a great movie cinema experience, is just as powerful 20 or 40 or 60 years after its first telling as it was upon its debut. I want to give my son and soon-to-arrive daughter the opportunity to see Casablanca, Star Wars, High Noon, Princess Bride, Blade Runner, Doctor Zhivago, Raiders of the Lost Ark, and others in the theater (once each child reaches the mommy-ordained age-appropriateness for the given movie).

So why not just watch these movies at home?

Watch the sunrise in the first part of Lawrence, the Star Destroyer in the first scene of New Hope, and Princess Bride (the whole film) by yourself in front of the best home video system you can find. Then come watch them at my cinema.

<img src="/blog/2011/04/sunrise.jpg" style="margin: 10px; border: 1px solid black" />
]]></description>
         <link>http://www.gryphonshafer.com/blog/2011/04/good_story_doesnt_stale_with_t.html</link>
         <guid>http://www.gryphonshafer.com/blog/2011/04/good_story_doesnt_stale_with_t.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Personal Log</category>
        
        
         <pubDate>Sat, 23 Apr 2011 15:53:43 -0800</pubDate>
      </item>
            <item>
         <title>Rounded Corners and Borders</title>
         <description><![CDATA[I dislike rounded corners and rounded borders. It seems like whenever business owners want to “update” their web sites but they don’t have any useful functionality improvements in mind, they resort to asking for rounded corners. To be clear, I don’t particularly hate the visual design choice of rounded corners; what I dislike is the implementation annoyance of rounded corners in practice. Until a majority of browser installs (read most common Internet Explorer version) fully supports CSS3, implementing rounded corners <b>will never be ROI positive</b>. Of course, negative ROI rarely stops business owners. So here’s one possible rounded corners implementation that seems to work across most browser types and versions and is fairly reasonable to maintain.

In CSS3, the way you create rounded corners is by listing multiple images and positions of those images for background. With CSS3, you’re no longer limited to only a single background image per DOM element. So the HTML for a CSS3 implementation could be as simple as:

<pre>&lt;div class="rounded blue box_type_a"&gt;
    &lt;h4&gt;Rounded Edge Blue Background&lt;/h4&gt;
    &lt;p&gt;This is a simple rounded edge &lt;div&gt;.&lt;/p&gt;
&lt;/div&gt;
</pre>

The “rounded” class is where in the CSS I’d define the four different corners (or maybe a fifth if I wanted a centered background image). The other two classes setup visual style and physical container box dimensions. You can of course push these three of these into a single class, but having them separated like this lets you mix and match, inheriting cascading styles, which is sort of the whole point.

Anyway, starting with the above HTML, which seems fairly reasonable to maintain, I wanted to make a CSS-less-than-3 implementation that kept code badness to a minimum. So the first thing is that in a CSS-less-than-3 world, we need to have four “things” inside the &lt;div&gt; to which we can attach background images; namely, the four corners. As much as I dislike doing this, it seems like JavaScript needs to come to the rescue.

<pre>function addRoundedCornersSupport() {
    var corners = new Array(
        "top_left", "top_right",
        "bottom_left", "bottom_right"
    );
    var divs    = document.getElementsByTagName("div");

    for ( var i = 0; i &lt; divs.length; i++ ) {
        var classes = divs[i].className.split(" ");
        for ( var j = 0; j &lt; divs.length; j++ ) {

            if (
                classes[j] == "rounded" ||
                classes[j] == "bordered"
            ) {

// for each &lt;div&gt; that has the class of either "rounded" or
// "bordered", embed four &lt;div&gt;s, one for each corner
for ( var k = 0; k &lt; corners.length; k++ ) {
    var corner = document.createElement("div");
    corner.className = "corner_" + classes[j] + " " + corners[k];

    // this is where we prepare the corner &lt;div&gt; and
    // append it to the &lt;div&gt; that should have corners
    // (see below)
}

            }
        }
    }
}
</pre>

This function, which we’ll run on page load, finds every &lt;div&gt; that has the class name of “rounded” or “bordered”. (I’ll explain what that second class does later.) If the &lt;div&gt; has that class, we’re going to add four &lt;div&gt;s to it, each with appropriate class names we can style in CSS.

Now enter the first of a few IE6 hacks. It seems all other modern browsers (browsers that are in active use by a non-trivial percentage of users) can position an empty &lt;div&gt; with explicit height according to a bottom range without ignoring the height setting. IE6 will collapse the height. I truly hate the stupid that is IE6. Anyway, to overcome this, and only for IE6, you have to add the corner <img> graphics into the bottom two &lt;div&gt;s instead of just styling them in with CSS. Otherwise, all you need is to add a space into the &lt;div&gt;.

<pre>// if the browser is IE6, the bottom left and right corners
// must have the image explicitly added using an embedded
// &lt;img&gt; tag instead of relying on CSS
if (
    navigator.appVersion.indexOf("MSIE 6") != -1 &&
    classes[j] == "bordered" &&
    corners[k].substr( 0, 6 ) == "bottom"
) {
    var borderImg    = document.createElement("img");
    borderImg.width  = "14";
    borderImg.height = "14";
    borderImg.alt    = "";
    borderImg.src    = corners[k] + "_border.gif";
    corner.appendChild(borderImg);
}
else {
    corner.appendChild( document.createTextNode(" ") );
}

divs[i].appendChild(corner);
</pre>

Now we need to call this when the page loads, but it’s bad to do that by setting window.onload because there’s a good chance in a complex site there will be some overwriting of that setting. Instead, we’re going to attach and event or listener.

<pre>// the following block causes the
// addRoundedCornersSupport function to run on
// page load without overriding 
// any other onLoad or attached events
if ( window.attachEvent ) {
    window.attachEvent(
        "onload", addRoundedCornersSupport
    );
}
else if ( window.addEventListener ) {
    window.addEventListener(
        "load", addRoundedCornersSupport, false
    );
}
else {
    document.addEventListener(
        "load", addRoundedCornersSupport, false
    );
}
</pre>

At this point, we only need to add some CSS to finish off the implementation. All we need to do is add some basic styling for rounded &lt;div&gt;s and the internal corners we added.

<pre>div.rounded {
    background-color: #cccccc;
    padding: 15px;
    position: relative;
}

div.rounded div.corner_rounded,
div.rounded div.corner_bordered {
    position: absolute;
    width: 14px;
    height: 14px;
    /*
        prevents older browsers from 
        forcing a height > 14px
    */
    line-height: 1px;
}

div.rounded div.top_left {
    top: 0px;
    left: 0px;
    background-image: url( top_left.gif );
}

div.rounded div.top_right {
    top: 0px;
    right: 0px;
    background-image: url( top_right.gif );
}

div.rounded div.bottom_left {
    left: 0px;
    /*
        This removes a 1px
        underhang at the bottom
    */
    bottom: -1px;
    background-image: url( bottom_left.gif );
}

div.rounded div.bottom_right {
    right: 0px;
    /*
        This removes a 1px
        underhang at the bottom
    */
    bottom: -1px;
    background-image: url( bottom_right.gif );
}
</pre>

That’s basically it. Of course, you’ll want to add styles for “blue” and similar style classes and “box_type_a” and similar structural CSS classes.

OK, so I promised to explain what the “bordered” class was all about. Well, as annoying as the implementation of rounded corners is, having a rounded corners with a border is even more annoying. I went through several different approaches to solving this problem until I hit on this one, which seems to work in every browser (including IE6) and isn’t too terrible to maintain. Again, let’s start with the HTML:

<pre>&lt;div class="rounded blue box_type_a"&gt;
    &lt;div class="rounded bordered white"&gt;
        &lt;h4&gt;Rounded Edge Bordered White Background&lt;/h4&gt;
        &lt;p&gt;
            This is a rounded edge blue bordered,
            white background block.
        &lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;
</pre>

I’m not the biggest fan of this. Personally, I’d rather have this be all one &lt;div&gt;. I guess with a bit more JavaScript, we could implement this with a single &lt;div&gt; with a key class name. I opted to have this be two &lt;div&gt;s to keep the JavaScript a bit more sane. And looking at purely the HTML, it seems to make sense having the two <divs>. Still, I can see the argument to go the other way.

So with this, look back at the JavaScript and you’ll notice how the “bordered” section picks up corners as well but with different classes. The CSS to implement those classes is as follows:

<pre>div.bordered {
    margin: -14px;
    position: relative;
}

div.rounded div.bordered div.top_left {
    background-image: url( top_left_border.gif );
    _left: -15px; /* IE6 only */
}

div.rounded div.bordered div.top_right {
    background-image: url( top_right_border.gif );
    _right: 1px; /* IE6 only */
}

div.rounded div.bordered div.bottom_left {
    background-image: url( bottom_left_border.gif );
    _left: -15px; /* IE6 only */
    _bottom: 1px; /* IE6 only */
}

div.rounded div.bordered div.bottom_right {
    background-image: url( bottom_right_border.gif );
    _bottom: 1px; /* IE6 only */
    _right: 1px;  /* IE6 only */
}
</pre>

Unfortunately, you’ll see here I had to include quite a lot of IE6-only styles. I hate doing that, but otherwise, this is going to look like garbage in IE6.

Of course, you’ll need the graphics for the four corners (and the corners for the border corners), but I figure that’s fairly obvious how to put together. I decided on 14 pixel tall and wide graphics for fairly pronounced corners. Other implementations may want smaller corners, maybe 8 to 10 pixels tall and wide.

You can <a href="/content/rounded-stuff/example.html">view the full example implementation</a> to see all the pieces working together.
]]></description>
         <link>http://www.gryphonshafer.com/blog/2009/05/rounded_corners_and_borders_1.html</link>
         <guid>http://www.gryphonshafer.com/blog/2009/05/rounded_corners_and_borders_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Web Development</category>
        
        
         <pubDate>Fri, 08 May 2009 12:35:02 -0800</pubDate>
      </item>
            <item>
         <title>That was 7 years ago...</title>
         <description><![CDATA[<b>Caller:</b> "Hello sir. I am calling you today from COMPANY."

<b>Gryphon:</b> "From COMPANY? OK..."

<b>Caller:</b> "I'm seeing that you have several web sites hosted with us including: gryphonshafer.com, goldenguru.com, (etc., etc.)"

<b>Gryphon:</b> "I did many years ago, yes. They've been hosted elsewhere since then."

<b>Caller:</b> "Sir, I'm seeing that your account with us is past due."

<b>Gryphon:</b> "I canceled my account with COMPANY several years ago. I think it was about 7 years ago."

<b>Caller:</b> "I see. But you still have an account with us."

<b>Gryphon:</b> "No, I canceled my account several years ago."

<b>Caller:</b> "Can you provide me the confirmation code number from the cancellation confirmation email?"

<b>Gryphon:</b> "No. That was 7 years ago. I don't have that email anymore."

<b>Caller:</b> "Well, you have an account balance that needs to be paid for time between then and now unless you can provide a confirmation code."

<b>Gryphon:</b> "No, I don't. You're records are in error. Fix them. I have not done any business with your company for 7 years. I canceled my account 7 years ago. There was no account balance due when I canceled my account."

<b>Caller:</b> "Well, OK, I can give you a code that if you email to our support department, they can delete your account."

<b>Gryphon:</b> "No. I decline to take action in this matter. I canceled my account with COMPANY several years ago. I haven't heard from COMPANY since then. It is not relevant what records you have or what data you have on your servers. You can delete that data, archive it, or ignore it. I am not going to take any additional action. There is an error in your records that you need to fix, not me. Is that clear?"

<b>Caller</b>: "OK, I will email them on your behalf; but that will mean that your data may be deleted."

<b>Gryphon:</b> "...that's fine. Thank you."
]]></description>
         <link>http://www.gryphonshafer.com/blog/2008/12/that_was_7_years_ago.html</link>
         <guid>http://www.gryphonshafer.com/blog/2008/12/that_was_7_years_ago.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Commentary</category>
        
        
         <pubDate>Thu, 18 Dec 2008 12:01:50 -0800</pubDate>
      </item>
            <item>
         <title>S-CRAP Methodology</title>
         <description><![CDATA[Introducing the S-CRAP software development lifecycle methodology.

<img alt="S-CRAP Methodology" src="/blog/2008/09/s-crap_methodology.png" width="529" height="843" />
]]></description>
         <link>http://www.gryphonshafer.com/blog/2008/09/scrap_methodology.html</link>
         <guid>http://www.gryphonshafer.com/blog/2008/09/scrap_methodology.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Philosophy</category>
        
        
         <pubDate>Fri, 05 Sep 2008 09:25:48 -0800</pubDate>
      </item>
            <item>
         <title>Business Ethics</title>
         <description><![CDATA[I happen to be a successful software developer, businessman, and entrepreneur. I’m no millionaire (yet), but by most measures I’m doing pretty well. I started my own business in November, 2007 with a single client and a single employee (me). It’s now 9 months later, and I have five people on staff and am seriously contemplating the purchase of another company that would result in a total combined staff of about 15. The whole time, the business has been profit-positive.

Is that because I’m some sort of business genius? No. (I’m above average, but my staff are all smarter than me.) What about being a guru of a software developer. Nope. (Again, above average here, but there are a lot of developers whose programming foo I envy and will never attain.) Maybe because I just got really lucky? Not a chance. (I don’t believe luck happens on its own. You make your own luck by working really hard and being thereby ready to pick up on opportunities as they arrive.)

So what is it that made me successful, not just in this latest business venture but across my career and life? There are a lot of little things: I strive for excellence in whatever I do. I’m always pushing myself to achieve more, grow more, learn more. I try to have a positive attitude (most of the time). I try to bridge knowledge and experience cross-discipline. But honestly, I think none of these things really makes a difference in the long-run because I constantly see people who have these things and more who are not as successful. Instead, what I see in every enduringly successful person is ethics.

I always strive to do the ethical thing, and I know my staff does the same. I only hire people who have ethics, and I choose (as much as I’m able) to only work with people with ethics. Most of my friends and colleagues have ethics.

Basic business ethics can be defined a hundred different ways, but I think of it as doing what’s morally right, what’s in the client’s best interests, and what’s in your staff’s best interests. If any action violates any of those three spheres, it’s avoided. Seems simple, right? Seems like everybody would be doing this; but the sad truth is that so many people demonstrate such poor ethics that the few who do stand out like a flashlight across a dessert in the middle of a moonless midnight.

<h4>Air Flight from Boston</h4>

Back in May, my family and I traveled to the Boston area, specifically Sandwich on Cape Cod, for a wedding. On the return, the flight had to make a bit of a detour around some weather, and this was to put us a few minutes late to the gate in Seattle. One of the flight attendants got on the radio to inform the passengers of the situation and note that there were six passengers who had connecting flights that departed within a few short minutes of our updated arrival estimate. The flight attendant asked that those who were not booked on one of these flights remain in our seats until the six could deplane.

Some number of minutes went by, and as we were on our final decent into SeaTac airport, the flight attendant again came over the intercom and reminded us of the plight of the six. She then asked that those six signal their positions in the plane by pressing their call lights. Within a few seconds, five or so lights had turned on. Then about a minute later, the number had grown to about ten. A minute later, it exploded to nearly half the plane.

It was simultaneously laughable and depressing. Everyone on the plane, those of us who didn’t signal and those who did, all knew that nearly half the plane were a bunch of unethical, selfish liars. But that didn’t seem to phase anybody. Nobody seemed embarrassed. Nobody switched off their call light.

As the plane stopped at the gate, nearly every passenger sprung to his or her feet and into the isle, just as they would have had the fight attendant never said a word. A few of us stayed in our seats, marveling at what we saw. There was a lady a seat or two ahead of us who kept pleading with the people in front of her, “Please, I’m one of the six. My plane leaves in four minutes. Please let me by.” Not a single person even looked her in the eye.

<h4>Five Stages of Ethical Erosion</h4>

In business, I’ve noticed a set of five distinct stages of ethical erosion. There’s what’s illegal, what’s legal but unethical, ethical but against company policy, not against policy but not in the client’s best interests, and finally what’s not really contrary to the client’s best interests but isn’t really going to benefit them either.

The sad fact is that most people think anything conducted in the last three bubbles is acceptable provided you don’t get caught.

<img alt="business_ethics.png" src="http://www.gryphonshafer.com/blog/2008/08/business_ethics.png" width="470" height="270" />

You’ve probably seen this too. You’re at work, and you notice people surfing the Internet for personal reasons (or just to waste a few minutes before a meeting). It’s not something that if caught would cause the employee to find himself without a job, but it’s certainly not benefiting the client. The employee is probably thinking, “But so what? I work really hard, and I’m just taking a few minutes before a meeting to read a few online comics and write some personal email.”

Here’s the thing, though. Over time, these things add up; not explicitly into a codified list, but implicitly, almost subconsciously in the minds of employers. An employer will likely never be able to pinpoint the problem but will instead talk about employee productivity or efficiency.

<h4>Employee Compensation</h4>

Often, employees believe they should be compensated at or above what they’re worth to the company, because replacing them would cost the company more than what the company is currently paying them. But companies (with non-profits as the only exception) exist for the purpose of making profit. The only way a company can make profit is if it pays its employees a little less than what they’re worth. Think about it: If what you do for the company results in the company grossing an additional $50,000, the company cannot possibly pay you $50,000 and stay in business. The company can’t even pay you $50,000 minus tax implications and stay in business for long. The company has to pay you some amount a little under the value you bring to the company. And over time, that small discrepancy adds up to profits that can be invested back into the business, used to repay or fund dividends to investors, or spent in the form of bonuses and additional incentives and benefits to employees.

Every time an employee does anything that falls into any of the above ovals, the company makes less profit, either in the short-term with things like stuff that’s not in the company’s best interests or long-term with things like unethical and illegal activities.

<h4>Seeing is Believing</h4>

Thing is, when you and your company act under a strict code of ethics, clients know this. They’ll see it right away. They see it because they see the contrast. There’s some sort of subconscious feeling that this person, employee, consultant, agency, company isn’t trying to cheat me out of anything; not going to steal or try to convince me to do something that’s not in my best interests. This employee isn’t out to do what’s best for himself to the exclusion of my business but is instead out to do what’s best for my business and thereby help himself more over the long-term as a result.

Same goes for employers. Employers who treat their employees poorly tend to loose good employees. That results in turnover, which is expensive and inefficient. And that cuts into profits.

Ethics matters more than talent and intelligence. Think about this: If you were going to hire landscapers to do some work on your yard, would you hire a company that you knew was unethical but had some very bright and skilled people on staff, or would you hire a company that was merely above average but had high integrity. Yeah. Me too.
]]></description>
         <link>http://www.gryphonshafer.com/blog/2008/08/i_happen_to_be_a.html</link>
         <guid>http://www.gryphonshafer.com/blog/2008/08/i_happen_to_be_a.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Philosophy</category>
        
        
         <pubDate>Wed, 13 Aug 2008 14:57:33 -0800</pubDate>
      </item>
            <item>
         <title>My Firefox Add-Ons</title>
         <description><![CDATA[I upgraded my Firefox installation to version 3 today. Yes, I know; I must be the last geek on the planet to do this. I'm just not an early adopter. (I won't admit how long I procrastinated switching to Firefox from Mozilla.) The upgrade was rapid and relatively painless. My favorite theme isn't supported and won't ever be supported in 3, which depressed me a bit, since it's a great theme. Oh well. However, all my important add-ons have FF3 compatibility, so that was great.

Over the last few years, I've been slowly using an increasing number of add-ons, first in Navigator as plug-ins, then Mozilla, and now Firefox. I'm not sure how useful this is going to be for others, but I love seeing lists of favorite add-ons. There's often some tool that someone's using that would help me be more productive, so I love reading those lists. Here's mine. Here's my complete list of add-ons sorted alphabetically.

<b>Aardvark</b>
Selector utility for selecting elements and doing various actions on them. It can be used for cleaning up a page prior to printing it (by removing and isolating elements), and for web development.
<a href="https://addons.mozilla.org/en-US/firefox/addon/4111">https://addons.mozilla.org/en-US/firefox/addon/4111</a>

<b>ChatZilla</b>
Internet Relay Chat (IRC) client.
<a href="https://addons.mozilla.org/en-US/firefox/addon/16">https://addons.mozilla.org/en-US/firefox/addon/16</a>

<b>ColorZilla</b>
Eyedropper, ColorPicker, Page Zoomer and other colorful goodies.
<a href="https://addons.mozilla.org/en-US/firefox/addon/271">https://addons.mozilla.org/en-US/firefox/addon/271</a>

<b>Compact Menu 2</b>
Replaces the original menubar boxes into a single button or icon.
<a href="https://addons.mozilla.org/en-US/firefox/addon/4550">https://addons.mozilla.org/en-US/firefox/addon/4550</a>

<b>CookieCuller</b>
Cookie management tool.
<a href="https://addons.mozilla.org/en-US/firefox/addon/82">https://addons.mozilla.org/en-US/firefox/addon/82</a>

<b>CuteMenus - Crystal SVG</b>
Adds icons to all menus along with some other features.
<a href="https://addons.mozilla.org/en-US/firefox/addon/1330">https://addons.mozilla.org/en-US/firefox/addon/1330</a>

<b>DOM Inspector</b>
Inspect and edit the live DOM of any web document or XUL application. The DOM hierarchy can be navigated using a two-paned window that allows for a variety of different views on the document and all nodes within.
<a href="https://addons.mozilla.org/en-US/firefox/addon/6622">https://addons.mozilla.org/en-US/firefox/addon/6622</a>

<b>DownThemAll!</b>
Download manager/accelerator built inside Firefox.
<a href="https://addons.mozilla.org/en-US/firefox/addon/201">https://addons.mozilla.org/en-US/firefox/addon/201</a>

<b>Extended Statusbar</b>
A Statusbar with Speed, Percentage, Time, and loaded size.
<a href="https://addons.mozilla.org/en-US/firefox/addon/1433">https://addons.mozilla.org/en-US/firefox/addon/1433</a>

<b>Firebug</b>
Development tools while browsing. Allows for edit, debug, and monitor of CSS, HTML, and JavaScript live.
<a href="https://addons.mozilla.org/en-US/firefox/addon/1843">https://addons.mozilla.org/en-US/firefox/addon/1843</a>

<b>Forecastfox l10n</b>
Weather forecasts from Weather.com, displayed in any toolbar or statusbar
<a href="https://addons.mozilla.org/en-US/firefox/addon/2024">https://addons.mozilla.org/en-US/firefox/addon/2024</a>

<b>FoxClocks</b>
Place small clocks in your statusbar.
<a href="https://addons.mozilla.org/en-US/firefox/addon/1117">https://addons.mozilla.org/en-US/firefox/addon/1117</a>

<b>functions for keyconfig</b>
Core functions for keyboard operation.
<a href="http://www.pqrs.org/tekezo/firefox/extensions/functions_for_keyconfig/">http://www.pqrs.org/tekezo/firefox/extensions/functions_for_keyconfig/</a>

<b>GooglePreview</b>
Inserts preview images (thumbnails) of web sites into the Google and Yahoo search results pages.
<a href="https://addons.mozilla.org/en-US/firefox/addon/189">https://addons.mozilla.org/en-US/firefox/addon/189</a>

<b>Greasemonkey</b>
Allows you to customize the way a webpage displays using small bits of JavaScript.
<a href="https://addons.mozilla.org/en-US/firefox/addon/748">https://addons.mozilla.org/en-US/firefox/addon/748</a>

<b>gui:config</b>
Makes it easier to change preferences that can only be found in the about:config.
<a href="https://addons.mozilla.org/en-US/firefox/addon/5523">https://addons.mozilla.org/en-US/firefox/addon/5523</a>

<b>HTML Validator</b>
Adds HTML validation to status bar. Errors seen off an icon in the status bar when browsing.
<a href="https://addons.mozilla.org/en-US/firefox/addon/249">https://addons.mozilla.org/en-US/firefox/addon/249</a>

<b>JavaScript Debugger (Venkman)</b>
Venkman provides JavaScript debugging environment.
<a href="https://addons.mozilla.org/en-US/firefox/addon/216">https://addons.mozilla.org/en-US/firefox/addon/216</a>

<b>JSView</b>
Adds ability to view the source code of external JS and CSS files.
<a href="https://addons.mozilla.org/en-US/firefox/addon/2076">https://addons.mozilla.org/en-US/firefox/addon/2076</a>

<b>Link Alert</b>
Changes the cursor to indicate the target of a link.
<a href="https://addons.mozilla.org/en-US/firefox/addon/3199">https://addons.mozilla.org/en-US/firefox/addon/3199</a>

<b>Linkification</b>
Auto-converts link-possible text into genuine, clickable links.
<a href="https://addons.mozilla.org/en-US/firefox/addon/190">https://addons.mozilla.org/en-US/firefox/addon/190</a>

<b>Live HTTP Headers</b>
View live HTTP headers of pages while browsing.
<a href="https://addons.mozilla.org/en-US/firefox/addon/3829">https://addons.mozilla.org/en-US/firefox/addon/3829</a>

<b>MeasureIt</b>
Draw out a ruler to get the pixel width and height of any elements on a webpage.
<a href="https://addons.mozilla.org/en-US/firefox/addon/539">https://addons.mozilla.org/en-US/firefox/addon/539</a>

<b>PrefBar</b>
Quick access to toggle-able browser settings.
<a href="http://prefbar.mozdev.org/installation.html">http://prefbar.mozdev.org/installation.html</a>

<b>Save Image in Folder</b>
Save images in personally customized folders.
<a href="https://addons.mozilla.org/en-US/firefox/addon/614">https://addons.mozilla.org/en-US/firefox/addon/614</a>

<b>Save Link in Folder</b>
Save links in personally customized folders.
<a href="https://addons.mozilla.org/en-US/firefox/addon/613">https://addons.mozilla.org/en-US/firefox/addon/613</a>

<b>Tab Scope</b>
Preview and navigate tab contents through popup.
<a href="https://addons.mozilla.org/en-US/firefox/addon/4882">https://addons.mozilla.org/en-US/firefox/addon/4882</a>

<b>Tamper Data</b>
View and modify HTTP/HTTPS headers and post parameters. Trace and time HTTP response/requests.
<a href="https://addons.mozilla.org/en-US/firefox/addon/966">https://addons.mozilla.org/en-US/firefox/addon/966</a>

<b>Web Developer</b>
Adds a menu and a toolbar with various web developer tools.
<a href="https://addons.mozilla.org/en-US/firefox/addon/60">https://addons.mozilla.org/en-US/firefox/addon/60</a>

<b>YSlow</b>
YSlow analyzes web pages and tells you why they're slow based on Yahoo's rules for high performance web sites.
<a href="https://addons.mozilla.org/en-US/firefox/addon/5369">https://addons.mozilla.org/en-US/firefox/addon/5369</a>

<h4>My Personal Favorites</h4>

So as for my favorites, especially when it comes to doing web development, are as follows:

<ul><li>HTML Validator</li>
<li>Web Developer</li>
<li>Firebug</li>
<li>YSlow</li>
<li>JSView</li>
<li>Live HTTP Headers</li>
<li>ColorZilla</li>
<li>CookieCuller</li>
<li>MeasureIt</li></ul>

A few of the things on this list make web development so significantly easier that it's difficult for me to imagine how we did our jobs before they existed. Firebug and the Web Developer toolbar are sufficiently awesome as to require a switch to Firefox alone. If you're going be working on improving performance, you'll want to look into YSlow and Live HTTP Headers.
]]></description>
         <link>http://www.gryphonshafer.com/blog/2008/07/my_firefox_addons.html</link>
         <guid>http://www.gryphonshafer.com/blog/2008/07/my_firefox_addons.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Web Development</category>
        
        
         <pubDate>Thu, 17 Jul 2008 14:47:55 -0800</pubDate>
      </item>
            <item>
         <title>Remote Administration</title>
         <description>Fortunately, everything is likely back to normal. I say “likely” because I can’t confirm my assumption that the mail server is restored. I can only go on faith.

Our neighbor from across the street was graciously willing to get into our garage and investigate why the mail server was offline, with my assistance over the phone. We quickly realized the server was simply off, and so with a single push of a button, it booted back up.

I found out later from the neighbor that we had a power outage for a couple hours yesterday. The UPSes probably lasted as long as they could, but eventually gave out. (We don’t yet have a generator.) Apparently, the other server hardware all restarted automatically when power was restored. But the mail server didn’t.

However, while I think the server is back in regular operation, I can’t test it. I’m in a land without wifi or even land-line Internet access. I’ve been searching around for coffee shops or any other signs of network service, but so far, I’ve failed. It’s quite annoying. I have a lot of work I need to get done, but it all requires some form of network connectivity.

Likely what I’ll have to do is take today and tomorrow off as “vacation” days, then work on Saturday when we’re back in Boston (which I’m pretty sure has a handful of spare connections to the Internet).
</description>
         <link>http://www.gryphonshafer.com/blog/2008/05/remote_administration.html</link>
         <guid>http://www.gryphonshafer.com/blog/2008/05/remote_administration.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Personal Log</category>
        
        
         <pubDate>Thu, 15 May 2008 16:30:03 -0800</pubDate>
      </item>
            <item>
         <title>Mail Server Crash</title>
         <description>Sometimes I love technology, and sometimes I feel like it conspires against me in an ongoing covert war. Yesterday, Sharalyn, Alexander, and I flew out from Seattle (SeaTac) to Boston. We’re heading out today by car to Sandwich, Massachusetts. We had to leave our house early in the morning, so the last time I had checked email was the night before.

After checking-in at our hotel in Brockton and being off the grid for nearly 24 hours at that point, I tried picking up a wifi signal and checking email. Getting connectivity was easy, but much to my dismay, I found that our mail server (which up until this point had experienced no downtime for nearly a year) had crashed. Joy.

I really need to check-in with how a few things are progressing with work, but that’s not going to happen for a while. I need to wait until about 11:00 AM before Sharalyn can call our next-door neighbor to ask her to go to the data center and reboot the mail server. I have no idea why the sever is offline; I’m hoping it’s just something simple, but I’m worried it could be something significant, maybe a failed hard drive or fried something.

It couldn’t have come at a worst time. Had we been home, I could have hit the machine myself. Worst-case, I could have rebuilt an entirely new mail server and restored from backups in an hour or two.
</description>
         <link>http://www.gryphonshafer.com/blog/2008/05/mail_server_crash.html</link>
         <guid>http://www.gryphonshafer.com/blog/2008/05/mail_server_crash.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Personal Log</category>
        
        
         <pubDate>Thu, 15 May 2008 10:45:30 -0800</pubDate>
      </item>
            <item>
         <title>Travel Wars, Episode 6 (Return of the Jetlag)</title>
         <description>Our saga continues but does not conclude in the very early morning of Sunday, December 16, 2007. After spending a day in hell (a.k.a. airports and airplanes), I spent two days in Princeton, New Jersey involved in a variety of business meetings, software development, Christmas parties, and even voluntary community service. In all, it was a productive, positive, and polite two days. But alas, like all good things, it was destined to come to an end.

Sunday morning around 5:30 AM eastern time, I left my hotel room and caught an elevator to the lobby. In a surprising twist from the previous Thursday’s events, my driver was waiting patiently by the desk. He was 30 minutes early. (He was well on his way to a good tip.) The driver was professional, polite, and actually pretty cool. On the ride to the airport, we struck up a conversation about Philadelphia, about how I’ve driven through the town at least 6 times, but I’ve never physically set food on the ground there. The driver commented that a week earlier, he had spent the day giving a tour of the city to a few businessmen from out of town. Had I known what was in store for me at the airport, I would have asked him to do the same for me.

We got to the airport ahead of schedule, sometime around 6:30 AM, and I headed inside at a good clip. It was cold outside, and I wanted to get through security with enough time to have a semi-relaxed breakfast near my departure gate. I was flying through Chicago, and I expected very little time on the ground there; it was likely I wouldn’t get a chance to eat lunch. As it ends up, I had far, far more time than I could have ever guessed.

Upon reaching the ticket counter, a large man of some importance walked up and down the back of the counter shouting, “All flights to Chicago are canceled due to snow.”

“Awesome. And so it begins again,” I thought.

I checked with both United (the carrier I was scheduled to fly with), a few of the other carriers, and then finally the corporate travel agency. They all said the same thing. There were no other flights to Chicago save one: a United flight departing Philadelphia at 4:35 PM. “Well, I guess I have about 10 hours to do a little writing or coding,” I thought, trying to stay positive.

I booked myself a ticket on the 4:35 PM flight; this one was going through Denver. Then I tried to check my luggage but was rejected. According to United, passengers aren’t allowed to check their luggage more than 4 hours prior to scheduled departure. Worst yet, all the restaurants, Internet hot spots, and various fancy amenities like chairs were all on the other side of the security check-point.

Fortunately, my suitcase is almost (although annoying not quite) thick enough to double as a make-shift chair. I fashioned myself a nook near the windows opposite the ticket counter, far enough away from the foot traffic to be left alone but with a few feet of buffer from the windows that were leaking cold into the terminal similar to how Niagara Falls leaks water into the Atlantic. I pulled out a good book and proceeded to pass the time.

After several chapters, it was mid-day, and I was allowed to check my chair... er... I mean luggage. Now I was off to TSA gauntlet. Passing security was no more painless than what I had experienced in Seattle, so I was in a pretty good mood when I reached my departure gate with about three hours of time left. I found a restaurant and ordered some food, a Philly cheese steak. It was yummy and all, but I still don’t understand what all the fuss is about. Of course, this was at an airport restaurant, not Gino’s. Perhaps that makes all the difference.

Then I discovered something that made me depressed. I inadvertently sat along a row of windows that looked back on the section of the airport where I had spent the previous six or seven hours. I noted with some sadness that in that time, I had traveled about 100 yards.

After eating, I plopped in a real chair (such luxury) and tried to get online. The good news was there was free wifi. The bad news was that about 40% of the other would-be passengers in the terminal had already discovered the free wifi. At one point, for about 20 minutes, I was able to write a few emails, get on my private IRC server, and even SSH to a few servers. But alas, it was not to last; and quickly I found myself taking my frustrations out on my keyboard.

Right on time, a nice looking United airplane parked at the gate. A steady steam of passengers exited the plane while I packed my laptop and prepared to stand in line yet again. Boarding was uneventful, but when I got to my seat, I found myself sandwiched between two nice but large people in a space that must have been designed for my 6-year-old son. “It’s only a three-hour flight,” I thought. “Suck it up.”

Everyone got seated quickly, and we appeared to be ready to depart. I sat up a bit and was able to look up the plane, through first-class, through the open cockpit door, and into the cockpit. But I wasn’t greeted with the typical comforting sight of a thousand little yellow and blue LEDs indicating overall warm fuzzy feelings about the aircraft. Instead, I saw darkness.

About 15 minutes later, the head flight attendant got on the speaker: “Folks, our pilots aren’t here yet. They’re flying in from San Diego. We expect them to land fairly soon. We’ll keep you informed of their progress.” Worse yet, things were starting to heat up. I had on no coat, but I was starting to sweat. I twisted on the air above me, but only the weakest of ventilation occurred.

There was a small part of me that thought, “Aside from those thousand little yellow and blue LEDs, it’s still just stick and rudder. I can do this.”

About another 15 minutes later, I spotted two pilot-looking types walk into the airplane and head into the cockpit. A couple minutes later, LEDs were glowing and the passenger AC system blasted an Artic ice wind. Relief at least.

The flight, like all the flights in this saga, was uneventful. We landed and taxied to the gate. We were pretty late, but the flight attendants assured us that we would make our connections. I left the aircraft and jogged to my departure gate. I was the second to last person to get on board.

The rest of the saga played out without much incident. When I got home, it was around 2:00 AM on Monday, making it almost 24-hours of “traveling.”</description>
         <link>http://www.gryphonshafer.com/blog/2007/12/travel_wars_episode_6_return_o.html</link>
         <guid>http://www.gryphonshafer.com/blog/2007/12/travel_wars_episode_6_return_o.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Personal Log</category>
        
        
         <pubDate>Tue, 18 Dec 2007 19:31:00 -0800</pubDate>
      </item>
            <item>
         <title>Travel Wars, Episode 5 (TSA Strikes Back)</title>
         <description>The saga of travelling to Princeton, New Jersey continued as I approached the first TSA officer at the security check-point at SeaTac airport. I presented my boarding pass and ID. Unfortunately, the ticket (ordered by the client and by no fault of theirs) was issued to my nickname rather than my legal name. So the TSA officer said I would need to go back to the airline ticket counter and get my name changed on the ticket. “OK, no problem,” I thought, “This will burn some time, but otherwise, no big deal.”

I got to the ticket counter, explained the situation to an agent, and asked for a name change. The agent fiddled a little on her computer and relied, “OK, that will be $153.” Apparently, you can’t correct incorrect data on a ticket without paying some ridiculous fee. I guess it costs a lot of money to move around electrons in the airline computer systems. I politely inquired why it cost so much money to correct the error, and the agent looked a little nervous, then told me I could fly without ID.

She wrote “NO ID – SSSS” on the ticket and handed it back to me. According to her, and quite contrary to the repeat announcements on the airport intercom system, you don’t need ID to fly a domestic flight. I’d have to go through some extra security on my way to the gate, but otherwise, I could fly.

After getting through the basic part of security, I was taken off to one of the side areas were a very nice, very large TSA guard walked me through the extra security checks he was going to conduct. He checked me and my carry-one in great detail, pulling most of everything out of the carry-one and checking it for chemical residue. Through the entire experience, he was both polite and professional, so I felt pretty comfortable.

Once through security, I checked my watch and realized that I had just enough time to get to the proper gate before boarding. No time to eat lunch. I’d have to eat in Pheonix about three hours later. It was approaching 11:30 AM at this point. I got to the gate, sat down for a moment, and almost instantly the call came over the intercom for boarding.

After take-off, we encountered a bit of turbulence climbing out of the Seattle area, but it was nothing too remarkable. In fact, the entire flight was fairly unremarkable. The passenger next to me was a mathematics doctoral student, so we struck up a conversations about Stephen Wolfrum, cellular atomata, neural networks, and bioinformatics. It was a fascinating conversation. The student was very curious about what I did for a living because he enjoys writing Perl for bioinformatics.

We landed uneventfully in Phoenix, and after exiting the plane, I checked the nearby screens for my next flight’s gate. It was in concourse B, and I was in concourse A. The trouble was that it was due to start boarding in 5 minutes. OK, time to jog a little.

I headed down these incredibly long walkways, made a couple turns, and ended up in the right concourse. “I hope they move my bags as quickly as I’m moving.” I quickly spotted the right gate and a mob of people congregated around it, so I moved to the side a bit and found a place to stand. Then an announcement hit the air like a brick hits a window: the flight was delayed due to snow in Philadelphia. It was rather jarring in contrast to the fact that Phoenix felt like a summer day in Seattle.

The flight was either going to be delayed for two or more hours, or it was going to be canceled entirely. The people at the counter weren’t sure which at this point. “Well, look on the bright side,” I thought to myself, “I have plenty of time to eat.”

I waltzed to the nearby Burger King, bought a Whopper, and enjoyed it about as well as it’s possible for anyone to truly enjoy airport-quality fast food.

Roughly an hour later and much to everyone’s surprise (and glee), one of the gate attendants announced conditions had improved and the flight was ready to board. We boarded uneventfully, and we taxied and departed rather quickly. It felt like we were in the air heading northeast in no time.

This flight, like the previous, was uneventful. There was an in-flight movie and a few inches more leg room. I was fairly pleased. The movie was Resurrecting the Champ. Pretty dull and poorly written, but almost anything will do when you have no other options.

We landed in Philadelphia a bit ahead of schedule and pulled off the taxiway and up near the terminal. Then we stopped. We were about 50 feet away from the terminal, and there we stayed. A few minutes passed, and then the co-pilot’s voice blared over the internal speakers.

“Folks, we’ve traveled over 2,000 miles, most of which was above 30,000 feet and at speeds in excess of 600 miles per hour. We did all this unassisted. But federal law says that for the last 50 feet, a distance we will cover at a speed of about 5 miles per hour, we need assistance. We’ve requested this assistance 4 times, but we’re still waiting. We’re as frustrated as you are, and we don’t know why it’s taking so long. Please just be patient for a little longer and we’ll get you on your way.”

It was a good 15 minutes later before a little yellow truck drove recklessly in front of the airplane. A minute later, there was a bit of a lurch, and we pulled up to the terminal.

Once off the airplane, the only words in my brain were, “Baggage claim.” It was about 11:00 PM now, and I was getting really tired. Heading through the concourse, a group of us from the flight naturally formed a mob of about 20, all seeking exit. We call descended upon the nearest security gateway, but it was closed. Closed. OK, well, there must be another way out of this airport. We turned and backtracked into another concourse. At the end of the walkway, there was another security station, but this one was open, staffed by close to 10 guards, all of which were standing idly by.

Once through, our mob turned sharply and headed off to Baggage Claim. I was supposed to meet up with my driver, but neither he nor the other drivers were anywhere to be seen. They were probably all standing on the far side of the closed security check-point.

Several minutes passed, and I spotted a group of 5 or 7 drivers all walking rapidly our direction. “Mine must be in there somewhere,” I thought as I scanned by my name. Sure enough, there he was. My bag, which I almost assumed would be lost considering my traveling luck thus far, plopped onto the rotating belt about half-way through the run. We picked up the bag and headed to the car. At this point, it was about 11:45 PM, maybe midnight.

Once in the car, the driver typed the address of the hotel into his GPS, but its database didn’t recognize the street. So we both rummaged through our papers in search of the hotel’s phone number. They were able to confirm directions, and so we were off. The trip went fairly fast; it was only about an hour, heading straight along a freeway from Philadelphia to Princeton.

I forget exactly when I got to the hotel. I was pretty wiped out at this point. The man behind the desk asked for my ID and proceeded to check me in. Then he asked, “And how will you be settling the bill?” In a calm but annoyed voice that must have sounded like I was pretty pissed, I said, “It should have been prepaid by the company that’s flying me out.”

After a little more checking and back and forth with the guy behind the desk, who, I might add, hardly spoke a word of English (sigh), I got my room key and went upstairs. I got to my room around 1:30 AM. And thus the saga of my trip to Princeton, New Jersey came to an end, or so I thought.
</description>
         <link>http://www.gryphonshafer.com/blog/2007/12/travel_wars_episode_5_tsa_stri.html</link>
         <guid>http://www.gryphonshafer.com/blog/2007/12/travel_wars_episode_5_tsa_stri.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Personal Log</category>
        
        
         <pubDate>Sun, 16 Dec 2007 08:08:00 -0800</pubDate>
      </item>
            <item>
         <title>Travel Wars, Episode 4</title>
         <description>Our saga begins in the early morning of Thursday, December 13, 2007, a day that will live in infamy. My mission, one I foolishly accepted, was to travel from Port Orchard, Washington to Princeton, New Jersey. A simple goal in principle and on plan, but one in which the details, the devil within each they say, destroyed all since of efficiency.

This morning, I awoke an hour too early after a restless night’s sleep. I tossed and turned for a while, trying in vein to get back to sleep for at least a few minutes. Sharalyn’s alarm got her out of bed, and we both proceeded to prepare for the day, she for nursing at a hospital, I for a saga of missteps. We woke Xander a little late, and he and Sharalyn were rushed to get out the door. They had driven several blocks away before I received a phone call on my cell: Xander had forgotten his jacket. They were rushing back to fetch it. I met them in the driveway, suddenly realizing that my idea to go without a jacket of my one was a mistake.

My taxi arrived about 30 minutes later, exactly on time at 6:15 AM, and we hurried off to the Bremerton airport. The sky was still pitch black save for a few spots of fog and low clouds that reflected heavy lighting from below. We arrived at the airport some 30 minutes before my flight was to arrive at 7:00 AM. But unlike virtually every other taxi I have ever taken in my life, this particular taxi wouldn’t take anything except cash. Fortunately, I had enough on me to cover the ride, but I had hoped to use some of those funds for lunch.

I had chartered a flight through Island Air, a direct shot from Bremerton to SeaTac airport. Unfortunately for me, the sky had already fallen, literally. The ceiling was at ground-level; I was standing in a soup of water vapor. Looking toward the south, I could see the weather instruments atop the automatic weather reporting station. There was no wind. Not even a small breath. The fog was there to stay.

Time seems to pass quickly, though; however, I was getting oftly nervous as 7:00 AM came and passed. About 7:20 AM, my pilot George called. He had tried to make a landing, but the fog was just too dense. He could fly through the soup just fine on instruments alone, but before he can safely touch-town, he has to locate some visual indicator, the runway, runway lights, a beacon, something.

I had heard him fly overhead. It sounded like he was close, very close, but I never saw him. I expected to see a shadow, maybe even the blink of his lights, but there was nothing, just the strong rumble of the Maule’s engine that faded into the void like my hopes of catching my SeaTac flight.

It was clear there was no way I was going to make my connecting flight at SeaTac. Since the Airport Diner was now open, I picked up my luggage and headed inside to get warmed by their heaters and coffee. After consuming a yummy breakfast roll, I started making phone calls.

According to George, the fog was packed in right over Bremerton and out toward the west, but to the east, the clouds were high enough for save landings. Fortunately for me, there’s a small, private, grass strip about 20 minutes taxi ride east of Bremerton, and George knew the owner. While he called up to get permission to land, I called a corporate travel agent to switch my flight.

The good news here, according to the agent, was that there was a later opportunity to get from Seattle to Philadelphia. The bad news was that it would contain a layover in Phoenix. Not being one to complain, I eagerly requested the switch. One small problem: The change would increase the fare by something a bit over a hundred dollars, and I had no budgetary authority. So the agent started making phone calls to the only number I had on me. After about 15 minutes of repeat calling, she was able to get through to someone and secured authorization. Now it was up to me to get to the private airfield, Vaughn.

Vaughn airfield is a beautiful little grass strip with a few little bumps and hills along its stretch. It’s not straight; it curves slightly to the right as you head south, and it also slopes downhill. Pilots are only allowed to land northbound and depart southbound. Rimmed along the edges of the strip are a series of homes of all different variety. Some are mobile homes next to hangers while others are mini mansions. It sounds almost idealic, and from the short time I was there, it looked the part too. The trouble with Vaughn airfield is finding it.

To get there, you must follow a country road to a small, unassuming dirt driveway. On the way there, the taxi driver completely missed the turn, and so did I. It wasn’t until we reached an intersection a ways farther that I realized our mistake. We doubled-back, found the proper mailbox, and made the trek along the bumpy dirt road.

At this point in my day, I encountered my first bit of good luck: Upon reaching the crest of the hill, I immediately spotted George’s plane parked on a taxiway, ready to go.

This new taxi driver fortunately accepted MasterCard, my cash funds being depleted from the previous taxi fare. He took my card and tried to call-in to his dispatcher on his cell phone to make the transaction. Unfortunately, we were in the middle of nowhere; his cell signal didn’t reach anything. I popped-open my cell and started hunting for a signal. I found one; it was faint, but it was there, so I handed my phone to the driver.

After paying the fare, I headed up the hill to meet George, and things started to seem like they were going to fall into place now. George is the best pilot I know, the skys over the airport were clear of low-level clouds and fog, and I had well over two hours before my new departure time at SeaTac. “No problem,” I thought, “I can relax now.”

As we departed the airport, I was again impressed by the power of George’s Maule. I’m used to flying in Piper Cubs, which do very little fast. The Maule lifted off the grass strip and soared upward at a remarkable rate. George turned a sharp but gentile left and climbed to about 1,300. We headed east across the Vashon Island and Puget Sound.

As we approached SeaTac airspace, George picked up the ATIS and then called the tower. At first, it seemed like the tower didn’t even hear us. George paused for a while, listening to the other traffic and waiting for a break, then made a second call. This time the tower replied. “Busy traffic this morning and we are operating on one runway due to construction. Estimate landing in two-zero minutes.”

There was a long pause in the Maule, but finally George relied, “Uh, tower, we don’t need much runway. Is there anyway you could fit us in?” The tower said, “Stand-by.”

We started doing circles just west of the pattern, and I started wondering if I was ever going to get to Philadelphia. Then it dawned on me; we could divert to Boeing field, and from there I could take a taxi to SeaTac. Boeing field is only about a 10 minute drive from SeaTac, maybe less if I was lucky enough to get a daring driver. I suggested the idea to George, and he agreed it would be good alternate if the tower couldn’t get us into the queue.

Sure enough, about a minute later, the tower came back in. “Can sequence you down in about two-ze... one-five minutes.” George looked over at me with the look that said, “I don’t believe him; what do you want to do?” I simply nodded, and George started the diversion to Boeing.

As we were shifting onto final at Boeing, George called up Galvin Flight, an FBO on the field, and asked them to call me a cab. We touched down in a perfect three-point landing, and George pulled over into Galvin, remarking that he really likes to fly into Boeing because everybody knows what they’re doing and is tremendously professional.

After saying thanks and good-bye to George, I headed through Galvin’s base and out to the parking lot beyond. From there, I met up with the taxi and got to SeaTac in short order. “OK, maybe now things will get a little easier,” I thought.

I checked my bag at curb-side check-in and received my boarding passes. I casually walked inside the terminal and made a straight path for the security check-point. The lines appeared relatively short, so I was pretty confident I’d have enough time once through to get some lunch before the flight, but my over-confidence was my weakness.</description>
         <link>http://www.gryphonshafer.com/blog/2007/12/travel_wars_episode_4.html</link>
         <guid>http://www.gryphonshafer.com/blog/2007/12/travel_wars_episode_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Personal Log</category>
        
        
         <pubDate>Thu, 13 Dec 2007 18:58:00 -0800</pubDate>
      </item>
            <item>
         <title>Perl Jobs Auto-Emailer</title>
         <description><![CDATA[<p>Here's an interesting Perl mini application. A friend of mine who's somewhat jealous of the fact that I'm working for Discovery remotely from home wants to find a new Perl job. There's a great web site for that: <a href="http://jobs.perl.org">http://jobs.perl.org</a>, but the secret is out, and a lot of people check that site all day. He doesn't want to have to check it all day to get in front of the blizzards of resumes that get sent to each job. I figure this is a job for Perl.</p>

<p>What we want is a small application that runs every hour by cron, checks an RSS feed from the Perl jobs web site, looks for any new jobs posted, and emails a summary.</p>

<p>First, the usual (and mandatory) intro block followed by four useful CPAN modules:</p>

<pre>#!/usr/bin/perl
use strict;
use warnings;
use LWP::Simple 'get';
use XML::RSS;
use List::MoreUtils 'any';
use Mail::Mailer;
</pre>

<p>I need some way to keep track of previously seen job postings so I don't send duplicate emails. So I'm going to have a text file with the links to the job posts seen since each link is unique. So when the application starts up, I need to load all the previously seen links into an array.</p>

<pre>use constant LINKS =&gt; '/some/directory/links.log';

open( my $seen_links, '&lt;', LINKS )
    or die "Can't read from log $!";
my @links = map { chomp; $_; } ( &lt;$seen_links&gt; );
close($seen_links);
</pre>

<p>Now it's time to go fetch the RSS XML, parse it, and find all the jobs that are new. What I'm doing here is taking all the "items" in the RSS feed and checking their links against the links I've already seen. If I haven't seen a link yet, I'm going to store off the item in an array.</p>

<pre>my $rss = XML::RSS->new();
$rss-&gt;parse( get('http://jobs.perl.org/rss/standard.rss') );

my @new_jobs = grep {
    my $link = $_-&gt;{'link'};
    not any { $link eq $_ } @links;
} @{ $rss-&gt;{'items'} };
</pre>

<p>Finally, the fun part. Once I have a list of new job postings (assuming there are new postings), I'm going to create an email and send it off with all the new job postings summarized.</p>

<pre>if (@new_jobs) {
    my $mailer = Mail::Mailer-&gt;new();
    $mailer-&gt;open({
        'From'    =&gt; 'somebody@example.com',
        'To'      =&gt; 'user@example.com',
        'Subject' =&gt; 'Job' . ( ( @new_jobs &gt; 1 ) ? 's' : '' ),
    }) or die "Can't open mail $!\n";

    open( my $new_links, '&gt;&gt;', LINKS )
        or die "Can't write to log $!";

    foreach my $job (@new_jobs) {
        my $attrib = sub {
            return
                $job-&gt;{'http://jobs.perl.org/rss/'}{ $_[0] }
                || 'Undefined';
        };
        print {$new_links} $job-&gt;{'link'}, "\n";
        print {$mailer}
            $job-&gt;{'title'}, "\n",
            $job-&gt;{'link'}, "\n\n",
            ' Company: ', $attrib-&gt;('company_name'), "\n",
            'Location: ', $attrib-&gt;('location'), "\n",
            '   Hours: ', $attrib-&gt;('hours'), "\n",
            '   Terms: ', $attrib-&gt;('employment_terms'), "\n",
            '  Posted: ', $attrib-&gt;('posted_date'), "\n\n\n";
    }

    close($new_links);
    $mailer-&gt;close();
}
</pre>

<p>You'll notice that I use a closure just inside the foreach loop. I did that because I wanted to avoid writing the nested data structure call and "or undefined" stuff five times. I'm kind of OCD like that. I guess it would have been fine to leave it in the last print statement.</p>

<p>So that's it. Less than 50 lines of code. Of course, it needs comments, POD, and better error checking and reporting, but it'll do for something quick and dirty.</p>]]></description>
         <link>http://www.gryphonshafer.com/blog/2007/08/perl_jobs_autoemailer.html</link>
         <guid>http://www.gryphonshafer.com/blog/2007/08/perl_jobs_autoemailer.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Perl</category>
        
        
         <pubDate>Fri, 03 Aug 2007 15:46:18 -0800</pubDate>
      </item>
            <item>
         <title>JavaScript namespace support</title>
         <description><![CDATA[Following up to my previous post about window.onload problems with JavaScript, the other problem I encounter with JavaScript is its lack of namespace support. JavaScript has no concept of namespaces, so there are bound to be conflicts between libraries unless you get lucky and all your JavaScript library authors picked different function and global variable names.

To solve this problem, you can do something rather funky and wrap everything in a function with the same name as the JavaScript library file. Thus, magical JavaScript namespaces, sort of. Here’s the new and “improved” script.js file:

<pre>var TestScript = function () {
    return {
        displayListings : function () {
            TestScript._buildStuff(
                "place_to_put_stuff",
                "Some stuff and things"
            );
        },
        _buildStuff : function ( id, text ) {
            var li = document.createElement("li");
            li.appendChild( document.createTextNode(text) );
            document.getElementById(id).appendChild(li);
        }
    };
}();
</pre>

The rest of the script.js file is the same, with the exception of changing every instance of displayListings to TestScript.displayListings. Notice that even within the TestScript “namespace” you have to refer to _buildStuff by it’s full name.

This seems like a neat idea, although I haven’t tried it for anything real. I am curious how it would work when building a larger-scale set of home-grown JavaScript libraries. For the enterprise development environment, you have to either enforce using this solution or enforce some sort of long naming convention.

<pre>function TestScript_displayListings() {
    TestScript__buildStuff(
        "place_to_put_stuff",
        "Some stuff and things"
    );
}
</pre>

Both options require enforcing a style and best practice across your enterprise, which means that you have to actually enforce something, which means that it probably won’t get enforced (unless you have free time to enforce things, which means you’re not very busy, which means you should probably get downsized). Which one do you think is easier to enforce?]]></description>
         <link>http://www.gryphonshafer.com/blog/2007/07/following_up_to_my_previous.html</link>
         <guid>http://www.gryphonshafer.com/blog/2007/07/following_up_to_my_previous.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Web Development</category>
        
        
         <pubDate>Tue, 03 Jul 2007 15:18:26 -0800</pubDate>
      </item>
            <item>
         <title>Not Overwriting window.onload</title>
         <description><![CDATA[<p>One of the things that has bugged me for a long time with JavaScript is how to deal with a large web site system with multiple JavaScript libraries written by various different authors at different skill levels. Two issues come to mind right away that need to be resolved:</p>

<p>First, JavaScript has no concept of namespaces, so there are bound to be conflicts between libraries unless you get lucky and all your JavaScript library authors picked different function and global variable names.</p>

<p>Second, and perhaps more annoying, are the times when multiple, independent libraries need to set the window.onload event. When you’re writing your own application, where you are starting from scratch or are the architect, this isn’t a problem. You just design around it. If you’re building a new JavaScript central library system, this also isn’t much of a problem because you can implement a “window.onload registry” where each library that needs access to the event just pushes a function name or reference to an array. Then there’s only one library that calls window.onload, and that library executes each function in the array. Easy.</p>

<p>But what if you’ve got an existing site with random JavaScript libraries in various patterns across various pages? Some libraries need window.onload, but others don’t. And you now need to write a new library or two (or three) that may each need to register with window.onload, but you don’t want to overwrite existing library calls to window.onload, and you want to ensure your call doesn’t get overridden.</p>

<p>Well, there’s a two-part solution. For all non-stupid browsers (i.e. everyone except IE), register a DOMContentLoaded event. For the most popular stupid browser, use the &lt;script&gt; tag’s “defer” attribute along with a creative use of the onreadystatechange event.</p>

<p>Suppose you have an XHTML file like so:</p>

<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"&gt;
    &lt;head&gt;
        &lt;title&gt;Page Title&lt;/title&gt;
        &lt;script type="text/javascript" src="script.js"&gt;&lt;/script&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h2&gt;Page Header&lt;/h2&gt;
        &lt;ul id="place_to_put_stuff"&gt;&lt;/ul&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<p>What you want to do is have a function contained within script.js execute after page load is complete that will push some data into that empty &lt;ul&gt;. Of course, you could just inline all your JavaScript inside the &lt;ul&gt;, but that’s ugly, bad, and evil. (If you don’t know why, stop reading now and go buy a good elementary programming book like Code Complete.)</p>

<p>Looking at this particular example, the code in script.js is very simple:</p>

<pre>function displayListings() {
    _buildStuff( "place_to_put_stuff", "Some stuff and things" );
    _buildStuff( "place_to_put_stuff", "More stuff and things" );
}

function _buildStuff( id, text ) {
    var li = document.createElement("li");
    li.appendChild( document.createTextNode(text) );
    document.getElementById(id).appendChild(li);
}
</pre>

<p>You just need to get the browser to run displayTVListings() at the right time but without resorting to setting window.onload. One solution is to add the following to the bottom of script.js:</p>

<pre>if ( document.addEventListener ) {
    document.addEventListener(
        "DOMContentLoaded", displayListings, false
    );
}
else if ( document.all &amp;&amp; ! window.opera ) {
    document.write(
        '&lt;script type="text/javascript" id="displayListingsLoader" ' +
        'defer="defer" src="javascript:void(0)"&gt;&lt;\/script&gt;'
    );
    var contentLoader = document.getElementById("displayListingsLoader");
    contentLoader.onreadystatechange = function () {
        if ( this.readyState == "complete" ) displayListings();
    }
}
</pre>

<p>This will register the DOMContentLoaded event for all non-stupid browsers. For IE, you have to resort to using the “defer” attribute of the &lt;script&gt; tag.</p>

<p>I found this idea when reading a few blogs on the subject:</p>

<ul>
    <li><a href="http://dean.edwards.name/weblog/2005/09/busted/">The window.onload Problem - Solved</a></li>
    <li><a href="http://dean.edwards.name/weblog/2006/06/again/">window.onload (again)</a></li>
    <li><a href="http://webreflection.blogspot.com/2006/09/better-domcontentloaded.html">a better DOMContentLoaded</a></li>
</ul>

<p>So that solves the window.onload problem, but what about the namespaces issue? Well, more on that in a later post.</p>]]></description>
         <link>http://www.gryphonshafer.com/blog/2007/07/not_overwriting_windowonload.html</link>
         <guid>http://www.gryphonshafer.com/blog/2007/07/not_overwriting_windowonload.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Web Development</category>
        
        
         <pubDate>Tue, 03 Jul 2007 14:25:29 -0800</pubDate>
      </item>
            <item>
         <title>I Hate Security</title>
         <description><![CDATA[Growing up, I walked to school by myself, walked into the unlocked and unguarded school building without passing through metal detectors, and spent less than 10 minutes at the security check-in when flying on vacation with my parents. When I first started playing around with the Internet, hackers were a good thing, spam was a type of meat no one wanted to eat, and everyone used telnet. (Of course, back then, the Web didn’t exist either, but we had Gopher and we liked it!)

Now days, everyone uses SSH, anything sensitive is encrypted, and every server farm has a firewall. In fact, often you’ll see a data center where every box has its own firewall. Encrypting passwords with “crypt” is no longer good enough. And email? Spam Assassin, UCE blockers, Pyzor, Razor, DCC, Courier Authentication, PAM, SSL encrypted tunnels for reading email remotely, and ClamAV; and this is only on my personal mail server in my garage.

I hate security. The fact that I feel compelled to spend time securing systems that have virtually no value to anyone in the world except me makes me angry. It angers me that I have to lock my door at night despite living in a very nice neighborhood in a <a href=" http://maps.google.com/maps?f=q&hl=en&q=Port+Orchard,+WA&ie=UTF8&ll=47.54103,-122.63851&spn=0.146011,0.230713&t=k&z=12&om=1">quiet nook in the Puget Sound</a>.

Why do we have to deal with all this security? Because there are bad people out there in the world, people who will do anything for money. However, there’s now one less bad person doing bad things on the Internet. Robert Soloway has for quite a while been spamming the universe, but today he’s behind bars, hopefully where he will stay for a long time. Once he’s let out, I hope the judge puts a Mitnick gag on him preventing him from even thinking about touching a computer. We’ll see.

Soloway is a very bad man, one of the top spammers in the world. He’s been indicted for mail fraud, wire fraud, e-mail fraud, aggravated identity theft, and money laundering. Microsoft sued him for $7 million and won. An ISP in Oklahoma sued him for $10 million and won. But both these judgments didn’t stop Soloway. What stopped him, and this is depressing, was government annoyance. His illegal spam got so bad that it caused some government agencies to spend thousands per week to fight back the tidal wave. It took that along with hundreds of other complaints for authorities to go after the guy.

Now, keep in mind, this guy had already lost two civil suits, was breaking anti-spam law, and was engaging in fraud and identity theft. Still, authorities did little. It was only after hundreds of complaints that authorities began their investigation.

If you want to really make this world a better place, the solution isn’t security. Security helps stop bad people from doing certain bad things. The real solution is to remove the freedom of bad people to do bad things by putting them in jail. Do that enough, and bad people who aren’t in jail and have half a brain will think twice about doing bad things. And the bad people who don’t have half a brain are usually distracted by pretty colors.]]></description>
         <link>http://www.gryphonshafer.com/blog/2007/05/i_hate_security.html</link>
         <guid>http://www.gryphonshafer.com/blog/2007/05/i_hate_security.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Commentary</category>
        
        
         <pubDate>Thu, 31 May 2007 15:20:03 -0800</pubDate>
      </item>
      
   </channel>
</rss>

