Accessibility
 
 
ColdFusion Advanced Custom Tag Syntax

By Ray Camden
Principal Spectra Compliance Engineer
Macromedia, Inc.

Custom tags are a great tool for code reuse and portability. By developing a custom tag you not only make your current project easier but future ones as well. By providing the Developer Exchange, Macromedia provides a space for developers to share custom tags with thousands of other programmers and site builders.

ColdFusion allows developers to not only develop custom tags, but to also create tags that can wrap content, exist within other tags, and interact with other tags. This article will deal specifically with the ability to wrap content. As an example, imagine a custom tag that must take a body of text and replace any instance of an e-mail address with a hyperlink. Normally, this isn't a problem if the content comes from a database, as follows:

<CFSET Content=TheQuery.CommentColumn> <CF_Linkify TEXT="#Content#" RETURN="LinkedContent"> <CFOUTPUT> #LinkedContent# </CFOUTPUT>

The only problem with this is the need to send information to the custom tag, return it to a second variable, and then output that variable. What if, instead of being forced to send your text to the tag in the method above, you could simply wrap your content with the custom tag:

<CFSET Content=TheQuery.CommentColumn> <CFOUTPUT> <CF_Linkify> #Content# </CF_Linkify> </CFOUTPUT>

Or even better:

<CFOUTPUT> <CF_Linkify> #TheQuery.CommentColumn# </CF_Linkify> </CFOUTPUT>
One of the things I have always loved about ColdFusion is the way it integrates with HTML. Unlike creating CGIs in Perl, ColdFusion feels closer or more ingrained with the static design and HTML entities of the page. With the new tag syntax, this integration is even greater.

Creating the CF_STYLE Custom Tag

One of the problems I used to have with designing web pages was the constant fight to cover all possible browsers. I wanted my sites to look good, but, being a naturally lazy person I also wanted to accomplish this as easy as possible. With the introduction of the 4.0 browsers and Cascading Style Sheets (CSS), it was finally possible to create really nice looking copy with very fine control. The only problem was that you gave up all this control whenever a 3.0 browser hit your site. Even now, a full 48% of the visitors to my site use a 3.0 browser. What I really needed was an easy way to use CSS for the 4.0 browsers and <FONT> tags for 3.0 and lower browsers. This tool would need to work with both with dynamic content and static content.

I knew that one solution would be to simply query the USER_AGENT string from the browser. By doing some simple checking on the browser version, I could determine if the visitor was using a 4.0 or 3.0 (and lower) browser. At that point, I wasn't sure what to do. I could create two versions of the site and just push people using 3.0 browsers into a different folder. But that would mean maintaining two separate versions of the site--not something I had a lot of time to do.

I decided to go with a solution that was made possible with the extensions to custom tags in ColdFusion. Here is the beginning of the CF_Style custom tag. There is nothing magical here, just simple error checking and global variable setting.

<CFSETTING ENABLECFOUTPUTONLY="Yes"> <!--- Name : Style Author : Raymond Camden Created : September 5, 1998 Last Updated : September 6, 1998 History : Added CLASS attribute. ---> <CFPARAM NAME="Attributes.StyleDef" DEFAULT=""> <CFPARAM NAME="Attributes.Class" DEFAULT=""> <CFPARAM NAME="Attributes.FontDef" DEFAULT=""> <CFPARAM NAME="Bold" DEFAULT="False"> <CFPARAM NAME="Ital" DEFAULT="False"> <CFPARAM NAME="FONT_SIZE" DEFAULT=""> <CFPARAM NAME="FONT_COLOR" DEFAULT=""> <CFIF Attributes.StyleDef IS AND Attributes.Class IS> <CFOUTPUT> CF_Style Error: Attribute StyleDef or Class must be defined. </CFOUTPUT> <CFABORT> </CFIF> <CFIF Attributes.FontDef IS> <CFOUTPUT> CF_Style Error: Attribute FontDef must be defined. </CFOUTPUT> <CFABORT> </CFIF>
After declaring the variables I will be using for the rest of the tag, I did some simple checking to make sure the user provided the proper attributes. The tag requires that you set either StyleDef or Class (this covers 4.0 browsers) and the attributes FontDef (this covers 3.0 and lower browsers).

The StyleDef and Class attributes are pretty simple. CSS allows you to define a style for each block of content (using the attribute: val; syntax) or point to a global style declaration (class).

The FontDef attribute is a bit more complex. I wanted a way for the user to provide as much detail as possible but only in one attribute. I mimicked the CSS attribute a bit by using a syntax delimited by semicolons. The FontDef value would refer to: FACE (Arial, Helvetica, etc.), COLOR (red, green, blue, etc), SIZE (+2,-1,3, etc.) and a combination of bold and italics. The attribute would be set by simply dividing the values by semicolons. Bold and italics could be used as the fourth item in the attribute, and would be used in the letter 'b' or 'i' appeared. Here is an example: FontDef="Arial,Helvetica;+1,red,bi". This would create a font face using Arial, Helvetica, a font size of "+1", a color of red, with bold and italics turned on. Here is the code that parses the FontDef:

<!--- Manhandle the FontDef a bit ---> <CFSET FLEN=ListLen(Attributes.FontDef,";")> <CFSET FONT_FACE=ListGetAt(Attributes.FontDef,1,";")> <CFIF FLEN GT 1> <CFSET FONT_COLOR="COLOR=" & ListGetAt(Attributes.FontDef,2,;) &> </CFIF> <CFIF FLEN GT 2> <CFSET FONT_SIZE="SIZE="& ListGetAt(Attributes.FontDef,3,;)> </CFIF> <CFIF FLEN GT 3> <CFSET MISC_MARKUP=ListGetAt(Attributes.FontDef,4,";")> <CFIF FindNoCase(b,MISC_MARKUP)> <CFSET BOLD=True> </CFIF> <CFIF FindNoCase(i,MISC_MARKUP)> <CFSET ITAL=True> </CFIF> </CFIF>
After setting all the values and parsing the FontDef attribute, my next task was to get the actual browser version. I did this with some simple text parsing of the HTTP_USER_AGENT variable:

<CFSET BROWSER=CGI.HTTP_USER_AGENT> <CFSET VERSION=Val(ListGetAt(ListGetAt(BROWSER,1,""),2,"/"))>
Ok, so far, nothing really magical. Now it's time for the fun stuff. As I said above, the new tag syntax allows for:

<CF_Style> ….. </CF_Style>
By using a variable, ThisTag.ExecutionMode, you can determine if you are either at the beginning of the tag or at the end. In this case, if I am at the beginning of the tag, I want to output either the <SPAN> tag for the 4.0 browsers or <FONT>(<B><I>) tag combination for the 3.0 browsers. Conversely, when the tag is called at the end (with the / syntax), I want to either output </SPAN> or (</I></B>)</FONT>. By using CFSWITCH, I can easily spit out the correct content:

<CFSWITCH EXPRESSION="#ThisTag.ExecutionMode#"> <CFCASE VALUE="Start"> <CFIF VERSION GTE 4> <CFOUTPUT> </CFOUTPUT><span <CFIF NOT Attributes.StyleDef IS> STYLE="#Attributes.StyleDef#" </CFIF> <CFIF NOT Attributes.Class IS> CLASS="#Attributes.Class#" </CFIF> > <CFELSE> <CFOUTPUT> <font FACE="#FONT_FACE#" #FONT_COLOR# #FONT_SIZE#> <CFIF BOLD> </CFIF> <b> <CFIF ITAL> </CFIF> </b></font></CFOUTPUT> </span> </CFIF> </CFCASE> <CFCASE VALUE="End"> <span <CFIF NOT Attributes.StyleDef IS> STYLE="#Attributes.StyleDef#" </CFIF> <CFIF NOT Attributes.Class IS> CLASS="#Attributes.Class#" </CFIF> ><font FACE="#FONT_FACE#" #FONT_COLOR# #FONT_SIZE#><b> <i> </i></b></font></span> <CFIF VERSION GTE 4> <i><b><font FACE="#FONT_FACE#" #FONT_COLOR# #FONT_SIZE#></font></b></i><CFOUTPUT> <i><b><font FACE="#FONT_FACE#" #FONT_COLOR# #FONT_SIZE#><span <CFIF NOT Attributes.StyleDef IS> STYLE="#Attributes.StyleDef#" </CFIF> <CFIF NOT Attributes.Class IS> CLASS="#Attributes.Class#" </CFIF> ></span></font></b></i></CFOUTPUT><font FACE="#FONT_FACE#" #FONT_COLOR# #FONT_SIZE#><b><i> <CFELSE> </i></b></font><CFOUTPUT><b><font FACE="#FONT_FACE#" #FONT_COLOR# #FONT_SIZE#> <CFIF ITAL> <i> </i> </CFIF> <CFIF BOLD> </CFIF> </font></b> </CFOUTPUT> </CFIF> </CFCASE> </CFSWITCH> <b><b> <CFSETTING ENABLECFOUTPUTONLY="No">

The CFSWITCH command begins by checking the current execution mode. If the tag is beginning, the code under the Start CASE is executed. Ditto for the End CASE.

The really powerful part of this tag is that without retooling major parts of my site I can add functionality for 4.0 browsers and still cover the old browsers. I can use CSS and old syntax without making all of my static content take the form of variables I must pass to an 'old style' ColdFusion 3.0 custom tag.