Ask a Jedi: Creating lowercased cookies in ColdFusion
Chris asks:
When creating a cookie with cfcookie, the cookie is created in uppercase characters. Is there a way to make it lowercase?
What he means is, if you do this:
<cfcookie name="x" value="Foo">
And you examine your cookies, you will see that ColdFusion created the cookie and named it X. This may not seem like a big deal, but it gets important if you want to read the cookie in JavaScript.
I tried to get fancy and set a cookie like so:
<cfset cookie["y"] = 2>
But that made no difference. I did a quick Google and discovered a tech note from - wait for it - 2004:
The cfcookie tag is unable to create or delete mixed case or lowercase cookies
The solution they provide though still works today. You can use the cfheader tag to make the cookie yourself:
<cfheader name="Set-Cookie" value="mycookie=z;expires=#DateFormat(CreateDate(2009,12,31), 'ddd, dd-mmm-yyyy')# #TimeFormat(CreateTime(00,00,00), 'HH:mm:ss')# GMT">
Things get weird though if you use both forms:
<cfheader name="Set-Cookie" value="mycookie=z;expires=#DateFormat(CreateDate(2009,12,31), 'ddd, dd-mmm-yyyy')# #TimeFormat(CreateTime(00,00,00), 'HH:mm:ss')# GMT">
<cfset cookie.mycookie = 'm'>
I should have two cookies, one named mycookie, value z and another named MYCOOKIE, value is m. But when you view the cookie scope from ColdFusion, you see 2 keys with the exact same name (MYCOOKIE), both with a value of m.
So watch out if you are using ColdFusion to manipulate cookies that may also be manipulated via JavaScript.
Ask a Jedi: Building a JavaScript Widget with ColdFusion
Shimju asks:
How can we create Javascript widget from a coldfusion query.
In more detail, we will ask our website visitors to signup for using our widget. While doing so, they will be asked to select which news categories they are interested and how many news they want to display on their site. And once user clicks submit button a Javascript code should generate and they can place it on their websites for appearing our widget with selected number of news from subscribed categories. Please advice how to do this?
This is fairly simple. You are really talking about two particular things here. Building a JavaScript widget driven by URL parameters, and building a front-end "designer" to let folks get the JavaScript code. I'm going to focus on the widget aspect as the front end part you mention is rather trivial once you have the widget built.
The process to embed a widget from another site is simple. I can embed a JavaScript file from Adobe by doing this:
<script src="http://www.adobe.com/foo.js"></script>
Since the browser doesn't care about the file extension, I could also do:
<script src="http://www.adobe.com/foo.cfm"></script>
As long as the script outputs valid JavaScript code, it will work just fine. You specifically talked about embedding news, so lets look at a simple example of this. First I'll build a random query:
<cfset news = queryNew("articleurl,title,category")>
<cfloop index="x" from="1" to="20">
<cfset newurl = "http://someurl.com/#x#.html">
<cfset newtitle = "News Story #x#">
<cfset newcategory = listGetAt("Cats,Dogs",randRange(1,2))>
<cfset queryAddRow(news)>
<cfset querySetCell(news, "articleurl", newurl)>
<cfset querySetCell(news, "title", newtitle)>
<cfset querySetCell(news, "category", newcategory)>
</cfloop>
Obviously this stuff would be a "real" database hit. Next I want to allow folks to filter both by a max number of entries as well as by category:
<cfparam name="url.max" default="10">
<cfparam name="url.category" default="">
<!--- handle getting right data --->
<cfquery name="news" dbtype="query" maxrows="#url.max#">
select *
from news
<cfif url.category is not "">
where lower(category) = <cfqueryparam cfsqltype="cf_sql_varchar" value="#lcase(url.category)#">
</cfif>
</cfquery>
Nothing too complex about that so I won't cover it in detail. I will mention one tip about maxrows however. If you pass a maxrows of -1, it is the same as all rows. This is a handy way to use maxrows and still allow folks to not specify a maximum number of rows.
Ok, so far so good. Now we need to output some JavaScript. If I just output simple text, the browser won't know what to do with it. I chose to use document.write. This is probably not the best idea, but it works. I'm definitely open to better alternatives though. Here is my output:
<cfoutput>
document.write("<table border='1' bgcolor='yellow'><tr><td>");
<cfloop query="news">
document.write("<a href='#articleurl#'>#title#</a> [#category#]<br>");
</cfloop>
document.write("</table>");
</cfoutput>
As you can see, I used a simple table to output the query. Now if you wanted to embed this on your site, you would just use this syntax to get 4 Cat stories
<script src="http://192.168.1.102/test2.cfm?max=4&category=cats"></script>
Or this syntax to get 10 stories on dogs and cats.
<script src="http://192.168.1.102/test2.cfm"></script>
Pretty simple stuff, but you can make it more complex of course. Does anyone have any examples of this they would like to share?
Spry's Password Validation
Yesterday I blogged about my initial look into Spry's form validation widgets. I began with the checkbox validation class. Today I looked at password field validation. As before, the widget is employed by loading in a JavaScript and CSS file, surrounding the form field with a span, and then lastly enabling the widget with a line of JavaScript code. Let's look at a very simple example of this:
Quick and Dirty JSON/Query Example
Yesterday in the ColdFusion chat room someone asked a question (a technical question at that - guess how rare that is?) about how they could use a JSON-encoded query. I whipped up a quick example that I thought others might like as well. This example does not use Ajax to load the JSON data - which is also pretty rare - but I wanted something that I could run all in one file. So here we go...
The first thing I did was get a query of data and serialize it using SerializeJSON:
<cfquery name="getstuff" datasource="blogdev" maxrows="5">
select id, title, posted
from tblblogentries
</cfquery>
<cfset jsondata = serializeJSON(getstuff)>
At this point, jsondata is a string. Here is what it looked like:
{"COLUMNS":["ID","TITLE","POSTED"],"DATA":[["905D9689-0130-1A16-62272F586A771C0C","mmm","May, 15 2007 10:31:00"],["905DAAED-E300-3B9D-7E25E43985CA9507","nn","May, 15 2007 10:31:00"],["905DE931-B8DB-B1AD-F77FC505851C2E9A","j","May, 15 2007 10:31:00"],["905E0C6A-99B8-97EC-4862D9064B9EC659","mmmmm","May, 15 2007 10:31:00"],["905E5910-0E2D-C566-5DCD39E6FD48ED06","NUMBER 11","May, 15 2007 10:32:00"]]}
I wanted to work with this on the client side (the whole point of this entry), so I needed to set this data to a JavaScript variable. The cool thing about JSON is that it can be evaled (think of this like ColdFusion's evaluate function) directly to a variable, so I used this code to assign it:
<script>
mydata = eval(<cfoutput>#jsondata#</cfoutput>)
For my demo, I built a quick form with a button. The idea is that you would hit the button, and I'd then run code that would loop over the query and display the contents. Here is the form I used and the 'area' I would use for output:
<input type="button" value="Show Data" onClick="showData()">
<div id="content" />
Ok, nothing complex yet. Now let's take a look at showData():
function showData() {
var output = document.getElementById('content');
var colMap = new Object();
//first - find my columns
for(var i = 0; i < mydata.COLUMNS.length; i++) {
colMap[mydata.COLUMNS[i]] = i;
}
for(var i = 0; i < mydata.DATA.length; i++) {
var str = mydata.DATA[i][colMap["TITLE"]] + " was posted at " + mydata.DATA[i][colMap["POSTED"]] + "<br />";
output.innerHTML += str;
}
}
The first line of code simply creates a pointer to my div. I'll be writing out to that later. Now I need to loop over my query. The question is - how? If you look at the JSON string I output earlier, you will see that it is an object with two main properties - columns and data. The columns property is a list of columns. The data property contains my rows of data. Note though that it isn't indexed by column names. Instead - the first item in the first row of data matches the first column name. So what I need to do is figure out what my columns are. To do this I create a column map - i.e., a mapping of columns to indexes. The first FOR loop does this.
Once I have that - then it is a trivial matter to loop over my rows of data and pick the values I need. So for example, to get the title for row i, I use:
mydata.DATA[i][colMap["TITLE"]]
Make sense? I'll include the full source of the code below, but before I do, a few notes:
- The SerializeJSON function takes an optional second argument that is only used for queries. If set to true, the structure is pretty different than what I described above. I'll blog about that later today. (Someone will probably need to remind me.)
- While SerializeJSON is new to ColdFusion 8, ColdFusion 7 introduced the toScript function, which is another way to go from a ColdFusion variable to a JavaScript variable. (It is actually simpler than what I used above.)
Now for the complete template:
<cfquery name="getstuff" datasource="blogdev" maxrows="5">
select id, title, posted
from tblblogentries
</cfquery>
<cfset jsondata = serializeJSON(getstuff)>
<script>
mydata = eval(<cfoutput>#jsondata#</cfoutput>)
function showData() {
var output = document.getElementById('content');
var colMap = new Object();
//first - find my columnsco
for(var i = 0; i < mydata.COLUMNS.length; i++) {
colMap[mydata.COLUMNS[i]] = i;
}
for(var i = 0; i < mydata.DATA.length; i++) {
var str = mydata.DATA[i][colMap["TITLE"]] + " was posted at " + mydata.DATA[i][colMap["POSTED"]] + "<br />";
output.innerHTML += str;
}
}
</script>
<input type="button" value="Show Data" onClick="showData()">
<div id="content" />
Bug with JavaScript bind and textareas
I was playing around with a little something today when I ran across a bug. If you try to use cfajaxproxy and bind a textarea to a JavaScript function, you will get an error if the textarea contains a newline. Consider:
<cfajaxproxy bind="javascript:setCount({body2@keyup})">
<script>
function setCount(r) {
var cdiv = document.getElementById('counter');
cdiv.innerHTML = cdiv.innerHTML + 'you did it' + r + '<br>';
}
</script>
<cfform name="ray">
<cftextarea id="body2" name="body2"></cftextarea>
</cfform>
<div id="counter"></div>
Turns out the newline breaks the JavaScript call. If you switch to a CFC call it works fine, but for what I was doing, I didn't need to call the server. Todd pointed out that this is rather trivial code, I could have just done this:
<cftextarea id="body2" name="body2" onkeyup="javascript:setCount(this.value);"></cftextarea>
But I wanted to keep it inside cfajaxproxy. I'll report a bug on this in a few minutes.
JSON/Paging Fix for Spry
Use JSON and PagedViews for Spry? Turns out there is a small bug when you use the pathIsObjectOfArrays property, like in the following example:
<script>
var dsContent = new Spry.Data.JSONDataSet("/index.cfm?event=json.getcontent&dd_nobody=1&dd_class=#class.getID()#",{path:"DATA", pathIsObjectOfArrays: true});
dsContent.setColumnType("DOWNLOADS","number");
var pvContent = new Spry.Data.PagedView(dsContent, { pageSize: 5 });
</script>
The bug is in SpryJSONDataSet.js, line 331 which is:
row.ds_RowID = i;
It should be:
row.ds_RowID = j;
Kin Blas of the Spry team found this and has told me it will be in the next release. This bug didn't break paging, but it broke my Page A of B functionality that was in use on a client site.
Slick image cropping demo by Todd Sharp
Todd Sharp has released a cool new project on RIAForge: cfImageCropper. This is a custom tag wrapper to some nice JavaScript code that lets you do image cropping. It is part client side part server server using ColdFusion 8 for the actual image manipulation. Check his blog entry for more information and a link to a cool demo.
Ask a Jedi: Sizing a window with ColdFusion
Jay asks:
OK, I should know how to do this and I feel stupid for asking but I am going to anyway.Is there a simple way in Coldfusion to grab the current size of the window you have opened?
This isn't stupid - but is one of the many questions that reveal that you may have forgotten that ColdFusion is completely server side. ColdFusion's only interaction with the browser is with the HTML returned via the web server.
So with that being said, you can use JavaScript to check the size of the window. I found a few methods, but these properties seem to work fine in Firefox. (And I'm too lazy to start Parallels just for IE, so I'm fine with people correcting me.)
window.outerWidth
window.outerHeight
You can check these values and if they are too small, resize the window. Consider this complete example:
<html>
<head>
<title>Min Size Test</title>
<script>
function checkMinSize() {
if(window.outerWidth < 500) window.resizeTo(500, window.outerHeight);
if(window.outerHeight < 500) window.resizeTo(window.outerWidth,500);
}
</script>
</head>
<body onLoad="checkMinSize()">
</body>
</html>
All this does is check the width and height. If either are less then 500 pixels, the window is resized to the correct size. I do this in two steps because it is possible only one dimension is too small.
JSEclipse Updated
Just found this via Marco Casario's blog - JSEclipse, a free Eclipse plugin for JavaScript development, has been updated at Adobe Labs. What is really cool is that Spry is now supported. I haven't tested this but I'm downloading it right now.
Quick example of JSON versus XML
If you haven't heard of JSON (JavaScript Object Notation), I think the simplest way to think of it is as a serialized form of data. Much like the old CFWDDX that few people use anymore, JSON is a way to take some data, any data, and convert it into a string. Like WDDX, you can both serialize and deserialize JSON data.
One of the benefits of JSON is that it is a lot less weightier than XML. While it isn't nearly as readable, when it comes to AJAX it has the benefit of simply shifting less bits back and forth while passing the same information.
I had not really paid much attention to JSON, but with the size issues cropping up in ColdFire, I took a look and I was really surprised by how much JSON cuts down on the size of the data. Consider the following code:
<cfset data = queryNew("id,name,age,active","integer,varchar,integer,bit")>
<cfloop index="x" from="1" to="100">
<cfset queryAddRow(data)>
<cfset querySetCell(data,"id",x)>
<cfset querySetCell(data,"name","User #x#")>
<cfset querySetCell(data,"age",randRange(20,90))>
<cfset querySetCell(data,"active",false)>
</cfloop>
This creates a rather simple query of 100 rows. I'll use my toXML CFC to convert this into an XML packet and then report on the size:
<cfset xmlVersion = queryToXML(data,"people","people")>
<cfoutput>
<h2>XML Version (#len(xmlVersion)# chars)</h2>
#htmlCodeFormat(xmlVersion)#
</cfoutput>
I ended up with a string that was 7939 characters long. Now lets convert that to JSON using CFJSON from Thomas Messier.
<cfset jsonVersion = encode(data)>
<cfoutput>
<h2>JSON Version (#len(jsonVersion)# chars)</h2>
#htmlCodeFormat(jsonVersion)#
</cfoutput>
Size of the JSON packet? 1881 characters. That's pretty significant. Spry does not yet support JSON, but when/if it does, I certainly plan on switching where appropriate.

