Thursday, March 11, 2010

Custom jQuery Selector for External and Internal Links

jQuery LogoI was working on a website for my fiancee and her church group called The Diocese of Atlanta Young Adults. They were hosting an event they call the Young Adult Summit.

One of the requirements I had was to provide a warning to users before they left the page by after having clicked an external link. Using jQuery, I was able to bind to the click event with easy cross browser compatibility and I used jQueryUI to open a modal dialog box. Pretty basic stuff.

The one thing I regretted was that I had to use a class name to determine which links were external and which were internal. A few days ago, I discovered that jQuery supports custom selectors and I decided to write a pair of custom selectors for identifying internal and external links.
jQuery.extend(
  jQuery.expr[ ":" ],
  {
    /*
      /:\/\// is simply looking for a protocol definition.
      technically it would be better to check the domain
      name of the link, but i always use relative links
      for internal links.
    */

    external: function(obj, index, meta, stack)
    {
      return /:\/\//.test($(obj).attr("href"));
    },

    internal: function(obj, index, meta, stack)
    {
      return !/:\/\//.test($(obj).attr("href"));
    }
  }
);
I do have a few items of note. First, you'll notice that I'm not actually looking for the current domain name in the links. That's because I use relative links for all of my internal links these days. Thus, I know that if there's not a protocol definition that it's an internal link. If you need to identify internal links by domain as well, you can just pull that out of top.location.href and check for it too.

Second, you could technically use this selector on any DOM object. There are several ways to get around this. One way would be to verify that the object is an anchor tag. Another would be to verify that the href attribute exists. I just plan on using common sense.

Here's a quick usage example:
$("a:external").click(verifyNavigateAway);
$("a:internal").css("font-weight", "bold");

The Accidental Programmer

Airplane AccidentI've been in the software field for some time now, and over the years I have worn many hats. I have been the sole developer in a psychopharmacological research lab; I have been a private contractor and security analyst; I have developed human resources software and data warehouses; I've been a programmer, a tech lead, a project manager, a VP, and a partner. I've hired and fired developers, laid off friends (and a fiancee), and interviewed at least a hundred candidates locally and overseas.

In all of my experience, I've discovered an unavoidable and intolerable fact: most programmers can't program. Just so we're clear, by "most programmers," I'm talking roughly 90 - 95 percent. Now, I know this isn't an original sentiment. Jeff Atwood talked about this in 2007 in an article which has since been quoted by dozens of people in the development community including folks like Phil Haack. So, if it's an already beaten dead horse, why am I writing about it again?

Well, frankly, I was wondering why there are so many bad programmers out there who seem evidently to be doing so well. Why are there so many crappy programmers getting work and making bank? Look at how many large companies are learning hard lessons from off-shoring experiences, yet everybody still wants to send work overseas! It's like there's a big chronic "WTF barrier" between business people thinking they could save a few bucks and programmers telling them how much it'll cost them in the long run.

Why doesn't anybody notice? Why doesn't everybody realize that these people don't know what they're doing? Well, it's because they produce programs that partially work. Jeff's right that most programmers can't even write a single line of code, so how is it that they manage to produce work that's functional enough to convince the world that they're capable developers? Well, I figured it out. They do it by accident; I call them accidental programmers.

By combining the forces of the internet, feature rich IDEs, code templating, and auto completion, an accidental programmer has all the tools he or she needs to accidentally write a semi-functional bad program without ever having to write a real line of code.

Now, I am sure my readers wouldn't let me get away with making such claims without providing empirical evidence, so here are some code snippets I believe you couldn't write on purpose:
tbPassword.Text.Trim().ToString().Trim();
protected void btnLogin_Click(object sender, EventArgs e)
{
    SqlClientUtilities sqlData = new SqlClientUtilities();
    SqlDataReader drPassinfo = null;

    string sLoginSQL = "select a.agentid,u.userName,a.FirstName,a.Lastname from dbo.person a, dbo.users u where  ";
    sLoginSQL +=  "  a.userid = u.userid and u.username='" + txtUserName.Text.Trim() + "'";
    sLoginSQL += " AND personid = '" + txtPassword.Text.Trim() + "'";

    drPassinfo = sqlData.SqlClientExecuteDataReader(sLoginSQL);

    if (drPassinfo.HasRows)
    {
        AppSupportUtils.WriteError("Records found");

        //valid login - redirect
        Session["userlogin"] = txtUserName.Text.Trim();
        Response.Redirect("Manage.aspx",false);
    }
}
/// <summary>
/// This class just returns an object which holds a date
/// </summary>
public class Date
{
    public int Year = 0;
    public int Month = 0;
    public int Day = 0;

    public Date()
    {
        Year = 1901;
        Month = 1;
        Day = 1;
    }

    public Date(System.DateTime dt)
        : this()
    {
        Year = dt.Year;
        Month = dt.Month;
        Day = dt.Day;
    }

    public Date(string dt)
        : this()
    {
        // en-US     M/d/yyyy
        CultureInfo MyCultureInfo = new CultureInfo("en-US");
        try
        {
            DateTime MyDateTime = DateTime.Parse(dt, MyCultureInfo);
            Year = MyDateTime.Year;
            Month = MyDateTime.Month;
            Day = MyDateTime.Day;
        }
        catch { ;}
    }

    public override string ToString()
    {
        return Month.ToString() + "/" + Day.ToString() + "/" + Year.ToString();
    }

    public override int GetHashCode()
    {
        return (int)(Year * 12) + (Month * 30) + Day;
    }
}
function invertBool(bool)
{
  if (bool == false) return true;
  return false;
}
function sendSecureVote(index)
{
  var checksum = Math.round(getFormattedDate() * 57 / 33 - 147 + 2009);
  startAjaxRequest("http://www.theserverhasbeenanonymized.com/voteCounter.php?checksum=" + checksum + "&voteFor=" + index);
}

function getFormattedDate()
{
  var date = new Date();
  return date.getMonth() + date.getDate() + date.getHours();
}
var ssnValidator = /[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/;