Friday, August 28, 2009

No, my glasses do not need buffing. They just need a little javacast (StringBuffer gotcha)

I was working with the StringBuffer class earlier and ran into a small gotcha. I needed to create a StringBuffer with a capacity larger than default (16 characters). So I used one of the alternate constructors:


<cfscript>
sb = createObject("java", "java.lang.StringBuffer").init( 256 );
</cfscript>


As I tested the results, I noticed some numeric characters kept appearing at the end of the string. At first I thought was it was a mistake in my code. The base data did contain a lot of numbers. (Did I mention the string was reversed as well?). Yes, eventually I realized the value I kept seeing was .. wait for it .. the capacity value.

I was a bit surprised. The last time I checked 256 was a numeric value, and well within the range of Integer.MIN_VALUE and Integer.MAX_VALUE. However, there is another constructor for the StringBuffer class that accepts a java.lang.String and apparently that constructor won out in the "battle of the data types". So my object was initialized with the contents "256" rather than a capacity of 256 characters.

So lesson of the day: Sometimes it pays to use javacast. Even when you think you do not need it.

Wednesday, August 12, 2009

An episode of CSI that I actually *would* watch

Like most people, my brain is conditioned to tune out ad banners. But while reading a blog entry today, it did register something seemed off with one of the rotating images. So I actually waited, and watched all of the rotating ads, until one in particular came around again. When I saw the CSI logo, I laughed out loud. No wonder it caught my attention ;)

http://www.fusion-reactor.com/events/cfunited09.cfm

(Nice to know my ColdFusionOnlyFilter is working properly)

Friday, August 7, 2009

ColdFusion 8: CFINPUT DateField Experiment (Setting Minimum and Maximum Dates)

In a previous entry, I mentioned a question on stackoverflow.com about restricting the "selectable" dates in a calendar control. I eventually figured out how to customize a cfcalendar. But that got me to thinking about how you might do the same for an HTML datefield.

Not being very familiar with the inner workings of datefields, I poked around a bit and discovered that these controls are based on the YUI Calendar Library. After reviewing the YUI documentation, I realized it opened up a lot of possibilities for customization. To start with, the YUI documentation mentions you can limit the available dates by setting the configuration properties: mindate and maxdate. But since cfinput does not have those attributes, the question was how to customize the ColdFusion scripts to add those properties. It turns out it was easier than I thought.


if(!ColdFusion.Calendar){
ColdFusion.Calendar={};
ColdFusion.Calendar.lookupByID={}; // NEW CODE
}

Further down in the file, new calendar instances are created and assigned to a rather cryptically named variable _14. Just after that section is where I added the rest of the custom code. First, I store new calendar instances in the look-up structure.

...

ColdFusion.Calendar.lookupByID[_e.id]=_14;

The next step is detecting whether a minimum or maximum date was specified for the current field. Then adding those dates to the calendar settings. Now obviously minDate and maxDate are not official attributes of <cfinput>. (If they were, there would be no need for this entry.) However, ColdFusion will simply ignore them and pass them through to the generated HTML. So it is easy to extract any extra attributes with a little DOM magic.

// Apply the minimum date to the calendar instance
if ( document.getElementById && _e.getAttribute("minDate") ) {
_14.cfg.setProperty("mindate", _e.getAttribute("minDate"));
}

// Apply the maximum date to the calendar instance
if ( document.getElementById && _e.getAttribute("maxDate") ) {
_14.cfg.setProperty("maxdate", _e.getAttribute("maxDate"));
}

Once the calendar settings are updated, any dates outside the allowed range will be disabled.



As users can also enter dates manually, I needed a validation function as well. Fortunately, the robust YUI library already contains a function for detecting whether a date is outside the allowed range. So with a small amount of code, I created a function that could be called from the onValidate event.


ColdFusion.Calendar.checkDate = function(_form, _field, _value) {
var cal = ColdFusion.Calendar.lookupByID[_field.id];
var str = _value.replace(/\s+/g, '');
var isOK = str.length == 0;

if (!isOK) {
var dtTime = Date.parse(_value);
if (dtTime && !isNaN(dtTime )) {
var dt = new Date(dtTime );
isOK = !cal.isDateOOB( dt );
}
}

return isOK;
}

A few form changes later, I had an instant, customized datefield. Now the code below is not highly tested. But I thought it would be useful to show an example of how you might customize datefields.

As always, any comments/questions or suggestions are welcome.


CF Example
<cfform format="html">

<cfinput type="datefield"
name="startDate"
id="startDate"
required="true"
mask="MM/dd/yyyy"
minDate="08/5/2009"
maxDate="09/15/2009"
onValidate="ColdFusion.Calendar.checkDate"
message="Start Date is invalid or out of range" >
<cfinput type="submit" name="submit" value="Submit Form">
</cfform>


CFCalendar.js (Changes only)
Notes:
1. If you are smart, you will make a backup before making any modifications ;)
2. For brevity, the code below uses Date.parse to convert input values to date objects. You may prefer to use something a bit more robust.

// ...

if(!ColdFusion.Calendar){
ColdFusion.Calendar={};
/////////////////////////////////////
// BEGIN CUSTOM CODE

ColdFusion.Calendar.lookupByID={};

// END CUSTOM CODE
/////////////////////////////////////
}

// ... more code

ColdFusion.objectCache[_11+_b]=_14;

/////////////////////////////////////
// BEGIN CUSTOM CODE

// Add this calendar to the lookup structure (key = form field element ID)
ColdFusion.Calendar.lookupByID[_e.id]=_14;

// Apply any minimum date to the calendar instance
if ( document.getElementById && _e.getAttribute("minDate") ) {
_14.cfg.setProperty("mindate", _e.getAttribute("minDate"));
}

// Apply any maximum date to the calendar instance
if ( document.getElementById && _e.getAttribute("maxDate") ) {
_14.cfg.setProperty("maxdate", _e.getAttribute("maxDate"));
}

// Verify the text field value is a date and is within range
ColdFusion.Calendar.checkDate = function(_form, _field, _value) {
// lookup the calendar instance for this field
var cal = ColdFusion.Calendar.lookupByID[_field.id];
var str = _value.replace(/\s+/g, '');
var isOK = str.length == 0;

// if the text box value is not empty ..
if (!isOK) {
var dtTime = Date.parse(_value);
// verify this is a valid date, and is not outside of the allowed range
if (dtTime && !isNaN(dtTime )) {
var dt = new Date(dtTime );
isOK = !cal.isDateOOB( dt );
}
}

return isOK;
}

// END CUSTOM CODE
/////////////////////////////////////

// ... more code

...Read More

Thursday, August 6, 2009

Mission Impossible: Restrict Selectable Dates in CFCalendar / Format="html"

A recent question on stackoverflow.com prompted me to revisit the idea of restricting the selectable dates in a cfcalendar. For example, allowing only future dates to be selected. It is relatively easy if you are using flash forms. But it turned out to be a bit more challenging with html forms.


<!--- disable all dates in the current month, prior to today --->
<cfset endRange = dateAdd("d", -1, now())>
<cfset startRange = dateAdd("d", -( day(endRange)-1 ), endRange)>

<!--- generate the actionScript to set the date ranges (once) --->
<cfsavecontent variable="asCode">
<cfoutput>
if (myCalendar["rangeInitialized"] == undefined) {
var displayDate = new Date(myCalendar.displayedYear, myCalendar.displayedMonth, 1);
var range = {};
#ToScript(endRange, "range.rangeStart", false, true)#;
myCalendar.selectableRange = range;
// modifying selectableRange alter the display month. so restore the previous selection
myCalendar.displayedMonth = displayDate.getMonth();
myCalendar.displayedYear = displayDate.getYear();
myCalendar["rangeInitialized"] = true;
}
</cfoutput>
</cfsavecontent>

<cfform format="html">
<cfcalendar name="myCalendar"
onFocus="#asCode#"
startRange="#startRange#"
endRange="#endRange#"
width="300"
height="200"
/>
</cfform>


Get Smart
If you would prefer to leave the navigation arrows enabled, you could use similar code with disabledRanges. But ... there is a catch. By default, cfcalendar displays today's date with a different background color. For some reason, when you modify disabledRanges, it causes that day of the month to disappear from subsequent months. (Really the font color is probably changed to "ffffff", making it appear invisible).



The only work-around I could find was to disable the highlighting. Not perfect, but it does work. (Now if only I could find that magical style setting...)


<cfset endRange = dateAdd("d", -1, now())>
<cfset startRange = dateAdd("d", -( day(endRange)-1 ), endRange)>
<cfsavecontent variable="asCode">
<cfoutput>
if (myCalendar["rangeInitialized"] == undefined) {
var range = {};
#ToScript(endRange, "range.rangeEnd", false, true)#;
myCalendar.disabledRanges = [ range ];
myCalendar.showToday = false;
myCalendar["rangeInitialized"] = true;
}
</cfoutput>
</cfsavecontent>

<cfform format="html">
<cfcalendar name="myCalendar"
onFocus="#asCode#"
startRange="#startRange#"
endRange="#endRange#"
width="300"
height="200"
/>
</cfform>


Ah, cfcalendar. "Good Times. Good Times." ;)

...Read More

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep