<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://web.resource.org/cc/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

			<channel>
			<title>Raymond Camden's Blog</title>
			<link>http://www.raymondcamden.com/index.cfm</link>
			<description>Raymond Camden's personal blog covering jQuery, Mobile, ColdFusion, and Web Development.</description>
			<language>en-us</language>
			<pubDate>Fri, 10 Feb 2012 01:58:51 -0500</pubDate>
			<lastBuildDate>Wed, 08 Feb 2012 10:41:00 -0500</lastBuildDate>
			<generator>BlogCFC</generator>
			<docs>http://blogs.law.harvard.edu/tech/rss</docs>
			<managingEditor>raymondcamden@gmail.com</managingEditor>
			<webMaster>raymondcamden@gmail.com</webMaster>
			<itunes:subtitle />
			<itunes:summary />
			<itunes:category text="Technology" />
			<itunes:category text="Technology">
				<itunes:category text="Podcasting" />
			</itunes:category>
			<itunes:category text="Technology">
				<itunes:category text="Tech News" />
			</itunes:category>
			<itunes:keywords />
			<itunes:author />
			<itunes:owner>
				<itunes:email>raymondcamden@gmail.com</itunes:email>
				<itunes:name />
			</itunes:owner>
			
			<itunes:explicit>no</itunes:explicit>
			
			
			
			
			
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/RaymondCamdensColdfusionBlog" /><feedburner:info uri="raymondcamdenscoldfusionblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
				<title>Adobe eSeminar on ColdFusion and Monitoring</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/KDolsC8Jk8w/Adobe-eSeminar-on-ColdFusion-and-Monitoring</link>
				<description>(Sorry for the somewhat simple blog posts this week - all day offsite with Adobe. I promise to return to my normal incredibly exciting posts soon...)

&lt;h2&gt;Monitoring ColdFusion with FusionReactor&lt;/h2&gt;

February 22, 2012

If your CF server starts acting up, how do you go about resolving problems? If you're on ColdFusion 8 or 9 Enterprise, you may know that you have a built-in ColdFusion Server Monitor. Did you know there is an alternative tool that supplements it well? FusionReactor is a commercial third-party tool, which can monitor not only any version of CF (6, 7, 8 or 9, whether Standard or Enterprise) but also Livecycle and any other Java web application or server in your environment. (www.fusion-reactor.com)

Such monitoring is about more than "watching a screen". You can arrange to receive email alerts with valuable information (sort of a black box recording before a crash), and FusionReactor also creates really valuable logs that can also help with post-mortem analysis. They can also assist with deciding on CF server configuration settings, watch trends for hardware upgrades, and more. And as of FusionReactor 4, these logs now track information that previous only the CF Server Monitor displayed (but didn't log at all).  Finally, an additional tool, FusionAnalytics, can help analyze and visualize that data over minutes, hours, days, weeks, months, and so on.

In this 50-minute session, veteran CF troubleshooter and independent consultant Charlie Arehart will introduce and demonstrate these and other key features of FusionReactor (including stack tracing and crash protection), and will end with a brief demo of FusionAnaytics (www.fusion-analytics.com)

Register Now: &lt;a href="http://www.adobe.com/cfusion/event/index.cfm?event=detail&amp;id=1965964&amp;loc=en_us"&gt;http://www.adobe.com/cfusion/event/index.cfm?event=detail&amp;id=1965964&amp;loc=en_us&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/P5BMg8S6Z1Yhjj9sEzo2QAC5BVw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/P5BMg8S6Z1Yhjj9sEzo2QAC5BVw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/P5BMg8S6Z1Yhjj9sEzo2QAC5BVw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/P5BMg8S6Z1Yhjj9sEzo2QAC5BVw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/KDolsC8Jk8w" height="1" width="1"/&gt;</description>
				
				
				<category>ColdFusion</category>
				
				<pubDate>Wed, 08 Feb 2012 10:41:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/2/8/Adobe-eSeminar-on-ColdFusion-and-Monitoring</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/2/8/Adobe-eSeminar-on-ColdFusion-and-Monitoring</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>ColdFusion offer from Intergral</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/2_kBmWn4gTc/ColdFusion-offer-from-Intergral</link>
				<description>Intergral, makers of FusionReactor and FusionAnalytics, have a pretty cool offer for folks buying or upgrading ColdFusion:

&lt;blockquote&gt;
Intergral and Adobe Inc. are running the following promotion up to Feb 29 2012.   If you purchase either a new or upgrade license to ColdFusion 9 Enterprise, you will receive FusionReactor Enterprise Server Monitor 1 Year Subscription (RRP $399), absolutely free.   If you purchase a new license of ColdFusion 9 Standard, you will receive a copy of FusionReactor Standard 1 Year Subscription (RRP $249).  All software must be purchased via Intergral &lt;a href="http://www.fusion-reactor.com/adobe"&gt;http://www.fusion-reactor.com/adobe&lt;/a&gt; to be eligible for this offer.  Our prices for CF are very competitive, making it all a very good deal.

If you purchase Platinum Support with your new ColdFusion license, then you are entitled to receive all upgrades and updates to ColdFusion in the next 12 months following your purchase - read more &lt;a href="http://www.adobe.com/support/programs/customer/platinum_solutions_brief.pdf"&gt;http://www.adobe.com/support/programs/customer/platinum_solutions_brief.pdf&lt;/a&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/1k0v1UrAf-P428xASJHozEK5w14/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/1k0v1UrAf-P428xASJHozEK5w14/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/1k0v1UrAf-P428xASJHozEK5w14/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/1k0v1UrAf-P428xASJHozEK5w14/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/2_kBmWn4gTc" height="1" width="1"/&gt;</description>
				
				
				<category>ColdFusion</category>
				
				<pubDate>Tue, 07 Feb 2012 08:38:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/2/7/ColdFusion-offer-from-Intergral</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/2/7/ColdFusion-offer-from-Intergral</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>Job Opening for ColdFusion Developer at Arizona State University</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/Mu3Bg7vu38A/Job-Opening-for-ColdFusion-Developer-at-Arizona-State-University</link>
				<description>Just passing it along... (btw reader - I never did want to be a 'job poster' for the CF community sinc eothers do- but I've done a couple recently. A few because they were for my last job, and one for Adobe. Do folks mind these? Find them helpful?)

&lt;a href="https://www.asu.edu/go/employment/?auth=guest&amp;jobid=27899&amp;SiteId=1&amp;PostingSeq=1"&gt;Web Application Developer&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/EQIjRuV4a8u_quFU_qnz2HavXVQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EQIjRuV4a8u_quFU_qnz2HavXVQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/EQIjRuV4a8u_quFU_qnz2HavXVQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EQIjRuV4a8u_quFU_qnz2HavXVQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/Mu3Bg7vu38A" height="1" width="1"/&gt;</description>
				
				
				<category>ColdFusion</category>
				
				<pubDate>Sat, 04 Feb 2012 14:42:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/2/4/Job-Opening-for-ColdFusion-Developer-at-Arizona-State-University</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/2/4/Job-Opening-for-ColdFusion-Developer-at-Arizona-State-University</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>Job Opening for Sr. ColdFusion Developer in CA/ San Fernando Valley</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/rqxiCutq7dY/Job-Opening-for-Sr-ColdFusion-Developer-in-CA-San-Fernando-Valley</link>
				<description>Just passing it on...

Job Opening for Sr. ColdFusion Developer in CA/ San Fernando Valley Position:
&lt;ul&gt;
&lt;li&gt;Senior ColdFusion Developer
&lt;li&gt;Contract to hire.
&lt;li&gt;Seeking an advanced-level developer who lives to push ColdFusion and Object-Oriented Web development to the edge.
&lt;/ul&gt;

Job Description
&lt;ul&gt;
&lt;li&gt;Sr. Web Developer / Architect to drive the direction of the company's technology solutions, define and implement best practices and build lean, powerful and extensible functionality.
&lt;li&gt;Work closely with Business/Systems Analysts, Database Administrators, and Stakeholders to architect, design, and implement robust and user friendly insurance business applications.
&lt;/ul&gt;

More info at his blog post: &lt;a href="http://cf-click.blogspot.com/2012/02/job-opening-for-sr-coldfusion-developer.html"&gt;Job Opening for Sr. ColdFusion Developer in CA/ San Fernando Valley&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/8xjSC50bvI7oCsWxa30OiB15LX8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/8xjSC50bvI7oCsWxa30OiB15LX8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/8xjSC50bvI7oCsWxa30OiB15LX8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/8xjSC50bvI7oCsWxa30OiB15LX8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/rqxiCutq7dY" height="1" width="1"/&gt;</description>
				
				
				<category>ColdFusion</category>
				
				<pubDate>Fri, 03 Feb 2012 13:41:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/2/3/Job-Opening-for-Sr-ColdFusion-Developer-in-CA-San-Fernando-Valley</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/2/3/Job-Opening-for-Sr-ColdFusion-Developer-in-CA-San-Fernando-Valley</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>Update to my highlight/fadeout example - now with CSS hotness</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/G5GHXgBJYh8/Update-to-my-highlightfadeout-example--now-with-CSS-hotness</link>
				<description>Yesterday I &lt;a href="http://www.raymondcamden.com/index.cfm/2012/2/2/Creating-a-highlightfadeout-text-effect-on-a-tag-cloud"&gt;posted&lt;/a&gt; an example of highlight/fadeout effects done with jQuery. It wasn't necessarily that exciting, but it's not something I've done before so it was fun to build. Fellow evangelist (and my boss, so yeah, his comments get special attention) Kevin Hoyt &lt;a href="http://www.raymondcamden.com/index.cfm/2012/2/2/Creating-a-highlightfadeout-text-effect-on-a-tag-cloud#c83DEA5A5-0E00-001F-606D0CD5790170A1"&gt;commented&lt;/a&gt; that what I had achieved would be possible with CSS and transitions.&lt;p&gt;

Now - I will admit to being a bit codependent on jQuery. It's not like I'm addicted. I can stop using jQuery whenever I want to. In fact, I went out of my way to in &lt;a href="http://www.raymondcamden.com/index.cfm/2012/1/27/A-look-at-JavaScript-Form-Validation"&gt;this post&lt;/a&gt;. I love jQuery, but I can definitely recognize that I'm almost depending on it like a crutch, so I thought I'd take the opportunity to try it with pure CSS.

&lt;p&gt;

Before even approaching the idea of an animation, I thought I'd try a simple hover. That turned out to be trivial:

&lt;p&gt;

&lt;code&gt;
.tagcloudword { opacity: 0.5; }

.tagcloudword:hover {
     opacity:1;
     font-weight: bold;
}
&lt;/code&gt;

&lt;p&gt;

That worked well enough where even if everything else I did failed, I'd be ok. Notice I also added the bold there to make it a bit more fancy. You can try this here: &lt;a href="http://www.raymondcamden.com/demos/2012/feb/3/take2.html"&gt;http://www.raymondcamden.com/demos/2012/feb/3/take2.html&lt;/a&gt;

&lt;p&gt;

I knew next to nothing about animation and transitions in CSS, but quickly found the docs at MDN to be incredibly useful. Begin with their CSS page: &lt;a href="https://developer.mozilla.org/en/CSS"&gt;https://developer.mozilla.org/en/CSS&lt;/a&gt;. They've got a page on &lt;a href="https://developer.mozilla.org/en/CSS/CSS_animations"&gt;animations&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en/CSS/CSS_transitions"&gt;transitions&lt;/a&gt;. Obviously transitions are what we want here. 

&lt;p&gt;

I won't try to replicate the excellent docs over at MDN, but the basic gist of it seems to be this:

&lt;p&gt;

&lt;ul&gt;
&lt;li&gt;Define your CSS for the item.
&lt;li&gt;Define your CSS for the hover.
&lt;li&gt;Then tell the CSS what properties are changing, how it changes (from a list of options like ease-in), and the duration.
&lt;/ul&gt;

&lt;p&gt;

The only thing missing from the docs that may trip you up is the use of vendor prefixes. MDN focuses on what works for them - the moz prefix. For Chrome, I had to add webkit as well. This means you have to duplicate your code, which is kinda sucky, but passable. So here was my first update:

&lt;p&gt;

&lt;code&gt;
	.tagcloudword {
        opacity: 0.1;
        -webkit-transition: opacity;
        -webkit-transition-timing-function: ease-out;
        -webkit-transition-duration: 2000ms;
        -moz-transition: opacity;
        -moz-transition-timing-function: ease-out;
        -moz-transition-duration: 2000ms;
    }

    .tagcloudword:hover {
        opacity: 1;
        -webkit-transition: opacity;
        -webkit-transition-timing-function: ease-out;
        -webkit-transition-duration: 2000ms;
        -moz-transition: opacity;
        -moz-transition-timing-function: ease-out;
        -moz-transition-duration: 2000ms;
    }
&lt;/code&gt;

&lt;p&gt;

That's a lot of code, but if you ignore the moz lines it's really just 3 lines per state. Something else not immediately obvious to me was that if you define the transitions on hover and &lt;b&gt;not&lt;/b&gt; on the core style block, you won't get an animation backwards from your hover state. You can demo this here: &lt;a href="http://www.raymondcamden.com/demos/2012/feb/3/test3.html"&gt;http://www.raymondcamden.com/demos/2012/feb/3/test3.html&lt;/a&gt; Be sure to notice that I slowed the transition down quite a bit to make it more obvious. I'd probably use 500ms instead. 

&lt;p&gt;

Ok - finally. So how about multiple changes? Just add additional properties. Here's how I added some color too. In Chrome it seems to work really well. It goes from black to blood red to a pure red. (Don't hate me for color choices.)

&lt;p&gt;

&lt;code&gt;
	.tagcloudword {
        opacity: 0.1;
        color: black;
        -webkit-transition: opacity,color;
        -webkit-transition-timing-function: ease-out;
        -webkit-transition-duration: 2000ms;
        -moz-transition: opacity,color;
        -moz-transition-timing-function: ease-out;
        -moz-transition-duration: 2000ms;
    }

    .tagcloudword:hover {
        opacity: 1;
        color: red;
        -webkit-transition: opacity,color;
        -webkit-transition-timing-function: ease-out;
        -webkit-transition-duration: 2000ms;
        -moz-transition: opacity,color;
        -moz-transition-timing-function: ease-out;
        -moz-transition-duration: 2000ms;
    }
&lt;/code&gt;

&lt;p&gt;

You can view this one here: &lt;a href="http://www.raymondcamden.com/demos/2012/feb/3/test4.html"&gt;http://www.raymondcamden.com/demos/2012/feb/3/test4.html&lt;/a&gt;

&lt;p&gt;

It works in Chrome and Firefox. In IE9 it is &lt;i&gt;not&lt;/i&gt; supported, but it fails ok. You still get a hover effect.
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/n4cPK-YECC78Gw7Gc-84b2txu9o/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/n4cPK-YECC78Gw7Gc-84b2txu9o/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/n4cPK-YECC78Gw7Gc-84b2txu9o/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/n4cPK-YECC78Gw7Gc-84b2txu9o/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/G5GHXgBJYh8" height="1" width="1"/&gt;</description>
				
				
				<category>Development</category>
				
				<category>HTML5</category>
				
				<pubDate>Fri, 03 Feb 2012 09:28:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/2/3/Update-to-my-highlightfadeout-example--now-with-CSS-hotness</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/2/3/Update-to-my-highlightfadeout-example--now-with-CSS-hotness</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>Creating a highlight/fadeout text effect on a tag cloud</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/TlC1SwRFxDo/Creating-a-highlightfadeout-text-effect-on-a-tag-cloud</link>
				<description>Yesterday I &lt;a href="http://www.raymondcamden.com/index.cfm/2012/2/1/Generate-a-tag-cloud-from-an-RSS-feed-with-ColdFusion"&gt;blogged&lt;/a&gt; a simple example of how to turn an RSS feed into a tag cloud. Today reader JP &lt;a href="http://www.raymondcamden.com/index.cfm/2012/2/1/Generate-a-tag-cloud-from-an-RSS-feed-with-ColdFusion#c8079B8DB-C5F3-1EEF-FC1B1A8B73029B38"&gt;commented&lt;/a&gt; that it would be cool if I could mimic an effect he saw in a Flash based tag cloud. Basically, as you mouse over each word, they light up. Here's what I came up with.&lt;p&gt;

First off - I tweaked my data a bit. Instead of working with an RSS feed, I decided to hit my copy of CFBloggers and scan &lt;i&gt;all&lt;/i&gt; the category data. My local database is a good 6 months old, but still has 48 thousand entries. This gave me a nice set of data. If you remember, Pete's tag cloud code already wraps every word with a span. The class on the span is based on the word's relative "score" compared to other words. I decided then to simply append to that:

&lt;p&gt;

&lt;code&gt;
&lt;cfoutput&gt;&lt;span class="tagcloudword #class#"&gt;#w#&lt;/span&gt;&lt;/cfoutput&gt;
&lt;/code&gt;

&lt;p&gt;

I then added a CSS style for tagcloudword to make it a bit light by default:

&lt;p&gt;

&lt;code&gt;
.tagcloudword { opacity: 0.5; }
&lt;/code&gt;

&lt;p&gt;

And then it was time for the jQuery. Turned out - it was pretty damn trivial:

&lt;p&gt;

&lt;code&gt;
$(function() {
	$(".tagcloudword").mouseover(function(e) {
		$(this).animate({opacity:1.0},400);
	});
	$(".tagcloudword").mouseout(function(e) {
		$(this).animate({opacity:0.5},400);
	});
});	
&lt;/code&gt;

&lt;p&gt;

As you can see, I'm simply using the animate API to change opacity when you mouse over and out of the word. And that's it. You can even make it simpler with the hover event:

&lt;p&gt;

&lt;code&gt;
$(".tagcloudword").hover(function(e) {
	$(this).animate({opacity:1.0},400);
},function(e) {
	$(this).animate({opacity:0.5},400);
});
&lt;/code&gt;

&lt;p&gt;

You can see a demo of this yourself by clicking the big demo button below. Note that I saved out the result as an HTML, so it's not "live". 

&lt;p&gt;


&lt;a href="http://www.raymondcamden.com/demos/2012/feb/2/cfbtagcloud.htm"&gt;&lt;img src="http://www.raymondcamden.com/images/icon_128.png" title="Demo, Baby" border="0"&gt;&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/zvgp9Pv2gA4zwH9FIMJXpEWFBEQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zvgp9Pv2gA4zwH9FIMJXpEWFBEQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/zvgp9Pv2gA4zwH9FIMJXpEWFBEQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zvgp9Pv2gA4zwH9FIMJXpEWFBEQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/TlC1SwRFxDo" height="1" width="1"/&gt;</description>
				
				
				<category>jQuery</category>
				
				<category>JavaScript</category>
				
				<pubDate>Thu, 02 Feb 2012 16:17:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/2/2/Creating-a-highlightfadeout-text-effect-on-a-tag-cloud</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/2/2/Creating-a-highlightfadeout-text-effect-on-a-tag-cloud</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>Generate a tag cloud from an RSS feed with ColdFusion</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/WrB-ZhOt4pY/Generate-a-tag-cloud-from-an-RSS-feed-with-ColdFusion</link>
				<description>Earlier today &lt;a href="http://henke.ws/"&gt;Mike Henke&lt;/a&gt; asked if there was a way to generate a tag cloud from an RSS feed. While he was able to find a solution quick enough (&lt;a href="http://www.wordle.net"&gt;Wordle&lt;/a&gt;), I thought it would be kind of fun to try this myself. I knew that Pete Freitag had already blogged on &lt;a href="http://www.petefreitag.com/item/396.cfm"&gt;tag clouds and ColdFusion&lt;/a&gt;, so all I had to do was generate my word data and pass it to his code. Here's what I came up with.&lt;p&gt;

I began with a simple call to my RSS URL to generate a query of data. For my testing, this was the only thing I cached. Obviously &lt;i&gt;all&lt;/i&gt; of my "crunching" could have been cached. 

&lt;p&gt;

&lt;code&gt;
&lt;cfset rss = cacheGet("rss")&gt;
&lt;cfif isNull(rss)&gt;
	&lt;cfset feedUrl = "http://feedproxy.google.com/RaymondCamdensColdfusionBlog"&gt;
	&lt;cffeed source="#feedUrl#" query="rss"&gt;
	&lt;cfset cacheput("rss", rss,createTimespan(0,1,0,0))&gt;
&lt;/cfif&gt;
&lt;/code&gt;

&lt;p&gt;

Now for the fun part. In order to use Pete's code, I need to know each word and the number of times it appears. I began with an empty struct:

&lt;p&gt;

&lt;code&gt;
&lt;cfset allwords = {}&gt;
&lt;/code&gt;

&lt;p&gt;

Next, I created a list of "stop" words, words I'd always ignore. (Note, this list was kind of arbitrary. Also note I added some spaces in the blog entry just so it would wrap better.)

&lt;p&gt;

&lt;code&gt;
&lt;cfset stopwords = "and,this,the,a,it,as,was,to,don't,has,you, you're,you've,with,why,which,when,were,we've,we're, then,than,i,i'll,i'm,i've,i'd,it's,for,of,is,if,in,that,but,my,not,can,are,',done, off,their,isn't,yes,what's,them,they,'',be,being,all, only,does,here,an,by,would,like,at,do,want,or,could, out,our,while,what,had,each,into,where,That's,will,else, let's,about,got,using,before,over,actually,going,some,well"&gt;
&lt;/code&gt;

&lt;p&gt;

I then split by word boundary and added them to the struct. Note that this word boundary also includes ' so I can match "don't". This is &lt;i&gt;not&lt;/i&gt; perfect, but good enough.

&lt;p&gt;

&lt;code&gt;
&lt;cfloop query="rss"&gt;
	&lt;cfset words = reMatch("[\w']+",bigstring)&gt;
	&lt;cfloop index="w" array="#words#"&gt;
		&lt;cfif len(w) gt 1 and not listFindNoCase(stopwords, w)&gt;
			&lt;cfif not structKeyExists(allwords, w)&gt;
				&lt;cfset allwords[w] = 0&gt;
			&lt;/cfif&gt;
			&lt;cfset allwords[w]++&gt;
		&lt;/cfif&gt;
	&lt;/cfloop&gt;
&lt;/cfloop&gt;	
&lt;/code&gt;

&lt;p&gt;

I had quite a few words, so I decided to remove all words with less than 5 instances.

&lt;p&gt;

&lt;code&gt;
&lt;cfloop item="k" collection="#allwords#"&gt;
	&lt;cfif allwords[k] lte 5&gt;
		&lt;cfset structDelete(allwords,k)&gt;
	&lt;/cfif&gt;
&lt;/cfloop&gt;
&lt;/code&gt;

&lt;p&gt;

Now comes Pete's code to generate high/low values.

&lt;p&gt;

&lt;code&gt;
&lt;cfset minval = 999999&gt;
&lt;cfset maxval = 0&gt;
&lt;cfloop item="k" collection="#allwords#"&gt;
	&lt;cfif allwords[k] lt minval&gt;
		&lt;cfset minval = allwords[k]&gt;
	&lt;cfelseif allwords[k] gt maxval&gt;
		&lt;cfset maxval = allwords[k]&gt;
	&lt;/cfif&gt;
&lt;/cfloop&gt;

&lt;cfset diff = maxval - minval&gt;
&lt;cfset distribution = diff / 3&gt;
&lt;/code&gt;

&lt;p&gt;

And finally, the output:

&lt;p&gt;

&lt;code&gt;
&lt;h2&gt;Word Cloud&lt;/h2&gt;
&lt;cfloop item="w" collection="#allWords#"&gt;
	&lt;cfif allWords[w] EQ minval&gt;
		&lt;cfset class="smallestTag"&gt;
	&lt;cfelseif allWords[w] EQ maxval&gt;
		&lt;cfset class="largestTag"&gt;
	&lt;cfelseif allWords[w] GT (minval + (distribution*2))&gt;
		&lt;cfset class="largeTag"&gt;
	&lt;cfelseif allWords[w] GT (minval + distribution)&gt;
		&lt;cfset class="mediumTag"&gt;
	&lt;cfelse&gt;
		&lt;cfset class="smallTag"&gt;
	&lt;/cfif&gt;
	&lt;cfoutput&gt;&lt;span class="#class#"&gt;#w#&lt;/a&gt;&lt;/cfoutput&gt;
&lt;/cfloop&gt;
&lt;/p&gt;
&lt;/code&gt;

&lt;p&gt;

Sexy, eh? Here is the output from my blog:

&lt;p&gt;

&lt;img src="http://www.raymondcamden.com/images/ScreenClip23.png" /&gt;

&lt;p&gt;

I then pointed it at the RSS feed from &lt;a href="http://www.coldfusionbloggers.org"&gt;ColdFusionBloggers&lt;/a&gt;:

&lt;p&gt;

&lt;img src="http://www.raymondcamden.com/images/ScreenClip24.png" /&gt;

&lt;p&gt;

I probably could have shortened that a lot more with my minimum filter. Anyway, I then did one more tweak. Instead of counting words, I simply took the category list:

&lt;p&gt;

&lt;code&gt;
&lt;cfloop query="rss"&gt;
	&lt;cfset words = listToArray(categorylabel)&gt;
&lt;/code&gt;

&lt;p&gt;

This tag cloud then represents categories from the RSS feed:

&lt;p&gt;

&lt;img src="http://www.raymondcamden.com/images/ScreenClip25.png" /&gt;

&lt;p&gt;

And that's it. Totally and completely stupid, but fun. Here's the current script, although it's a bit messy. As I said, normally you would want to cache &lt;b&gt;all&lt;/b&gt; of the crunching.

&lt;p&gt;

p.s. Words a bit hard to read in the pictures? Right click and open in new tab. Sorry about that!

&lt;p&gt;

&lt;code&gt;
&lt;cfset rss = cacheGet("rss")&gt;
&lt;cfif isNull(rss)&gt;
	&lt;cfset feedUrl = "http://www.coldfusionbloggers.org/rss.cfm"&gt;
	&lt;cffeed source="#feedUrl#" query="rss"&gt;
	&lt;cfset cacheput("rss", rss,createTimespan(0,1,0,0))&gt;
&lt;/cfif&gt;

&lt;!--- create a count of words ---&gt;
&lt;cfset allwords = {}&gt;
&lt;cfset stopwords = "and,this,the,a,it,as,was,to,don't,has,you,you're,you've,with,why,which,when,were,we've,we're,then,than,i,i'll,i'm,i've,i'd,it's,for,of,is,if,in,that,but,my,not,can,are,',done,off,their,isn't,yes,what's,them,they,'',be,being,all,only,does,here,an,by,would,like,at,do,want,or,could,out,our,while,what,had,each,into,where,That's,will,else,let's,about,got,using,before,over,actually,going,some,well"&gt;

&lt;cfloop query="rss"&gt;
	&lt;!---
	&lt;cfset words = reMatch("[\w']+",bigstring)&gt;
	---&gt;
	&lt;cfset words = listToArray(categorylabel)&gt;
	&lt;cfloop index="w" array="#words#"&gt;
		&lt;cfif len(w) gt 1 and not listFindNoCase(stopwords, w)&gt;
			&lt;cfif not structKeyExists(allwords, w)&gt;
				&lt;cfset allwords[w] = 0&gt;
			&lt;/cfif&gt;
			&lt;cfset allwords[w]++&gt;
		&lt;/cfif&gt;
	&lt;/cfloop&gt;
&lt;/cfloop&gt;	

&lt;!--- remove where val &lt; 5, 5 being a bit arbitrary ---&gt;
&lt;!---
&lt;cfloop item="k" collection="#allwords#"&gt;
	&lt;cfif allwords[k] lte 0&gt;
		&lt;cfset structDelete(allwords,k)&gt;
	&lt;/cfif&gt;
&lt;/cfloop&gt;
---&gt;

&lt;!--- get min, max ---&gt;
&lt;cfset minval = 999999&gt;
&lt;cfset maxval = 0&gt;
&lt;cfloop item="k" collection="#allwords#"&gt;
	&lt;cfif allwords[k] lt minval&gt;
		&lt;cfset minval = allwords[k]&gt;
	&lt;cfelseif allwords[k] gt maxval&gt;
		&lt;cfset maxval = allwords[k]&gt;
	&lt;/cfif&gt;
&lt;/cfloop&gt;

&lt;cfset diff = maxval - minval&gt;
&lt;cfset distribution = diff / 3&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;&lt;/title&gt;
	&lt;meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1" /&gt;	
	&lt;meta name="description" content="" /&gt;
	&lt;meta name="keywords" content="" /&gt;

	&lt;link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css"&gt;
	&lt;!--[if lt IE 9]&gt;&lt;script src="http://html5shim.googlecode.com/svn/trunk/html5.js"&gt;&lt;/script&gt;&lt;![endif]--&gt;
	&lt;script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"&gt;&lt;/script&gt;
	&lt;script type="text/javascript"&gt;
		$(function() {
			
		});	
	&lt;/script&gt;
	&lt;style&gt;
	.smallestTag { font-size: xx-small; }
	.smallTag { font-size: small; }
	.mediumTag { font-size: medium; }
	.largeTag { font-size: large; }
	.largestTag { font-size: xx-large; } 
	&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

	&lt;div class="container"&gt;
		&lt;h2&gt;Word Cloud&lt;/h2&gt;
		&lt;cfloop item="w" collection="#allWords#"&gt;
			&lt;cfif allWords[w] EQ minval&gt;
				&lt;cfset class="smallestTag"&gt;
			&lt;cfelseif allWords[w] EQ maxval&gt;
				&lt;cfset class="largestTag"&gt;
			&lt;cfelseif allWords[w] GT (minval + (distribution*2))&gt;
				&lt;cfset class="largeTag"&gt;
			&lt;cfelseif allWords[w] GT (minval + distribution)&gt;
				&lt;cfset class="mediumTag"&gt;
			&lt;cfelse&gt;
				&lt;cfset class="smallTag"&gt;
			&lt;/cfif&gt;
			&lt;cfoutput&gt;&lt;span class="#class#"&gt;#w#&lt;/a&gt;&lt;/cfoutput&gt;
		&lt;/cfloop&gt;
		&lt;/p&gt;

	&lt;/div&gt;

&lt;/body&gt;
&lt;/html&gt;
&lt;/code&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/2183xCF0nR-GoJe-CAB3ENCC_-M/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/2183xCF0nR-GoJe-CAB3ENCC_-M/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/2183xCF0nR-GoJe-CAB3ENCC_-M/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/2183xCF0nR-GoJe-CAB3ENCC_-M/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/WrB-ZhOt4pY" height="1" width="1"/&gt;</description>
				
				
				<category>ColdFusion</category>
				
				<pubDate>Wed, 01 Feb 2012 15:42:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/2/1/Generate-a-tag-cloud-from-an-RSS-feed-with-ColdFusion</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/2/1/Generate-a-tag-cloud-from-an-RSS-feed-with-ColdFusion</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>Reminder - Adobe Cookbooks</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/sbDw_nRRwrM/Reminder--Adobe-Cookbooks</link>
				<description>As a gentle reminder, don't forget the excellent resource that is the &lt;a href="http://cookbooks.adobe.com/home"&gt;Adobe Cookbook&lt;/a&gt;, including over 200 &lt;a href="http://cookbooks.adobe.com/coldfusion"&gt;ColdFusion Cookbook&lt;/a&gt; entries. Looks like it has been a while since content was added, so take this as a gentle push to yall to consider adding something. 

&lt;img src="http://www.raymondcamden.com/images/ScreenClip22.png" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/g9q1-ZDMNVs-mY5oPmera3tIx78/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/g9q1-ZDMNVs-mY5oPmera3tIx78/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/g9q1-ZDMNVs-mY5oPmera3tIx78/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/g9q1-ZDMNVs-mY5oPmera3tIx78/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/sbDw_nRRwrM" height="1" width="1"/&gt;</description>
				
				
				<category>Development</category>
				
				<category>ColdFusion</category>
				
				<pubDate>Wed, 01 Feb 2012 10:42:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/2/1/Reminder--Adobe-Cookbooks</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/2/1/Reminder--Adobe-Cookbooks</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>RIACon 2012</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/GMRhzSukveQ/RIACon-2012</link>
				<description>RIACon 2012 will be held August 6th and 7th in beautiful Rockville, MD at The Legacy Hotel and Conference Centre. The official call for speakers will begin next week, but for more information, check out the web site: &lt;a href="http://www.riacon.com/"&gt;http://www.riacon.com&lt;/a&gt;. I missed last year but hope to speak at this years conference!

&lt;img src="http://www.raymondcamden.com/images/ScreenClip21.png" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/GXkb18k-_syNoQrCmlICGDjzOqw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GXkb18k-_syNoQrCmlICGDjzOqw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/GXkb18k-_syNoQrCmlICGDjzOqw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GXkb18k-_syNoQrCmlICGDjzOqw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/GMRhzSukveQ" height="1" width="1"/&gt;</description>
				
				
				<category>Mobile</category>
				
				<category>JavaScript</category>
				
				<category>HTML5</category>
				
				<category>ColdFusion</category>
				
				<pubDate>Mon, 30 Jan 2012 15:31:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/1/30/RIACon-2012</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/1/30/RIACon-2012</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>Bad use of tablet space - an example</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/3LhHba3LKco/Bad-use-of-tablet-space--an-example</link>
				<description>I came across something interesting in a tablet app today and I thought it was a great example of bad design. I hate to go critical, especially for a service I love, and &lt;b&gt;especially&lt;/b&gt; since I'm no expert in tablet design myself, but I thought it would be a good illustration and a good discussion here.Late last year I discovered &lt;a href="http://www.tripit.com/"&gt;TripIt&lt;/a&gt;. Apparently everyone at Adobe uses it and I was just late to the party. It is - essentially - a way to track flights and trip information. It's real handy when you are traveling and need a quick way to look up your flights and hotel information. I used to simply copy and paste this information into an Evernote note called "Travel", and if I forgot to do so, I was screwed. Tripit makes this nicer though - especially since you can actually forward them emails from your travel agent. Anyway - go to the site - check it out - love it - etc. 

I've had the mobile version for some time now, but just today I decided to check out their iPad2 version. I was pretty surprised by how badly they made use of the tablet form. After selecting a trip, this is what you see:


&lt;img src="http://www.raymondcamden.com/images/IMG_0054 - Copy.PNG" /&gt;

That's a heck of a lot of white space. I thought maybe I had hit the wrong button, but as far as I can see, that's the main view. To actually see your itinerary, you click the itinerary button to make it fly out:

&lt;img src="http://www.raymondcamden.com/images/IMG_0055 - Copy.PNG" /&gt;

I find it really odd that the default view makes so little use of the space. Maybe there is other data that shows up there, but wouldn't the itinerary normally be the most critical information? Compare this to their Android version:

&lt;img src="http://www.raymondcamden.com/images/Screenshot_2012-01-30-11-35-58.png" /&gt;

I thought that was the end of it until I accidentally oriented my iPad into Landscape mode and saw this:

&lt;img src="http://www.raymondcamden.com/images/IMG_0056 - Copy.PNG" /&gt;

Now &lt;i&gt;that&lt;/i&gt; makes sense to me. So any ideas why the Portrait mode is so bare? As I said, maybe there is trip information folks store that normally show up there. I only use TripIt in a pretty basic manner so I may be missing something.
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ycabzg7dGykBdMIuGuvT9k_gMgw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ycabzg7dGykBdMIuGuvT9k_gMgw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ycabzg7dGykBdMIuGuvT9k_gMgw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ycabzg7dGykBdMIuGuvT9k_gMgw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/3LhHba3LKco" height="1" width="1"/&gt;</description>
				
				
				<category>Mobile</category>
				
				<pubDate>Mon, 30 Jan 2012 11:18:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/1/30/Bad-use-of-tablet-space--an-example</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/1/30/Bad-use-of-tablet-space--an-example</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>Searching across all resources in Chrome</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/jL9rLeTo3uY/Searching-across-all-resources-in-Chrome</link>
				<description>Thanks for this goes to &lt;a href="http://paulirish.com/"&gt;Paul Irish&lt;/a&gt;. For a while now I've been trying to find a way to search for a value across multiple files that are loaded in a browser request. But every time I used the Search field in Chrome Dev tools, it would only search the current file. I'm talking about the highlighted search field below:


&lt;img src="http://www.raymondcamden.com/images/ScreenClip19.png" /&gt;

Yes - I know - my MSPaint skills suck. ;) As I said, that only searches the current file. If there are a bunch of JavaScript files in one request, you're screwed unless you feel like manually switching from one to another.

But it turns out there's &lt;i&gt;another&lt;/i&gt; search form you can open with ctrl-shift-f (cmd-opt-f on OSX):

&lt;img src="http://www.raymondcamden.com/images/ScreenClip20.png" /&gt;

Not only will it search across all files (not just JavaScript), it supports case sensitivity and regex based searches as well. 

Anyway - thanks again to Paul for sharing the tip and I hope it helps others.
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/WK07-5qJjA4fPZt_sfzk6m3W69Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WK07-5qJjA4fPZt_sfzk6m3W69Q/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/WK07-5qJjA4fPZt_sfzk6m3W69Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WK07-5qJjA4fPZt_sfzk6m3W69Q/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/jL9rLeTo3uY" height="1" width="1"/&gt;</description>
				
				
				<category>Development</category>
				
				<pubDate>Fri, 27 Jan 2012 16:31:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/1/27/Searching-across-all-resources-in-Chrome</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/1/27/Searching-across-all-resources-in-Chrome</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>A look at JavaScript Form Validation</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/p9X_u26n9w4/A-look-at-JavaScript-Form-Validation</link>
				<description>Validating forms with JavaScript has been possible since the very beginning of time... or at least the introduction of LiveScript (the original name before the marketing drones got ahold of it). While it's not particular new or as exciting as Canvas based games, JavaScript form validation is one of the best uses of the language (imo) and something that can dramatically improve your user's experience. I recently had a reader ask how to do JavaScript form validation so I thought it might be nice to write up a simple tutorial. This is not meant to cover every possible way, of course, and will be a very manual approach (no frameworks or plugins!) to the problem. As always, I welcome folks comments and suggestions below.&lt;p/&gt;

Before we write a lick of code, let's look at this topic at a high level. JavaScript form validation comes down to four basic steps:

&lt;p/&gt;

The first thing you must do is prevent your forms from doing their default behavior - submitting. There are a few ways of doing this but our method will focus on simply listening for the submit event for the form. One tricky thing here to watch out for is ensuring our code doesn't have any errors. Yeah, sure, we should &lt;i&gt;always&lt;/i&gt; do that, right? Well one thing that may trip you up is that if an error occurs in your code then the browser will go ahead and submit the form as if nothing happened. This is probably a good thing for the web at large, but during development it can be a bit of a pain. See my random tips at the end for a workaround for this.

&lt;p/&gt;

The next thing is to introspect the form and figure out what needs to be checked for what. So that's part technical ("how do I know what form field X is set to?") and part business ("what fields do I care about?"). Obviously this guide can't cover every detail, but hopefully it will give you enough to go on to build more complex form validation routines. 

&lt;p/&gt;

The third concern will be reporting the error back to the user. Again, there are a couple of options here. You can use the old Alert prompt, which is ugly, but direct, or simply update the DOM with nice error messages. These can be on top of the form, to the side, underneath, or even next to the particular fields that are incorrect. Again, you've got options, but obviously you need to let the user know what in the heck is wrong.

&lt;p/&gt;

And lastly, you &lt;b&gt;must absolutely 100% no excuses test your form with JavaScript turned off&lt;/b&gt;. While the amount of people without JavaScript is pretty minuscule, there are far too many sites that react badly when you submit a form with JavaScript turned off. I think it's perfectly reasonable to require JavaScript and simply return nothing, or a blunt error, to a user with JavaScript turned off, but you want to ensure bad things don't happen. Assume users will not send the right form fields, will send more data then you expect, less data, etc. Your form submission page is the exhaust port of the Death Star. It's dangerous.

&lt;p/&gt;

Ok, enough blather. Let's begin with a simple example. I've got a form with two simple fields that I will want to validate. Let's look at the code before any JavaScript code is added.

&lt;p/&gt;

&lt;code&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
	
&lt;head&gt;
&lt;title&gt;Form Validation&lt;/title&gt;
&lt;/head&gt;	
	
&lt;body&gt;
	
&lt;form action="something.php" method="post"&gt;
	username: &lt;input type="text" name="username" value=""&gt;&lt;br/&gt;
	password: &lt;input type="password" name="password" value=""&gt;&lt;br/&gt;
	&lt;input type="submit" value="Login"&gt;
&lt;/form&gt;

&lt;/body&gt;	
&lt;/html&gt;
&lt;/code&gt;

&lt;p/&gt;

If for some reason you want to run this as is, you can view it here: &lt;a href="http://www.raymondcamden.com/demos/2012/jan/27/test1.html"&gt;http://www.raymondcamden.com/demos/2012/jan/27/test1.html&lt;/a&gt;.

&lt;p/&gt;

So - let's begin. I mentioned that our first task is to listen for, and take over, the form submission. We want to prevent the form from doing what it normally does - submit. 

&lt;p/&gt;

&lt;code&gt;
//get a handle to my form
var myform = document.getElementById("myform");
    
//listen for submit
myform.addEventListener("submit", function(e) {
&lt;/code&gt;

&lt;p&gt;

In order for this to work, we had to add an ID to the form. We could get the form object other ways, but IDs are the most direct, simple way:

&lt;p/&gt;

&lt;code&gt;
&lt;form action="something.php" method="post" id="myform"&gt;
&lt;/code&gt;

&lt;p/&gt;

Now let's dig into the event handler I started earlier:

&lt;p/&gt;

&lt;code&gt;
myform.addEventListener("submit", function(e) {
     console.log("submit");
        
     var username = document.getElementById("username").value;
     var password = document.getElementById("password").value;
     var errors = '';
        
     if(username == '') errors += 'Please specify a username.\n';
     if(password == '') errors += 'Please specify a password.\n';
        
 
     if(errors != '') {
         alert(errors);
         e.preventDefault();
     }
        
});
&lt;/code&gt;

&lt;p&gt;

For our form, we want to check the values of the username and password field and ensure they have something in them. My first two lines grab the fields and their values using getElementById. Those of you used to jQuery know that the framework makes this call a bit simpler, but as I said in the beginning I'm intentionally ignoring frameworks for now. I also create a simple errors string variable. This will store the text I'll show to the user in case an error.

&lt;p&gt;

The next block begins my simple validation. In my case, I'm just comparing both values to empty strings. Not the most complex validation, but it works. (For extra credit, I could have trimmed the space from my inputs. I've been to many forms that let me bypass their validation by just hitting the space bar.) Note how for each "rule" I simply append to the errors string. This then let's me check if the string is blank. If it isn't, it means something went wrong. I use the alert feature to tell the user.

&lt;p&gt;

Here's a critical part. I want to ensure the form does &lt;b&gt;not&lt;/b&gt; proceed as normal. Without this one line, it would: e.preventDefault(). e is the name of the variable that the browser will pass the Event object in. Think of it as the actual form submission event. It's not the form, it's the action of me clicking submit. 

&lt;p&gt;

If you want to test this, use the URL here. Note that I did NOT bother building a file to accept the form input. something.php doesn't exist. If that confuses people, I'll put in a handler, but for now, expect a 404 when the form submits: &lt;a href="http://www.raymondcamden.com/demos/2012/jan/27/test2.html"&gt;http://www.raymondcamden.com/demos/2012/jan/27/test2.html&lt;/a&gt;

&lt;p&gt;

And here is the complete page:

&lt;p&gt;

&lt;code&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
	
&lt;head&gt;
&lt;title&gt;Form Validation&lt;/title&gt;
&lt;script&gt;
function init() {
    console.log("init");
    
    //get a handle to my form
    var myform = document.getElementById("myform");
    
    //listen for submit
    myform.addEventListener("submit", function(e) {
        console.log("submit");
        
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;
        var errors = '';
        
        if(username == '') errors += 'Please specify a username.\n';
        if(password == '') errors += 'Please specify a password.\n';
        
 
        if(errors != '') {
            alert(errors);
            e.preventDefault();
        }
        
    });
}
&lt;/script&gt;
&lt;/head&gt;	
	
&lt;body onload="init()"&gt;
	
&lt;form action="something.php" method="post" id="myform"&gt;
	username: &lt;input type="text" name="username" id="username" value=""&gt;&lt;br/&gt;
	password: &lt;input type="password" name="password" id="password" value=""&gt;&lt;br/&gt;
	&lt;input type="submit" value="Login"&gt;
&lt;/form&gt;

&lt;/body&gt;	
&lt;/html&gt;
&lt;/code&gt;

&lt;p&gt;

So, that works, but is a bit ugly. Let's get rid of the alert. It smells of 1997. How about inserting some text into the page instead? I added a new div, and relevant style, to my page:

&lt;p&gt;

&lt;code&gt;
&lt;style&gt;
#errorDiv {
    color: red;
    font-weight: bold;
}
&lt;/style&gt;
...

&lt;div id="errorDiv"&gt;&lt;/div&gt;
&lt;/code&gt;

&lt;p&gt;

The div is empty for now which means the user won't see it until I actually put something in it. Now instead of creating an alert, I'm going to just inject HTML into the div. I modified my error messages to use BR tags. (In case it wasn't clear, the alert uses line breaks, \n, to create the breaks.)

&lt;p&gt;

&lt;code&gt;
if(username == '') errors += 'Please specify a username.&lt;br/&gt;';
if(password == '') errors += 'Please specify a password.&lt;br/&gt;';
&lt;/code&gt;

&lt;p&gt;

And to inject it, I modify the div's innerHTML property:

&lt;p&gt;

&lt;code&gt;
var errorDiv = document.getElementById("errorDiv");
if(errors != '') {
     errorDiv.innerHTML=errors;
     e.preventDefault();
} else errorDiv.innerHTML='';
&lt;/code&gt;

&lt;p&gt;

So - what's up with the else there? When the user hits the form, it's possible that they will make some type of mistake. When they do, a message is added into the div. When they correct the mistake and hit submit, the form submission will go along as normal. But if the form processor is slow, then the user will continue to see the error message. By clearing it, we remove any doubt in the user's mind that their form is being processed. 

&lt;p&gt;

You can demo this here: &lt;a href="http://www.raymondcamden.com/demos/2012/jan/27/test3.html"&gt;http://www.raymondcamden.com/demos/2012/jan/27/test3.html&lt;/a&gt; The full code is below:

&lt;p&gt;

&lt;code&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
	
&lt;head&gt;
&lt;title&gt;Form Validation&lt;/title&gt;
&lt;script&gt;
function init() {
    console.log("init");
    
    //get a handle to my form
    var myform = document.getElementById("myform");
    
    //listen for submit
    myform.addEventListener("submit", function(e) {
        console.log("submit");
        
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;
        var errors = '';
        
        if(username == '') errors += 'Please specify a username.&lt;br/&gt;';
        if(password == '') errors += 'Please specify a password.&lt;br/&gt;';
        
        var errorDiv = document.getElementById("errorDiv");
        if(errors != '') {
            errorDiv.innerHTML=errors;
            e.preventDefault();
        } else errorDiv.innerHTML='';
        
    });
}
&lt;/script&gt;
&lt;style&gt;
#errorDiv {
    color: red;
    font-weight: bold;
}
&lt;/style&gt;
&lt;/head&gt;	
	
&lt;body onload="init()"&gt;

&lt;div id="errorDiv"&gt;&lt;/div&gt;
	
&lt;form action="something.php" method="post" id="myform"&gt;
	username: &lt;input type="text" name="username" id="username" value=""&gt;&lt;br/&gt;
	password: &lt;input type="password" name="password" id="password" value=""&gt;&lt;br/&gt;
	&lt;input type="submit" value="Login"&gt;
&lt;/form&gt;

&lt;/body&gt;	
&lt;/html&gt;
&lt;/code&gt;

&lt;p&gt;

Alrighty... now that we've got the basics down, it's time to kick it up a notch. Let's begin by looking at a slightly more complex form:

&lt;p&gt;

&lt;code&gt;
&lt;form action="something.php" method="post" id="myform"&gt;
    &lt;p&gt;
	name: &lt;input type="text" name="name" id="name" value=""&gt;
	&lt;/p&gt;
	
	&lt;p&gt;
	bio: &lt;textarea name="bio" id="bio"&gt;&lt;/textarea&gt;
    &lt;/p&gt;
    
    &lt;p&gt;
    gender: &lt;select name="gender" id="gender"&gt;
    &lt;option value=""&gt;Pick One&lt;/option&gt;
    &lt;option value="female"&gt;Female&lt;/option&gt;
    &lt;option value="male"&gt;Male&lt;/option&gt;
    &lt;/select&gt;&lt;br/&gt;
    &lt;/p&gt;
    
    &lt;p&gt;
    favorite colors:&lt;br/&gt;
    &lt;input type="checkbox" name="favcolor" id="favcolor-red" value="red"&gt; &lt;label for="favcolor-red"&gt;Red&lt;/label&gt;&lt;br/&gt;
    &lt;input type="checkbox" name="favcolor" id="favcolor-blue" value="blue"&gt; &lt;label for="favcolor-blue"&gt;Blue&lt;/label&gt;&lt;br/&gt;
    &lt;input type="checkbox" name="favcolor" id="favcolor-green" value="green"&gt; &lt;label for="favcolor-green"&gt;Green&lt;/label&gt;&lt;br/&gt;
    &lt;/p&gt;

    &lt;p&gt;
    favorite food:&lt;br/&gt;
    &lt;input type="radio" name="favfood" id="favfood-pizza" value="pizza"&gt; &lt;label for="favfood-pizza"&gt;Pizza&lt;/label&gt;&lt;br/&gt;
    &lt;input type="radio" name="favfood" id="favfood-beer" value="beer"&gt; &lt;label for="favfood-beer"&gt;Beer&lt;/label&gt;&lt;br/&gt;
    &lt;input type="radio" name="favfood" id="favfood-cookie" value="cookie"&gt; &lt;label for="favfood-cookie"&gt;Cookies&lt;/label&gt;&lt;br/&gt;
    &lt;/p&gt;
    
	&lt;input type="submit" value="Submit"&gt;
&lt;/form&gt;
&lt;/code&gt;

&lt;p&gt;

We've now got a text field, a textarea, a select box, a set of checkboxes, and a radio group. The code we used earlier to grab form values will need to be updated a bit. So for example, we can't just get the value of a set of checkboxes. 

&lt;p&gt;

In order to simplify things a bit, we're going to upgrade our code a bit to something more modern - the &lt;a href="http://www.w3.org/TR/selectors-api2/"&gt;Selectors API&lt;/a&gt;. Roughly, this gives us jQuery-style APIs to the DOm and simplifies things for us quite a bit, especially in terms of our radio/checkbox groups. The API comes down to two main method: querySelector and querySelectorAll. I recommend this blog entry for a good introduction: &lt;a href="http://webdirections.org/blog/html5-selectors-api-its-like-a-swiss-army-knife-for-the-dom/"&gt;HTML5 selectors API -- It's like a Swiss Army Knife for the DOM&lt;/a&gt;. This is - yet again - one more cool aspect of HTML5 that seems to be passed over for demos of Canvas apps and kittens. Sigh. That being said, the support for this is actually rather good. When we talk about support and HTML features, typically the bugaboo is IE. In this case, IE8 and above supports the feature. Good enough for me. Let's look at one change:

&lt;p&gt;

&lt;code&gt;
var myform = document.getElementById("myform");
&lt;/code&gt;

&lt;p&gt;

versus:

&lt;p&gt;
var myform = document.querySelector("#myform");
&lt;/code&gt;

&lt;p&gt;

Not too terribly different, right? And if you are used to jQuery then this is familiar. You aren't saving a lot of keystrokes here, but wait, it gets better. Let's take a look at how we grab some of these form fields:

&lt;p&gt;

&lt;code&gt;
var name = document.querySelector("#name").value;
var bio = document.querySelector("#bio").value;
&lt;/code&gt;

&lt;p&gt;

Ok - not too different. Basically we just get to the DOM via the newer API versus the older one. How about the select? Well, select fields have a selectedIndex. Our business rule will be to simply ensure the first item isn't picked, so all we care about is the index.

&lt;p&gt;

&lt;code&gt;
var gender = document.querySelector("#gender").selectedIndex;
&lt;/code&gt;

&lt;p&gt;

Ok, now it's time to get fancy. How do we ensure our users pick at least one from the checkbox group? We could use document.getElementById for all four fields and ensure at least one has their checked property set to true. That's not horrible per se, but it's a lot of typing I would hope we can skip. Our radio group has same validation rule. How can we do this nice and easy?

&lt;p&gt;

&lt;code&gt;
var colorcbs = document.querySelectorAll("input[name='favcolor']:checked");
var foodcbs = document.querySelectorAll("input[name='favfood']:checked");
&lt;/code&gt;

&lt;p&gt;

Let's look at the first one since the second example is the same ignoring the name. Our selector is: input[name='favcolor']:checked. Reading left to right we have: Give me input fields that have an attribute name with the value favcolor, and filter to those that are checked. Or in English - what did I check in the favcolor group? These two calls both return an array of DOM items for any checked field. What's cool is that I can then just check the length of this array. This would also let me support things like, "Pick at least one favorite food but no more than three." Let's take a look now at the validation code:

&lt;p&gt;

&lt;code&gt;
if(name == '') errors += 'Enter a name.&lt;br/&gt;';
//bio not required
if(gender == 0) errors += 'Select a gender.&lt;br/&gt;'
if(colorcbs.length == 0) errors += 'Select a favorite color.&lt;br&gt;';
if(foodcbs.length == 0) errors += 'Select a favorite food.&lt;br&gt;';
&lt;/code&gt;

&lt;p&gt;

Not too difficult, right? You can view the full demo here: &lt;a href="http://www.raymondcamden.com/demos/2012/jan/27/test4.html"&gt;http://www.raymondcamden.com/demos/2012/jan/27/test4.html&lt;/a&gt;

&lt;p&gt;

And here is the complete template:

&lt;p&gt;

&lt;code&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
	
&lt;head&gt;
&lt;title&gt;Form Validation&lt;/title&gt;
&lt;script&gt;
function init() {
    console.log("init");
    
    //get a handle to my form
    var myform = document.querySelector("#myform");
    
    //listen for submit
    myform.addEventListener("submit", function(e) {
        console.log("submit");
        
        var errors = '';
        var errorDiv = document.querySelector("#errorDiv");
               
        var name = document.querySelector("#name").value;
        var bio = document.querySelector("#bio").value;
        var gender = document.querySelector("#gender").selectedIndex;

        var colorcbs = document.querySelectorAll("input[name='favcolor']:checked");
        var foodcbs = document.querySelectorAll("input[name='favfood']:checked");

        if(name == '') errors += 'Enter a name.&lt;br/&gt;';
        //bio not required
        if(gender == 0) errors += 'Select a gender.&lt;br/&gt;'
        if(colorcbs.length == 0) errors += 'Select a favorite color.&lt;br&gt;';
        if(foodcbs.length == 0) errors += 'Select a favorite food.&lt;br&gt;';
        
        if(errors != '') {
            errorDiv.innerHTML=errors;
            e.preventDefault();
        } else errorDiv.innerHTML='';
            
            
    });
}
&lt;/script&gt;
&lt;style&gt;
#errorDiv {
    color: red;
    font-weight: bold;
}
&lt;/style&gt;
&lt;/head&gt;	
	
&lt;body onload="init()"&gt;

&lt;div id="errorDiv"&gt;&lt;/div&gt;
	
&lt;form action="something.php" method="post" id="myform"&gt;
    &lt;p&gt;
	name: &lt;input type="text" name="name" id="name" value=""&gt;
	&lt;/p&gt;
	
	&lt;p&gt;
	bio: &lt;textarea name="bio" id="bio"&gt;&lt;/textarea&gt;
    &lt;/p&gt;
    
    &lt;p&gt;
    gender: &lt;select name="gender" id="gender"&gt;
    &lt;option value=""&gt;Pick One&lt;/option&gt;
    &lt;option value="female"&gt;Female&lt;/option&gt;
    &lt;option value="male"&gt;Male&lt;/option&gt;
    &lt;/select&gt;&lt;br/&gt;
    &lt;/p&gt;
    
    &lt;p&gt;
    favorite colors:&lt;br/&gt;
    &lt;input type="checkbox" name="favcolor" id="favcolor-red" value="red"&gt; &lt;label for="favcolor-red"&gt;Red&lt;/label&gt;&lt;br/&gt;
    &lt;input type="checkbox" name="favcolor" id="favcolor-blue" value="blue"&gt; &lt;label for="favcolor-blue"&gt;Blue&lt;/label&gt;&lt;br/&gt;
    &lt;input type="checkbox" name="favcolor" id="favcolor-green" value="green"&gt; &lt;label for="favcolor-green"&gt;Green&lt;/label&gt;&lt;br/&gt;
    &lt;/p&gt;

    &lt;p&gt;
    favorite food:&lt;br/&gt;
    &lt;input type="radio" name="favfood" id="favfood-pizza" value="pizza"&gt; &lt;label for="favfood-pizza"&gt;Pizza&lt;/label&gt;&lt;br/&gt;
    &lt;input type="radio" name="favfood" id="favfood-beer" value="beer"&gt; &lt;label for="favfood-beer"&gt;Beer&lt;/label&gt;&lt;br/&gt;
    &lt;input type="radio" name="favfood" id="favfood-cookie" value="cookie"&gt; &lt;label for="favfood-cookie"&gt;Cookies&lt;/label&gt;&lt;br/&gt;
    &lt;/p&gt;
    
	&lt;input type="submit" value="Submit"&gt;
&lt;/form&gt;

&lt;/body&gt;	
&lt;/html&gt;
&lt;/code&gt;

&lt;p&gt;

So... what do you think? If you've never worked with JavaScript, or are just beginning, does this make sense? Any question?

&lt;p&gt;

p.s. So I mentioned above that one of the things you have to watch our for is errors in your submit handler. One way to work around that - at least in Chrome - is to enable Console/Preserve log upon navigation. This will keep the error in your log even after the form goes to the submit page.
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/5o-5gh_WgnIT-ks4cb72EAEJu-4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/5o-5gh_WgnIT-ks4cb72EAEJu-4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/5o-5gh_WgnIT-ks4cb72EAEJu-4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/5o-5gh_WgnIT-ks4cb72EAEJu-4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/p9X_u26n9w4" height="1" width="1"/&gt;</description>
				
				
				<category>Development</category>
				
				<category>JavaScript</category>
				
				<pubDate>Fri, 27 Jan 2012 09:03:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/1/27/A-look-at-JavaScript-Form-Validation</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/1/27/A-look-at-JavaScript-Form-Validation</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>A database utility class for PhoneGap</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/19ispTvPjP8/A-database-utility-class-for-PhoneGap</link>
				<description>I've done a few projects now that make use of PhoneGap's &lt;a href="http://docs.phonegap.com/en/1.3.0/phonegap_storage_storage.md.html#Storage"&gt;database support&lt;/a&gt;. Like most things in PhoneGap, it just plain works. But I've encountered a few things I thought could be done a bit easier, so I've built a simple utility class for my future projects. I thought I'd share it with folks and see if it would be useful for others.&lt;p/&gt;

My utility library has the following features:

&lt;p/&gt;

executeBatch: Given a file path (via URL), you can have PhoneGap read in an XML file that contains a set of SQL commands. So for example:

&lt;p/&gt;

&lt;code&gt;
myDbController.executeBatch("sql/createtables.xml",successHandler,errHandler);
&lt;/code&gt;

&lt;p/&gt;

Where your XML would look like so:

&lt;p/&gt;

&lt;code&gt;
&lt;sql&gt;
&lt;statement&gt;
create table foo if not exists foo(....)
&lt;/statement&gt;
&lt;statement&gt;
create table moo if not exists foo(....)
&lt;/statement&gt;
&lt;/sql&gt;
&lt;/code&gt;

&lt;p/&gt;

This was done to support the fact that unlike Adobe AIR, the SQLite support in PhoneGap doesn't allow you to ship a pre-populated database. (Although it &lt;b&gt;is&lt;/b&gt; possible via a workaround: &lt;a href="http://gauravstomar.blogspot.com/2011/08/prepopulate-sqlite-in-phonegap.html?"&gt;Prepopulate SQLite DataBase in PhoneGap Application&lt;/a&gt;.) The syntax for this was based on work from my fellow evangelist, &lt;a href="http://coenraets.org/blog/"&gt;Christope Coenraets&lt;/a&gt;. 

&lt;p/&gt;

executeSql: As you can guess, this simply wraps up executing SQL. While PhoneGap doesn't make this necessarily hard, I found the API a bit awkward in terms of all the callbacks you had to use. ("All" sounds like a lot - it's more like two - but you get the idea.) So for example:

&lt;p/&gt;

&lt;code&gt;
dbController.executeSql("select * from notes", gotNote, errHandler);
&lt;/code&gt;

&lt;p/&gt;

The other nice thing this will do is automatically take the result set and return it as a simple array of objects. Again, it's not difficult to work with the normal result set, this is just a bit simpler. 

&lt;p/&gt;

Finally, the class has a simple init() handler that sets up your connections for you. What makes it nice is that it can also automatically call your batch scripts for you. So for example:

&lt;p/&gt;

&lt;code&gt;
dbController.init("main","data/seed.xml",dbReady);
&lt;/code&gt;

&lt;p/&gt;

The code is below and is free to use by anyone.

&lt;p/&gt;

&lt;code&gt;
var DBController = function() {

	var db,success,failure;
	
	return {

		init:function(name,importscript,successHandler)	{
			//todo - allow for version
			db = window.openDatabase(name,"1.0",name,100000);			
			if(typeof importscript !== "undefined") {
				console.log("being asked to run a script");
				if(typeof successHandler === "undefined") throw "Invalid call - must pass success handler when importing data";
				this.executeBatch(importscript,successHandler);
			}
		},

		executeBatch:function(path,successHandler,errorHandler) {
			success=successHandler;
			failure=errorHandler;
			
			$.get(path, {}, this.gotFile, "xml");
		},

		//sql, successHandler, errorHandler are required
		executeSql:function(sql,args,successHandler,errorHandler) {
			console.log('going to run '+sql+ '    '+arguments.length);
			//Don't like this - but way to make args be optional and in 2nd place
			if(arguments.length == 3) {
				successHandler = arguments[1];
				errorHandler = arguments[2];
				args = [];
			}
			db.transaction(
				function(tx) { tx.executeSql(sql,args,function(tx,res) {
					//todo - figure out fraking scoping rules and why line below didnt work, nor this.X
					//res = translateResultSet(res);
					var result = [];
					for(var i=0; i&lt;res.rows.length; i++) {
						result.push(res.rows.item(i));
					}
					successHandler(result);
				})}
			, errorHandler)	
		},
		
			
		gotFile:function(doc) {
			var statements = [];
			var statementNodes=doc.getElementsByTagName("statement");
			for(var i=0; i&lt;statementNodes.length; i++) {
				statements.push(statementNodes[i].textContent);
			}
			if(statements.length) {
				db.transaction(function(tx) {
					//do nothing
					for(var i=0;i&lt;statements.length;i++) {
						tx.executeSql(statements[i]);
					}
				}, failure,success);
			}
		},
		
		translateResultSet:function(res) {
			var result = [];
			for(var i=0; i&lt;res.rows.length; i++) {
				result.push(res.rows.item(i));
			}
			return result;
			
		}
			
	}
	
};
&lt;/code&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/WRfUqSlzGel6_lhi8YpHZLWxd1A/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WRfUqSlzGel6_lhi8YpHZLWxd1A/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/WRfUqSlzGel6_lhi8YpHZLWxd1A/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WRfUqSlzGel6_lhi8YpHZLWxd1A/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/19ispTvPjP8" height="1" width="1"/&gt;</description>
				
				
				<category>Mobile</category>
				
				<category>Development</category>
				
				<pubDate>Thu, 26 Jan 2012 08:36:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/1/26/A-database-utility-class-for-PhoneGap</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/1/26/A-database-utility-class-for-PhoneGap</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>New Adobe Inspire Magazine out (with an article by yours truly...)</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/ntry2ZLdTl4/New-Adobe-Inspire-Magazine-out-with-an-article-by-yours-truly</link>
				<description>The title says it all. The latest issue of Adobe's &lt;a href="http://www.adobe.com/newsletters/inspire/february2012/index.html"&gt;Inspire Magazine&lt;/a&gt; is out. There's lots of good content with the only low point being my own article (grin) on PhoneGap: &lt;a href="http://www.adobe.com/newsletters/inspire/february2012/articles/article5/index.html"&gt;Combining your web skills with PhoneGap to build mobile apps&lt;/a&gt;. Probably nothing new for folks who read my blog entries here.
&lt;img src="http://www.raymondcamden.com/images/ScreenClip18.png" /&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/MyHlZRhbq0lYjX6XSwES_jRZypM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/MyHlZRhbq0lYjX6XSwES_jRZypM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/MyHlZRhbq0lYjX6XSwES_jRZypM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/MyHlZRhbq0lYjX6XSwES_jRZypM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/ntry2ZLdTl4" height="1" width="1"/&gt;</description>
				
				
				<category>Mobile</category>
				
				<category>Development</category>
				
				<pubDate>Wed, 25 Jan 2012 08:22:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/1/25/New-Adobe-Inspire-Magazine-out-with-an-article-by-yours-truly</guid>
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/1/25/New-Adobe-Inspire-Magazine-out-with-an-article-by-yours-truly</feedburner:origLink></item>
			
		 	
			
			
			<item>
				<title>PhoneGap RSS Reader - Part 3</title>
				<link>http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~3/K4lBuWmYJPs/PhoneGap-RSS-Reader--Part-3</link>
				<description>Welcome to my third entry for my (what was at first) simple PhoneGap RSS reader. If you haven't yet, please be sure to read &lt;a href="http://www.raymondcamden.com/index.cfm/2011/10/11/PhoneGap-RSS-Reader"&gt;part 1&lt;/a&gt; and &lt;a href="http://www.raymondcamden.com/index.cfm/2011/12/19/PhoneGap-RSS-Reader--Part-2"&gt;part 2&lt;/a&gt; so you have some context about how this application works. In this part, I'm going to tackle two enhancements suggested to me by my readers:&lt;p/&gt;

&lt;ol&gt;
&lt;li&gt;First - support rendering the entries if the user is offline.
&lt;li&gt;Second - clean up the UX a bit so that when a user views an entry, leaves, and then comes back to another entry, they don't see the old text there for a split second.
&lt;/ol&gt;

&lt;p/&gt;

Let's tackle the offline question first. I spent some time thinking about this and tried a few things that didn't quite work out the way I wanted. The first thing I tried was checking navigator.onLine. (See this &lt;a href="http://stackoverflow.com/questions/2384167/check-if-internet-connection-exists-with-javascript"&gt;Stackoverflow entry&lt;/a&gt; for details.) This did not work well for me. When I switched my device to airplane mode it still reported me as online. I then looked into PhoneGap's &lt;a href="http://docs.phonegap.com/en/1.3.0/phonegap_connection_connection.md.html#Connection"&gt;Connection&lt;/a&gt; API. This &lt;i&gt;kinda&lt;/i&gt; worked. It didn't seem to grok my Airplane mode at all. It certainly didn't report it as online (it returned a null actually) and I could have handled it, but I then had the issue of how I was going to handle coordinating the deviceready event along with the jQuery Mobile pageinit method.

&lt;p/&gt;

Then I realized something. I already had an Ajax error handler. And it worked. That's obvious of course, but it occurred to me. Why not use this error handler? It would not only support offline mode, but any error on the remote server as well. At the end of the day, if I can't get the RSS feed, who cares if I'm offline or if the server is down. I &lt;i&gt;could&lt;/i&gt; see caring, and if so, you would obviously want to use the PhoneGap Connection value, but I figured, why not go with the simple route.

&lt;p/&gt;

As for storage - that turned out to be trivial - LocalStorage. Have you guys figured out yet that I &lt;b&gt;really love HTML5 LocalStorage&lt;/b&gt;? 

&lt;p/&gt;

So, I decided to get rid of AjaxSetup. I only have one Ajax call in the entire application, so why not tie it to that call. So I switched from a $.get to a $.ajax:

&lt;p/&gt;

&lt;code&gt;
$.ajax({
	url:RSS,
	success:function(res,code) {
		entries = [];
		var xml = $(res);
		var items = xml.find("item");
		$.each(items, function(i, v) {
			entry = { 
				title:$(v).find("title").text(), 
				link:$(v).find("link").text(), 
				description:$.trim($(v).find("description").text())
			};
			entries.push(entry);
		});
		//store entries
		localStorage["entries"] = JSON.stringify(entries);
		renderEntries(entries);
	},
	error:function(jqXHR,status,error) {
		//try to use cache
		if(localStorage["entries"]) {
			$("#status").html("Using cached version...");
			entries = JSON.parse(localStorage["entries"])
			renderEntries(entries);				
		} else {
			$("#status").html("Sorry, we are unable to get the RSS and there is no cache.");
		}
	}
})
&lt;/code&gt;

&lt;p/&gt;

This is - for the most part, the same code as before, just using the core $.ajax method. You can see where the error function will look into LocalStorage for the cached copy, and where the success function always stores a copy. The renderEntries function is simply an abstracted out version of the display code:

&lt;p/&gt;

&lt;code&gt;
function renderEntries(entries) {
    var s = '';
    $.each(entries, function(i, v) {
        s += '&lt;li&gt;&lt;a href="#contentPage" class="contentLink" data-entryid="'+i+'"&gt;' + v.title + '&lt;/a&gt;&lt;/li&gt;';
    });
    $("#linksList").html(s);
    $("#linksList").listview("refresh");		
}
&lt;/code&gt;

&lt;p/&gt;

Woot. That works. Now for the next request. We want to ensure that users don't see the old content when loading in the RSS entry detail page. This turned out to be a bit weird. jQuery Mobile has logic to say, "Do this when a page is hiding, or before it hides", but for the life of me (and &lt;b&gt;please&lt;/b&gt; correct me if I'm wrong) there doesn't seem to be a good way to get the page that is leaving. You get passed the page you are going to, but not the &lt;i&gt;current&lt;/i&gt; page. I really feel like I'm missing something here, so please note this may get corrected later. For me though I simply added an event listener to the main page. It now sees if a previous page exists, and if so, clears out the text:

&lt;p/&gt;

&lt;code&gt;
$("#mainPage").live("pagebeforeshow", function(event,data) {
	if(data.prevPage.length) {
		$("h1", data.prevPage).text("");
		$("#entryText", data.prevPage).html("");
	};
});
&lt;/code&gt;

&lt;p/&gt;

And that's it. I've included the entire JavaScript file below (the HTML hasn't changed from the previous entry) and a zip of the entire project may be downloaded for the low cost of free.

&lt;p/&gt;

&lt;code&gt;



//EDIT THESE LINES
//Title of the blog
var TITLE = "ColdFusion Jedi";
//RSS url
var RSS = "http://feedproxy.google.com/RaymondCamdensColdfusionBlog";
//Stores entries
var entries = [];
var selectedEntry = "";

//listen for detail links
$(".contentLink").live("click", function() {
	selectedEntry = $(this).data("entryid");
});

function renderEntries(entries) {
    var s = '';
    $.each(entries, function(i, v) {
        s += '&lt;li&gt;&lt;a href="#contentPage" class="contentLink" data-entryid="'+i+'"&gt;' + v.title + '&lt;/a&gt;&lt;/li&gt;';
    });
    $("#linksList").html(s);
    $("#linksList").listview("refresh");		
}

//Listen for main page
$("#mainPage").live("pageinit", function() {
	//Set the title
	$("h1", this).text(TITLE);
	
	$.ajax({
		url:RSS,
		success:function(res,code) {
			entries = [];
			var xml = $(res);
			var items = xml.find("item");
			$.each(items, function(i, v) {
				entry = { 
					title:$(v).find("title").text(), 
					link:$(v).find("link").text(), 
					description:$.trim($(v).find("description").text())
				};
				entries.push(entry);
			});
			//store entries
			localStorage["entries"] = JSON.stringify(entries);
			renderEntries(entries);
		},
		error:function(jqXHR,status,error) {
			//try to use cache
			if(localStorage["entries"]) {
				$("#status").html("Using cached version...");
				entries = JSON.parse(localStorage["entries"])
				renderEntries(entries);				
			} else {
				$("#status").html("Sorry, we are unable to get the RSS and there is no cache.");
			}
		}
	});
	
});

$("#mainPage").live("pagebeforeshow", function(event,data) {
	if(data.prevPage.length) {
		$("h1", data.prevPage).text("");
		$("#entryText", data.prevPage).html("");
	};
});

//Listen for the content page to load
$("#contentPage").live("pageshow", function(prepage) {
	//Set the title
	$("h1", this).text(entries[selectedEntry].title);
	var contentHTML = "";
	contentHTML += entries[selectedEntry].description;
	contentHTML += '&lt;p/&gt;&lt;a href="'+entries[selectedEntry].link + '"&gt;Read Entry on Site&lt;/a&gt;';
	$("#entryText",this).html(contentHTML);
});
&lt;/code&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/qfX7SOFItm4HF1KY08CpK-pvJ2k/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qfX7SOFItm4HF1KY08CpK-pvJ2k/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/qfX7SOFItm4HF1KY08CpK-pvJ2k/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qfX7SOFItm4HF1KY08CpK-pvJ2k/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/RaymondCamdensColdfusionBlog/~4/K4lBuWmYJPs" height="1" width="1"/&gt;</description>
				
				
				<category>Mobile</category>
				
				<category>jQuery</category>
				
				<category>JavaScript</category>
				
				<pubDate>Tue, 24 Jan 2012 17:10:00 -0500</pubDate>
				<guid isPermaLink="false">http://www.raymondcamden.com/index.cfm/2012/1/24/PhoneGap-RSS-Reader--Part-3</guid>
				
				
				
				
				
			<feedburner:origLink>http://www.raymondcamden.com/index.cfm/2012/1/24/PhoneGap-RSS-Reader--Part-3</feedburner:origLink><enclosure url="http://feedproxy.google.com/~r/RaymondCamdensColdfusionBlog/~5/duqgmpfJJUw/RSSDemo1.zip" length="737022" type="application/x-zip-compressed" /><feedburner:origEnclosureLink>http://www.raymondcamden.com/enclosures/RSSDemo1.zip</feedburner:origEnclosureLink></item>
			
		 	
			</channel></rss>

