HTML5 form inputs in CakePHP 1.2

At work, we’re building out a mobile version of our replicated sites.  As anyone who’s used an iPhone or Android browser to fill out a web form knows… using an iPhone or Android browser to fill out a web form is really annoying, and it’s best to make things as user-friendly as possible.  That’s why I wanted to leverage the power of HTML5 form elements to minimize that annoyance.

On the iPhone, Mobile Safari renders its on-screen keyboard differently for “email,” “tel” and “url” form fields — you automatically get the keys that make the most sense for each specific situation.

HTML5 email form input rendered on an iPhone

HTML5 email form input rendered on an iPhone

HTML5 tel form input rendered on an iPhone

HTML5 tel form input rendered on an iPhone

HTML5 url form input rendered on an iPhone

HTML5 url form input rendered on an iPhone

However, the version of CakePHP that we’re running our replicated sites on (1.2.5) doesn’t have support for these awesome HTML5 inputs — any unknown input type is automatically rendered as a textarea.  (What kind of weird default is that?)

The HTML5 inputs are supported in CakePHP 2.0 (currently in beta), but we’re stuck with 1.2.5 for the time being.  Lennaert Ekelmans back-ported that support to 1.3.6, but his solution didn’t work for me out of the box either — apparently there were a couple of changes in the Form helper between 1.2.5 and 1.3.6.  So I back-back-ported his code to work with 1.2.5.  Here it is:

 <?php
# app/views/helpers/html5_form.php
App::import('Helper', 'Form');
class Html5FormHelper extends FormHelper {
    /**
     * Generates a form input element complete with label and wrapper div
     *
     * Options - See each field type method for more information. Any options that are part of
     * $attributes or $options for the different type methods can be included in $options for input().
     *
     * - 'type' - Force the type of widget you want. e.g. ```type => 'select'```
     * - 'label' - control the label
     * - 'div' - control the wrapping div element
     * - 'options' - for widgets that take options e.g. radio, select
     * - 'error' - control the error message that is produced
     *
     * @param string $fieldName This should be "Modelname.fieldname"
     * @param array $options Each type of input takes different options.
     * @return string Completed form widget
     *
     * different from regular form helper only in the textarea/default case -- adds support for HTML5 form types and attributes like placeholder
     *
     */
    function input($fieldName, $options = array()) {
        $view =& ClassRegistry::getObject('view');
        $this->setEntity($fieldName);
        $entity = join('.', $view->entity());

        $defaults = array('before' => null, 'between' => null, 'after' => null);
        $options = array_merge($defaults, $options);

        if (!isset($options['type'])) {
            $options['type'] = 'text';

            if (isset($options['options'])) {
                $options['type'] = 'select';
            } elseif (in_array($this->field(), array('psword', 'passwd', 'password'))) {
                $options['type'] = 'password';
            } elseif (isset($this->fieldset['fields'][$entity])) {
                $fieldDef = $this->fieldset['fields'][$entity];
                $type = $fieldDef['type'];
                $primaryKey = $this->fieldset['key'];
            } elseif (ClassRegistry::isKeySet($this->model())) {
                $model =& ClassRegistry::getObject($this->model());
                $type = $model->getColumnType($this->field());
                $fieldDef = $model->schema();

                if (isset($fieldDef[$this->field()])) {
                    $fieldDef = $fieldDef[$this->field()];
                } else {
                    $fieldDef = array();
                }
                $primaryKey = $model->primaryKey;
            }

            if (isset($type)) {
                $map = array(
                    'string'  => 'text',     'datetime'  => 'datetime',
                    'boolean' => 'checkbox', 'timestamp' => 'datetime',
                    'text'    => 'textarea', 'time'      => 'time',
                    'date'    => 'date',     'float'     => 'text'
                );

                if (isset($this->map[$type])) {
                    $options['type'] = $this->map[$type];
                } elseif (isset($map[$type])) {
                    $options['type'] = $map[$type];
                }
                if ($this->field() == $primaryKey) {
                    $options['type'] = 'hidden';
                }
            }

            if ($this->model() === $this->field()) {
                $options['type'] = 'select';
                if (!isset($options['multiple'])) {
                    $options['multiple'] = 'multiple';
                }
            }
        }
        $types = array('text', 'checkbox', 'radio', 'select');

        if (!isset($options['options']) && in_array($options['type'], $types)) {
            $view =& ClassRegistry::getObject('view');
            $varName = Inflector::variable(
                Inflector::pluralize(preg_replace('/_id$/', '', $this->field()))
            );
            $varOptions = $view->getVar($varName);
            if (is_array($varOptions)) {
                if ($options['type'] !== 'radio') {
                    $options['type'] = 'select';
                }
                $options['options'] = $varOptions;
            }
        }

        $autoLength = (!array_key_exists('maxlength', $options) && isset($fieldDef['length']));
        if ($autoLength && $options['type'] == 'text') {
            $options['maxlength'] = $fieldDef['length'];
        }
        if ($autoLength && $fieldDef['type'] == 'float') {
            $options['maxlength'] = array_sum(explode(',', $fieldDef['length']))+1;
        }

        $out = '';
        $div = true;
        $divOptions = array();

        if (array_key_exists('div', $options)) {
            $div = $options['div'];
            unset($options['div']);
        }

        if (!empty($div)) {
            $divOptions['class'] = 'input';
            $divOptions = $this->addClass($divOptions, $options['type']);
            if (is_string($div)) {
                $divOptions['class'] = $div;
            } elseif (is_array($div)) {
                $divOptions = array_merge($divOptions, $div);
            }
            if (in_array($this->field(), $this->fieldset['validates'])) {
                $divOptions = $this->addClass($divOptions, 'required');
            }
            if (!isset($divOptions['tag'])) {
                $divOptions['tag'] = 'div';
            }
        }

        $label = null;
        if (isset($options['label']) && $options['type'] !== 'radio') {
            $label = $options['label'];
            unset($options['label']);
        }

        if ($options['type'] === 'radio') {
            $label = false;
            if (isset($options['options'])) {
                if (is_array($options['options'])) {
                    $radioOptions = $options['options'];
                } else {
                    $radioOptions = array($options['options']);
                }
                unset($options['options']);
            }
        }

        if ($label !== false) {
            $labelAttributes = $this->domId(array(), 'for');
            if (in_array($options['type'], array('date', 'datetime'))) {
                $labelAttributes['for'] .= 'Month';
            } else if ($options['type'] === 'time') {
                $labelAttributes['for'] .= 'Hour';
            }

            if (is_array($label)) {
                $labelText = null;
                if (isset($label['text'])) {
                    $labelText = $label['text'];
                    unset($label['text']);
                }
                $labelAttributes = array_merge($labelAttributes, $label);
            } else {
                $labelText = $label;
            }

            if (isset($options['id'])) {
                $labelAttributes = array_merge($labelAttributes, array('for' => $options['id']));
            }
            $out = $this->label($fieldName, $labelText, $labelAttributes);
        }

        $error = null;
        if (isset($options['error'])) {
            $error = $options['error'];
            unset($options['error']);
        }

        $selected = null;
        if (array_key_exists('selected', $options)) {
            $selected = $options['selected'];
            unset($options['selected']);
        }
        if (isset($options['rows']) || isset($options['cols'])) {
            $options['type'] = 'textarea';
        }

        $empty = false;
        if (isset($options['empty'])) {
            $empty = $options['empty'];
            unset($options['empty']);
        }

        $timeFormat = 12;
        if (isset($options['timeFormat'])) {
            $timeFormat = $options['timeFormat'];
            unset($options['timeFormat']);
        }

        $dateFormat = 'MDY';
        if (isset($options['dateFormat'])) {
            $dateFormat = $options['dateFormat'];
            unset($options['dateFormat']);
        }

        $type     = $options['type'];
        $before     = $options['before'];
        $between = $options['between'];
        $after     = $options['after'];
        unset($options['type'], $options['before'], $options['between'], $options['after']);

        switch ($type) {
            case 'hidden':
                $out = $this->hidden($fieldName, $options);
                unset($divOptions);
            break;
            case 'checkbox':
                $out = $before . $this->checkbox($fieldName, $options) . $between . $out;
            break;
            case 'radio':
                $out = $before . $out . $this->radio($fieldName, $radioOptions, $options) . $between;
            break;
            case 'text':
            case 'password':
                $out = $before . $out . $between . $this->{$type}($fieldName, $options);
            break;
            case 'file':
                $out = $before . $out . $between . $this->file($fieldName, $options);
            break;
            case 'select':
                $options = array_merge(array('options' => array()), $options);
                $list = $options['options'];
                unset($options['options']);
                $out = $before . $out . $between . $this->select(
                    $fieldName, $list, $selected, $options, $empty
                );
            break;
            case 'time':
                $out = $before . $out . $between . $this->dateTime(
                    $fieldName, null, $timeFormat, $selected, $options, $empty
                );
            break;
            case 'date':
                $out = $before . $out . $between . $this->dateTime(
                    $fieldName, $dateFormat, null, $selected, $options, $empty
                );
            break;
            case 'datetime':
                $out = $before . $out . $between . $this->dateTime(
                    $fieldName, $dateFormat, $timeFormat, $selected, $options, $empty
                );
            break;
            case 'textarea':
                $out = $before . $out . $between . $this->textarea($fieldName, $options + array('cols' => 30, 'rows' => 6));
            break;
            default:
                $out = $before . $out . $between . $this->defaultInput($type, $fieldName, $options);
            break;
        }

        if ($type != 'hidden') {
            $out .= $after;
            if ($error !== false) {
                $errMsg = $this->error($fieldName, $error);
                if ($errMsg) {
                    $out .= $errMsg;
                    $divOptions = $this->addClass($divOptions, 'error');
                }
            }
        }
        if (isset($divOptions) && isset($divOptions['tag'])) {
            $tag = $divOptions['tag'];
            unset($divOptions['tag']);
            $out = $this->Html->tag($tag, $out, $divOptions);
        }
        return $out;
    }

    /**
    * Creates a default input widget, whose type is determined by $options['type'].
    *
    * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
    * @param array $options Array of HTML attributes.
    * @return string A generated HTML input element
    * @access public
    */
    function defaultInput($type, $fieldName, $options = array()) {
            $options = $this->_initInputField($fieldName, array_merge(
                    array('type' => $type), $options
            ));
            return sprintf(
                    $this->Html->tags['input'],
                    $options['name'],
                    $this->_parseAttributes($options, array('name'), null, ' ')
            );
    }
}    
?>

Unlike Lennaert, I chose to extend the regular form helper by creating my own helper class instead of adding the necessary code to Cake’s own form helper. Now I can get good HTML5 form inputs by adding the Html5Form helper to my controller and then calling the following code in my view:

echo $html5Form->input('email',
    array(
        'autofocus' => 'autofocus',
        'placeholder' => 'email@example.com',
        'type' => 'email',
        'label' => __('Email address', true),
        'required' => 'required',
        'div' => 'required'
    )
);
echo $html5Form->input('phone',
    array(
        'placeholder' => '801-867-5309',
        'type' => 'tel',
        'label' => __('Phone', true),
        'required' => 'required',
        'div' => 'required'
    )
);
echo $html5Form->input('phone',
    array(
        'placeholder' => 'http://www.curtisgibby.com',
        'type' => 'url',
        'label' => __('Web site', true),
        'required' => 'required',
        'div' => 'required'
    )
);

That results in the following HTML:

<div>
    <label for="ContactEmail">Email address</label>
    <input name="data[Contact][email]" type="email" autofocus="autofocus" placeholder="email@example.com" required="required" value="" id="ContactEmail" />
</div>
<div>
    <label for="ContactPhone">Phone</label>
    <input name="data[Contact][phone]" type="tel" placeholder="801-867-5309" required="required" value="" id="ContactPhone" />
</div>
<div>
    <label for="ContactPhone">Web site</label>
    <input name="data[Contact][phone]" type="url" placeholder="http://www.curtisgibby.com" required="required" value="" id="ContactPhone" />
</div>

(There doesn’t seem to be an easy way to output HTML5′s standalone attributes as simply autofocus or required rather than the more verbose XHTML-ish autofocus = ‘autofocus’ or required = ‘true’ — without hacking the CakePHP base helper class. I’m okay with this slightly lengthier version for now though.)

If you find this code useful, let me know in the comments.

Posted by Curtis Gibby on July 28th, 2011

Filed under Programming, Work | No Comments »

The breakdown: WIRED magazine, June 2011

This year, I got a subscription to WIRED magazine for Christmas.  It’s been a couple of years since I had a magazine to read every month, and I noticed something this time around that seemed indicative of a wider change in the journalism industry: more ads.  It appeared that every other page in this magazine was advertising content rather than editorial content.  No longer was I the consumer and the magazine the product.  Instead, I was the product and advertisers were the consumers.

By the time the May issue came in the mail, I was annoyed enough that I started ripping the all-ad pages out of the magazine and complaining about it on Facebook: “Curtis Gibby just ripped out 10 of the first 16 pages in my new WIRED magazine because they were useless ads on both sides. Who says print journalism is dying?”  A couple of people took notice of my post, and I thought it would be interesting to expand on the subject when the June issue rolled around.

And now it’s time for a breakdown

The magazine in its wrapper, before I started ripping it apart

The magazine in its wrapper, before I started ripping it apart

It came in a little plastic bag and had a subscription card for the New Yorker behind it.

The back cover, immediately after opening the wrapper

The back cover, immediately after opening the wrapper

I immediately noticed that both sides of the back cover were actually ads. I had decided to tear out any sheet that was advertising on both sides, but I didn’t want to destroy the physical integrity of the magazine by ripping off the cover.  Curse you, Conde Nast editors!

The back cover - an ad

The back cover - an ad

The inside back cover - another ad

The inside back cover - another ad

I started by going through the whole magazine from the front cover, and counting every page as either editorial content or an ad.  I gave the publishers the benefit of the doubt, calling anything that actually had to do with the magazine itself “content,” regardless of whether it was a boring index or a photo illustration without any actual information.  (As long as it wasn’t actively trying to get me to purchase a Best Buy TV, I called it good.)

The inside front cover and first page - both ads.

The inside front cover and first page - both ads.

The first several pages were not promising — after the front cover, there were five ad pages in a row.  In fact, I was surprised to find out that most of the first half of the magazine had an ad on every other page.  It wasn’t until I got to the actual articles section near the back that I got several pages in a row of content without ads.

One page had a vertical ad that took up a column about a third of the page size.

The single split page with both editorial and ad

The single split page with both editorial and ad

I tore out twenty sheets (ones that had ads on both sides) and collected four silly subscription cards.

The twenty ripped-out pages

The twenty ripped-out pages

The complete magazine after the breakdown

The complete magazine after the breakdown

tl;dr

In the end, I found that the magazine was 53.17% editorial content — 94.64 pages were editorial versus 83.36 pages of ads.

I’ve posted a spreadsheet listing every page at Google Docs.  The spreadsheet also lists where I found the insert cards and specifies the cumulative percentage of editorial content.

Posted by Curtis Gibby on June 3rd, 2011

Filed under The Breakdown | 4 Comments »

Tweaking Firefox tab colors, part 2 (The Firefox 4 edition)

Colored tabs in Firefox 4 - not very colored anymore

Just when I got used to my new tab colors described in my previous post, Firefox 4 was released and broke what I’d done.  I could still see a teeny difference between my green, yellow, and red tabs — a 2-pixel strip at the top of each of them.  But most of the tab was the default chrome — nice-looking, but not what I wanted.

So I started looking for a solution that would let me change the whole tab’s color in FF4, not just the top of the tab.  I downloaded Tab Mix Plus, which did work in Firefox 4, and reverse-engineered the CSS that it added to the page.  I found that it was adding a background-image to the tab’s CSS, like this:

background-image: -moz-linear-gradient(bottom,rgba(10%,10%,10%,.4) 1px,transparent 1px), -moz-linear-gradient(#a6dfa6,#8dd68d) !important;
Much better!

I added that to the relevant section of my userChrome.css, restarted Firefox, and presto! My tab was colored the way that it should be.

Then I realized that FF4′s chrome had inactive and hover states that were much more pronounced than before, much darker than an active tab.  And unlike my previous solution, Firefox didn’t automatically convert this into those hover and active states.

So I had to go back and add plain old CSS :hover and [selected="true"] rules to each of my colors, making the inactive tab the darkest, the hover, slightly lighter, and the active the lightest.  This is what I ended up with:

/* Localhost colors */
tab[image*="-dev.net"]
{
 -moz-appearance: none;
 background-color:#d8f1d8 !important; /* what we had before -- the tiny little strip at the top */
 background-image: -moz-linear-gradient(bottom,rgba(10%,10%,10%,.4) 1px,transparent 1px), /* the line on the bottom */
 -moz-linear-gradient(#a6dfa6,#8dd68d) !important; /* the main color we're adding */
}

/* Localhost hover colors */
tab[image*="-dev.net"]:hover
{
 background-image: -moz-linear-gradient(bottom,rgba(10%,10%,10%,.4) 1px,transparent 1px),
 -moz-linear-gradient(#bfe8bf,#a6dfa6) !important;
}

/*  Localhost active colors */
tab[image*="-dev.net"][selected="true"]
{
 background-image: -moz-linear-gradient(#d8f1d8,#bfe8bf) !important; /* no line on the bottom here, since it's the active one */
}

(Of course, repeated for each of my color groups.)

This is what the “live” red group looks like:

L: Inactive, C: Active, R: Hover

L: Inactive, C: Active, R: Hover

Posted by Curtis Gibby on March 31st, 2011

Filed under Programming | 2 Comments »

Creating on-the-fly MP3 playlists on the NMT

I bought the excellent Popcorn Hour media server a couple of years ago, and I’ve always loved the job it does on my video files, especially with the equally fantastic YAMJ that rebuilds the video jukebox every time I get a new TV show or movie.

But I’ve never really gotten the NMT to play well with my audio files — it doesn’t handle standard playlist formats like .pls or .m3u, so you’re stuck playing one song at a time unless you can figure out its own insane .jsp playlist format. Read the rest of this entry »

Posted by Curtis Gibby on March 1st, 2011

Filed under Programming | No Comments »

CSS woes at eBay

I don’t generally pay a ton of attention to web typography, but I must have it on my mind after building a new backend for our replicated sites.  I spotted this on an eBay order confirmation screen this morning:

Yes, that’s three different fonts for three different headers… on the same page.

The first is an H2 of class “g-m0 g-m dsp” that just inherits the body’s “Arial,Helvetica,sans-serif” font-family.

The second is another H2, this one with class “h2Titles”.  It gets font-family “Trebuchet” — which apparently I don’t have installed on my machine, because I see the crappy default Times New Roman.  (Yes, they specified exactly one font — incorrectly.  Apparently they haven’t heard of font stacks.)

The third H2 is probably what they wanted for all of these.  It has class “h2Title” (as opposed to “h2Titles” above) and gets the correct font “Trebuchet MS”.

What a mess.  This is one of the biggest e-commerce companies in the world and they can’t get their CSS right.  (This isn’t exactly bleeding-edge web technology, guys — the font-family part of the CSS spec came out 14 years ago.)

Here’s how they could have prevented this train wreck of a page.  First, set up a CSS reset that hits every single H2 (and anything else that you may want to look good) with a solid definition.  Next, add some progressive enhancement to certain H2s by adding a class.  Third, check that the specified font rules are actually doing something to the intended target (as in the number 2 “Trebuchet” case above).  Finally, at the very least, give your page a quick once-over to make sure that everything looks as you intended.

At least they all got the same color — #5D5D5D looks pretty nice.

Posted by Curtis Gibby on January 14th, 2011

Filed under Programming | No Comments »

Different tab colors for different contexts

I love Firefox.  Love love love it, especially because I can tweak many things about it to make things work just the way I want.  (Like creating Greasemonkey scripts to make other peoples’ websites do my bidding.  Or syncing not only my bookmarks and passwords between computers, but also my Stylish userstyles.)  Google Chrome may be zippier, but until I can run AdBlock Plus, All-in-One Gestures, Colorzilla, Firebug, FireShot, Firefox Sync, Greasemonkey, Open With, Stylish, and Update Notifier in it, I’m sticking with Firefox.

Anyway, the point here is that I wanted to have my Firefox tabs for different environments show up as different colors in my browser.  As a web developer, I spend a lot of time in a localhost development server, on a staging dev server, and on a live web site.  I may have ten tabs open, each of which could be from any of my environments, and each of which may look identical until you click into it to see the actual URL.  So I spent a lot of time clicking back and forth between them trying to find the other page I’d been working on.  I wanted a quick visual representation to show which of those environments I was in.

If you’ve never heard of userChrome.css, you can use it to change a lot of stuff that you don’t like about the “chrome” (or the look of the browser window itself — completely unrelated to the browser Google Chrome).  You add some CSS to the file (which in Windows is found in %appdata%/Mozilla/Firefox/Profiles/<profile-id>/chrome ), then save it and restart Firefox, and voilà, some change has been made to your chrome.

I found a Stylish forum that asked about my very question: can you customize the color or other style of the tab based on the domain of the URL?  Fortunately, the answer was yes.  What you have to do is write a CSS selector based on the favicon in the tab window.  (So if a site doesn’t have a favicon, this hack won’t work.  Fortunately, all of the ones I wanted to style do have a favicon, and most other sites on the web do too.)  The basic CSS selector looks like this:

tab[image*="example.com"] {background:red !important;}

That means, “For every tab that has a favicon whose url includes the text ‘example.com’, give it a background-color of red.”  In my case, I wanted my localhost to show light green, the staging server to show light yellow, and the live server to show light red.  This is the css that I came up with:

tab[image*="localhost"] {background-color:#CBEDCB !important;} /* light green */
tab[image*="dev.mycompany.com"] {background-color:#EDEDCB !important;} /* light yellow */
tab[image*="live.mycompany.com"] {background-color:#EDCBCE !important;} /* light red */


This worked perfectly for me — now I can easily distinguish between all of my development environments.  Firefox even makes the active tab a brighter version of the same background color (or is it, “Firefox dims the non-active tabs”?  That seems more likely.), so you don’t have to specify a style to tell which tab is the active one.

I’d be interested to know if there’s a way to accomplish the same thing in other major browsers, but this is how I did it in Firefox.

Posted by Curtis Gibby on December 16th, 2010

Filed under Cool Web Stuff, Programming | 3 Comments »

Our year… in six photos

We did a lot of fun and fantastic things this year and, of course, added a baby to the mix.  Take a look at our 2010 “Christmas letter” — a personalized memory game that I made on Match The Memory.  Have fun playing it!

Posted by Curtis Gibby on December 9th, 2010

Filed under Family | No Comments »

Telling Stories

Tonight when we were coming home from Grandma Gibby’s house, we were playing games made up by a 5-year-old. (Audrey said, “Let’s play a game called, ‘Let’s see if you can sing a song that you know’. How you play is, you sing a song that you know.”)

When we got to the game called “Tell a story” (“How do you play it?” Sarah wondered aloud, and we laughed), Audrey told one about a queen and king who had a 5-year-old named Audrey and a baby named Claire and a 2-year-old named Nathan.  They lived in a royal palace and had lots of servants. One day they took a walk and found some more servants who didn’t have anywhere to live, so they hired them.  The end.

Sarah told an engaging story about a little dragon who wanted to breathe fire like his big brother, but no matter how hard he huffed and puffed, he just got a little bit of smoke.  Audrey was INTO this story, so much so that she started coming up with solutions for the little dragon’s problem.  Sarah whispered to me, “Who’s telling this story?”

When it was my turn, I came up with a highly inventive tale that went something like this: “Once there was a little boy named Randy who loved to play with his cowboy doll named Sheriff Moody.  [Moody rhymes with "goody" and "hoodie" in this story.]  Randy had a lot of toys that he liked to play with, but Sheriff Moody was his favorite.”

“Then Randy had a birthday, and all the toys were afraid that he would get a toy that was cooler than they were and he wouldn’t play with them anymore.  Randy’s mom gave him a spaceman toy called… Muzz Gightlear.  Randy thought Muzz Gightlear was really cool.”

At this point, Nathan interjected, “An’ it was Buzz!”  We asked Audrey if she knew that I was really talking about Andy and Woody and Buzz Lightyear and she said no, but Nathan recognized my thinly veiled cribbing of the plot of Disney-Pixar’s beloved 1995 hit, Toy Story.  Pretty good for a kid who’s not yet three.

Posted by Curtis Gibby on September 26th, 2010

Filed under Family, Random Stories | No Comments »

And then there were five… (The story of Claire’s birth)

The set-up

When Sarah was about 20 weeks along with this baby, she began to lobby for us to not find out what the gender was until the baby was actually born.  After all, she figured, we could be surprised by learning if it was a boy or a girl at 20 weeks, or at 40 weeks.  Also, we already had one of each, so she wasn’t especially pining to have either a girl or a boy, and we already had the clothing for whichever kind of baby came out.  She figured that if she didn’t know, she’d also end up spending less money on clothes and stuff.  I supported her decision, even though I personally would have rather found out.  So we never got an ultrasound.

But as the weeks wore on, we began to regret our decision to not find out.  We had a great name picked out for a girl (Claire Rebecca Gibby), but we struggled with coming up with a good boy name.  We considered several — Seth, Henry, Thomas, Joseph — and spent hours trying to find one that had all the right qualities.  For me, that meant it couldn’t be too popular, but it still had to be common and recognizable;  Sarah wanted something distinctive, with character.  But we never settled on anything, even as Sarah’s due date drew closer.  We realized that if we’d gotten an ultrasound like most people do, we would have known whether it was a boy or a girl, and if we even had to worry about thinking up a boy name.

Baby Time

Our other two kids were early — 12 days for Audrey, and 10 days for Nathan — so we weren’t expecting Sarah to even make it to 39 weeks.  But as 39 and then 40 weeks came and went, Sarah started getting more miserable.  “I want my body back!” she’d exclaim.  Every time Sarah had a few good contractions, we’d hope that this was it… but then she’d get up and do something, and the contractions would peter out.  “I’m going to be pregnant forever,” she moaned.

On Monday, September 13 (three days after her due date), Sarah woke up with the same kind of contractions, but they were pretty regular.  I went to work, confident in the knowledge that I could get home within 5 minutes of when she called me.  Sarah got her doctor to come check her by mid-morning, and she was at a 4-5 cm dilation and 90% effaced — it was going to happen sometime today!  He went back to work (probably to cancel a bunch of other patients!) but promised that he’d be back when she needed him.  Sarah called our moms and her sisters to come down to our house, and got me to come home from work at lunch time.

By the time I got home, her contractions were almost a minute long and less than four minutes apart.  She really was in good labor.  Judy took Audrey to school, and my mom took Nathan to the store with her to keep him out of our hair.  Dr. Wright came back and we got the pool set up in our bathroom by about 1:30 p.m.

I got my swimsuit on and jumped in the tub with Sarah, and she got to work having good contractions.  I was against the back wall, and she was leaning back on me for support.  I rubbed her back and tummy, poured water on her belly, whispered encouraging words.  Being in the water helped her manage the pain.  A midwife and a midwife-in-training showed up to lend a hand to Dr. Wright, and Alaura started snapping pictures.  Once between contractions, my mom brought Nathan up to see what was going on, and he was happy and shy.

When the doctor checked her again, she was just at 5-6 cm dilated and fully effaced.  She was afraid that it would take a long time to be fully dilated, but soon Sarah’s “vocalizations” got louder and more intense and she felt the urge to push — it was really baby time.  At one point in the middle of a contraction, she cried out, “I’m so excited to have this baby!”  The midwives had never heard someone so enthusiastic and happy to be giving birth.

Eventually, Sarah could reach down and feel the baby’s head.  It only took her half an hour to get from that 5-6 to 10 centimeters.  When she actually pushed the baby out, the doctor grabbed her and placed her onto Sarah’s chest. We covered the baby with towels and got them wet to keep her warm.  We didn’t even check to see if she was a boy or a girl for a few minutes; we just looked and looked at her head and fingers, so happy that she was finally with us.

When the doctor peeked under the towel and announced that it was a girl, we were very relieved.  And we immediately said her name, “Claire.”

Sarah’s sister Zilpha missed the birth by five minutes because she hadn’t gotten the first messages that we’d left for her, but Judy and Alaura were both there to witness it, and Mom and Nathan came up to see the baby while we were still in the tub.  Audrey got home from school an hour after Claire was born, and she was so happy that we’d had a little sister for her instead of a brother.

Conclusions

Claire ended up being 7 pounds, 14 ounces and 20 inches long, which surprised us — we thought she’d be bigger because she had longer to cook in the womb.  But she’s perfectly healthy and we’re happy to have her with us.

This was our first home birth, and we liked how it all turned out.  We knew where everything was and could easily grab anything we needed during the labor and delivery.  We didn’t have to rush to the hospital, we didn’t have to fill out forms before we could have a baby, we didn’t have to listen to any security lectures about not letting strangers walk off with our child, and we didn’t have to hear any other newborns crying all night.  Dr. Wright and his crew took care of cleaning up the tub and the towels and everything in the bathroom.  We just quickly settled back into life at home with our three kids.

Posted by Curtis Gibby on September 15th, 2010

Filed under Family | 3 Comments »

Syncing Stylish userstyles with Dropbox

I’ve used Firefox’s Stylish plugin for years to get rid of stuff that I don’t want to see on all kinds of web sites.  Once you know how CSS works, the plugin makes it easy to tweak the look of any page by adding a quick {width:100%;} or {display:none;} to your own personalized CSS userstyle for each site. Read the rest of this entry »

Posted by Curtis Gibby on August 24th, 2010

Filed under Programming | 2 Comments »