Sunday, October 17, 2010

jQuery UI Introduction Slides

Yesterday I presented at the jQuery Boston Conference 2010. I’ve never attended a jQuery Conference before, so this was an awesome experience all around.
Each year, there is a jQuery UI Introduction talk and this year I was privileged to present the material.
  • Slides – The web presentation is best viewed with Google Chrome
In order to spice up the content some, I included some bacon demos using the draggable, droppable, and resize interactions. I hope to add more bacon slides in the near future ;)

If you were able to attend my talk, I would appreciate it if you could rate my presentation at SpeakerRate.

My slides are based on the Ruby Slide Show Gem tool and uses a modified version of the HTML5 Rocks template that was used to generate the HTML5 Rocks Presentation.

I use embedded jsFiddle in my examples to view and execute all of my code example. You can launch the full jsFiddle from within the slides to view, edit, run, and share the code.

I want to thank the jQuery Team for allowing me to speak this year and a special shout out to Leah Silber, Ralph Whitbeck, and the others that put tons of hours into putting this event together.

Wednesday, September 15, 2010

Can a JavaScript file tell what URL requested itself?

I've been working on a project recently where I'm appending the current date to the end of script files so that they won't be cached.

I'm surrounding the code with lots of Unit Tests and I was trying to figure out the best way I could test that the script was actually loaded with the appended timestamp.

How could a JavaScript file tell what URL requested itself? Could the script know what URL make itself load?

Initially I was thinking location.href, but that ends up being the URL of the page the script was loaded on, not the script URL itself. Someone else suggested document.referrer, but that turns out to be the URL that referred the current page.

After a little time passed and several tweets passed by my stream, the correct answer flowed by...

Thanks to @cowboy @InfinitiesLoop @ashitvora for the answer and it is sooooo cleaver!

var scripts = document.getElementsByTagName( "script" );
var lastScript = scripts[scripts.length - 1];
alert( lastScript.src );

Since the script files execute in order, if you grab the scripts on the document while you are executing, then the last script listed should be the one you are currently in!

This question has been asked on stackoverflow as well ;)

As it turns out, the above technique only works for pages that have the <script> tag manually declared. If you dynamically inject your <script> tags programmatically, then the above technique doesn't work correctly. For my scenario to work, I had to tweak the solution a little bit.

Fortunately, I knew what file I was in, so I could do some partial matching to grab the script tag on the page to look at the src.

alert( $("script[src*=sourcefile1.js]").attr("src") );

The above code depends on jQuery, but I already have it loaded by this point, so it works out fine ;)

Thanks to everyone who helped me along in this fun and interesting problem.

Wednesday, August 25, 2010

I work for appendTo - The jQuery Company

appendToLogoAs you may or may not seen on Twitter the last 3 weeks or so, I accepted an offer to work with appendTo, LLC - The Company Dedicated to jQuery where I will be doing JavaScript Consulting as well as other activities.

I first met the CEO (Mike Hostetler) and President (Jonathan Sharp) of appendTo back in January while I was at the launch of jQuery 1.4 in Washington DC.

After the unfortunate turn of events at my last job (raided by the FBI & IRS), I was forced to look for a new job opportunity.

One of the first places I thought of working for was appendTo. After several weeks of talks everything worked out and now I am an official employee!

Since I’ve started I’ve done some writing, did an architectural review of a client’s code, and most recently I’ve been diving deep in developing a JavaScript and jQuery library.

I am really excited about this opportunity. The group of people that I work alongside are top notch! I find it impressive that appendTo has 5 official jQuery Team members working for them.

It looks like I’ll be able to attend the Boston jQuery Conference this coming October 16-17, 2010. I hope to see you there! Sign up while tickets last.

By the way, thank all of you who sent me job leads, contacting me about working with you, or tweeted on my behalf while I was looking for a job. I appreciate all your efforts!

Thursday, July 08, 2010

@ElijahManor is Looking for a jQuery &| ASP.NET MVC Job

If you haven’t heard already, I am looking for a new job opportunity… and yes, I realize the title of this blog is speaking in the 3rd person ;)

I am primarily looking for a full-time position where I can either work in a Nashville office or work remotely from Nashville.

I am a Microsoft MVP of ASP.NET and a member of the ASPInsiders group.

I specialize in developing with jQuery and ASP.NET MVC. I tend to focus on the UI only (everything up to the web service call to the middle tier).

I enjoy blogging, writing technical articles, speaking, and playing with the latest and greatest web development tools and libraries.

You can find my resume on my LinkedIn profile.

I look forward to hearing more about your company & the positions that you have available.

Please contact me either through LinkedIn, Twitter (@elijahmanor), or my Contact page.

Thursday, July 01, 2010

jQuery jqGrid Plugin: Add, Edit, Delete with ASP.NET MVC

 

Introduction

There are a lot of articles out there that show you how to integrate with the jQuery jqGrid Plugin from a listing, paging, sorting approach, but I haven’t seen many that show the integration with the add/edit/delete features that jqGrid offers.

It turns out that it isn’t very difficult to do, but it took quite a bit of digging in the jqGrid API documentation for me to find all the things I needed.

The following article will show how to customize the add/edit/delete modal experience inside of the jqGrid with ASP.NET MVC.

Contact ViewModel

First, you start off with your ViewModel. In this case the following is really bare bones. I haven’t annotated the properties with metadata because I actually do that manually in the jqGrid columns. That isn’t optimal, but it would be nice to have these automatically mapped. That sounds like another blog post ;)

public class ContactViewModel
{
    public System.Guid ContactId { get; set; }

    public string Name { get; set; }

    public string Email { get; set; }

    public string PhoneNumber { get; set; }

    public DateTime DateOfBirth { get; set; }

    public bool IsMarried { get; set; }
}

Contact View

The following code setups up the jqGrid to support add, edit, and delete.

The first function you’ll see is a custom validator that checks to see if the phone number has a length of 14. Yes, it isn’t bullet-proof validation by any stretch of the imagination, but its more of an example of what can be done.

Next you’ll see the updateDialog object literal defining the look and behavior of the add, edit, and delete dialog windows. The main property to define is the URL where the AJAX requests will post the data. You can also control whether the dialog closes immediately, if it’s a modal dialog, etc…

The next important thing to notice is the “key: true” property of the ContactId column. If you don’t set this property then the POST for the delete command  only send the relative ID that jqGrid generates, not the ContactId that you need. So, this is important ;)

Note: You’ll see some code below setting global properties for the jqGrid such as the title of the dialogs, buttons, etc. If you don’t do this then you’ll get generic titles. I figured these customizations made the user experience a little nicer, but things will work just find without them.

function isValidPhone(value, name) {
    console.log('isValidPhone');
    var errorMessage = name + ': Invalid Format';
    var success = value.length === 14;
    return [success, success ? '' : errorMessage];
}  

$(document).ready(function () {
    var updateDialog = {
        url: '<%= Url.Action("Update", "Contact") %>'
        , closeAfterAdd: true
        , closeAfterEdit: true
        , afterShowForm: function (formId) {
            $("#PhoneNumber").mask("(999) 999-9999");
            $("#DateOfBirth").datepicker();
        }
        , afterclickPgButtons: function (whichbutton, formid, rowid) {
            $("#PhoneNumber").mask("(999) 999-9999");
        }
        , modal: true
        , width: "400"
    };

    $.jgrid.nav.addtext = "Add";
    $.jgrid.nav.edittext = "Edit";
    $.jgrid.nav.deltext = "Delete";
    $.jgrid.edit.addCaption = "Add Contact";
    $.jgrid.edit.editCaption = "Edit Contact";
    $.jgrid.del.caption = "Delete Contact";
    $.jgrid.del.msg = "Delete selected Contact?";

    $("#list").jqGrid({
        url: '<%= Url.Action("List", "Contact") %>',
        datatype: 'json',
        mtype: 'GET',
        colNames: ['ContactId', 'Name', 'Date of Birth', 'E-mail', 'Phone Number', 'Married'],
        colModel: [
            { name: 'ContactId', index: 'ContactId', width: 40, align: 'left', key: true, editable: true, editrules: { edithidden: false }, hidedlg: true, hidden: true },
            { name: 'Name', index: 'Name', width: 300, align: 'left', editable: true, edittype: 'text', editrules: { required: true }, formoptions: { elmsuffix: ' *'} },
            { name: 'DateOfBirth', index: 'DateOfBirth', width: 200, align: 'left', formatter: 'date', datefmt: 'm/d/Y', editable: true, edittype: 'text', editrules: { required: true, date: true }, formoptions: { elmsuffix: ' *'} },
            { name: 'Email', index: 'Email', width: 200, align: 'left', formatter: 'mail', editable: true, edittype: 'text', editrules: { required: true, email: true }, formoptions: { elmsuffix: ' *'} },
            { name: 'PhoneNumber', index: 'PhoneNumber', width: 200, align: 'left', editable: true, edittype: 'text', editrules: { required: true, custom: true, custom_func: isValidPhone }, formoptions: { elmsuffix: ' *'} },
            { name: 'IsMarried', index: 'IsMarried', width: 200, align: 'left', editable: true, edittype: 'checkbox', editoptions: { value: "True:False" }, editrules: { required: true}}],
        pager: $('#listPager'),
        rowNum: 1000,
        rowList: [1000],
        sortname: 'ContactId',
        sortorder: "desc",
        viewrecords: true,
        imgpath: '/Content/Themes/Redmond/Images',
        caption: 'Contact List',
        autowidth: true,
        ondblClickRow: function (rowid, iRow, iCol, e) {
            $("#list").editGridRow(rowid, prmGridDialog);
        }
    }).navGrid('#listPager',
        {
            edit: true, add: true, del: true, search: false, refresh: true
        },
        updateDialog,
        updateDialog,
        updateDialog
    );
}); 

Contact Controller Update Action

The add/update/delete feature takes one URL where you can change the logic based on the operation type. The MVC Modal Binder will map the fields into your ViewModel in most cases. The exception is the “id” that is passed on the delete operation, but there is a way to get around that later in this post ;)

public ActionResult Update(ContactViewModel viewModel, FormCollection formCollection)
{
    var operation = formCollection["oper"];
    if (operation.Equals("add") || operation.Equals("edit"))
    {
        repository.SaveOrUpdate(new ContactViewModel
        {
            ContactId = viewModel.ContactId,
            DateOfBirth = viewModel.DateOfBirth,
            Email = viewModel.Email,
            IsMarried = viewModel.IsMarried,
            Name = viewModel.Name,
            PhoneNumber = viewModel.PhoneNumber
        });
    }
    else if (operation.Equals("del"))
    {
        repository.Delete(new ContactViewModel
        {
            ContactId = new Guid(formCollection["id"])
        });
    }

    return Content(repository.HasErrors.ToString().ToLower()); 
}

What About Using Complex Keys?

Instead of having ContactId (“key: true”) as your key to delete, you might have a more complex key to identify which item to delete. As it turns out, you can bind to the onclickSubmit event of the add/edit/delete dialog and change what data is POST’ed to the controller.

A nice side effect of this is that you name your property such that the MVC Modal Binder works.

Updated Dialog Object Literal

var updateDialog = {
    url: '<%= Url.Action("Update", "Contact") %>'
    , closeAfterAdd: true
    , closeAfterEdit: true
    , afterShowForm: function (formId) {
        $("#PhoneNumber").mask("(999) 999-9999");
        $("#DateOfBirth").datepicker();
    }
    , afterclickPgButtons: function (whichbutton, formid, rowid) {
        $("#PhoneNumber").mask("(999) 999-9999");
    }
    , modal: true
    , onclickSubmit: function (params) {
        var ajaxData = {};

        var list = $("#list");
        var selectedRow = list.getGridParam("selrow");
        rowData = list.getRowData(selectedRow);
        ajaxData = { ContactId: rowData.ContactId };

        return ajaxData;
    }
    , width: "400"
};

Updated Contact Controller Update Action

public ActionResult Update(ContactViewModel viewModel, FormCollection formCollection)
{
    var operation = formCollection["oper"];
    if (operation.Equals("add") || operation.Equals("edit"))
    {
        repository.SaveOrUpdate(new ContactViewModel
        {
            ContactId = viewModel.ContactId,
            DateOfBirth = viewModel.DateOfBirth,
            Email = viewModel.Email,
            IsMarried = viewModel.IsMarried,
            Name = viewModel.Name,
            PhoneNumber = viewModel.PhoneNumber
        });
    }
    else if (operation.Equals("del"))
    {
        repository.Delete(new ContactViewModel
        {
            ContactId = viewModel.ContactId
        });
    }

    return Content(repository.HasErrors.ToString().ToLower()); 
}

Conclusion

I hope you found the above article of some use in your everyday coding. The jqGrid also provides an inline editing feature similar to what you might experience in an Excel grid. You might look into that if you are interested.

Please give me your feedback. Thanks!

Download Source Code



Tuesday, June 29, 2010

Filling Address Fields using HTML5 Geolocation and jQuery

I saw an interesting tweet by Remy Sharp (@rem) the other day that sparked my interest.

47b091843184dc342cdd0f26c4943ed0

So, I proceeded to research the Geolocation API and look up various web services that allowed me to utilize JSONP requests from jQuery.

Here is some pseudocode to describe what I came up with…

if (Your browser supports the GeoLocation API) { 
  if (Geolocation data contains address) { 
    Use address to populate form (Firefox) 
  } else { 
    Use Long/Lat to get address from web service & populate form (Chrome) 
  }
} else {
  Use IP address to get address from web service & populate form (IE)
}

 

cooltext439925164

Note: You may notice depending on the browser not all of the fields are being filled out. This is because of the web services that I am utilizing are lacking one or more fields or vary depending on your location. If I find a better web service then I’ll update the script.

Monday, June 21, 2010

BDD-Style QUnit Testing ASP.NET MVC’s jQuery Validation

Client-Side Unit Testing



The goal of this blog post is to show how you can utilize some helpful techniques to easily Unit Test your Web Application. In this post I will focus on Unit Testing the Client-Side validation rules that ASP.NET MVC generates. You can apply the following techniques to pretty much any scenario, but since this is something I do, I thought I’d share.

Our sample applications is a Contact Manager. At this point we only have a toolbar with a “New Contact” button. When the button is clicked a “New Contact” dialog will appear with several input fields and a “Save” and “Cancel” button. All of the fields are required, so if the user clicks the “Save” button client-validation should verify that all fields have a value.

ASP.NET MVC Contact ViewModel


First lets take a look at our ViewModel which will drive the rules of our client-side validation.

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace jQueryUnitTestingFormValidation.Models
{
    public class ContactViewModel
    {
        [HiddenInput(DisplayValue = false)]
        public System.Guid Id { get; set; }

        [Required(ErrorMessage = "Name Required")]
        [DisplayName("Name")]
        [StringLength(50, ErrorMessage = "Name must be less than or equal to 50 characters")]
        public string Name { get; set; }

        [Required(ErrorMessage = "Email Required")]
        [DisplayName("E-mail")]
        [StringLength(50, ErrorMessage = "Email must be less than or equal to 50 characters")]
        [DataType(DataType.EmailAddress)]
        [RegularExpression(@"^([a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\s*;?\s*)+$", ErrorMessage = "Email must be valid")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Phone Number Required")]
        [DisplayName("Phone Number")]
        [StringLength(50, ErrorMessage = "Phone must be less than or equal to 50 characters")]
        public string PhoneNumber { get; set; }

        [Required(ErrorMessage = "Date of Birth Required")]
        [DisplayName("Date of Birth")]
        public DateTime? DateOfBirth { get; set; }

        [Required(ErrorMessage = "Required")]
        [DisplayName("Is Married")]
        public bool IsMarried { get; set; }
    }
}

ASP.NET MVC Contact View


Our View is pretty simple. I decided for this sample to not modify the MasterPages that the Templated Helpers use. You’ll see that I’m call the LabelFor, EditorFor, and ValidationMessageFor and organizing them as I wish. If you are interested in a cleaner way to do this you can check out a previous blog entry I wrote entitled Opinionated ASP.NET MVC 2 Template Helpers.

<div id="createDialog" title="New Contact" style="display: none;">
    <% Html.EnableClientValidation(); %> 
    <% using (Html.BeginForm("Create", "Contact", FormMethod.Post, new {@id = "createPost"})) { %>
        <dl>
            <dt><%= Html.LabelFor(m => m.Name) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.Name) %>
                <%= Html.ValidationMessageFor(m => m.Name) %>
            </dd>
            <dt><%= Html.LabelFor(m => m.Email) %></dt>            
            <dd>
                <%= Html.EditorFor(m => m.Email) %>
                <%= Html.ValidationMessageFor(m => m.Email)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.PhoneNumber) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.PhoneNumber) %>
                <%= Html.ValidationMessageFor(m => m.PhoneNumber)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.DateOfBirth) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.DateOfBirth) %>
                <%= Html.ValidationMessageFor(m => m.DateOfBirth)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.IsMarried) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.IsMarried) %>
                <%= Html.ValidationMessageFor(m => m.IsMarried)%>
            </dd>            
        </dl>
    <% } %>    
</div>

JavaScript Contact Revealing Module


The Contact Revealing Module contains the logic to initialize the button and dialog events, post the form to the Controller, etc…

Note: I am utilizing the Revealing Module Pattern for those of you who might not be aware of it. It is very helpful in splitting up your JavaScript code into testable and reusable modules.

var contactCreateModule = (function () {
    var public = {},
        dialogWidth = 800;

    public.createDialog;
    public.createPost;

    public.init = function () {
        public.createDialog = $("#createDialog");
        public.createPost = $("#createPost");

        public.initEventHandlers();
    };

    public.initEventHandlers = function () {
        public.initToolbar();

        public.initDialog();
    };

    public.initToolbar = function () {
        $("#toolbar button").button();

        $("#createContact").click(function () {
            public.displayCreate();
        });
    };

    public.initDialog = function () {
        $(".datePicker").datepicker();

        public.createDialog.dialog({
            autoOpen: false
            , width: dialogWidth
            , modal: true
            , open: validationModule.clearValidationMessages
            , buttons: {
                "Cancel": function () {
                    public.createDialog.dialog("close");
                },
                "Save": function () {
                    if (public.createPost.valid()) {
                        public.createDialog.dialog("close");
                        public.postContact();
                    }
                }
            }
        });
    };

    public.displayCreate = function () {
        public.createDialog
            .dialog("open")
            .find("input:first")
            .focus();
    };

    public.postContact = function (callback) {
        $.ajaxSettings.traditional = true;
        $.ajax({
            url: public.createPost.attr("action"),
            data: public.createPost.serialize(),
            type: "POST",
            success: function (data, textStatus, xhr) {
                public.postContactSuccess(data, textStatus, xhr);
                callback(data && data.Success);
            },
            error: public.postContactError
        });
    };

    public.postContactSuccess = function (data, textStatus, xhr) {
        if (data && data.Success) {
            notificationModule.displayMessage(true, "Your contact has been created!");
        } else {
            notificationModule.displayMessages(data.Success, data.Messages);
        }
    };

    public.postContactError = function (xhr, textStatus, error) {
        var errorMessage = exception ? exception : xhr.statusText;
        notificationModule.displayMessage(false, "There was an error creating your contact: " + errorMessage);
    };

    return public;
} ());

Classic QUnit Style Tests


I initially started this blog post using standard QUnit syntax, but there was something about it that didn’t stand well with me. In particular, I didn’t like how I had a bunch of asserts all lumped together. Later in this article I switch out the classic QUnit-Style with a BDD-Style syntax.

The following are some screenshots from the Unit Tests…


The following is a slightly zoomed in view of the above image. You can see that all the asserts for one particular test are hidden beneath it. You can expand &| collapse the test to see each individual assert.


You can view the unit tests below that generated the above screen shots.
Since I don’t want the $.ajax call to actually occur in my Unit Tests, I swap out the default functionality with a stub function instead in the module setup (which is called before each test). In the module teardown (which is called after each test) I restore the default functionality in case any future test needs it.

var contactWasPosted = false;
module("Contact: Create", {
    setup: function () {
        contactWasPosted = false;

        contactCreateModule.postContactBackup = contactCreateModule.postContact;
        contactCreateModule.postContact = function (callback) {
            contactWasPosted = true;
        };
    },
    teardown: function () {
        contactCreateModule.postContact = contactCreateModule.postContact;
        contactCreateModule.createDialog.dialog('close');
    }
});

test("When New Contact Button Clicked", function () {
    //Arrange

    //Act
    $("#createContact").click();

    //Assert
    ok($("#createDialog:visible").length, "Dialog Should Display");
    ok($("#Name_validationMessage:not(:visible)").length, "Name Validation Should Not Display");
    ok($("#Email_validationMessage:not(:visible)").length, "Email Validation Should Not Display");
    ok($("#PhoneNumber_validationMessage:not(:visible)").length, "PhoneNumber Validation Should Not Display");
    ok($("#DateOfBirth_validationMessage:not(:visible)").length, "DateOfBirth Validation Should Not Display");
    ok($("#IsMarried_validationMessage:not(:visible)").length, "IsMarried Validation Should Not Display");
});

test("When Click Save On an Empty Form", function () {
    //Arrange
    $("#createContact").click();

    //Act
    $(".ui-button-text:contains('Save')").parent().click();

    //Assert
    ok($("#Name_validationMessage:visible").length, "Name Validation Should Display");
    ok($("#Email_validationMessage:visible").length, "Email Validation Should Display");
    ok($("#PhoneNumber_validationMessage:visible").length, "PhoneNumber Validation Should Display");
    ok($("#DateOfBirth_validationMessage:visible").length, "DateOfBirth Validation Should Display");
    ok($("#IsMarried_validationMessage:visible").length, "IsMarried Validation Should Display");
    ok($("#createDialog:visible").length, "Dialog Should Remain Displayed");
});

test("When Click Save On an Complete Form", function () {
    //Arrange
    $("#createContact").click();

    $("#Name").val("xNamex");
    $("#Email").val("tasty@bacon.com");
    $("#PhoneNumber").val("xPhoneNumberx");
    $("#DateOfBirth").val("xDateOfBirthx");
    $("#IsMarried").attr("checked", true);

    //Act
    $(".ui-button-text:contains('Save')").parent().click();

    //Assert
    ok($("#Name_validationMessage:not(:visible)").length, "Name Validation Should Not Display");
    ok($("#Email_validationMessage:not(:visible)").length, "Email Validation Should Not Display");
    ok($("#PhoneNumber_validationMessage:not(:visible)").length, "PhoneNumber Validation Should Not Display");
    ok($("#DateOfBirth_validationMessage:not(:visible)").length, "DateOfBirth Validation Should Not Display");
    ok($("#IsMarried_validationMessage:not(:visible)").length, "IsMarried Validation Should Not Display");
    ok(contactWasPosted, "Contact Should Post");
    ok($("#createDialog:not(:visible)").length, "Dialog Should Be Closed");
});

BDD Style QUnit Tests


After talking more with Dan Mohl (@dmohl) I decided I wanted to try to find a Behavior Driven style of writing QUnit tests. I know there are several other BDD Client-Side Unit Test frameworks out there, but I wanted to keep to the QUnit runner for now.

So, during my research the author of Pavlov, Michael Monteleone (@namelessmike), let me know about his project, which ended up to be exactly what I was looking for.

The following is the output of my tests using QUnit and Pavlov…


Here is a slightly zoomed in view of the QUnit Pavlov test output…


The structure of the Unit Tests is dramatically different from the above classic Unit Tests.

The first thing you’ll notice is that I am extending the Assertion definitions to clean up some of the assert code that I had in my previous Unit Tests.

I still have the same logic in from the above Unit Tests that was in the setup and teardown methods, but now you can find those in the before and after methods.

The syntax of Pavlov is very readable from an English standpoint. You basically describe some scenario in words, and then split it out into code. It was very refreshing once I put it all together.

QUnit.specify.extendAssertions({
    isNotDisplayed: function(actual, expected, message) {
        ok(actual.is(":hidden") || actual.text().length == 0, message || "okay: isNotDisplayed");
    },
    isDisplayed: function (actual, expected, message) {
        ok(actual.is(":visible") || actual.text().length > 0, message || "okay: isDisplayed");
    }
});

QUnit.init({ moduleTestDelimeter: ", it " });
QUnit.specify.globalApi = true;
QUnit.specify("Contact", function () {

    describe("Create", function () {

        var contactWasPosted;

        before(function () {
            contactWasPosted = false;

            contactCreateModule.postContactBackup = contactCreateModule.postContact;
            contactCreateModule.postContact = function (callback) {
                contactWasPosted = true;
            };

            $("#createContact").click();
        });

        after(function () {
            contactCreateModule.postContact = contactCreateModule.postContact;
            contactCreateModule.createDialog.dialog('close');
        });

        describe("When the contact button is clicked", function () {
            it("should display the dialog", function () {
                assert($("#createDialog:visible").length).isEqualTo(1);
            });

            it("should not display name validation", function () {
                assert($("#Name_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display email validation", function () {
                assert($("#Email_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isNotDisplayed();
            });
        });

        describe("When clicking save on an empty form", function () {
            before(function () {
                $(".ui-button-text:contains('Save')").parent().click();
            });

            it("should keep the dialog displayed", function () {
                assert($("#createDialog:visible").length).isEqualTo(1);
            });

            it("should display Name validation", function () {
                assert($("#Name_validationMessage:visible")).isDisplayed();
            });

            it("should display Email validation", function () {
                assert($("#Email_validationMessage:visible")).isDisplayed();
            });

            it("should display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isDisplayed();
            });

            it("should display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isDisplayed();
            });

            it("should display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isDisplayed();
            });
        });

        describe("When clicking save on a completed form", function () {
            before(function () {
                $("#Name").val("xNamex");
                $("#Email").val("tasty@bacon.com");
                $("#PhoneNumber").val("xPhoneNumberx");
                $("#DateOfBirth").val("xDateOfBirthx");
                $("#IsMarried").attr("checked", true);

                $(".ui-button-text:contains('Save')").parent().click();
            });

            it("should not display name validation", function () {
                assert($("#Name_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display email validation", function () {
                assert($("#Email_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isNotDisplayed();
            });

            it("should post contact", function () {
                assert(contactWasPosted).isTrue();
            });

            it("should close the dialog", function () {
                assert($("#createDialog:visible").length).isEqualTo(0);
            });
        });

    });

});

Conclusion


The more and more I find myself creating highly dynamic websites, the more I find the need to Unit Test the browser interaction.

I hope you found the above example helpful. I would be interested to hear what tools you use to help Unit Test your client-side code. Please share… it makes us all better :)
You can find some other helpful client-side Unit Testing tools in the Script Junkie article I wrote entitled jQuery Test-Driven Development. In addition I wrote several other jQuery related articles that you can find on Script Junkie.

Note: It was not my intention to exhaustively Unit Test everything in the above example. There are other things I would Unit Test, but to make this example easy to understand in a bite-sized chuck, I limited myself to some simple examples.

Download Source Code


Wednesday, June 16, 2010

My 7 jQuery Articles on Script Junkie








Recently a brand new Microsoft website has been launched called Script Junkie that focuses on JavaScript, HTML, CSS, and more…
Over the past 7 months or so I’ve written 7 articles that are now available on Script Junkie. I was honored to write these articles for the launch of the website. I hope this clues you into why the blog posts on this site have been lacking.
  1. Six Things Every jQuery Developer Should Know
  2. How to Debug Your jQuery Code
  3. History and Back Button Support
  4. Intro to Error Handling in Ajax Apps
  5. Custom jQuery Events and Selector Filters
  6. How to Create Your Own jQuery Plugin
  7. jQuery Test-Driven Development
I am taking a break from writing for Script Junkie for a while. Our 3rd baby is due in August ;)
I hope you find the above articles helpful.

Friday, June 11, 2010

Switching to the Strategy Pattern in JavaScript

A Typical Switch Statement Scenario

Recently I’ve been working on some highly dynamic User Interfaces and at one point in the project I found my first reflex on a certain task to use a switch statement in JavaScript.

Now, my previous training in studying the Design Patterns has told me that the switch statement is bad. The design pattern drilled into me to resolve the switch statement is the the Strategy Pattern, however, I’ve never used the Strategy Pattern in JavaScript before.

The following example is a simple demo application where I start with a switch statement and then refactor the code later in the blog post to use the Strategy Pattern.

A Simple Super Hero Demo Application

SuperHeroShadow

The sample application is a Super Hero creator. You provide a Super Hero name and then you select your Super Hero power. The power can be a variety of things ranging from Flying, Invisibility, to nothing at all. Some of the powers have additional questions (metadata) about that particular power.

Once the Super Hero is created, the application should build up a object with the appropriate power type and supporting metadata. For now, I’m just outputting the object in the Firebug Lite console using console.dir().

First Pass at a Switch Statement Solution

Here is the above application written using a switch statement to build up the Power type and metadata. The solution is fairly simple in nature. I am utilizing the Revealing Module Pattern.

Note: You can view, run, and edit the following code… http://jsfiddle.net/elijahmanor/KzAMX/

var superHeroModule = (function () {
    var public = {};
    
    public.init = function() {
        $("input:radio[name='Power']").click(function() {
            var selectedPower = this.value;
            
            $("#powers .power").hide();
            $("#powers ." + selectedPower).show();
        });
        
        $("#create").click(function() {
            var superHero = public.scrapeSuperHero();
            console.dir(superHero);
        });
    };
    
    public.scrapeSuperHero = function() {
        var superHero = {};
        
        superHero.Name = $("#Name").val();
        superHero.Power = public.scrapePower();
        
        return superHero;
    };
    
    public.scrapePower = function() {
        var power = {};

        var selectedPower = $("input:radio[name='Power']:checked").val();
        switch (selectedPower) {
            case "Flying" : 
                power.type = "Flying";
                power.speed = $("#flyingSpeed").val();
                break;
            case "Invisibility" :
                power.type = "Invisibility";
                break;
            case "Strength" :
                power.type = "Strength";
                power.lift = $("#strengthLift").val();
                power.strongerThan = $("#strengthStrongerThan").val();
                break;
            case "Vision" : 
                power.type = "Vision";
                power.distance = $("#visionDistance").val();
                power.seeInDark = $("#visionDark").is(":checked");
                power.xrayVision = $("#visionXray").is(":checked");
                break;
        }
                
        return power;
    };
    
    return public;
} ());

superHeroModule.init();

cooltext439925164

Pros of this Solution

  • Simple Implementation
  • All the code is in one place to handle building up the Super Hero power metadata

Cons of this Solution

For those of you aware of Bob Martin’s (@unclebobmartin) SOLID Principles, the “O” represents the Open/Closed Principle which states that…

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

The switch solution violates the Open/Closed Principle in that every time a new Super Hero power is added to the list that the same piece of code will need to be modified. This is problematic in that new bugs can easily be introduced as new features are added or as defects are resolved.

Let’s look at a better way to solve the solution.

Refactoring Using the Strategy Pattern

A lof of the code will remain the same in the refactored solution. We mainly want to pull out the switch statement into something that is more maintainable and less error prone for future enhancment.

Note: You can view, run, and edit the following code… http://jsfiddle.net/elijahmanor/K8CkZ/

var superHeroModule = (function () {
    var public = {};
    
    public.init = function() {
        $("input:radio[name='Power']").click(function() {
            var selectedPower = this.value;
            
            $("#powers .power").hide();
            $("#powers ." + selectedPower).show();
        });
        
        $("#create").click(function() {
            var superHero = public.scrapeSuperHero();
            console.dir(superHero);
        });
    };
    
    public.scrapeSuperHero = function() {
        var superHero = {};
        
        superHero.Name = $("#Name").val();
        superHero.Power = public.scrapePower();
        
        return superHero;
    };
    
    public.scrapePower = function() {
        var power = {};
        
        var selectedPower = $("input:radio[name='Power']:checked").val();
        var scrapePowerFunction = "public.scrapePower" + selectedPower;
        power = eval(scrapePowerFunction)();
        
        return power;
    };
    
    public.scrapePowerFlying = function() {
        var power = {};
        
        power.type = "Flying";
        power.speed = $("#flyingSpeed").val();
        
        return power;
    };
    
    public.scrapePowerInvisibility = function() {
        var power = {};
        
        power.type = "Invisibility";
        
        return power;
    };
    
    public.scrapePowerStrength = function() {
        var power = {};

        power.type = "Strength";
        power.lift = $("#strengthLift").val();
        power.strongerThan = $("#strengthStrongerThan").val();        
        
        return power;    
    };
    
    public.scrapePowerVision = function() {
        var power = {};
        
        power.type = "Vision";
        power.distance = $("#visionDistance").val();
        power.seeInDark = $("#visionDark").is(":checked");
        power.xrayVision = $("#visionXray").is(":checked");
        
        return power;
    };
    
    return public;
} ());

superHeroModule.init();

cooltext439925164

Pros of this Solution

  • Abides by the Open/Closed Principle by opening the code for extension, but closing it for modification.

Cons of this Solution

  • The solution is more complex than the previous switch statement implementation
  • You might have noticed the use of the eval function. Douglas Crockford labeled the eval function as a Bad Part of JavaScript for some very good reasons, however, in this case I think the benefits it brings in this situation outweighs the risks. With every hard and fast rule, there comes a place and time to examine the benefits and risks.

Conclusion

I found that separating the switch statement logic into the above Strategy Pattern has made my current project much more maintainable and less error prone when adding new features.

How have you addressed similar situation? Have you implemented a different solution to the switch statement problem? Please share with me, I’d love to know!

Friday, June 04, 2010

Couch Potato 1.1 Bookmarklet – Expand/Collapse

4146293_ac8f

I updated the Couch Potato bookmarklet to add the ability to expand and collapse the details of each document from the list page by clicking the “+” to the left of the document key.

You can also click the “Expand All Documents” action located in the right navigation column.

The Bookmarklet

To use the bookmarklet, drag the following link to your bookmark/favorites list:

» Couch Potato 1.1 «

Friday, May 28, 2010

Couch Potato Bookmarklet – Lazy Features for CouchDB’s Futon

The Problem

We’ve been using CouchDB in our current project at work and I’ve been using the CouchDB Futon manager more and more lately to create, edit, or delete documents.

FutonBeforeDropShadow

After a lot of test data gets into our document store, I end up needing to clear out the documents. I could Delete the whole database with a button, but I’d rather not remove the CouchDB design documents defining custom views.

In order to delete documents from Futon, you have to manually drill down into each document and then click the delete button. I found this very monotonous, so I decided to make a bookmarklet to help me out some.

The Solution

So, I created the CouchDB Potato Bookmarklet (because I’m lazy). The bookmarklet creates a new delete column and provides a “Delete Documents” link to delete all the checked documents. I also added a “Select All Documents” which only selects non-design documents (so that I don’t accidentally delete a CouchDB view). These links can be found in the right navigation column under the “Recent Databases” section.

FutonAfterDropShadow

The Bookmarklet

To use the bookmarklet, drag the following link to your bookmark/favorites list:

» Couch Potato «

Then, when you're on a listing page in Futon, just click the bookmarklet.

Ideas for Future Development

I’ve considered adding some more functionality to Couch Potato such as

  • Converting it into a GreaseMonkey script (Google Chrome & Firefox)
  • Add tooltips to the document rows showing the details of the stored document

Do you have any ideas you’d like to see?

Wednesday, May 19, 2010

Opinionated ASP.NET MVC 2 Template Helpers

If you have used ASP.NET MVC any, then you are probably aware of the MVC Contrib project hosted on CodePlex. It is a helpful library that provides useful tools that ASP.NET MVC doesn’t give you out of the box.

Opinionated Input Builders

One of the pieces that I really like is the Opinionated Input Builders that Eric Hexter wrote. The builder allows you to provide one property at a time from your View Model and it will output the label, required indicator, the appropriate input control, and whatever else that it needs to display.

<% using (Html.BeginForm()) { %>

    <%= Html.Input(m => m.FirstName) %>
    <%= Html.Input(m => m.LastName) %>
    <%= Html.Input(m => m.Email) %>

    <p class="actions">
        <input type="submit" value="Create" />
    </p>

<% } %>

 

MvcContribPicNik

ASP.NET MVC 2 EditorForModel

As it turns out, the ASP.NET MVC team took a similar approach when putting together the Template Helpers in  ASP.NET MVC 2. I ended up switching to the Template Helpers.

<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm()) { %>

    <%= Html.EditorForModel() %>

    <p class="actions">
        <input type="submit" value="Create" />
    </p>

<% } %>

With some CSS styling, the output of the EditorForModel is close to what the Opinionated Input Builder, but there are some problems as we’ll discuss in the next section.

Mvc2ModelPikNic

ASP.NET MVC 2 EditorFor

The problem is that I usually don’t want to display or edit my entire model. I often times have things in my View Model that I don’t particularly want to display.  I want to rather manually choose which properties I want to display or edit.

<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm()) { %>

    <%= Html.EditorFor(m => m.FirstName) %>
    <%= Html.EditorFor(m => m.LastName)%>
    <%-- Purposely Not Show the Email –%>

    <p class="actions"> 
        <input type="submit" value="Create" /> 
    </p> 

<% } %>

Using the above syntax doesn’t quite give the output I was looking for. Only the input controls are rendered (as seen in the following screenshot) instead of providing the scaffolding of label, input, and validation fields like the EditorForModel method provides.

Mvc2TemplatedHelperBeforePicNik

You end up having to provides the layout of the controls and explicitly mention the label, input controls, and validation for each property. 

Modify the Template Helpers MasterPage

I soon began to miss the simple syntax of rendering all the necessary code like I was used to when using the Opinionated Input Builders outputted from the MVC Contrib.

Well, a while back Brad Wilson (one of the ASP.NET MVC 2 developers), wrote an awesome series about Templated Helpers and the last post in the series, ASP.NET MVC 2 Templates, Part 5: Master Page Templates, he addressed this granular Opinionated Input Builder type syntax!

All you do, is to override the Master Page that the templates use internally. So, inside my Master Page I layout where I want the label, validation, and input controls to go and then ASP.NET MVC 2 does the rest by resolving which Template Helper to use!

I modified the Master Page some to suite my needs (I use divs instead of tables), but overall it is the same one that he lays out on his blog.

EditorTemplates/Tempate.Master

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<script runat="server">
    protected override void OnInit(EventArgs e) {
        base.OnInit(e);
 
        if (ViewData.ModelMetadata.HideSurroundingHtml) {
            TablePlaceholder.Visible = false;
        }
        else {
            Controls.Remove(Data);
            DataPlaceholder.Controls.Add(Data);
        }
    }
</script>
<asp:ContentPlaceHolder runat="server" id="Data" />
<asp:PlaceHolder runat="server" id="TablePlaceholder">
    <div class="displayLayout">
        <div class="displayLabel">
            <asp:ContentPlaceHolder runat="server" id="Label">
                <%= ViewData.ModelMetadata.IsRequired ? "*" : "" %>
                <%= ViewData.ModelMetadata.GetDisplayName() %>
            </asp:ContentPlaceHolder>
        </div>
        <div class="displayField">
            <asp:PlaceHolder runat="server" id="DataPlaceholder" />
            <asp:ContentPlaceHolder runat="server" ID="Validation">
                <%= Html.ValidationMessage("") %>
            </asp:ContentPlaceHolder>            
        </div>
        <div style="clear: both;"></div>
    </div>    
</asp:PlaceHolder>

EditorTemplate/String.aspx

<%@ Page Language="C#" MasterPageFile="Template.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="Data" runat="server">
    <%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,
                     new { @class = "text-box single-line" }) %>
</asp:Content>

There are several other files that you’ll need. I’ve grabbed most of them from Brad’s blog post and have them in the demo application below that you can download.

0003d725337bf3a4617919ac6126dc07PicNik

Now we can try the above scenario one more time and get the output that we were expecting.

<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm()) { %>

    <%= Html.EditorFor(m => m.FirstName) %>
    <%= Html.EditorFor(m => m.LastName)%>
    <%-- Purposely Not Show the Email –%>

    <p class="actions"> 
        <input type="submit" value="Create" /> 
    </p> 

<% } %>

 

Mvc2TemplatedHelperAfterPicNik

 

cooltext439925016

Wednesday, April 28, 2010

Tips using Firebug Lite and Full Screen with jsFiddle

jsFiddle

I’ve been using jsFiddle more and more to test and share JavaScript and jQuery code.

Although I love that jsFiddle shows the HTML, JavaScript, CSS, and Results window all at the same time, I also would love to somehow see the output of the console window in the User Interface. Depending on the browser someone views my jsFiddle projects, the console may or may not be defined. I really don’t like alert statements and sometimes it is just easier to console.log() than it is to write out messages to the DOM.

Firebug Lite

So, I’ve been finding myself adding Firebug Lite as a resource to jsFiddle and letting Firebug be part of the rendered output.

If you’ve tried this, then you may have noticed that Firebug auto-opens once jsFiddle is rendered. Seeing that the Rendered section isn’t very big this could be a big problem.

jsFiddleFireBugBig

Thankfully, there are several options you can provide to Firebug Lite to customize it’s behavior.

I find myself setting the following three options on a regular basis.

<script type="text/javascript">
    firebug.env.height = 200; //Set the initial height of the Firebug window
    firebug.env.debug= false; //Set Firebug to be initially launched or minimized
    firebug.env.detectFirebug = true; //If browser has Firebug, don’t interfere with it
</script>

 

jsFiddleFireBugMinimized

I also like to use JsBin quite a bit and honestly switch between it and jsFiddle quite often depending on what I am doing.

Full Screen Option

One of the features that I really like in JsBin is that you can launch the session to Full Screen and not inside the editor. This is helpful if you want to interact with the DOM with your browser’s debugging tools, minimize any side-effects the editor might be creating, etc…

As it turns out, jsFiddle has this same feature, but you have to know how to get to it. You can just add “/show” to the end of your jsFiddle URL to show it without the editor. To swap back to the edit view you can simply remove the “/show” from the URL.

Full Screen / Editor Toggle Button

Now that we know we can swap back and forth between Full Screen and Edit mode, what I miss is the nice little JsBin link in the upper-right hand corner of the page allowing you to easily swap between the two environments.

JsBin

As it turns out, I’ve recreated this in jsFiddle by adding the following code to my JavaScript window…

$("<a />", {
    id : "show",
    text : window.location.pathname.indexOf("show/light") > 0 ?
    "View Full Screen" : "Edit using jsFiddle",
    href : window.location.pathname.indexOf("show/light") > 0 ?
        window.location.pathname.replace(/light\/$/gi, "") :
        window.location.pathname.replace(/show\/$/gi, ""),
    target : "_blank"
}).appendTo('body');  

…and here is the supporting CSS that I put in the CSS window of the jsFiddle Editor.

#show {
    position: fixed;
    top: 0px;
    right: 0px;
    padding: 5px;
    background-color: #3D6F9A;
    color: #ffffff;
    border-bottom: 1px solid rgb(153, 153, 153);    
    border-left-width: 1px solid rgb(153, 153, 153);
    -webkit-border-bottom-left-radius: 10px;
    -moz-border-radius-bottomleft: 10px;
    border-bottom-left-radius: 10px;     
    border-right-width: 0px;
    border-top-width: 0px;
    text-decoration: none;
    color:#FFFFFF;
    font-size:11px;
    font-weight:bold;
    text-decoration:none;
    text-shadow:0 1px 0 #0C131C;
    font-family:"Lucida Grande","Lucida Sans","Lucida Sans Unicode","Luxi Sans",Tahoma,sans-serif;
    display: block;
}

The following screenshot is what the result window looks like inside the jsFiddle Editor

jsFiddleFullScreen

The following screenshot is what the window looks like in jsFiddle Full Screen mode

 jsFiddleFullScreen2

You can view, run, and edit the above code snippets using my jsFiddle

cooltext439925164

Tuesday, April 27, 2010

Stepping Down from The Official jQuery Podcast

ps.nrdkgcvf.170x170-75 In case you haven’t heard from the end of the Rey Bomb #2 Episode of The Official jQuery Podcast, I announced that after 21 episodes I am stepping down as my role of co-host.

Around the time I first joined the podcast I also started to become involved with some intense writing projects (as will become apparent in the near future), although I can’t say much about it right now :)

As a result, I haven’t had much margin in my life. I’ve been running on empty for quite some time and other more important things in my life have gotten the shaft! Unfortunately, one of those was my family and I intend to change that.

I don’t plan to fall off the face of the earth. I plan to refocus on blogging and tweeting. I would like to do some more speaking, but probably not for the rest of the year as baby #3 is coming in August.

I am honored to have been asked to join The Official jQuery Podcast and during my involvement I got to meet many great people that I hadn’t previously known. I am glad to have formed these relationships, and look forward to further investing in them.

I hope the future of The Official jQuery Podcast is bright and I encourage you to either start or continue to listen.

Thursday, March 18, 2010

Mix10 Session - 6 Things Every jQuery Developer Must Know

This year I was chosen by the community to speak at Mix10 as one of the Open Call sessions. I was honored to be selected and thoroughly enjoyed attending Mix10 and building relationships.

My session was entitled 6 Things Every jQuery Developer Should Know

This seminar provides an overview of several in-depth concepts that developers need to learn to bring their jQuery development to the next level. We review six things that you should know, such as: "What Is This?", "Am I Referencing the jQuery Object or a DOM Element?", "Understanding Selectors", "Combine and Minify Your Scripts", "Different Ways of Storing Data", and "The Dos and Don'ts of Events". Along the way, we share several helpful websites and tools to make your job as a jQuery developer easier.

Mix10TalkImage

For each of the 6 points I started with beginning material and then progressed to intermediate then advanced concepts. When I moved to the next point the content would go back to beginner and then progress to advanced. In this way, I hoped to reach a wider audience with things tips they could take away from the session. 

The video from my talk is now available from the Mix10 website. You can watch the video and also download my web-based presentation along with the code I showed.

Unfortunately, the main demo that I wanted to show didn’t work during the presentation. The demo worked the night before & it works now. My guess is that the Picasa Web API was temporarily down during my talk, which is unfortunate. I guess I should have expected something to go wrong. Anyway, overall I think it went well. I had several questions at the end and people stayed after to ask questions as well.

During the presentation I listed several resources that I recommended. I pulled those out and provided them below for quick access them. Some of them I demonstrated during the talk and others I just referred to as good resources you may want to check out.

3h. Knowing Selectors

6. Combine & Minify Scripts

If you were able to attend my session at Mix10 or watch the above video recording, I would appreciate if you could take a moment to rate my talk on speakerrate.com. Thank you again for voting my session into Mix10.

Note: Someone asked me what Visual Studio theme I was using to show the code near the end of the session. As it turns out, I was using JetBrains WebStorm IDE. Jeffrey Way did a great mini-screencast review of the new IDE on Nettuts.

Wednesday, February 03, 2010

Getting Those Old Firefox Add-ons to Work

Although I’ve switched to Google Chrome as my primary web browser, I still switch over Firefox every once in a while for one reason or another.

With the recent release of Firefox 3.6 you might find some of your favorite add-ons no longer in working because they haven’t yet been updated to support the new version number.

Add-Ons-Nightly-Arrow

You can easily get around this limitation by installing the Nightly Tester Tools Firefox Add-on which allows you to override the maximum version the add-ons say they can support.

b1de0cb5e33b6cc3c3e409cdba038ea4

Keep in mind, even though you are forcing your add-ons to run with your current browser version it doesn’t mean they will work, but usually I find that they do work just fine.

OverrideCompatibility-Arrow

I’m a sucker for running the latest and greatest build of Firefox so the Nightly Tester Tools Firefox Add-on is a must for me.

I tend to use the Portable Versions of Firefox so they are sandboxed in their own little folders. That way I can also have several versions on my box for testing.

As a total side-note, but related point I also like these following Portable Applications that you can find from the same site…

Thursday, January 28, 2010

Phil Haack’s Mini Math Challenge

Today Phil Haack posted a mini math challenge on twitter…

24a3e96dbc652aa721bed99dcea38b6d

This seemed like a good opportunity to launch js.Fiddle and hack out some code…

 

The answer... is 2520 ;)

Tuesday, January 19, 2010

ASCII Font URL Encryption

I was looking at some source code the other day and found some 3D ASCII text.

It brought back memories of using UNIX and PINE for e-mail during my college days.

I decided to go on a hunt for the 3D ASCII font and found this site… http://patorjk.com/software/taag/

Once you find the font that you like…

then you can create a link to share with others, but I noticed something strange in the URL

http://patorjk.com/textart/msg1.php?font=Larry%203D.flf&hs=true&bg=%23FFFFFF&fc=%23000000&align=left&txt=Ryvwnu

Do you notice anything strange about the txt URL parameter? “Ryvwnu”… what is that? Well, it stands for “Elijah”

I put on my cracker hat and found that it was a simple character substitution algorithm. Take the letter you want “E” and add 13 characters to it “R”.

How is that for some advanced encryption ;)

Monday, January 18, 2010

Book Review: 51 Tips, Tricks, and Recipes with jQuery & ASP.NET Controls

842d59697bf6bdc2afa9cc8a899e8e52I recently finished reading the 51 Tips, Tricks, and Recipes with jQuery & ASP.NET Controls eBook by Suprotim Agarwal. The eBook is $14.99 and it comes with a 14 day 100% money back guarantee. It can be ordered from the DotNetCurry website.

This eBook reminded me a lot of Cody Lindley’s jQuery Enlightenment in that it split it’s eBook into small bit size installments (known as recipes) that can be consumed either sequentially or on a individual basis. The recipes are organized into situations or problems that you might want to solve using jQuery. This eBook differs from jQuery Enlightenment in that it focuses on the interaction with ASP.NET WebForms, which has a unique rendering of its server controls.

Most of my web development history has been doing ASP.NET WebForms (and more recently ASP.NET MVC) so, I enjoyed reading this eBook. I could understand the situations, problems, and pain points that the eBook addressed. It was nice to see how jQuery can resolve these issues in an unobtrusive and cross-browser way while only requiring a small amount of code.

Each recipe is formatted in the same way…

  • A challenge that explains the situation, problem, or pain point being addressed
  • The solution displayed in the form of HTML and jQuery
  • An explanation of the code broken up in paragraphs, code snippets, and screen shots
  • A link to a live demonstration
  • A list of supported browsers for the above recipe
  • Useful links that are appropriate to further explain features from the code.

I would have written some of the code differently and I did find some performance concerns, but nothing major and when push comes to push and shove many micro-optimizations become moot in the big picture. Selector optimization and looping performance can help make your code become more efficient, but it is really DOM manipulation that will kill your performance in the end.

Updated: I have updated this post with a link to my detailed findings, suggestions, and opinions. You can find my comments here.

Despite these minor issues, I do recommend this eBook for any ASP.NET WebForms developer hoping to learn how jQuery could be integrated into their environment.