CHM Support – why documentation sucks!

For those who don’t know, CHM is an acronym for Microsoft Compiled HTML Help.  It was released in 1997 when dial-up connections were used to access the Internet.  CHM is a solution for product help files.  Product documentation is contained in a single file that can be accessed offline.  This documentation, loosely based on HTML, can then be browsed using a desktop client.

CHM documentation viewed through IE4

The desktop client has a UI that appears to be based on IE4 and, for reasons that aren’t entirely clear to me, CHM support has persisted in the Microsoft ecosystem.  This support has continued even though Internet access is widely available and there are vastly superior techniques for gathering and discovering knowledge.

People with outhouses get a lot of bladder infections

CHM has all the convenience and smell of an outhouse I work for a company that creates a Web Content Management (WCMS) product.  We spend a lot of time figuring out how to make content easy to edit…and we’re certainly not alone in this quest.  By contrast, editing & publishing a CHM is excruciatingly difficult.

It is human nature to avoid (or postpone) tasks that are a giant hassle.  When you need to pee at 2:00 AM and your only solution is an outhouse, 100 yards away, in freezing temperatures then you’re going to wait as long as possible.  What I’m trying to say is “CHM support is a bladder infection waiting to happen”.

Here is the workflow I want when editing documentation:

  1. Click Edit
  2. Make the change
  3. Click Save

If I can fix documentation in 10 seconds, then I’ll do it.  If, on the other hand, I need to load some archaic desktop-based editor, recompile the documentation in Visual Studio and run a publishing script then I’ll probably do something else…

Blogging your way to documentation…

Why write docs when you can be a superstar blogger? Ultimately the by-product of all of this pain is documentation becomes scattered to the four corners of the Earth.  If you want to see this in action, look at Microsoft.  The best and most current information is never in the official documentation.  Instead, this documentation is sprinkled across several hundred blogs.

Other software ecosystems don’t have the severity of this problem.  Hell PHP has embraced the nature of the the web since…well, from the beginning.  During my PHP days my first stop was always PHP.net.  More often than not, I found my answer in the documentation or the comments.  

Do not attempt to install while exploring the Congo

Good, the vendor provided a CHM.  Now if I only had a power outlet... Our ridiculous dogged support for an outdated technology prevents us from embracing the advantages of modern content management solutions.  Furthermore, it prevents us from utilizing crowd-sourcing and generally ensures that documentation is stale.  And we do all this because someone might want to access documentation offline.

This needs to stop!

I’m sorry if you don’t have an Internet connection.  It’s probably not the best idea to attempt to install software while exploring the Congo…or wherever you are.  Supporting your unlikely scenario is making it harder for everyone else to do their job.  To be entirely blunt, supporting you isn’t worth compromising documentation for everyone else.

It’s time to retire CHM.  It served it’s purpose (I guess), but in 2011 we can do better.  Our support for CHM is holding back the progress of humanity.  We would probably have a permanent settlement on Mars if it weren’t for CHM.  Although Internet access would probably be dial-up speeds on Mars…which means we might need a good solution for offline documentation.

Address Validation using the Google Maps API

I’ve never been a fan of address forms that look like this;

Bad street address form design

From a data entry perspective, it forces users to hit TAB constantly.  Furthermore, users who are less web savvy will transition between the mouse and the keyboard to negotiate these fields.  It’s not a natural way to type an address. 

By contrast, here is a human friendly address form:

Good street address form design

While typing an address into this field, users can stay on the keyboard and simply type the address in a way that feels natural.  The problem [for programmers] is parsing and validating this raw text once the form has been submitted.

Client-side address validation using the Google Maps Geocode API

Converting raw text to a valid address

Overcoming this challenge was part of a recent project.  Thankfully, Google Maps does a great job of validating addresses.  Google has made these validation & geocoding services through an API. 

My plan was to tackle this challenge with the following 3 ingredients:

  1. jQuery
  2. jQuery Validator
  3. Google Maps API

On paper, this appeared relatively straight-forward.  However, in practice I encountered several hurdles.  Below I’ve detailed how I overcame these hurdles.

Note: This is the 2nd version of this blog post.  In my original blog post, I described how to use a server-side proxy service to relay results from Google’s geocode web service.  Thor Mitchell, who is the Google Maps API product manager, posted a comment telling me that the proxy technique is unnecessarily difficult.  In addition, this technique is against Google’s Terms of Service and vulnerable to quota exhaustion.  The technique I describe below is in accordance best practices and the Terms of Service.

Creating the web form

To get started, I’ll create the simple web form shown in the screenshot above:

<form id="MyForm" name="MyForm" action="form.html">
    <div id="map_canvas" style="float: right; height: 200px; width: 400px;"></div>
    <div>
        <label for="Name">Name</label>
        <input id="Name" name="Name" type="text" />
    </div>
    <div>
        <label for="FullAddress">Address</label>
        <textarea id="FullAddress" name="FullAddress" cols="40" rows="5" class="fulladdressvalidator"></textarea>
    </div>
    <div>
        <input id="Submit" name="Submit" value="Submit" type="button" />
    </div>
</form>

Note, the Google Maps API Terms of Service requires that a map be displayed in conjunction with any Google Maps web services utilized.  In other words, you are not allowed to geocode an address using the Google Maps API and not display a Google map.  This is why I’ve included the map_canvas in the code above.

Creating an address jQuery Validator

Next, I’ll create a custom jQuery validator that performs the following tasks:

  1. Geocodes the address using the Google Maps API
  2. Return a true/false value indicating whether the address is valid

This seems simple, but it involves an asynchronous call to the Google Maps service.  This means processing continues after the initial request is made.  Before the result is received the validator needs to return a true/false value indicating whether the address is valid or not.

To overcome this challenge, I added an IsChecking flag using jQuery’s data method.  This flag is set to true when the request is sent to the Google Maps API.  When a form is submitted I’m able to check this flag to see whether the field has truly been validated.

Here is the code for this jQuery full address validator:

// FullAddress jQuery Validator
function FullAddressValidator(value, element, paras) {

    // Convert the value variable into something a bit more descriptive
    var CurrentAddress = value;

    // If the address is blank, then this is for the required validator to deal with.
    if (value.length == 0) {
        return true;
    }

    // If we've already validated this address, then just return the previous result
    if ($(element).data("LastAddressValidated") == CurrentAddress) {
        return $(element).data("IsValid");
    }

    // We have a new address to validate, set the IsChecking flag to true and set the LastAddressValidated to the CurrentAddress
    $(element).data("IsChecking", true);
    $(element).data("LastAddressValidated", CurrentAddress);

    // Google Maps doesn't like line-breaks, remove them 
    CurrentAddress = CurrentAddress.replace(/\n/g, "");

    // Create a new Google geocoder
    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({ 'address': CurrentAddress }, function (results, status) {

        // The code below only gets run after a successful Google service call has completed.  
        // Because this is an asynchronous call, the validator has already returned a 'true' result
        // to supress an error message and then cancelled the form submission.  The code below 
        // needs to fetch the true validation from the Google service and then re-execute the 
        // jQuery form validator to display the error message.  Futhermore, if the form was 
        // being submitted, the code below needs to resume that submit.

        // Google reported a valid geocoded address
        if (status == google.maps.GeocoderStatus.OK) {

            // Get the formatted Google result
            var address = results[0].formatted_address;

            // Count the commas in the fomatted address.   
            // This doesn't look great, but it helps us understand how specific the geocoded address
            // is.  For example, "CA" will geocde to "California, USA".  
            numCommas = address.match(/,/g).length;

            // A full street address will have at least 3 commas.  Alternate techniques involve 
            // fetching the address_components returned by Google Maps.  That code looks even more ugly.
            if (numCommas >= 3) {

                // Replace the first comma found with a line-break
                address = address.replace(/, /, "\n");

                // Remove USA from the address (remove this, if this is important to you)
                address = address.replace(/, USA$/, "");

                // Check for the map_canvas, if it exists then position the Google Map
                if ($("#map_canvas").exists()) {
                    $("#map_canvas").show();
                    Map("map_canvas", results[0].geometry.location);
                }

                // Set the textarea value to the geocoded address
                $(element).val(address);

                // Cache this latest result
                $(element).data("LastAddressValidated", address);

                // We have a valid geocoded address
                $(element).data("IsValid", true);
            } else {
                // Google Maps was able to geocode the address, but it wasn't specific
                // enough (not enough commas) to be a valid street address.
                $(element).data("IsValid", false);
            }

            // Otherwise the address is invalid
        } else {
            $(element).data("IsValid", false);
        }

        // We're no longer in the midst of validating
        $(element).data("IsChecking", false);

        // Get the parent form element for this address field
        var form = $(element).parents('form:first');

        // This code is being run after the validation for this field,
        // if the form was being submitted before this validtor was 
        // called then we need to re-submit the form.
        if ($(element).data("SubmitForm") == true) {
            form.submit();
        } else {
            // Re-validate this property so we can return the result.
            form.validate().element(element);
        }
    });

    // The FullAddress validator always returns 'true' when initially called.
    // The true result will be return later by the geocode function (above)
    return true;
}

// Define a new jQuery Validator method
$.validator.addMethod("fulladdress", FullAddressValidator);

Putting it all together

The steps above describe how to:

  1. Geocode addresses using the Google Maps API
  2. Create a custom jQuery Validator to use Google Maps API for address validation

The final step is to combine all of these pieces into a working form.  Below is the completed client-side code for the form shown above.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Example Form </title>
    <link href="./css/site.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
    <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <div id="main">
            <form id="MyForm" name="MyForm" action="form.html">
                <div id="map_canvas" style="float: right; height: 200px; width: 400px;"></div>
                <div>
                    <label for="Name">
                        Name</label>
                    <input id="Name" name="Name" type="text" />
                </div>
                <div>
                    <label for="FullAddress">
                        Address</label>
                    <textarea id="FullAddress" name="FullAddress" cols="40" rows="5" class="fulladdressvalidator"></textarea>
                </div>
                <div>
                    <input id="Submit" name="Submit" value="Submit" type="button" />
                </div>
            </form>
        </div>
    </div>
    <script type="text/javascript">
        // The following code show execute only after the page is fully loaded
        $(document).ready(function () {
            if ($('#MyForm').exists()) {

                // Enable jQuery Validation for the form
                $("#MyForm").validate({ onkeyup: false });

                // Add validation rules to the FullAddress field
                $("#FullAddress").rules("add", {
                    fulladdress: true,
                    required: true,
                    messages: {
                        fulladdress: "Google cannot locate this address."
                    }
                });

                // This function will be executed when the form is submitted
                function FormSubmit() {
                    $.submitForm = true;
                    if (!$('#MyForm').valid()) {
                        return false;
                    } else {
                        if ($("#FullAddress").data("IsChecking") == true) {
                            $("#FullAddress").data("SubmitForm", true);
                            return false;
                        }

                        alert('Form Valid!  Submit!');
                        // return true;   // Uncomment to submit the form.
                        return false;     // Supress the form submission for test purpose.
                        
                    }
                }

                // Attach the FormSubmit function to the Submit button
                if ($('#Submit').exists()) {
                    $("#Submit").click(FormSubmit);
                }

                // Execute the ForumSubmit function when the form is submitted
                $('#MyForm').submit(FormSubmit);
            }
        });

        // Create a jQuery exists method
        jQuery.fn.exists = function () { return jQuery(this).length > 0; }

        // Position the Google Map
        function Map(elementId, geolocation) {
            var myOptions = {
                zoom: 13,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            }
            var map = new google.maps.Map(document.getElementById(elementId), myOptions);
            map.setCenter(geolocation);
        }

        // FullAddress jQuery Validator
        function FullAddressValidator(value, element, paras) {

            // Convert the value variable into something a bit more descriptive
            var CurrentAddress = value;

            // If the address is blank, then this is for the required validator to deal with.
            if (value.length == 0) {
                return true;
            }

            // If we've already validated this address, then just return the previous result
            if ($(element).data("LastAddressValidated") == CurrentAddress) {
                return $(element).data("IsValid");
            }

            // We have a new address to validate, set the IsChecking flag to true and set the LastAddressValidated to the CurrentAddress
            $(element).data("IsChecking", true);
            $(element).data("LastAddressValidated", CurrentAddress);

            // Google Maps doesn't like line-breaks, remove them 
            CurrentAddress = CurrentAddress.replace(/\n/g, "");

            // Create a new Google geocoder
            var geocoder = new google.maps.Geocoder();
            geocoder.geocode({ 'address': CurrentAddress }, function (results, status) {

                // The code below only gets run after a successful Google service call has completed.  
                // Because this is an asynchronous call, the validator has already returned a 'true' result
                // to supress an error message and then cancelled the form submission.  The code below 
                // needs to fetch the true validation from the Google service and then re-execute the 
                // jQuery form validator to display the error message.  Futhermore, if the form was 
                // being submitted, the code below needs to resume that submit.

                // Google reported a valid geocoded address
                if (status == google.maps.GeocoderStatus.OK) {

                    // Get the formatted Google result
                    var address = results[0].formatted_address;

                    // Count the commas in the fomatted address.   
                    // This doesn't look great, but it helps us understand how specific the geocoded address
                    // is.  For example, "CA" will geocde to "California, USA".  
                    numCommas = address.match(/,/g).length;

                    // A full street address will have at least 3 commas.  Alternate techniques involve 
                    // fetching the address_components returned by Google Maps.  That code looks even more ugly.
                    if (numCommas >= 3) {

                        // Replace the first comma found with a line-break
                        address = address.replace(/, /, "\n");

                        // Remove USA from the address (remove this, if this is important to you)
                        address = address.replace(/, USA$/, "");

                        // Check for the map_canvas, if it exists then position the Google Map
                        if ($("#map_canvas").exists()) {
                            $("#map_canvas").show();
                            Map("map_canvas", results[0].geometry.location);
                        }

                        // Set the textarea value to the geocoded address
                        $(element).val(address);

                        // Cache this latest result
                        $(element).data("LastAddressValidated", address);

                        // We have a valid geocoded address
                        $(element).data("IsValid", true);
                    } else {
                        // Google Maps was able to geocode the address, but it wasn't specific
                        // enough (not enough commas) to be a valid street address.
                        $(element).data("IsValid", false);
                    }

                    // Otherwise the address is invalid
                } else {
                    $(element).data("IsValid", false);
                }

                // We're no longer in the midst of validating
                $(element).data("IsChecking", false);

                // Get the parent form element for this address field
                var form = $(element).parents('form:first');

                // This code is being run after the validation for this field,
                // if the form was being submitted before this validtor was 
                // called then we need to re-submit the form.
                if ($(element).data("SubmitForm") == true) {
                    form.submit();
                } else {
                    // Re-validate this property so we can return the result.
                    form.validate().element(element);
                }
            });

            // The FullAddress validator always returns 'true' when initially called.
            // The true result will be return later by the geocode function (above)
            return true;
        }

        // Define a new jQuery Validator method
        $.validator.addMethod("fulladdress", FullAddressValidator);
    </script>
</body>
</html>

All this work, just so I can do this:

Client-side address validation using the Google Maps Geocode API

At least I didn’t need to create a proxy service.  ;)

Doing client-side address validation with the Google Maps Geocode API

Update:  If you look at the comments down below you’ll see a comment from Thor Mitchell.  Thor is the product manager for the Google Maps API.  His comment informed me that the technique I demonstrate below (using a server-side proxy service to relay geocoded addresses) is needlessly complicated and against the Google Terms of Service.  Consequently, I updated my code and published this code in an new blog post.  I recommend that you use alternate technique to validate street addresses.

I’ve never been a fan of address forms that look like this;

Bad address form design

From a data entry perspective, it forces users to hit TAB constantly.  And users who are less web savvy will transition between the mouse and the keyboard to negotiate these fields.  It’s not a natural way to type an address. 

By contrast, here is a human friendly address form:

Good address form design

While typing an address into this field, users can stay on the keyboard and simply type the address in a way that feels natural.  The problem [for programmers] is parsing and validating this raw text once the form has been submitted.

Client-side address validation using the Google Maps Geocode API

Converting raw text to a valid address

Overcoming this challenge was part of a recent project.  In a past life, I worked with the US Census Bureau TIGER data to validate & geocode addresses.  I was only marginally successfully at this task. 

Thankfully, since that time, Google Maps was created and Google made available an API for validating & geocoding addresses.  My plan was to tackle this challenge with the following 3 ingredients:

  1. jQuery
  2. jQuery Validator
  3. Google Maps Geocoding API

On paper, this appeared relatively straight-forward.  However, in practice I encountered several hurdles.  Below I’ve detailed how I overcame these hurdles.

Fetching a geocoded address from the Google Map API

It’s relatively simple to get a geocoded address from Google’s Map API.  Furthermore, Google does an incredible job of converting seemingly bad input into valid input.  Take, for example, the following address:

1600 Amphitheatre Pkwy

I’ve not specified a city, a state or a zip code.  This address looks invalid.  However, because this address is unique enough, Google is able to resolve this incomplete address into a valid address.  This can be done using the following URL:

http://maps.google.com/maps/api/geocode/json?address=1600%20Amphitheatre%20Parkway&sensor=false

The address parameter contains the value of the possible address.  Google will attempt to resolve this address.  If resolved, a geocoded full address is returned using JSON. Click the link above to see the JSON data and explore the structure of the data.

By the way, the address could also be returned in XML by replacing json, in the URL above, with xml.

Creating a proxy service for the Google Maps API

Fetching the Google geocode URL (shown above) is extremely easy.  As a result, my inclination was to write some Javascript to fetch this URL from the web browser and then use the result to validate (or invalidate) the address.

However, when I executed this client-side Javascript code, I received the following error:

XMLHttpRequest cannot load http://maps.google.com/maps/api/geocode/json?address=1600%20Amphitheatre%20Parkway&sensor=false. Origin http://localhost:52306 is not allowed by Access-Control-Allow-Origin.

After some research I discovered this is not possible due to cross site scripting prohibitions that Google has applied to the Google Maps API.  If you’re interested in more information about these limitations, check out the following links:

To make a long story short, it is not possible to get client-side scripting access to the Google Maps Geocode API and then handoff the result to custom Javscript method .  To overcome this challenge it is necessary to create a proxy service (server-side) that fetches the Google Maps API result on behalf of the client. 

Here are the basic steps this proxy service needs to perform:

  1. Accept a possible address from the client web browser
  2. Pass this address to Google to geocode
  3. Receive the result from Google
  4. Pass the result to the client

There are many different flavors of server-side environments & languages.  Consequently, I’m not going to be able to help everyone.  The language I use most often is C#.  The particular web site I was creating was based on ASP.NET MVC.  Below is the controller I created for this service.  I documented the steps in my code to help anyone who is converting this code to another languages:

~/Controllers/ApiController.cs

using System.Linq;
using System.Web.Mvc;
using System.Xml.Linq;
using MvcApplication.Models;

namespace MvcApplication.Controllers
{
    public class ApiController : Controller
    {
        /// <summary>
        /// The controller for http://myurl/api/geocode
        /// </summary>
        /// <param name="address">The address to be geocoded by Google Maps</param>
        /// <returns></returns>
        public JsonResult Geocode(string address)
        {
            // I hate magic strings, so I created a strongly-typed class to hold the address values
            var geoAddress = new Address();

            // This is the Google Maps API URL that will geocode this address
            var geoxml = XDocument.Load("http://maps.google.com/maps/api/geocode/xml" + "?address=" + address + "&sensor=false");

            if (geoxml.Descendants("result").Any())
            {
                // Use LINQ to XML to extract the geocode values from the returned XML
                var parts = from part in geoxml.Descendants("result").First().Descendants("address_component")
                    select new
                    {
                        LongName = part.Element("long_name").Value,
                        ShortName = part.Element("short_name").Value,
                        TypeName = part.Element("type").Value,
                    };

                // Ensure Google returned at least one result
                if (parts.Count() > 0)
                {
                    // Loop through each item
                    foreach (var part in parts)
                    {
                        switch (part.TypeName)
                        {
                            case "street_number":
                                geoAddress.StreetNumber = part.LongName;
                                break;
                            case "route":
                                geoAddress.StreetName = part.LongName;
                                break;
                            case "locality":
                                geoAddress.City = part.LongName;
                                break;
                            case "administrative_area_level_1":
                                geoAddress.State = part.LongName;
                                geoAddress.StateAbbreviation = part.ShortName;
                                break;
                            case "postal_code":
                                geoAddress.Zip = part.LongName;
                                break;
                        }
                    }
                }
            }

            // Serialize the Address class (created above) to JSON and return to the client
            return Json(geoAddress, JsonRequestBehavior.AllowGet);
        }
    }
}

I’m also using a strongly-typed model to represent the address.  Here is the code:

~/Models/Address.cs

namespace MvcApplication.Models
{
    public class Address
    {
        public string StreetNumber { get; set; }
        public string StreetName { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string StateAbbreviation { get; set; }
        public string Zip { get; set; }
    }
}

At the conclusion of this step, you should be able to make a request to your web server and receive a geocoded JSON address.  Example:

http://localhost/api/geocode?address=1600%20Amphitheatre%20Parkway

{"StreetNumber":"1600","StreetName":"Amphitheatre Pkwy","City":"Mountain View","State":"California","StateAbbreviation":"CA","Zip":"94043"}

Creating the web form

The proxy service (created above) can now relay address validation back & forth between the Google Maps API and the web client.  However, what is missing is the client-side Javascript to make all of this function.

To get started, I’ll create the simple web form shown in the screenshot above:

<form id="MyForm" name="MyForm" action="form.html">
    <div>
        <label for="Name">Name</label>
        <input id="Name" name="Name" type="text" />
    </div>
    <div>
        <label for="FullAddress">Address</label>
        <textarea id="FullAddress" name="FullAddress" cols="40" rows="5" class="fulladdressvalidator"></textarea>
    </div>
    <div>
        <input id="Submit" name="Submit" value="Submit" type="button" />
    </div>
</form>

Creating an address jQuery Validator

Next, I’ll create a custom jQuery validator that performs the following tasks:

  1. Sends the address to my proxy service
  2. Get the result from the proxy service
  3. Return a true/false value indicating whether the input is valid

This seems simple, but it involves an asynchronous call to the proxy service.  This means processing continues after the initial proxy service request is made.  Before the result is received the validator needs to return a true/false value indicating whether the address is valid or not.

To overcome this challenge, I added an IsChecking flag using jQuery’s data method.  This flag is set to true when the request is sent to the proxy service.  When a form is submitted I’m able to check this flag to see whether the field has truly been validated.

Here is the code for this jQuery full address validator:

function FullAddressValidator(value, element, paras) {

    // Convert the value variable into something a bit more descriptive
    var CurrentAddress = value;

    // If the address is blank, then this is for the required validator to deal with.
    if (value.length == 0) {
        return true;
    }

    // If we've already validated this address, then just return the previous result
    if ($(element).data("LastAddressValidated") == CurrentAddress) {
        return $(element).data("IsValid");
    }

    // We have a new address to validate, set the IsChecking flag to true and set the LastAddressValidated to the CurrentAddress
    $(element).data("IsChecking", true);
    $(element).data("LastAddressValidated", CurrentAddress);

    // The URL for my geocode proxy service
    var geocodeUrl = '/api/geocode?address=' + escape(CurrentAddress);

    // Make an asynchronous call to the geocode proxy service.  
    $.ajax({
        url: geocodeUrl,
        type: "GET",
        dataType: 'json',
        success: function (data, textStatus, XMLHttpRequest) {
            // The code below only gets run after a successful proxy service call has completed.  
            // Because this is an asynchronous call, the validator has already returned a 'true' result
            // to supress an error message and then cancelled the form submission.  The code below 
            // needs to fetch the true validation from the proxy service and then re-execute the 
            // jQuery form validator to display the error message.  Futhermore, if the form was 
            // being submitted, the code below needs to resume that submit.

            // If StreetNumber exists, then the address is valid
            if (data.StreetNumber) {
                var address = data.StreetNumber + ' ' + data.StreetName + "\n" + data.City + ', ' + data.StateAbbreviation + ' ' + data.Zip;

                $(element).val(address);
                $(element).data("LastAddressValidated", address);
                $(element).data("IsValid", true);
                // Otherwise the address is invalid
            } else {
                $(element).data("IsValid", false);
            }

            // We're no longer in the midst of validating
            $(element).data("IsChecking", false);

            // Get the parent form element for this address field
            var form = $(element).parents('form:first');

            // This code is being run after the validation for this field,
            // if the form was being submitted before this validtor was 
            // called then we need to re-submit the form.
            if ($(element).data("SubmitForm") == true) {
                form.submit();
            } else {
                // Re-validate this property so we can return the result.
                form.validate().element(element);
            }
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            $(element).data("IsValid", false);
            $(element).data("IsChecking", false);
            $(element).data("SubmitForm", false);
        }
    });

    return true;
}

$.validator.addMethod("fulladdress", FullAddressValidator);

Putting it all together

The steps above describe how to:

  1. Create a proxy service to the Google Maps Geocode API
  2. Create a custom jQuery Validator to use this proxy service for address validation

The final step is to combine all of these pieces into a working form.  Below is the completed client-side code for the form shown above.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Example Form </title>
    <link href="./css/site.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
    <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <div id="main">
            <form id="MyForm" name="MyForm" action="form.html">
            <div>
                <label for="Name">
                    Name</label>
                <input id="Name" name="Name" type="text" />
            </div>
            <div>
                <label for="FullAddress">
                    Address</label>
                <textarea id="FullAddress" name="FullAddress" cols="40" rows="5" class="fulladdressvalidator"></textarea>
            </div>
            <div>
                <input id="Submit" name="Submit" value="Submit" type="button" />
            </div>
            </form>
        </div>
    </div>
    <script type="text/javascript">
        // The following code show execute only after the page is fully loaded
        $(document).ready(function () {

            if ($('#MyForm').exists()) {

                // Enable jQuery Validation for the form
                $("#MyForm").validate();

                // Add validation rules to the FullAddress field
                $("#FullAddress").rules("add", {
                    fulladdress: true,
                    required: true,
                    messages: {
                        fulladdress: "Google cannot locate this address."
                    }
                });

                // This function will be executed when the form is submitted
                function FormSubmit() {
                    $.submitForm = true;
                    if (!$('#MyForm').valid()) {
                        return false;
                    } else {
                        if ($("#FullAddress").data("IsChecking") == true) {
                            $("#FullAddress").data("SubmitForm", true);
                            return false;
                        }

                        return false;
                    }
                }

                // Attach the FormSubmit function to the Submit button
                if ($('#Submit').exists()) {
                    $("#Submit").click(FormSubmit);
                }

                // Execute the ForumSubmit function when the form is submitted
                $('#MyForm').submit(FormSubmit);
            }
        });

        // Create a jQuery exists method
        jQuery.fn.exists = function () { return jQuery(this).length > 0; }

        // FullAddress jQuery Validator
        function FullAddressValidator(value, element, paras) {

            // Convert the value variable into something a bit more descriptive
            var CurrentAddress = value;

            // If the address is blank, then this is for the required validator to deal with.
            if (value.length == 0) {
                return true;
            }

            // If we've already validated this address, then just return the previous result
            if ($(element).data("LastAddressValidated") == CurrentAddress) {
                return $(element).data("IsValid");
            }

            // We have a new address to validate, set the IsChecking flag to true and set the LastAddressValidated to the CurrentAddress
            $(element).data("IsChecking", true);
            $(element).data("LastAddressValidated", CurrentAddress);

            // The URL for my geocode proxy service
            var geocodeUrl = '/api/geocode?address=' + escape(CurrentAddress);

            // Make an asynchronous call to the geocode proxy service.  
            $.ajax({
                url: geocodeUrl,
                type: "GET",
                dataType: 'json',
                success: function (data, textStatus, XMLHttpRequest) {
                    // The code below only gets run after a successful proxy service call has completed.  
                    // Because this is an asynchronous call, the validator has already returned a 'true' result
                    // to supress an error message and then cancelled the form submission.  The code below 
                    // needs to fetch the true validation from the proxy service and then re-execute the 
                    // jQuery form validator to display the error message.  Futhermore, if the form was 
                    // being submitted, the code below needs to resume that submit.

                    // If StreetNumber exists, then the address is valid
                    if (data.StreetNumber) {
                        var address = data.StreetNumber + ' ' + data.StreetName + "\n" + data.City + ', ' + data.StateAbbreviation + ' ' + data.Zip;

                        $(element).val(address);
                        $(element).data("LastAddressValidated", address);
                        $(element).data("IsValid", true);
                        // Otherwise the address is invalid
                    } else {
                        $(element).data("IsValid", false);
                    }

                    // We're no longer in the midst of validating
                    $(element).data("IsChecking", false);

                    // Get the parent form element for this address field
                    var form = $(element).parents('form:first');

                    // This code is being run after the validation for this field,
                    // if the form was being submitted before this validtor was 
                    // called then we need to re-submit the form.
                    if ($(element).data("SubmitForm") == true) {
                        form.submit();
                    } else {
                        // Re-validate this property so we can return the result.
                        form.validate().element(element);
                    }
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    $(element).data("IsValid", false);
                    $(element).data("IsChecking", false);
                    $(element).data("SubmitForm", false);
                }
            });

            return true;
        }

        $.validator.addMethod("fulladdress", FullAddressValidator);
    </script>
</body>
</html>

All this work, just so I can do this:

Client-side address validation using the Google Maps Geocode API

Damn!

jQuery does not have an ‘exists’ method

As developers, we’ve all solved problems only to forget the solution (and the problem) many months (or years) later.  We’re then forced to rediscover the solution to the old challenge.

I recently encountered this phenomenon while attempting to use jQuery and the ‘exists’ method.  I had tons of old Javascript / jQuery code that was using this method.  Consequently, I couldn’t understand why this code wasn’t working in a new project:

if ($('#MyForm').exists()) {
    // Do something
}

This very simple code resulted in error messages in all my web browsers:

Chrome
Uncaught TypeError: Object #<an Object> has no method ‘exists’

Firefox
Error: $("#MyForm").exists is not a function

IE 9 BETA:
SCRIPT438: Object doesn't support this property or method
(nice descriptive error message IE)

Safari
TypeError: Result of expression '$('#MyForm').exists' [undefined] is not a function.

Here is why I was getting this error:

Jquery doesn’t not have an ‘exists’ method.  The only reason this code worked in my old projects is because I had created my own ‘exists’ method.  The code for doing this is extremely simple:

// Create a jQuery exists method
jQuery.fn.exists = function () { return jQuery(this).length > 0; }

As long as this code executes before attempting to use the ‘exists’ method, then everything will work great.  Now I’ve written this blog post and documented this issue. 

Future Gabe, you’re welcome!

Fixing Entity Framework Model-First SQL Express Errors

Microsoft’s Entity Framework 4.0 includes the ability to generate databases from the models being defined.  This feature is also sometimes known as forward-mapping (Telerik’s OpenAccess ORM has supported forward-mapping for awhile).  This enables developers to start with models, instead of starting with the database.

If you’re interested in how to use this feature, Gil Fink wrote a detailed blog post that describes how to use the model first feature in Entity Framework 4.0.  After following these instructions, Entity Framework will generate a SQL script that must be manually executed to create the database and its tables.

The EDMX SQL script generated by Entity Framework

Simply execute this SQL script and the database will be created.  Easy right?

And that’s when everything went downhill…

I was running this little test project from my home computer.  On this computer, I’m using Visual Studio Web Developer Express and SQL Express.  I’m also using attached databases (AttachDbFilename).

These are free tools that Microsoft is offering to small & casual developers.  These tools are part of an attempt by Microsoft to entice the PHP audience.  Hopefully these tools will help halt the severe butt kicking IIS is getting in adoption:

Apache continues to hand IIS its rear in adoption

[ Chart courtesy of Netcraft ]

There are tons of online examples showing how to use these free tools to accomplish simple ‘Hello World’ style examples.  However, I am continually astounded at how quickly these tools fall short in a real projects.  Then you’re left researching errors and reading how to guides that assume you’re using the Visual Studio Ultimate Gold Edition.  I guess it’s back to PHP… 

But don’t leave yet, I can help you past this issue.

Step 1:  Running the Entity Framework SQL Script

As mentioned, I’m using Visual Studio Web Developer Express.  I don’t have SQL Server Management Studio installed (there is an express version available).  And even if I did, it’s a giant pain (because of file locks) to add, and then remove, an attached database file using SQL Server Management Studio. 

Here is what I did to execute this generated SQL script inside Visual Studio Web Developer Express:

1.  Double-click the MDF database file in App_Data in the Solution Explorer

The Database.mdf file in Visual Studio's Solution Explorer

This should open the Database Explorer.

2.  Right-click the MDF and click New Query

Executing a New Query using Visual Studio's Database Explorer

This query editor is not meant for executing a large SQL scripts.  Consequently, Visual Studio will throw up a barrage of user-friendly dialogs that have nothing to do with our intent.  Just keep clicking close.

The Add Table wizard for Visual Studio's Query Builder

3.  Copy and paste the Entity Framework SQL script into the “SELECT FROM” textbox:

Pasting the Entity Framework EDMX generate script into Visual Studio's Query Builder

4.  Click the Execute Query icon <- but it won’t work

The Execute Query button in Visual Studio's Query Builder

Tons and tons of errors

Visual Studio will attempt to execute the SQL script, however there are lots of problems with the SQL script that will prevent the SQL script from being executed successfully.

After clicking the Execute Query button, Visual Studio will display a warning: The Set SQL construct or statement is not supported 

Query Definitions Differ: The Set SQL construct or statement is not supported.

This warning is displayed because Visual Studio is attempting to parse and display a graphical representation of this query.  Because of the complexity of this SQL script, Visual Studio is choking in the attempt. 

Click Continue to say whatever, and continue on. 

Then comes a barrage of other errors:

Error Message: Incorrect syntax near 'GO'.  Database 'Demerits' does not exist.  Make sure the name is entered correctly.

Generated from EDMX file.  Error Message: Incorrect syntax near 'GO'

Step 2: Fixing the Entity Framework SQL script errors

There are 2 core issues with the Entity Framework generated SQL script.

  1. When using attached database files it is unnecessary to tell the database server which database to use.  Consequently, the USE [Demerits] command is invalid.

  2. The GO command is not valid SQL.  This is a made up command that is used by SQL Server Management Studio.  Visual Studio’s query builder (and most other SQL clients) do not recognize this command.  All GO references need removed.

To fix these errors:

1.  Remove the USE [Database] command from the SQL script.

2.  Do a search & replace (you can’t do this using the Query Builder, use Notepad instead) and replace all the GO keywords with nothing.  This will remove all the GO keywords.

3.  Click the Execute SQL button to re-run the SQL script

Finally!  Query has executed successfully.

Turning an internal wiki into public documentation

My co-workers can attest to my love of wiki's.  Specifically, my desire to use wiki's for product documentation.  Wiki's have set the bar for easy to edit for years.  By creating easily editable online documents, Wikipedia managed to surpass respected encyclopedias.

All of this is based on an simple premise: easily editable content is accurate and up to date. Hard to edit content is inaccurate and out of date.  When content can be easily edited, mistakes (or inaccuracies) are shallow and quickly fixed.  The benefits of continuous engagement outweigh the risk of occasional sabotage.

Too much of a good thing

On this subject, I spotted an article entitled The Sad Evolution of Wikis on Hackernews.  A few of the quotes from this article caught my attention.  Specifically this quote:

At NITI's peak, we had more than 30 internal developers, plus all our technical support staff, using [the wiki] daily. All our technical documentation was in there, and it was easy to read, easy to find, easy to link to, and (perhaps most importantly) easy to write. - (more)

This is music to my ears.  In my view, product documentation should become a place where information is given and received...constantly.  Conversations should move from email into documentation.  Furthermore, the documentation system should actively encourage casual edits from all employees.  Creating and sharing solutions through documentation should become an enjoyable experience.  Everyone wins!

It's the most fun I've ever had doing documentation. - (more)

The problem, as this article points out, is that some information isn't fit for public consumption.  Which means organizations need to filter their wiki comments.  This censorship diminishes the value derived internally from the wiki...which diminishes adoption.   Alternately, organizations could create 2 distinct wiki's (private & public).  However, this merely causes the public wiki to become stale:

Internally, we had some really great documentation. But the documentation of our open source stuff was mostly garbage, because it was mostly on the internal-only site and nobody internally visited the public one. - (more)

This is a problem.  To guarantee adoption the resource needs to be rewarding not only to the consumer, but also the contributor.

What is the simplest thing that could possibly work?

Perhaps it is overly simplistic, but I would simply propose a special wiki tag.

[internal]
This text can only be read by employees.
[/internal]

This seems fairly simple.  Employees are then free to add internal commentary that would not be rendered on the public wiki.  Maybe this is too simple a solution?

In any event, I hate to see small challenges like this kill the adoption of great tools and great ideas.  When faced with the choice between governance and adoption, I will chose adoption every single time.

Get your current Twitter status in 3 lines of .NET code

Below is the code needed to fetch my current (most recent) Twitter status using .NET 3.5+ and LINQ. 

First you’ll need the following class references:

using System;
using System.Linq;
using System.Xml.Linq;

And here is the code:

var twitterXML = XDocument.Load("http://twitter.com/statuses/user_timeline/gabesumner.rss?count=1");
var query = from t in twitterXML.Descendants("item")
            select t;
CurrentStatus.Text = query.First().Element("title").Value;

Insanely easy.  It barely merits a blog post, but I was delighted at how easy this was.