Skip to content

CategoryWeb Development

IsNumericList() Function in ColdFusion

Whipped up this UDF while working on a project. It checks to see if the list is valid to use in a TSQL WHERE IN Clause. I wanted to check for a list of valid positive integers (including 0), ignoring spaces.

<cffunction name="isNumericList" returntype="boolean" hint="Check for either single positive number or a set of positive numbers. Spaces ignored." >

  <!--- Useful when inserting into an "IN" TSQL list in the WHERE clause.  --->

  <cfargument name="list" required="true">
  
  <cfargument name="delimiter" required="false" default=",">   

  <cfset var isNumericList = false />
  
  <cfif REFind( "^(\d+)$|^(([\d\s]+#Arguments.delimiter#)+\s*\d+)$", Trim(Arguments.list) ) >
    
    <cfreturn true >
        
  </cfif>
  
  <cfreturn isNumericList >  

</cffunction>

Now let’s test it!

<!--- Dummy Data --->
<cfset dataList = ArrayNew(1) />
<cfset dataList[1] = "456c" />
<cfset dataList[2] = "456" />
<cfset dataList[3] = "45c," />
<cfset dataList[4] = "5,,," />
<cfset dataList[5] = "565,651,34,643232,45" />
<cfset dataList[6] = "454,c,45,5454,32" />
<cfset dataList[7] = "121.45,43,565,1,1,2" />
<cfset dataList[8] = "43,54,65,1," />
<cfset dataList[9] = "67,54,73,436," />
<cfset dataList[10] = ",6565,656,77,32,3" />
<cfset dataList[11] = "" />
<cfset dataList[12] = ",43656" />
<cfset dataList[13] = "4365,  55,31,24,   5,   5  ,1,      34" />

Now let’s use it!

<!--- Use it! --->
<cfoutput>
   <cfloop array="#dataList#" index="i">  
      [#i#] => #isNumericList(i)#<br />
   </cfloop>
</cfoutput>

Results!

[456c] => false

[456] => true

[45c,] => false

[5,,,] => false

[565,651,34,643232,45] => true

[454,c,45,5454,32] => false

[121.45,43,565,1,1,2] => false

[43,54,65,1,] => false

[67,54,73,436,] => false

[,6565,656,77,32,3] => false

[] => false

[,43656] => false

[4365, 55,31,24, 5, 5 ,1, 34] => true

Quick Simple Way to Show/Hide with a Checkbox using jQuery

Here’s a quick and simple way to do this. I often have to show and hide set of panels based on whether something is checked or not. jQuery code is pretty straightforward and it comes in handy.

$( function() {    

  function toggleCheckbox( trigger, hidethese )
  {
    if ( $( trigger ).attr("checked") )
    {
      $( hidethese ).hide();
    }
    else
    {
      $( hidethese ).show();
    }
  }
  
  // jQuery object that has your checkbox
  var triggerCheckbox = $( "#showPreonDescription" );
  
  // jQuery object for collection of elements you want to hide
  // For this example, I could've also just done #panel by itself
  // to hide everything
  var panelsToHide = $("#subpanel,#maincontent,#nav");  
  
  // Call the function on click.
  triggerCheckbox.click(
    function ()
    {
      toggleCheckbox( triggerCheckbox, panelsToHide );
    }
  );
});

I have the demo if you want the source as well.

Search In One File from Keywords in Another File

I needed to see if this list of email addresses were found in a logs file. So I had one file with a list of email addresses. Another file is a list emails sent. I needed to make sure that the emails were sent. Here’s a quick Python script I put together that does this:

import re
import sys

def searchInLogFile( FILE, query ):  
  FILE.seek( 0, 0 )  
  for line in LogFile:
    logLine = line.replace("\n","").replace("\r","").rstrip().lstrip()
    if re.search( query, logLine, re.IGNORECASE | re.MULTILINE ):
      return True

# This file has a list (\r\n delimited) email addresses.
EmailListFile = open( "email-list-internal.txt", "r")

# This is the log file which we'll use to see if email addresses are in here.
LogFile = open( "POST20100201.log", "r" )

EmailFound = []
EmailNotFound = []
breakTime = 0
# 0 = does the whole list
EmailsToSearchFor = 0

for emailLine in EmailListFile:
  email = emailLine.replace("\n","").replace("\r","").rstrip().lstrip()

  if ( searchInLogFile( LogFile, email ) ):
    print email, "was found"
    EmailFound.append( email )

  else:
    print email, "not found"
    EmailNotFound.append( email )  


  if ( EmailsToSearchFor != 0 ):
    breakTime += 1
    if ( breakTime == EmailsToSearchFor ):
      break;
  
LogFile.close()
EmailListFile.close()

# Log results to a file.
OutputFile = open( "output.log", "w" )

divider = "\n\n======== Found ========================================"
print divider
OutputFile.write( divider )
 
for i in EmailFound:
  print i
  OutputFile.write( "\n" + i )

divider = "\n\n======== Not Found ===================================="  
print divider
OutputFile.write( divider )

for i in EmailNotFound:
  print i
  OutputFile.write( "\n" + i )
  
OutputFile.close()  

Pretty straightforward. The script also writes a file called “output.log” which has a list of emails that were found (marked under “found”) and not found (marked under “not found”).

Generate Junk Files

The other day I was testing benchmarks for a delete script. I needed to create files with various file sizes. More specific, 1,000,000 files with 5K per file. A while ago I found this great snippet on StackOverflow to generate a junk random string:

junk =  (("%%0%dX" % (junk_len * 2)) % random.getrandbits(junk_len * 8)).decode("hex")

I’ve wrapped that around to make a utility function and snippet:

import os, random, sys

# This tool takes 3 parameters
#
#   testing   
#
# Example:
#
#   testing dan 100 500

def createLocalDirectory( directoryName ):
  if not os.path.exists( directoryName ):
    os.makedirs( directoryName )

folderName      = sys.argv[1]
how_many_files  = int(sys.argv[2])
junk_len        = int(sys.argv[3])


createLocalDirectory( folderName )

for i in range( 0, how_many_files ):  
  junk =  (("%%0%dX" % (junk_len * 2)) % random.getrandbits(junk_len * 8)).decode("hex")
  path = folderName + "/" + str(i) + ".txt"
  f = open( path, 'w' )
  f.write( junk )
  print f
  f.close()

TSQL Functions Inspired By ColdFusion’s Lists Functions

In my last project, there was a bit of data scrubbing on the database side (SQL Server 2008) that I decided to create a few UDF’s that function similar to ColdFusion’s Lists function. The one that varies a little bit is ListLen(), since I needed to take into account empty tokens. The ChopIf() was inspired by Perl’s chop() function. These UDFs should be SQL Server 2005-compatible.

I should say though, that some of these functions depend on each other. ListLen(), GetToken(), and ChopIf() are independent.

------------------------------------------------------------------
-- Functions similarly like ColdFusion ListSort() function,
-- except it currently only sorts strings. 
--
-- Example 1:
--    dbo.ListSort( 'dan is so mega awesome that he rules all the time', 'ASC', ' ' )
--
-- Returns: 
--    all awesome dan he is mega rules so that the time
--
-- Example 2:
--    dbo.ListSort( 'dan is so mega awesome that he rules all the time', 'DESC', ' ' )
--
-- Returns: 
--    time the that so rules mega is he dan awesome all
------------------------------------------------------------------

CREATE FUNCTION [dbo].[ListSort]
(
  @string    VARCHAR(2000),  
  @sort_type CHAR(3)       = 'ASC',
  @delimiter VARCHAR(2000) = ','
)
RETURNS VARCHAR(500)
AS

BEGIN

  DECLARE @position AS INT
  DECLARE @token AS VARCHAR (2000)
  DECLARE @counter   AS INT
  DECLARE @sortedList AS VARCHAR(500)

  DECLARE @sortTempTable TABLE ( token VARCHAR(500) )
  DECLARE @sortedTable   TABLE ( token VARCHAR(500) )  

  SELECT @string   = @string + @delimiter,
         @counter  = 1,
         @position = 0,
         @token    = ''

  WHILE ( PATINDEX( '%' + @delimiter + '%' , @string )  0 ) 
  BEGIN
    SELECT @position = PATINDEX('%' + @delimiter + '%' , @string ),
           @token    = LEFT( @string, @position - 1 ),
           @string   = STUFF( @string, 1, @position, NULL ),
           @counter  = @counter + 1

    INSERT @sortTempTable( token ) VALUES( @token )     
  END
  
  SET @sortedList = ''

  -- Let's sort the table and put it into @sortedTable
  -- Because of nature of Rank(), we can't set @sortedList in this statement.
  -- Have to separate it into another select clause.
  INSERT INTO @sortedTable
    SELECT LTRIM( token )
    FROM   @sortTempTable
    ORDER  BY CASE WHEN @sort_type = 'ASC'  THEN ( RANK() OVER ( ORDER BY LTRIM(token) ASC ) )
                   WHEN @sort_type = 'DESC' THEN ( RANK() OVER ( ORDER BY LTRIM(token) DESC ) )
              END  

  SELECT @sortedList = @sortedList + token + @delimiter
  FROM   @sortedTable

  RETURN dbo.ChopIf( @sortedList, @delimiter )

END
GO
------------------------------------------------------------------
-- Functions sort of like ColdFusion's ListLen() method, but it
-- takes into account empty tokens. 
--
-- Example 1:
--    dbo.ListLen( 'Dan is cool', ' ' )
--
-- Returns: 
--    3
-- 
-- Example 2:
--    dbo.ListLen( 'dan,,very,,,,awesome,', ',' )
--
-- Returns: 
--    8
------------------------------------------------------------------

CREATE FUNCTION [dbo].[ListLen]
(
  @string VARCHAR(2000),
  @delimiter VARCHAR(2000) = ','
)
RETURNS INT
AS
BEGIN

  DECLARE @loopCount INT, 
          @tokenCount INT

  SELECT @loopCount = 0, 
         @tokenCount = 0

  -- If it's an empty string, the list length is 0
  IF DATALENGTH( @string ) = 0
    BEGIN
      SET @tokenCount = 0
    END
  ELSE
    BEGIN
      -- Count tokens, including empty ones like dan,,very,,,,awesome,
      SET @tokenCount = @tokenCount + 1
      WHILE ( @loopCount < DATALENGTH( @string ) )
      BEGIN
        IF SUBSTRING( @string, @loopCount, DATALENGTH( @delimiter ) ) = @delimiter
          BEGIN
            SET @tokenCount = @tokenCount + 1
          END
        SET @loopCount = @loopCount + 1
      END
    END

  -- Handle extra count from space being delimiter
  IF @delimiter = ' '
    SET @tokenCount = @tokenCount - 1

  -- If there's no token to the right of the last delimiter, then count that
  -- as an empty token.
  IF ( RIGHT( @string, 1 ) = @delimiter ) 
  BEGIN
    SET @tokenCount = @tokenCount + 1
  END

  RETURN @tokenCount

END
GO

——————————————————————
— Functions like ColdFusion's ListLast()
— Gets token value that's been separated by a delimiter.

— Example:
— dbo.ListLast( 'Dan is cool', ' ' )

— Returns:
— cool
——————————————————————

CREATE FUNCTION [dbo].[ListLast]
(
@string VARCHAR(2000),
@delimiter VARCHAR(2000) = ','
)
RETURNS VARCHAR(2000)
AS
BEGIN

RETURN dbo.ListGetAt( @string, dbo.ListLen( @string, @delimiter ) , @delimiter )

END
GO

——————————————————————
— Wrapper for GetToken() Function
— Gets token value that's been separated by a delimiter.

— Example:
— dbo.ListGetAt( 'Dan is cool', 2, ' ' )

— Returns:
— is
——————————————————————

CREATE FUNCTION [dbo].[ListGetAt]
(
@string VARCHAR(2000),
@token INT,
@delimiter VARCHAR(2000)
)
RETURNS VARCHAR(2000)
AS
BEGIN
RETURN dbo.GetToken( @string, @token, @delimiter )
END
GO

——————————————————————
— Returns the first item in a tokenized list.

— Example:
— dbo.ListFirst( 'Dan is cool', ' ' )

— Returns:
— Dan
——————————————————————

CREATE FUNCTION [dbo].[ListFirst]
(
@string VARCHAR(2000),
@delimiter VARCHAR(2000) = ','
)
RETURNS VARCHAR(2000)
AS
BEGIN

RETURN dbo.ListGetAt( @string, 1, @delimiter )

END
GO

——————————————————————
— Functions similarly like ColdFusion GetToken() Function.
— Gets token value that's been separated by a delimiter.

— Example:
— dbo.GetToken( 'Dan is cool', 2, ' ' )

— Returns:
— is
——————————————————————

CREATE FUNCTION [dbo].[GetToken]
(
@string VARCHAR(2000),
@tokenPosition INT,
@delimiter VARCHAR(2000)
)
RETURNS VARCHAR(2000)
AS
BEGIN

DECLARE @position AS INT
DECLARE @token AS VARCHAR (2000)
DECLARE @counter AS INT

SELECT @string = @string + @delimiter,
@counter = 1,
@position = 0,
@token = ''

WHILE ( PATINDEX('%' + @delimiter + '%' , @string ) 0) AND ( @tokenPosition + 1 @counter )
BEGIN
SELECT @position = PATINDEX(‘%’ + @delimiter + ‘%’ , @string),
@token = LEFT(@string, @position-1),
@string = STUFF(@string, 1, @position, null),
@counter = @counter + 1
END

RETURN @token
END

------------------------------------------------------------------
-- Chops the last character if it's @chopped
--
-- Example:
--    dbo.ChopIf( 'Dan is cool!', '!' )
--
-- Returns: 
--    Dan is cool
------------------------------------------------------------------

CREATE FUNCTION [dbo].[ChopIf]
(
  @string VARCHAR(2000),
  @chopped VARCHAR(2000)
)
RETURNS VARCHAR(2000)
AS
BEGIN

  IF ( RIGHT( @string, DATALENGTH(@chopped) ) = @chopped )
  BEGIN
    SET @string = LEFT( @string, DATALENGTH( @string ) - DATALENGTH( @chopped ) ) 
  END 

  RETURN @string
  
END
GO

Implementing the Strategy Pattern in ColdFusion

In spirit of the amazing book Head First Design Patterns, I wanted to put together a ColdFusion example that depicts the Strategy pattern. Essentially, the Strategy Pattern lets you group related algorithms together so that an object is able to select which algorithm to run at runtime.

So let’s say for example you had a parent class that has two properties and two methods. You now create a subclass that inherits the parent class, meaning that it will gobble up all the properties and methods (whether you like it or not) from the parent class. You can’t choose, for example, what methods you subclass needs, even if some methods don’t make sense for your subclass. It’s an all-or-nothing solution. Yes, you can override, but what if there were 10 methods to override? Also, what if you had to create other types of subclasses – you’ll have to override those as well. Things can get a little sloppy at the end. That’s where the strategy pattern comes in.

With this pattern, you first think about related methods and algorithms. (One method can have various algorithms; different implementations for doing the same thing.) For this blog post’s example, we’ll think of different ways a SuperHero can punch. To keep things simple, let’s give him two ways (two different algorithms for punching) he can punch. He can punch normally, or he can punch you, which freezes you as well. Let’s UML this to make things clearer.

First what the lines mean:

Here’s the UML diagram for our SuperHero scenerio:

From the diagram, you can see that the SuperHero abstract class is able to choose a punch set of algorithms (a strategy). We also see that Punch and PunchFreeze are implementation classes (classes that serve to implement an interface). Both of them have a punch() method that return void (nothing) – in this example, they’ll do stuff, and not return anything.

To make things a little more interesting, and to follow closer the example in the Strategy Pattern chapter of the book, we’re going to also create a subclass called Freezer that inherits the SuperHero class. Also, we’ll create another strategy for kicking. Here’s what the UML for that looks like:

Here’s the CF code:

SuperHero.cfc

<cfcomponent>

<!--- This is our abstract class. Responsibility to implement is delegated to --->
<!--- classes that implement interfaces. --->

<cffunction name="init" access="public" returntype="SuperHero">  
  <!--- Some other init code goes here.  --->  
  <cfargument name="name" type="string">  
  <cfargument name="gender" type="string">  
  <cfset this.name = arguments.name />  
  <cfset this.gender = arguments.gender />  
  <cfreturn this />  
</cffunction>

<cffunction name="setPunchAlgorithm" access="public">
  <!--- The next two lines are key. It's where you set the implementation --->
  <!--- of punch from an object that's being passed in. --->
  <cfargument name="PunchAlgorithm" type="IPunchAlgorithm" required="true" />  
  <cfset this.punchAction = PunchAlgorithm.punch />
</cffunction>

<cffunction name="setKickAlgorithm" access="public">
  <cfargument name="KickAlgorithm" type="IKickAlgorithm" required="true" />  
  <cfset this.kickAction = KickAlgorithm.kick />
</cffunction>

<!--- This function will be overridden.  --->
<cffunction name="energyProject" access="public" returntype="SuperHero" >  
  You have been pointed at by a weak flash light.  
  <cfreturn this />
</cffunction>

</cfcomponent>

IPunchAlgorithm.cfc

<cfinterface>

<cffunction name="punch" access="public" />

</cfinterface>

Punch.cfc

<cfcomponent implements="IPunchAlgorithm">

<cffunction name="punch" access="public">
  You have been punched normally. Ouch.
</cffunction>

</cfcomponent>

PunchFreeze.cfc

<cfcomponent implements="IPunchAlgorithm">

<cffunction name="punch" access="public">
  You have been punched and are now frozen, stuck. Good luck thawing!
</cffunction>

</cfcomponent>

IKickAlgorithm.cfc

<cfinterface>

<cffunction name="kick" access="public" />

</cfinterface>

Kick.cfc

<cfcomponent implements="IKickAlgorithm">

<cffunction name="kick" access="public">
  You have been kicked in the gut. Yummy.
</cffunction>

</cfcomponent>

Freezer.cfc

<cfcomponent extends="SuperHero">

<!--- We are overriding the energyProject from Freezer's parent class.  --->
<cffunction name="energyProject">
  You have been snowed on. 
</cffunction>  

</cfcomponent>

Now let’s actually use these the pattern:

run.cfm

<!--- Create context object.  --->
<cfset IceMan = CreateObject( "component", "SuperHero" ).init( Name = "Iceman", Gender = "Male" ) />

<!--- Create a strategy #1 for punching.  --->
<cfset PunchStrategy_1 = CreateObject( "component", "Punch" ) />

<!--- Create a different strategy for punching.  --->
<cfset PunchStrategy_2 = CreateObject( "component", "PunchFreeze" ) />

<!--- Tell the IceMan object that you'll be using the Punch Strategy #2, NOT #1.  --->
<cfset IceMan.setPunchAlgorithm( PunchStrategy_2 ) />


<!--- Now let's create a kicking strategy... --->
<cfset KickStrategy = CreateObject( "component", "Kick" ) />

<!--- ... and now let's tell the IceMan object that you'll be using the Kick Strategy.  --->
<cfset IceMan.setKickAlgorithm( KickStrategy ) />


<!--- Now let's see some action!  --->

<!--- Punch, using the strategy chosen! --->
<cfset IceMan.punchAction() />

<!--- Punch, using the strategy chosen! --->
<cfset IceMan.kickAction() />


<!--- Let's create another SuperHero object. --->
<cfset Frosty = CreateObject( "component", "SuperHero" ).init( Name = "Frosty the Snowman", Gender = "Unknown" ) />

<!--- Let's inspect the objects --->
<p>Notice that both objects have different number of methods - only the methods they need. </p>
<cfdump var="#IceMan#" /><hr />
<cfdump var="#Frosty#" /><hr />


<!--- Let's create another SuperHero object. --->
<cfset Frostman = CreateObject( "component", "Freezer" ).init( Name = "Calvin Hobbes", Gender = "Male" ) />
<cfset Frostman.energyProject() />

You can download all the CF code here with the original Visio diagram source.

Tag a Revision using Subclipse

Tagging a revision is a simple task if you’re using the subclipse plugin for Eclipse. For this example, we’re going to tag the trunk in our repository. This also assumes that your repository uses the traditional “3 directory setup” of branches, tags, and trunk.

1. Make sure you have a SVN View tab in Eclipse.

To do this, go to:
Window > Show View > Other > SVN > SVN Repositories

2. Under “WebSites” (in the following screenshot), right click on the trunk, and select “Branch/Tag…”:

3. Make sure you use the appropriate tag version that adheres to your versioning needs. Also, make sure it’s under the “tags” folder: svn://WebSites/Project-2/tags/

NES Emulator in JavaScript

super-mario-bros-duck-hunt-u-_001

This is pretty impressive. It’s an emulator in JavaScript. I’ll have to see how many instructions get run through the profiler and how it reads the binary ROM file (perhaps it was converted beforehand).

JavaScript for the Windows Command Console

JSDB is a great JavaScript interpreter based on Mozilla’s Spidermonkey JavaScript engine. If you like working off the command console, I suggest you give it a go. Of course, not everything is supported, as there is no HTML/DOM context in the console. To compensate for it, the author of this tool has added some great features, like being able to connect to databases, use includes ( easily via the load(“file.js”) function ), and other network facilities, like fetching HTML content from other websites.

Best Practices for Speeding Up Your Web Site

Yahoo has this awesome article on speeding up your web pages. Granted, most of this could be plucked out directly from your site using the Yahoo! YSlow Firefox add-on, but I highly recommend you checking it out. Here’s a summary of what they have to offer:

  1. Minimize HTTP Requests
  2. Use a Content Delivery Network
  3. Add an Expires or a Cache-Control Header
  4. Gzip Components
  5. Put Stylesheets at the Top
  6. Put Scripts at the Bottom
  7. Avoid CSS Expressions
  8. Make JavaScript and CSS External
  9. Reduce DNS Lookups
  10. Minify JavaScript and CSS
  11. Avoid Redirects
  12. Remove Duplicate Scripts
  13. Configure ETags
  14. Make Ajax Cacheable
  15. Flush the Buffer Early
  16. Use GET for AJAX Requests
  17. Post-load Components
  18. Preload Components
  19. Reduce the Number of DOM Elements
  20. Split Components Across Domains
  21. Minimize the Number of iframes
  22. No 404s
  23. Reduce Cookie Size
  24. Use Cookie-free Domains for Components
  25. Minimize DOM Access
  26. Develop Smart Event Handlers
  27. Choose over @import
  28. Avoid Filters
  29. Optimize Images
  30. Optimize CSS Sprites
  31. Don’t Scale Images in HTML
  32. Make favicon.ico Small and Cacheable
  33. Keep Components under 25K
  34. Pack Components into a Multipart Document

Another great article to check out is this one.

JSDB from Aptana Studio

Aptana Studio is a great free IDE for JavaScript. By default, you can use the its Jaxer engine to interpret your JavaScript code. However, if you prefer to use JSDB, a more console-oriented interpreted based on Mozilla SpiderMonkey, keep reading. This will set you up so that from Aptana Studio, you can write JS code, and interpret it by hitting F5 and seeing results in the bottom panel.

1. Select External Tools

1

2. Enter the following in the textboxes:

2

Make sure the path to your project doesn’t have spaces! (bug with Eclipse)

3. Set the hotkey to F5 so it runs the external tool:

3

I’ve Switched to Python from Perl

So I’ve finally dumped Perl for my systems scripts. Partly was for maintainability. Overall, when doing some benchmarks myself, it seems that Perl beats Python in simple text parsing and file manipulation, which is most of the time is what I use it for. Ugh. I do find it though, that in most teams, Perl can be cryptic and unnecessarily harder for one to jump into. Python solves this. I think Python (after playing around with it for about a week) is a much more elegant language. Python will be a great addition to my toolkit for system automation. Much easier to apply OOP principles and write readable code. It’s a pleasure to write in this language and I look forward to learning more about it.

Also, while searching for performance tests on which language was “faster,” I ran across this site: The Great Win32 Computer Language Shootout . Of course, not to be used as a definitive guide, it does serve as a baseline, I think, for very simplistic tasks in a language.

On a related note, here’s a great video I saw on “Python in the Enterprise – How to Get Permission”:

If you start your own company or run your own project you can usually choose the programming language, but if you work for a large company there are probably architects and others who keep a tight rein on approved technology. How do you steer a big ship towards dynamic programming languages, and how fast can it turn? Come hear the story of one software developer employee who in 20 months facilitated the adoption of Python as the standard scripting language for an enterprise with 25,000 employees. Leave with ideas for advancing dynamic programming languages in your workplace, and with hope that change is possible.

I looked into Ruby, and found various similarites. Python sold me due to its larger community and greater applications in the wild. I took a look at PHP for system scripting and it wasn’t fast enough for parsing large files. Lastly, I thought about JavaScript on the console via JSDB but then realized its breadth of native library functions wasn’t as wide as that of Python. I really love that Python is getting a lot of momentum from Google and Microsoft is doing more to support the IronPython (Python on .NET) platform.

Head First AJAX

Currently reading a great AJAX book, Head First AJAX, by Rebecca Riordan. I was a little skeptical at first by the format of these “Head First” books. I had heard so much about these books that I decided to give it a shot. I have to say I’m very pleased. It makes the material very easy to understand, and I certainly need a good book to review my AJAX.

So what is AJAX?

At its core, AJAX is just making a part of the page do something else while not refreshing the entire page. So for example, you can press a button, and just the button alone would load data from the server and display it on another part of the page (maybe right above it) while not loading the entire page. This is benefit one. It uses the JavaScript XmlHttpRequest (XHR) object .

Benefit two, is that with that XHR object, you can make HTTP requests that traditional HTML pages cannot. An HTML page can make a GET or POST request (e.g. via a form). With an XHR object, you have all HTTP request methods available.

When AJAX first came out, it used just XML – the server side script (e.g. ASP page) would generate XML data and the XHR object would retrieve it. Now, you can use JSON (a native JavaScript data structure) or any form of data structure you want. So the “X” in AJAX really is any type of data structure you want to pass the data from the ASP page to the XHR object.

Some JavaScript frameworks make AJAX extremely easy to use. jQuery can make a GET request using one line of code (compared to the many in the example code from my site). Also, jQuery is part of the .NET framework (last I heard MS announcing it) and should be integrated into Visual Studio.NET.

Here’s a great snippet that bundles the logic to return an XmlHttpRequest object if the browser can in fact create the object. I find it quiet useful.

function createRequest() {
  try {
    request = new XMLHttpRequest();
  } catch (tryMS) {
    try {
      request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (otherMS) {
      try {
        request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (failed) {
        request = null;
      }
    }
  } 
  return request;
}

Some additional code, wrapped in functions, that sends the request object to the server, and fires the showUsernameStatus function. This is grabbed from the book site. Posting it here for my own benefit.

window.onload = initPage;

function initPage() {
  document.getElementById("username").onblur = checkUsername;
  document.getElementById("register").disabled = true;
}

function checkUsername() {
  document.getElementById("username").className = "thinking";
  request = createRequest();
  if (request == null)
    alert("Unable to create request");
  else {
    var theName = document.getElementById("username").value;
    var username = escape(theName);
    var url= "checkName.php?username=" + username;
    request.onreadystatechange = showUsernameStatus;
    request.open("GET", url, true);
    request.send(null);
  }
}

function showUsernameStatus() {
  if (request.readyState == 4) {
    if (request.status == 200) {
      if (request.responseText == "okay") {
        document.getElementById("username").className = "approved";
        document.getElementById("register").disabled = false;
      } else {
        document.getElementById("username").className = "denied";
        document.getElementById("username").focus();
        document.getElementById("username").select();
        document.getElementById("register").disabled = true;
      }
    }
  }
}

Here’s a simple way to read a text file (data.txt), barebones, on Firefox, without checking if XMLHttpRequest object exists or not.

<html>
<head>
<script language="JavaScript">  
ajax = new XMLHttpRequest();  
function ShowData()
{  
  ajax.open( "GET", "data.txt", true );  
  ajax.onreadystatechange = function() 
  {
    if ( ajax.readyState == 4 ) 
    {      
      document.getElementById("DataPanel").innerHTML = ajax.responseText;
    }
  }    
  ajax.send(null);
}  
</script>  

<title>hey!</title>
</head>
<body>
  <button onclick="ShowData()">Show Data</button>   
  <div id="DataPanel"></div>  
</body>
</html>

This is data.txt, which is in the same directory as the html page above:

05/12/2009  10:32 PM             1,024 .rnd
10/03/2009  12:55 AM               428 1.txt
05/11/2009  10:37 PM                 0 AUTOEXEC.BAT
09/29/2009  09:56 PM             5,256 bar.emf
05/11/2009  10:37 PM                 0 CONFIG.SYS
12/06/2009  12:43 AM              Dan
12/06/2009  12:45 AM                 0 data.txt
05/11/2009  11:00 PM              DELL
05/11/2009  10:40 PM              Documents and Settings
05/11/2009  11:28 PM              Intel
11/24/2009  11:53 PM              Program Files
07/18/2009  12:21 AM              temp
11/24/2009  11:57 PM              WINDOWS

Assigning Handlers on Load

Rather than assigning event handlers to elements inline from within HTML, like so:

<a id="site" href="http://www.shinylight.com">Go to my site!</a>

You can assign the event handler programmatically via the onload event of the window object. Like so, in the script element:

window.onload = function() 
{
  document.getElementById("linkcontent").onmouseover = Show;   
}

function Show( )
{  
  alert( "Hey there, how are you?" + this.href );
}

Here’s the HTML:



<div id="content">
  <a id="linkcontent" href="http://www.shinylight.com?var=yes">This is pretty cool</a>
</div>  



This is just a great way to keep HTML code away from JavaScript. In this case, the handler for window.load will run when all web page resources have finished loading (the full HTML and JavaScript files).