Friday 24 September 2010

AjaxControlToolkit MaskedEditExtender 2 Digit Date Issue

I have been using the AjaxControlToolkit for a few years now, and one of the most used controls (for me) has to be the Calendar extender. I usually implement this in addition to the Masked Edit Control to create a date input that you can either type into or select from the calendar.

Recently it was reported to me that one of my applications using this was not working, after investigation it turned out that the users were entering a 2 digit year into the date box. There is a 'Century' setting on the extender that is meant to deal with such scenarios but unfortunately it doesn't seem to work (though I failed to test this correctly and it also got through our entire testing team and UAT!).

A work item seems to have been logged on codeplex for this but despite the comments there doesn't seem to be a resolution. Though by what some of the guys have commented I think they have resolved it for themselves individually.

So, with no other option I set about fixing the issue for myself. First I downloaded the project from the codeplex site ajaxcontroltoolkit.codeplex.com

I had to updgrade the project since I'm working in Visual Studio 2010. The upgrade went OK but it complained about a missing file for ajax minifier tasks. I found a great blog post on this and installed the ajax javascript minifier (not really anything to do with Ajax, its just a JavaScript/CSS minifier).

The initial build got Error The "JSSourceFiles" parameter is not supported by the "AjaxMin" task. Verify the parameter exists on the task, and it is a settable public instance property.

Confusingly, I then found another version of the minifier here aspnet.codeplex.com/releases/view/40584. Installing this version (V4) seems to have sorted the issue.

Finally the project was building so I got into the code. First, I made the following change to the 'ConvFmtDate' function:

I removed the line:

while (Y.length < m_mask[this.get_CultureDateFormat().indexOf("Y")].length)
{
Y = "0" + Y;
}


And Added:


// If the year is 2 digit
if (Y.length <= 2)
{
// Get the current century
var century = this.get_Century().toString();

// Check that a value has been provided
if (!century)
{
// Otherwise get the current century
var currentDate = new Date();
century = currentDate.getYear().toString();
}

// Construct the year from the first 2 digits of the century and the yr entered
Y = century.substring(0, 2) + Y;
}


Unfortunately this solved only half of the problem. Putting in a 2 digit date would still pad with 0 (giving 0076 for 76) but once the text box regained focus the date would suddenly correct itself to 1976.

On investigation, there's an _onBlur() event that is fired. If the text has changed from the previous value it fires the _fireChanged() method (which creates the HTML event for change on the text box). Somewhere down the line this ends up calling the _GetDateElementText method.

Within the _GetDateElementText method, there's a piece of code that checks if the year value is less than 4 and pads it out with zeroes. Basically I needed to change this to do the same as the previous function.

I added a new method to the object to format the year string with either the century value set or the current century:


, FormatYearCentury: function (input)
{
// Get the current century
var century = this.get_Century().toString();

// Check that a value has been provided
if (!century)
{
// Otherwise get the current century
var currentDate = new Date();
century = currentDate.getYear().toString();
}

// Construct the year from the century and the yr entered
return century.substring(0, 4 - input.length) + input;
}


I then replaced the following lines (within the _GetDateElementText method):


if (Y4)
{
if (aux != "" && aux.length < 4)
{
while (aux.length < 4)
{
aux = "0" + aux;
}
m_arrDate[this.get_CultureDateFormat().indexOf("Y")] = aux;
}
}


With my updated get year method:


if (Y4)
{
if (aux != "" && aux.length < 4)
{
// Format the year with the requested or current century
aux = this.FormatYearCentury(aux);

m_arrDate[this.get_CultureDateFormat().indexOf("Y")] = aux;
}
}


I then went back to the ConvFmtDate function and replaced the new code with a call to my new FormatYearCentury function.


var Y = parseInt(m_arrDate[this.get_CultureDateFormat().indexOf("Y")], 10) + '';

// If the year is 2 digit or less
if (Y.length <= 2)
{
Y = this.FormatYearCentury(Y);
}


The only thing left to do was to build the sample website and copy the assembly to my project.

This may not be right, and to be honest I never enjoy digging around in other peoples code, especially with no comments. But it seems to work for me, and if it can save someone else the headache then its all good.

No comments:

Post a Comment