ColdFusion hCard Microformats Custom Tag
I a huge fan of of microformats (or µF), if you're not familiar with them take a look at www.microformats.org and the entry at Wikipedia.
Basically it's a way of marking up HTML to give it more more meaning, think of it as the semantic web (with a small s) that is available now.
hCard is a HTML representation of vCard and while no browers currently support it natively, the next versions of Firefox and IE will support it. There are also plugins currently available for Firefox (Operator and Tails ) and Safari (various bookmarklets) and IE (various favlets).
So what does a hCard look like?
<div class="fn">Justin Mclean</div>
<div class="adr"><span class="street-address">170 Campbell St</span>, <span class="locality">Sydney</span> <span class="region">NSW</span> <span class="postal-code">2010</span></div>
<div class="tel"><span class="type"><abbr title="work">Phone:</abbr></span> <span class="value">(02) 9368 1014</span></div>
<div class="tel"><span class="type"><abbr title="cell">Mobile:</abbr></span> <span class="value">0416119342</span></div>
<div>Email: <a class="email" href="mailto:justin@classsoftware.com">justin@classsoftware.com</a></div>
</div>
As you can see you can easily mark up an address as a microformat and then style it with CSS.
If you have several 100 addresses that you needed to convert it would get a bit tedious doing it all by hand. That's why I've created a ColdFusion custom tag to do the work for you.
Name : cfhcard
Author : Justin Mclean
Copyright : Class Software 2007 (http://www.classsoftware.com)
License : Licensed under Creative Commons attribution license
--->
<cfif thisTag.executionMode is "end">
<cfscript>
// function to take an address string and break it up into it's elements
function parseAddress(addressstring)
{
var nolines = 0;
var lastline = '';
var line = '';
var address = structnew();
// split address on commas and new lines
newline = chr(13) & chr(10);
newlinecomma = '#newline#,';
if (find(',', addressstring) or find(newline,addressstring)) {
nolines = listlen(addressstring, newlinecomma);
address.address = listfirst(addressstring, newlinecomma);
lastaddress = '';
// try and find state postcode line
for (i = 2; i lte nolines; i = i + 1) {
lastline = listgetat(addressstring, i-1, newlinecomma);
line = trim(listgetat(addressstring, i, newlinecomma));
// look for line matching city state and postcode
if (refind('[A-Z|a-z]+ [A-Z|a-z]+ [0-9]+$',line)) {
address.city = trim(listfirst(line, ' '));
address.state = trim(listgetat(line, 2, ' '));
address.postcode = trim(listlast(line, ' '));
break;
}
// look for line matching state and postcode
else if (refind('[A-Z|a-z]+ [0-9]+$',line)) {
address.address = lastaddress;
address.city = trim(lastline);
address.state = trim(listfirst(line, ' '));
address.postcode = trim(listlast(line, ' '));
break;
}
else {
lastaddress = address.address;
address.address = '#address.address##line##newline#';
}
}
// check for country if we still have lines to go
if (i lt nolines) {
line = trim(listgetat(addressstring, i+1, newlinecomma));
if (refind('[A-Z|a-z]+ [0-9]+$',line)) {
address.country = line;
}
}
}
return address;
}
</cfscript>
<cfset content = thisTag.GeneratedContent>
<cfscript>
// find address lines assume they comes first
newline = chr(13) & chr(10);
content = replace(content, ',', newline, 'ALL');
endaddress = 0;
address = structnew();
addressstr = '';
nolines = listlen(content, newline);
for (i=2; i lte nolines; i = i + 1) {
line = trim(listgetat(content, i, newline));
addressstr = '#addressstr##line##newline#';
// look for (city) state postcode line
if (refind('[A-Z|a-z]+ [0-9]+$', line) or refind('[A-Z|a-z]+ [A-Z|a-z]+ [0-9]+$', line)) {
endaddress = i;
break;
}
}
// check if next line could be country
if (endaddress gt 0) {
line = listgetat(content, endaddress + 1, newline);
if (refind('[A-Z][a-z]+$', line)) {
addressstr = '#addressstr##line##newline#';
endaddress = endaddress + 1;
}
}
// parse address
address = parseAddress(addressstr);
// set other address fields
address.name = listfirst(content, newline);
// check other lines for other known values
for (i = endaddress + 1; i lte nolines; i = i + 1) {
line = trim(listgetat(content, i, newline));
if (refind('[A-Z|a-z]+[\:|\-| ]+[0-9|\-|\+|\(|\)| ]+$',line)) {
label = trim(listfirst(line, ':-'));
if (lcase(label) is 'mobile' or lcase(label) is 'm') {
address.mobile = trim(listlast(line, ':-'));
}
if (lcase(label) is 'phone' or lcase(label) is 'telephone' or lcase(label) is 'work' or lcase(label) is 'p' or lcase(label) is 't' or lcase(label) is 'w') {
address.phone = trim(listlast(line, ':-'));
}
}
else if (refind('[0-9|\-|\+\(|\)| ]+$',line)) {
address.phone = line;
}
else if (refind('[A-Z|a-z]+[\:|\-| ]+[A-Z|a-z|0-9]+\@[A-Z|a-z|0-9|\.]+$', line)) {
label = listfirst(line, ':-');
if (lcase(label) is 'email') {
address.email = trim(listlast(line, ':-'));
}
}
else if (refind('[A-Z|a-z|0-9]+\@[A-Z|a-z|0-9|\.]+$', line)) {
address.email = line;
}
}
</cfscript>
<cfsavecontent variable="hcard">
<cfoutput>
<div class="vcard">
<div class="fn">#address.name#</div>
<div class="adr"><span class="street-address">#address.address#</span>, <span class="locality">#address.city#</span> <span class="region">#address.state#</span> <span class="postal-code">#address.postcode#</span><cfif isdefined("address.country")> <span class="country-name">#address.country#</span></cfif></div>
<cfif isdefined("address.phone")><div class="tel"><span class="type"><abbr title="work">Phone:</abbr></span> <span class="value">#address.phone#</span></div>
</cfif>
<cfif isdefined("address.mobile")><div class="tel"><span class="type"><abbr title="cell">Mobile:</abbr></span> <span class="value">#address.mobile#</span></div>
</cfif>
<cfif isdefined("address.email")><div>Email: <a class="email" href="mailto:#address.email#">#address.email#</a></div>
</cfif>
</div>
</cfoutput>
</cfsavecontent>
<cfset thisTag.GeneratedContent = hcard>
</cfif>
The above custom tag will take any plain text address and convert it to a hCard microformat, and while not perfect it understand a wide range of address formats and telephone and email addresses.
Currently the tag assumes Australian style addresses but it wouldn't be too hard to extend to other address formats/styles.
Here is a example of it's use:
Justin Mclean
170 Campbell St, Sydney, NSW 2010, Australia
email: justin@classsoftware.com
mobile: 0416119342
phone: (02) 9368 1014
</cf_hcard>
<cf_hcard>
Justin Mclean
170 Campbell St
Sydney
NSW 2010
Email: justin@classsoftware.com
Mobile: 0416119342
</cf_hcard>
<cf_hcard>
Justin Mclean
170 Campbell St
Sydney NSW 2010
M: 0416119342
W: +61 2 9368 1014
</cf_hcard>
<cf_hcard>
Justin Mclean
170 Campbell St
Sydney
NSW 2010
justin@classsoftware.com
041 6119 342
</cf_hcard>
Update - This project can now be found at:
http://cfhcard.riaforge.org/



