AJAX Libraries API from Google

Posted in Google, Javascript, Web2.0 on June 2nd, 2008 by admin / No Comments »

As you might have heard, Google has joined the ranks of AOL and others with their newly released AJAX Libraries API. What does this mean for you as a developer? Well, Google will now provide hosting of various JS libraries. So, you get the power of Google to serve the file to your clients, so you can depend on their reliability and incredible speed.

This was just one of the several major announcements that were released at last weeks Google I/O developer conference that I was able to attend. Here is what they say:

The AJAX Libraries API is a content distribution network and loading architecture for the most popular open source JavaScript libraries. By using the Google AJAX API Loader’s google.load() method, your application has high speed, globaly available access to a growing list of the most popular JavaScript open source libraries including:

Google works directly with the key stake holders for each library effort and accept the latest stable versions as they are released. Once we host a release of a given library, we are committed to hosting that release indefinitely.

Read more: http://code.google.com/apis/ajaxlibs/

Jpeg resolution via EXIF Metadata

Posted in ColdFusion, ColdFusion 8, Java on April 17th, 2008 by admin / No Comments »

I recently worked on a project that required obtaining the resolution of photograph. The approach I used was to limit the photographs to Jpeg format images only, which I could then use the new imageGetEXIFMetada() function in ColdFusion 8 to obtain the X-Resolution and Y-Resolution tags. Here is a quick summary of some of my findings and problems.

First off, lets make sure we’re all on the same page with EXIF - because I had no idea what the EXIF metadata was when I first started the project. So, lets have our friend wikipedia explain it to us:

Exchangeable image file format (Exif) is a specification for the image file format used by digital cameras. The specification uses the existing JPEG, TIFF Rev. 6.0, and RIFF WAV file formats, with the addition of specific metadata tags. It is not supported in JPEG 2000, PNG, or GIF.

Basically, the Jpeg file format allows for a variety of custom metadata segments to be stored in the Jpeg image file including the Exif metadata and the IPTC format (usually stores information about the author). So, if you have a digital camera, your Jpeg images will contain information about the camera and the photograph, including the resolution of the image.

EXIF - a standard?

However, from what I have experienced, this “standard” isn’t really that consistent, and depends largely upon the implementation chosen by camera manufacturers and photo editing software makers. Also, keep in mind that not all Jpegs will contain EXIF or IPTC metadata or may only contain some information.

IFD - wtf?

So, after performing some tests on several images using CF 8.0.1 and the imageGetExifMetadat() function, I thought perhaps there was more there than what was being returned by CF.

The Exif format uses several Image File Directories (IFD) to store information about the main image, a link to the sub IFD (if applicable), and a link to the location of IFD1, which is the IFD for the thumbnail image. In the end, this means that there are potentially 3 directories of metadata: 0 contains information about the main image, SubIFD contains information about the digital camera (such as shutter speed, focal length, etc…), and 1 contains information about the thumbnail and the thumbnail itself.

The first thing I did was download the Firefox plugin called Exif Viewer. This tool allows you to view all of the EXIF and IPTC metadata embedded into a local or remote Jpeg file. First thing I noticed with this is that it appears that CF (which in turn uses the metadata-extractor Java library) is pulling the resolution from the thumbnail properties.

Note: the reason why my thumbnail and main image have different resolutions is because I used PS to adjust the image’s resolution and size. I think it is safe to assume that many people will crop/alter photos prior to uploading them into a web application. However, for those that do not, the resolution of both the main image and the thumbnail should be the same - so you may not experience this issue when that is the case.

Testing it out

Using a sample image, lets look at the difference between the structure returned by CF (remember, its just a Java metadata-extractor library). Here is what is returned from the Exif Viewer Firefox plugin:

Exif IFD0
* Camera Make = Apple
* Camera Model = iPhone
* Picture Orientation = normal (1)
* X-Resolution = 3000000/10000 = 300
* Y-Resolution = 3000000/10000 = 300
* X/Y-Resolution Unit = inch (2)
* Software/Firmware Version = Adobe Photoshop CS3 Windows
* Last Modified Date/Time = 2008:04:15 09:15:42

Exif Sub IFD
* Lens F-Number/F-Stop = 14/5 = F2.8
* Original Date/Time = 2007:11:08 12:26:54
* Digitization Date/Time = 2007:11:08 12:26:54
* Colour Space = sRGB (1)
* Image Width = 1200 pixels
* Image Height = 900 pixels
* Unknown tag: Tagnum = 0xa500 ===> data = 11/5

Exif IFD1
* Compression = JPEG compression (6)
* X-Resolution = 72/1 = 72
* Y-Resolution = 72/1 = 72
* X/Y-Resolution Unit = inch (2)

And, here is what is returned from ColdFusion:

Wait a second? CF is telling me that the image has a resolution of 72 DPI, when Exif viewer is telling me that in fact the resolution of the image is 300 dpi and the resolution of the embedded thumbnail is 72 dpi. My inclinations tell me that CF is pulling the wrong information, but we know that CF is just putting a nice wrapper on the metadata-extractor Java lib. So, my thinking is that either CF’s implementation is flawed, the Java lib is flawed, or this whole concept of Exif metadata is flawed (which some have argued).

Using Metadata-extractor

Based on my previous test (and other tests that I performed), I wanted to determine if I could pull the resolution from the correct IFD using the metadata-extractor Java lib. For some reason I thought I could do better than the engineers at Adobe (hah! riggghhhttt) who are Java programmers (and I haven’t touched much Java code since college). Well, just to prove myself wrong, I tried (and didn’t get anywhere, if its worth noting) and here is what I came up with.

First, there are several approaches that can be used to get at the Exif metadata using this library. I decided to try 3 different methods:

  1. ExifReader class implementation,
  2. JpegSegmentReader class implementation, and
  3. JpegMetadataReader class implementation.

For each implementation I used some common variables that I param out at the beginning:

[-]View Code COLDFUSION
1
 

The following is code to read the Exif meta data and loop over each directory and output all the tag information for each implementation above:

ExifReader implementation

Code:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
extractor = createObject("java","com.drew.metadata.exif.ExifReader").init(createObject("java","java.io.File").init(expandPath('./')&photoname));
md = extractor.extract();
directories = md.getDirectoryIterator();
while (directories.hasNext()){
    directory = directories.next();
    tags = directory.getTagIterator();
    while (tags.hasNext()) {
         tag = tags.next();
         WriteOutput(tag.getTagName()&': '&tag.getDescription()&'');
    }
}

Output:
Make: Apple
Model: iPhone
Orientation: Top, left side (Horizontal / normal)
X Resolution: 72 dots per inch
Y Resolution: 72 dots per inch
Resolution Unit: Inch
Software: Adobe Photoshop CS3 Windows
Date/Time: 2008:04:15 09:15:42
F-Number: F2.8
Date/Time Original: 2007:11:08 12:26:54
Date/Time Digitized: 2007:11:08 12:26:54
Color Space: sRGB
Exif Image Width: 1200 pixels
Exif Image Height: 900 pixels
Unknown tag (0xa500): 2.2
Compression: JPEG (old-style)
Thumbnail Offset: 442 bytes
Thumbnail Length: 8874 bytes
Thumbnail Data: [8874 bytes of thumbnail data]

JpegSegmentReader class implementation

Code:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
segmentReaderObj = createObject("java","com.drew.imaging.jpeg.JpegSegmentReader");
segmentReader = segmentReaderObj.init(createObject("java","java.io.File").init(expandPath("./")&photoname));
exifSegment = segmentReader.readSegment(segmentReaderObj.SEGMENT_APP1);
iptcSegment = segmentReader.readSegment(segmentReaderObj.SEGMENT_APPD);
metadataObj = createObject("java","com.drew.metadata.Metadata");
exifReaderObj = createObject("java","com.drew.metadata.exif.ExifReader");
iptcReaderObj = createObject("java","com.drew.metadata.iptc.IptcReader");
metadata = metadataObj.init();
exifreader = exifReaderObj.init(exifSegment);
exifdata = exifreader.extract(metadata);
if (structKeyExists(variables,'iptcsegment')){
    iptcdata = iptcReaderObj.init(iptcSegment).extract(metadata);
}
 
//write out the exif data
directories = exifdata.getDirectoryIterator();
while (directories.hasNext()){
    directory = directories.next();
    tags = directory.getTagIterator();
    while (tags.hasNext()) {
         tag = tags.next();
         WriteOutput(tag.getTagName()&': '&tag.getDescription()&'');
    }
}
//write out the iptc data
if (structKeyExists(variables,'iptcsegment')){
    directories = iptcdata.getDirectoryIterator();
    while (directories.hasNext()){
        directory = directories.next();
        tags = directory.getTagIterator();
        while (tags.hasNext()) {
             tag = tags.next();
             WriteOutput(tag.getTagName()&': '&tag.getDescription()&'');
        }
    }
}

Output:
Make: Apple
Model: iPhone
Orientation: Top, left side (Horizontal / normal)
X Resolution: 72 dots per inch
Y Resolution: 72 dots per inch
Resolution Unit: Inch
Software: Adobe Photoshop CS3 Windows
Date/Time: 2008:04:15 09:15:42
F-Number: F2.8
Date/Time Original: 2007:11:08 12:26:54
Date/Time Digitized: 2007:11:08 12:26:54
Color Space: sRGB
Exif Image Width: 1200 pixels
Exif Image Height: 900 pixels
Unknown tag (0xa500): 2.2
Compression: JPEG (old-style)
Thumbnail Offset: 442 bytes
Thumbnail Length: 8874 bytes
Thumbnail Data: [8874 bytes of thumbnail data]
Directory Version: -23
Make: Apple
Model: iPhone
Orientation: Top, left side (Horizontal / normal)
X Resolution: 72 dots per inch
Y Resolution: 72 dots per inch
Resolution Unit: Inch
Software: Adobe Photoshop CS3 Windows
Date/Time: 2008:04:15 09:15:42
F-Number: F2.8
Date/Time Original: 2007:11:08 12:26:54
Date/Time Digitized: 2007:11:08 12:26:54
Color Space: sRGB
Exif Image Width: 1200 pixels
Exif Image Height: 900 pixels
Unknown tag (0xa500): 2.2
Compression: JPEG (old-style)
Thumbnail Offset: 442 bytes
Thumbnail Length: 8874 bytes
Thumbnail Data: [8874 bytes of thumbnail data]
Directory Version: -23

JpegMetadataReader class implementation

Code:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
metadataReaderObj = createObject("java","com.drew.imaging.jpeg.JpegMetadataReader");
metadata = metadataReaderObj.readMetadata(createObject("java","java.io.File").init(expandPath("./")&photoname));
//write out the exif data
directories = metadata.getDirectoryIterator();
while (directories.hasNext()){
    directory = directories.next();
    tags = directory.getTagIterator();
    while (tags.hasNext()) {
         tag = tags.next();
         //WriteOutput(tag.getTagName()&': '&tag.getDescription()&'');
        WriteOutput(tag.toString()&'');
    }
}

Output:
[Exif] Make - Apple
[Exif] Model - iPhone
[Exif] Orientation - Top, left side (Horizontal / normal)
[Exif] X Resolution - 72 dots per inch
[Exif] Y Resolution - 72 dots per inch
[Exif] Resolution Unit - Inch
[Exif] Software - Adobe Photoshop CS3 Windows
[Exif] Date/Time - 2008:04:15 09:15:42
[Exif] F-Number - F2.8
[Exif] Date/Time Original - 2007:11:08 12:26:54
[Exif] Date/Time Digitized - 2007:11:08 12:26:54
[Exif] Color Space - sRGB
[Exif] Exif Image Width - 1200 pixels
[Exif] Exif Image Height - 900 pixels
[Exif] Unknown tag (0xa500) - 2.2
[Exif] Compression - JPEG (old-style)
[Exif] Thumbnail Offset - 442 bytes
[Exif] Thumbnail Length - 8874 bytes
[Exif] Thumbnail Data - [8874 bytes of thumbnail data]
[Iptc] Directory Version - -23
[Jpeg] Data Precision - 8 bits
[Jpeg] Image Height - 900 pixels
[Jpeg] Image Width - 1200 pixels
[Jpeg] Number of Components - 3
[Jpeg] Component 1 - Y component: Quantization table 0, Sampling factors 1 horiz/1 vert
[Jpeg] Component 2 - Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert
[Jpeg] Component 3 - Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert

Conclusion

In summary, it appears that there is not a consistent and reliable way to determine the resolution of an image using CF. I am aware that there are external tools and libraries outside of Java and CF that I could use to obtain this information, such as using imageMagik, however, I have not attempted any of these approaches.

Maybe I am wrong with my approaches and implementations, and would love to hear any suggestions, thoughts, and experiences you have had with EXIF metadata or determining the resolution of an image using ColdFusion.

AIR Presentation

Posted in AIR, Adobe on April 14th, 2008 by admin / No Comments »
Adobe AIR Presentation thumbnail
I gave a brief presentation on Adobe AIR to my colleagues today for our introducing new technology segment of the Hamilton College ITS department meeting, and thought I would share it. The presentation was geared towards introducing AIR to technologists who are not web developers, but have a basic understanding of web technologies. I think the presentation was well accepted, and hopefully inspired some people as to the possibilities that AIR introduces.

Downloads:

Safely upload photos in CF

Posted in Adobe, ColdFusion on April 14th, 2008 by admin / No Comments »

Borrowing some code & ideas from Pete Freitag’s CFIMAGE presentation; this is a rather simple, safe, and easy way to upload photos using a User-Defined Function in ColdFusion. Uploading files is very straight forward in ColdFusion, but I like this function because it takes care of all the little details, so I can concentrate on working with the file afterwards, allowing me to move on to the next iteration.

So, let’s take a quick look at the uploadPhoto function that Pete wrote, which I then modified to add some additional checks and logic. First off, lets assume you have a simple form that looks like this:

1
2
3
4
5
6
7
8
9
 <cfform name="photouploadform" action="#cgi.script_name#" method="post" enctype="multipart/form-data">
  <fieldset>
    <legend>Upload your photo</legend>
      <div class="fm-line fm-req">
                  <label for="photo">Select photograph</label>
                <cfinput type="file" name="photo" value="" required="yes" message="Please select a file to upload"  validateat="onSubmit,onServer" />
      </div>
  </fieldset>
</cfform>

To upload the photo, we simply call the uploadPhoto function:

[-]View Code COLDFUSION
1
<cfset photofile = uploadPhoto('photo',photodir,'jpg,jpeg','overwrite',photoname)>

So, what does the code do? Lets take a look:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<cffunction name="uploadPhoto" returntype="string" hint="uploads a photo from a form field and returns the filename">
    <cfargument name="filefield" required="true" type="variablename" default="photo" hint="the form field that contains the photo">
    <cfargument name="destination" required="true" default="#expandPath('./')#" hint="specify the destination directory">
    <cfargument name="allowed_extensions" required="false" default="gif,jpg,jpeg,png" type="string" hint="specify valid file extensions">
    <cfargument name="name_conflict" required="false" type="variablename" default="makeunique" hint="specify action for name conflict (makeunique|overwrite|none)">
    <cfargument name="filename" required="false" hint="specify the desired name of the destination file">
    <cfargument name="create_destination" required="false" type="boolean" default="true" hint="specify if destination directory should be created if it does not exist, throw exception otherwise">
 
    <cfset var temp_dir = GetTempDirectory()>
    <cfset var file_name = "">
    <cfset var slash = CreateObject("java", "java.io.File").separator >
 
    <cfif NOT StructKeyExists(form, arguments.filefield)>
        <cfthrow message="filefield is not defined.">
    </cfif>
    <cffile action="upload" filefield="form.#arguments.filefield#" accept="image/jpg,image/jpeg,image/gif,image/png" nameconflict="#arguments.name_conflict#" destination="#temp_dir#">
    <cfif NOT ListFindNoCase(arguments.allowed_extensions, cffile.ClientFileExt) OR NOT ListFindNoCase(arguments.allowed_extensions, cffile.ServerFileExt)>
 
        <!--- this is why we uploaded to a temp dir so they can't execute it before we delete it--->
        <cffile action="delete" file="#cffile.ServerDirectory#/#cffile.ServerFile#">
        <cfthrow message="Sorry file type not allowed.">
    </cfif>
 
    <!--- Make sure directory exists, if not create it if arguments.create_destination is set to true, or throw error --->
    <cfif NOT DirectoryExists(arguments.destination)>
        <!--- Try expanding the path if destination does not start with a slash (UNC Path or *nix path) or drive letter followed by a colon --->
        <cfif NOT (ReFind('[/\\]',Left(arguments.destination,1)) OR Mid(arguments.destination,2,1) eq ':')>
            <cfset arguments.destination = expandPath(arguments.destination)>
        </cfif>
        <cfif NOT DirectoryExists(arguments.destination)>
            <cfif arguments.create_destination>
                <cfdirectory action="create" directory="#arguments.destination#">
            <cfelse>
                <cfthrow message="Destination directory does not exist." detail="#arguments.destination#">
            </cfif>
        </cfif>
    </cfif>
 
    <!--- Ensure that arguments.destination ends with a slash --->
    <cfif NOT ReFind('[/\\]',Right(arguments.destination,1))>
        <cfset arguments.destination &= slash>
    </cfif>
 
    <!--- Make the file name unique prior to moving file --->
    <cfif structKeyExists(arguments,'filename') AND trim(arguments.filename) neq ''>
        <cfset file_name = lcase(arguments.filename)>
    <cfelse>
        <cfset file_name = LCase(cffile.ServerFile)>
    </cfif>
    <cfif LCase(arguments.name_conflict) eq "makeunique">
        <cfset file_name = makeFilenameUnique(arguments.destination&file_name)>
    </cfif>
    <cffile action="move" source="#cffile.ServerDirectory#/#cffile.ServerFile#" destination="#arguments.destination##file_name#">
    <cfreturn file_name>
</cffunction>

The uploadPhoto() function starts by setting some local function variables, followed by checking that the field passed into the filefield argument exists within the ‘form’ scope in ColdFuion. Next, we actually perform the file transfer from the web server to ColdFusion’s temp directory. I am using the function GetTempDirectory() to obtain the directory. According to the documentation:

The directory depends on the account under which ColdFusion is running and other factors. Before using this function in an application, test to determine the directory it returns under your account.

After uploading the file to the temp directory, I check the file extension to ensure that the file is safe to move into under my web root. Remember that it is very easy to fool the mime type that is sent to the server in the request headers when the file is posted, so you need to check the file extension in addition to specify the accept attribute in the <cffile> tag.

Next, I perform some checks on the destination directory. First, I check if the destination directory exists as specified in the arguments. If not, I try to use the expandPath() function in ColdFusion to find the destination directory. Often times I will pass in either the full absolute path to the destination, or may just end up passing in the relative path. This makes it simple to use the function; I don’t need to worry about whether or not to specify the full path.

Lastly, I want to make sure that the file name is unique prior to moving the file from the temp directory to its final location. To do this, I wrote a simple makeFilenameUnique() method:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<cffunction name="makeFilenameUnique" access="public" output="true" returntype="string">
    <cfargument name="path" type="string" required="true" />
    <cfargument name="index" type="numeric" required="false" default="0">
    <cfscript>
        var fullfilename = listlast(arguments.path,"\");
        var pathwithoutfile = replace(arguments.path,fullfilename,"","ALL");
        var fileext = listlast(fullfilename,".");
        var filename = replace(fullfilename,"."&fileext,"","ALL");
        if (arguments.index gt 0){
            arguments.path = pathwithoutfile&filename&arguments.index&'.'&fileext;
        }
        //Writeoutput(arguments.path);
        if (not fileexists(arguments.path)){
         //return arguments.path;
            return fullfilename;
        }else{
            arguments.index++;
            return makeFilenameUnique(pathwithoutfile&filename&'.'&fileext,arguments.index);
        }
    </cfscript>
</cffunction>

This is a simple function that I wrote a while back that creates a unique filename in the same manner that ColdFusion does when using the overwrite=”makeunique” attribute when using some <cffile> actions. This function basically just attempts to append a single numerical digit to the end of the filename. If it is still not a unique file name, it keeps adding 1 to the number; and keeps going until the file name is unique.

Note: these functions use some CF8 features, such as the use of the ++ incremental operator. You can simply remove these and replace it with arguments.index = arguments.index + 1; for example, and this function will work with CF6 or greater.

ColdFusion 8.0.1 updater on linux

Posted in Adobe, ColdFusion, ColdFusion 8 on April 5th, 2008 by admin / No Comments »

I ran into two small hurdles when installing the ColdFusion 8.0.1 updater on my CentOs Linux server.  Here is a quick overview of what I did to get the installer to work.

First, the file that Adobe provides is incorrectly labeled as a ZIP archive file (the file extension is .zip).  This led me to believe that I had to first unzip the file, and then look for the shell script to execute that would start the installer.  Well, that is incorrect, and thanks to James Holmes on the CF-TALK listsev for helping me out.  All you need to do is make the file executable and then execute the script:

[-]View Code COLDFUSION
1
2
[root@localhost ~]$ chmod +x coldfusion-801-lin_updater.zip
[root@localhost ~]$ ./coldfusion-801-lin_updater.zip

Secondly, I ran into another problem during the updater process:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
Preparing to install...
Extracting the JRE from the installer archive...
Unpacking the JRE...
Extracting the installation resources from the installer archive...
Configuring the installer for this system's environment...
 
Launching installer...
 
./coldfusion-801-lin_updater.zip: line 2479: /tmp/install.dir.9795/Linux/resource/jre/bin/java: Permission denied
./coldfusion-801-lin_updater.zip: line 2479: /tmp/install.dir.9795/Linux/resource/jre/bin/java: Success

I actually ran into the same problem previously when I installed ColdFusion 8 on my server, but I couldn’t recall how I fixed the issue.  After doing some searching, I came across the last comment on the ColdFusion 7 Installation considerations page on livedocs.  To resolve this issue I set the environment variable IATEMPDIR, which is the temp directory used by InstallAnywhere:

[-]View Code COLDFUSION
1
2
[root@localhost ~]$ mkdir /opt/iatempdir/
[root@localhost ~]$ export IATEMPDIR=/opt/iatempdir

Thats it, the installer ran and quickly updated my installation to ColdFusion 8.0.1!

Enumerating with Ext.js

Posted in Javascript on April 4th, 2008 by admin / No Comments »

One of the features of the prototype library that I like to use is the Enumerable class.  Although highly debatable in regards to the legitimate necessity for such class, I like the simplicity and beauty of using Prototype’s enumerable class.

So, when using Ext as a base library (not using the Prototype adapter) for JavaScript DOM manipulation I looked into the enumeration methods available, such as Ext.each().  Here is a quick overview of how I used Ext.each to traverse members of a collection of HTML nodes.  I decided to split this up into main topics; first I will talk about why I like to use the enumerable class, and then show you a simple example of using the Ext.each() method.

Why use enumerable methods?

You might be asking yourself, why?  Great question.  Yes, I can traverse over nodes using several looping operators such as for().  However, using Ext’s traversal methods allows me to call other methods and maintain context over this.

Prototype’s enumerable class is inherited from Rails, and allows for ease of implementing helpers in Ruby that generate JavaScript code or coding in rjs (JS templating engine providing element and collection proxies), which is translated into JavaScript code when delivered to the client.  Therefore, Prototype’s Enumerable class implementation is basically borrowed from the Ruby Enumerable API; lending the elegant use of blocks (anonymous functions in JS) to apply a snippet of code to a sequence of objects.

Still not convinced about enumerables?  Well, lets compare two snippets of code that check all checkboxes within a form dependent upon the checked status of a single checkbox - think of the ‘ole ‘check all’ checkbox.

Without using enumerables, I might do the following (still using prototype library):

[-]View Code COLDFUSION
1
2
3
4
5
6
7
function checkAll(formid,ctrl){
  var checkboxes = $(formid).getInputs('checkbox');
  var state = $(ctrl).checked;
   for (var i=0;i<checkboxes.length;i++){
     checkboxes[i].checked = state;
  }
}

With Prototype’s enumerables, I would be able to accomplish this in one line (thanks to method chaining):

[-]View Code COLDFUSION
1
2
3
function checkAll(formid,ctrl){
  $(formid).getInputs('checkbox').each(function(checkbox){checkbox.checked=$(ctrl).checked;});
}

Using Ext.each() method

So, lets dive in and look at the Ext.each() method.  The method syntax looks like this:

eachArray/NodeList/Mixed array, Function fn, Object scope ) : void

Basically, for each element in the Array/NodeList/Mixed array argument, the function specified in the second argument is called passing in each item in the array as the first argument, bound to the object scope specified.  The following example using Ext.query() (using CSS selection) to obtain all elements with the class name ‘addlink’, and change the behavior of the link.  This is an unobtrusive method to applying behavior to HTML elements after the page loads:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
function addEventListeners(){
    var links = Ext.query('.addlink');
    if (links.length){
     Ext.each(links,function(el){
         var element = Ext.get(el);
         element.on('click', function(){
             ColdFusion.navigate(this.href,'editwindow');
             ColdFusion.Window.onShow('editwindow',windowShowHandler.createCallback('Add user','editwindow'));
             ColdFusion.Window.show('editwindow');
         },this,{stopEvent:true});
     });
    }
}

Lets take a look at this closely.  After obtaining a nodelist of elements whose class attribute contains the string ‘addlink’, I am traversing the list of nodes passing the individual node into an anonymous function.  Inside the anonymous function, I am adding an event listener to the click event, and preventing the default action from occurring.

Example

Lets look at the <cfwindow> example.  Notice that if you click the link below the grid ‘Add user’, a <cfwindow> is appropriately opened up with the form.  Now, disable JS and reload the page.  You should now notice that clicking the ‘Add user’ link does not open a cfwindow tag, rather it keeps the traditional behavior specified by the href attribute in the <a>tag.  Sidenote:  Of course, the <cfgrid> no longer works with JS disabled, so this isnt really a practical example, but at least shows how unobtrusive JavaScript fails gracefully.

Set cfwindow title from cfgrid

Posted in ColdFusion, ColdFusion 8, Ext, Javascript on April 2nd, 2008 by admin / 1 Comment »

If you are using ColdFusion 8’s <cfgrid> tag for a CRUD interface you might be utilizing the editable grid feature or you might be utilizing the <cfwindow> tag.  Because of some of the limitations of inline editing, such as the lack of a rich text editor, I am using <cfwindow> for a project and here is how I am setting the title programmatically from the <cfgrid> binding.

Its actually really simple.  First, I create a <cfajaxproxy> bind attribute to open a <cfwindow> when changing selected rows in the <cfgrid>:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<cfform>
    <cfajaxproxy bind="javascript:todetail({maingrid.id@change},{maingrid.firstname@none},{maingrid.lastname@none})" />
    <cfgrid format="html"
        name="maingrid"
        bind="cfc:#request.cfcpath#users.getGrid({cfgridpage},{cfgridpagesize},{cfgridsortcolumn},{cfgridsortdirection})"
        preservepageonsort="true"
        appendkey="no"
        autowidth="true"
        selectonload="false"
        width="100%"
        style="clear:both;"
        >
        <cfgridcolumn name="id" display="no" />
        <cfgridcolumn name="firstname" header="First name" />
        <cfgridcolumn name="lastname" header="Last name" />
        <cfgridcolumn name="phone" header="Phone" />
        <cfgridcolumn name="department" header="Department" />
    </cfgrid>
</cfform>

Notice that I am calling the todetail() JavaScript function @change of the selected row in the grid, passing in the corresponding hidden ‘id’ column value along with some additional parameters.  These will be used to set the title of the <cfwindow> from the todetail() method:

[-]View Code JAVASCRIPT
1
2
3
4
5
6
function todetail(id,firstname,lastname) {
  var name = firstname+' '+lastname;
  ColdFusion.navigate('#cgi.script_name#?action=form&id='+id,'editwindow');
  ColdFusion.Window.onShow('editwindow',windowShowHandler.createCallback('Edit user - '+name,'editwindow'));
  ColdFusion.Window.show('editwindow');
}

In this method, I am first changing the url of the <cfwindow> using ColdFusion.navigate().  Next, I am adding an event handler to the <cfwindow> (whose name attribute is ‘editwindow’) to call the windowShowHandler() function when the window is shown.  I am using Ext’s createCallback() method to create a reference to the function function where I explicitly pass in the window name and the title.  Lastly, I show the <cfwindow>.  Here is what the windowShowHandler() function looks like:

[-]View Code JAVASCRIPT
1
2
3
4
function windowShowHandler(title,name){
  var win = ColdFusion.Window.getWindowObject(name);
  win.setTitle(title);
}

In this function we simply get the Ext window object and set the title accordingly.  Thanks to Mike for letting me take the time to play with all of this!!

Now, check out the cool demo =)

Thawte - I think not!

Posted in Uncategorized on April 1st, 2008 by admin / No Comments »

Thinking of developing an Adobe AIR application?  Cool, just don’t think about applying for a code signing certificate through Thawte - the provider of the worse customer service I have experienced this year.  I was granted a free code signing certificate from the nice people at the Adobe AIR Marketplace and immediately applied for a code signing certificate for my business.  When I say my business, I mean myself since I am a sole proprietor (yes, this is a  legal business entity in case you forgot Thawte).

After applying for the code signing certificate, I was tole that I will need to provide proof of right; which includes the following documents as quoted directly from the email I received from Thawte:

Fax one of the following Proof of Right documents to (650) 237-8888A. Valid Business License or Business Registration documentB. Articles of Incorporation or Certificate of IncorporationC. Articles of Organization or FormationD. DBA (Doing Business As), Fictitious Business Name, Trade Name, or Assumed Name registrationF. Charter Documentation (For Banks, Universities and Government Agencies) These documents must show proof they have been properly filed by the appropriate government, state or county authority. 

Although thawte’s proof of right document says all I need to supply is a DBA, they are requiring my to file a certificate of authority with the state.  This certificate of authority has one sole purpose: to collect and remit sales tax.  Obviously, being a sole proprietor as a web consultant I do not collect sales tax (I am not required to do so in NY) and do not wish to file a certificate of authority.  I sent an email explaining this to thawte, but they insist that I send them a copy of a certificate of authority, and refused to provide me any additional information (thanks guys for being so helpful).  I still insist that there is absolutely no good reason for obtaining a certificate of authority, other than some numb-nut at the county office building told them I need to have one, for whatever reason:

In researching your filed dba with Oneida County they indicated your must also file a business status with NY State. I contacted NY state and the operator indicated you do need to file for a : Certificate of Authority.

Ironically, I actually called the same number Thawte did (the only information I was able to get from them was the 2 numbers they called to verify my DBA) and spoke with a nice person who indicated that there is NO reason for a business to apply for a certificate of authority if they do not collect state sales tax.

So, unfortunately, it looks like I will not be able to take advantage of this great offer Adobe has provided to AIR developers…  its rather unfortunate that individual developers who work as a sole proprietor cannot be granted a code signing certificate (at least according to thawte).

My question is whether or not an individual developer such as myself should even bother learning and developing for Adobe AIR when I cannot be granted a code signing certificate as a sole proprietor?

CF8 determining if request is Ajax

Posted in AJAX, ColdFusion, ColdFusion 8, Ext, Javascript, Web2.0 on March 28th, 2008 by admin / No Comments »

Have you ever wanted to know if a request is coming from an Ajax call or from a standard GET/POST from the browser when using the ColdFusion 8 Ajax functionality?  I know I have, so I looked to implement a solution as I did with the Prototype library.  The easiest way is to utilize the request headers sent by the browser to the server, which you can modify with an Ajax call programmatically pretty easily.

So how do you modify the headers sent by ColdFusion 8 when using the built-in Ajax functionality?

Unfortunately, since CF 8 uses Ext 1.1 and not Ext 2, there is no global Ext.Ajax object to utilize to manipulate the Ajax request headers.  So, we have two options:

  1. alter the code within your cfajax.js file (within your CFIDE/scripts/ajax/package folder)
  2. Or, we can modify the sendMessage() method within the ColdFusion.Ajax JS Object.

I wanted to stay away from modifying the server-wide cfajax.js file, so I went with option 2.

But if you want to use option 1, just open the file in your favorite IDE or editor, and then add:

[-]View Code COLDFUSION
1
req.setRequestHeader('X-My-header','my value');

before the req.send() method.  You can see where I made the modified below on lines 147 and 152 of cfajax.js:

[-]View Code JAVASCRIPT
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
$L.info("ajax.sendmessage.get","http",[url]);
req.open(_20b,url,_20d);
req.setRequestHeader("X-Requested-With","ColdFusion");
req.send(null);
}else{
$L.info("ajax.sendmessage.post","http",[url,_20c]);
req.open(_20b,url,_20d);
req.setRequestHeader("X-Requested-With","ColdFusion");
req.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
if(_20c){
req.send(_20c);
}else{
req.send(null);
}
}

The second option is to manipulate the sendMessage() method after it has been loaded into the DOM:

[-]View Code JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Ext.onReady(function(){
    ColdFusion.Ajax.sendMessage = function(url,_20b,_20c,_20d,_20e,_20f,_210){
        var $C = ColdFusion;
        var $A = $C.Ajax;
        var $L = $C.Log;
        var req=$A.createXMLHttpRequest();
        if(!_20b){
        _20b="GET";
        }
        if(_20d&&_20e){
        req.onreadystatechange=function(){
        $A.callback(req,_20e,_20f);
        };
        }
        if(_20c){
        _20c+="&_cf_nodebug=true&_cf_nocache=true";
        }else{
        _20c="_cf_nodebug=true&_cf_nocache=true";
        }
        if(window._cf_clientid){
        _20c+="&_cf_clientid="+_cf_clientid;
        }
        if(_20b=="GET"){
        if(_20c){
        _20c+="&_cf_rc="+($C.requestCounter++);
        if(url.indexOf("?")==-1){
        url+="?"+_20c;
        }else{
        url+="&"+_20c;
        }
        }
        $L.info("ajax.sendmessage.get","http",[url]);
        req.open(_20b,url,_20d);
        req.setRequestHeader("X-Requested-With","ColdFusion");
        req.send(null);
        }else{
        $L.info("ajax.sendmessage.post","http",[url,_20c]);
        req.open(_20b,url,_20d);
        req.setRequestHeader("X-Requested-With","ColdFusion");
        req.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        if(_20c){
        req.send(_20c);
        }else{
        req.send(null);
        }
        }
        if(!_20d){
        while(req.readyState!=4){
        }
        if($A.isRequestError(req)){
        $C.handleError(null,"ajax.sendmessage.error","http",[req.status,req.statusText],req.status,req.statusText,_210);
        }else{
        return req;
        }
        }
    };
});

In the example above, I am setting the request headers to include a custom header ‘X-Requested-With’ and setting the value to ‘ColdFusion’.  I don’t think either solution is extremely elegant, but both will do the job.  The biggest problem to either approach will be when we decide to upgrade ColdFusion, which will most likely not use the same syntax for the sendMessage() method (when using option 2), or we will loose the changes we made to the cfajax.js file (if you choose option 1).

Now, every Ajax request sent using CF8 Ajax will contain this parameter in the request headers, so all I need to do is check if the parameter exists, and the value is ‘ColdFusion’, just as we expect if the request is indeed coming from JavaScript.  Here is what my isAjaxRequest() method looks like:

[-]View Code COLDFUSION
1
2
3
4
5
6
7
8
9
<cffunction name="isAjaxRequest" output="false" returntype="boolean" access="public">
    <cfset var headers = GetHttpRequestData().headers>
    <cfif structkeyexists(headers, "X-Requested-With")>
        <cfif headers['X-Requested-With'] eq 'ColdFusion'>
            <cfreturn true>
        </cfif>
    </cfif>
    <cfreturn false>
</cffunction>

Have suggestion on how to do this easier or using a better approach?  Leave a comment below.

Adobe AIR Marketplace

Posted in AIR, Adobe on March 25th, 2008 by admin / No Comments »

In case you haven’t already checked it out, be sure to have a look at the applications available for download at the Adobe AIR Marketplace on Adobe Exchange.  What is the Adobe AIR Marketplace?  According the FAQ:

Adobe AIR Marketplace is a place where AIR developers can publish their Adobe AIR applications for users to download. You can find the Adobe AIR Marketplace at www.adobe.com/go/marketplace.

Right now the marketplace is still in beta, and only has 51 applications available for download right now.  So what does this mean to us developers?  Opportunity - the opportunity to create a killer application that is built with AIR, so get started with AIR and see what you’re missing.  After you’ve created your application, the Adobe AIR Marketplace is a great place to let people know about it:

The Adobe AIR Marketplace:

  • Allows you to post your application for download, including your own application description, application icon, and screen shot.
  • Allows users to download your application.
  • Lets users browse through categories or search for applications.
  • Lets users rate and review your applications.
  • Promotes different applications as featured item on a rotating basis.

Finally, my CFDocs on AIR is now available on the Adobe AIR Marketplace.

Entries (RSS)