For some time I have been wondering whether it was possible to select multiple dates with a cfcalendar. Technically, the answer is no.
After working with flash forms, I realized cfcalendar is based on the DateChooser component in Flex 1.5. Unfortunately, some of the niceties like selecting multiple dates or setting a minimum and maximum year displayed, were not introduced until Flex 2. I did eventually manage to simulate multiple date selections in flash forms, through a series of bizarre code gyrations, numerous expletives and light incantations. But having seen the calendar in Flex 2, I knew it was just a poor re-invention of the wheel.
But being the curious sort, I got to wondering if there was a way to use the Flex 2 calendar, from within ColdFusion 8. I began to realize it might be possible after reading a very interesting tip on Sam Farmer's blog about ColdFusion 8 and its Cool Ability to Compile Flex Applications. I could use it to create a swf containing a Flex 2 calendar. Then embed the calendar in my html form. The same way cfform does with cfcalendar. But I was not sure what to do from there.
It turns out there are several methods of communicating with javascript from Flash, such as the ExternalInterface introduced with Flash 8. As I understand it Flex 2 requires Flash 9+ anyway, so no problems there. Using my rudimentary Flex skills, I wrote a simple example that creates a calendar, enables multiple selections, and finally exposes the selectedRanges property to javascript using the addCallback method. (Loosely translated, selectedRanges is an array of structures. Each structure contains two keys: rangeStart and rangeEnd.) I then compiled the flex code, using the tip from Sam Farmer's blog.
<cfimport prefix="cfmxml" taglib="/WEB-INF/lib/cf-bootstrap-for-flex.jar">
<cfmxml:mxml>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="*"
backgroundAlpha="0"
backgroundGradientAlphas="[0,0]"
backgroundColor="0xffffff"
creationComplete="onApplicationCreated();"
>
<mx:Script>
<![CDATA[
import flash.external.*;
import mx.controls.Alert;
protected function onApplicationCreated():void
{
if (!ExternalInterface.available)
{
Alert.show("JavaScript is required to use this calendar.", "Error");
return;
}
multiCalendar.allowMultipleSelection = true;
// this allows the actionscript function to be accessed from javascript
ExternalInterface.addCallback("getRanges", getSelectedRanges);
}
public function getSelectedRanges():Array
{
return multiCalendar.selectedRanges;
}
]]>
</mx:Script>
<mx:Style>
Application
{
background-color:"";
background-image:"";
padding: 0px;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
paddingBottom: 0;
paddingLeft: 0;
paddingRight: 0;
paddingTop: 0;
horizontalAlign: Left;
}
</mx:Style>
<mx:DateChooser id="multiCalendar" width="100%" height="100%"/>
</mx:Application>
</cfmxml:mxml>
That created a randomly named swf in my current directory. With the help of
SWFObject, I embedded that swf in my html form. All that was left was to add a javascript function that retrieved and displayed the calendar dates in a javascript alert.
<html>
<head>
<script type="text/javascript" src="swfobject/swfobject.js"></script>
<script type="text/javascript">
// display the selected ranges from the cfcalendar object
function showRanges(id)
{
var ranges = document.getElementById(id).getRanges();
var arr = new Array();
arr.push("Selected ranges for: "+ id);
arr.push(" ");
for (var x in ranges)
{
arr.push("range["+ x +"]===========");
var obj = ranges[x];
for (var y in obj)
{
arr.push(y +"="+ obj[y]);
}
}
alert(arr.join("\n"));
}
</script>
</head>
<body>
<cfset calendarID = "myFlex2Calendar">
<cfoutput>
<script type="text/javascript">
swfobject.embedSWF("jspxxxxxxxxxxxxxx.swf","#calendarID#", "163","181", "9.0.0", "swfobject/expressInstall.swf");
</script>
<!--- container that will hold the final calendar --->
<div id="#calendarID#">
<p><a href="http://www.adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" border="0"/></a></p>
</div>
<form>
<input type="button" value="Show Dates" onClick="showRanges('#calendarID#');">
</form>
</cfoutput>
</body>
</html>
Voila! A calendar that allows multiple date selections.
Ultimately, I put the code in a cfc to make it completely dynamic. Then added in all of the basic cfcalendar settings, but also Flex 2 specific properties like the year navigation arrows and disabling multiple date ranges. Then passed those properties to flesh through
flashVars. Here is an example of the end result. Ignore the ugly test form. If you look closesly, the calendar on the right has year navigation buttons, which I have always missed in cfcalendar.
Bear in mind my Flex skills are basic at best. So this was more of an experiment than anything else. But overall it was a great learning experience about one of the many things you can do with CF and Flex.
...Read More