/*! http://keith-wood.name/datepick.html
Datepicker Validation extension for jQuery 5.1.1.
Requires Jörn Zaefferer's Validation plugin (http://plugins.jquery.com/project/validate).
Written by Keith Wood (wood.keith{at}optusnet.com.au).
Licensed under the MIT (http://keith-wood.name/licence.html) licence.
Please attribute the author if you use it. */
(function($) { // Hide the namespace
'use strict';
/** Apply a validation test to each date provided.
@private
@param {string} value The current field value.
@param {Element} element The field control.
@return {boolean} true
if OK, false
if failed validation. */
function validateEach(value, element) {
var inst = $.datepick._getInst(element);
var rangeSelect = inst.get('rangeSelect');
var multiSelect = inst.get('multiSelect');
var dates = (multiSelect ? value.split(inst.get('multiSeparator')) :
(rangeSelect ? value.split(inst.get('rangeSeparator')) : [value]));
var ok = (multiSelect && dates.length <= multiSelect) ||
(!multiSelect && rangeSelect && dates.length === 2) ||
(!multiSelect && !rangeSelect && dates.length === 1);
if (ok) {
try {
var dateFormat = inst.get('dateFormat');
var minDate = inst.get('minDate');
var maxDate = inst.get('maxDate');
var dp = $(element);
$.each(dates, function(i, v) {
dates[i] = $.datepick.parseDate(dateFormat, v);
ok = ok && (!dates[i] || (dp.datepick('isSelectable', dates[i]) &&
(!minDate || dates[i].getTime() >= minDate.getTime()) &&
(!maxDate || dates[i].getTime() <= maxDate.getTime())));
});
}
catch (e) {
ok = false;
}
}
if (ok && rangeSelect) {
ok = (dates[0].getTime() <= dates[1].getTime());
}
return ok;
}
/** Normalise the comparison parameters to an array.
@private
@param {array|object|string} params The original parameters.
@return {array} The normalised parameters. */
function normaliseParams(params) {
if (typeof params === 'string') {
params = params.split(' ');
}
else if (!$.isArray(params)) {
var opts = [];
for (var key in params) {
if (params.hasOwnProperty(key)) {
opts[0] = key;
opts[1] = params[key];
}
}
params = opts;
}
return params;
}
/** Determine the comparison date.
@private
@param {Element} element The current datepicker element.
@param {string|Date|jQuery|Element} source The source of the other date.
@param {boolean} noOther true
to not get the date from another field.
@return {Date[]} The date for comparison. */
function extractOtherDate(element, source, noOther) {
if (source.constructor === Date) {
return [source];
}
var inst = $.datepick._getInst(element);
var thatDate = null;
try {
if (typeof source === 'string' && source !== 'today') {
thatDate = $.datepick.parseDate(inst.get('dateFormat'), source, inst.getConfig());
}
}
catch (e) {
// Ignore
}
thatDate = (thatDate ? [thatDate] : (source === 'today' ?
[$.datepick.today()] : (noOther ? [] : $(source).datepick('getDate'))));
return thatDate;
}
// Add validation methods if validation plugin available.
if ($.fn.validate) {
$.datepick.selectDateOrig = $.datepick.selectDate;
/** Validation extensions to the {@linkcode module:Datepick|Datepick} plugin.
@module Datepick-valext */
/** Localisations for the validation messages.
Entries are objects indexed by the language code ('' being the default US/English).
@name regionalOptions
@property {string} [validateDate='Please enter a valid date'] Basic validation error message.
@property {string} [validateDateMin='Please enter a date on or after {0}'] Minimum date validation message.
@property {string} [validateDateMax='Please enter a date on or before {0}'] Maximum date validation message.
@property {string} [validateDateMinMax='Please enter a date between {0} and {1}']
Date range validation message.
@property {string} [validateDateCompare='Please enter a date {0} {1}'] Date comparison validation message.
@property {string} [validateDateToday='today'] Compare with today.
@property {string} [validateDateOther='the other date'] Compare with another date.
@property {string} [validateDateEQ='equal to'] Equals operator.
@property {string} [validateDateNE='not equal to'] Not equals operator.
@property {string} [validateDateLT='before'] Prior to operator.
@property {string} [validateDateGT='after'] Following operator.
@property {string} [validateDateLE='not after'] Not prior to operator.
@property {string} [validateDateGE='not before'] Not following operator. */
$.extend($.datepick.regionalOptions[''], {
validateDate: 'Please enter a valid date',
validateDateMin: 'Please enter a date on or after {0}',
validateDateMax: 'Please enter a date on or before {0}',
validateDateMinMax: 'Please enter a date between {0} and {1}',
validateDateCompare: 'Please enter a date {0} {1}',
validateDateToday: 'today',
validateDateOther: 'the other date',
validateDateEQ: 'equal to',
validateDateNE: 'not equal to',
validateDateLT: 'before',
validateDateGT: 'after',
validateDateLE: 'not after',
validateDateGE: 'not before'
});
$.extend($.datepick.defaultOptions, $.datepick.regionalOptions['']);
$.extend($.datepick, {
/** Trigger a validation after updating the input field with the selected date.
This function extends the {@linkcode module:Datepick~selectDate|existing one}.
@param {Element} elem The control to examine.
@param {Element} target The selected datepicker element.
@example $(selector).datepick('selectDate', $('div.datepick-popup a:contains(10)')[0]) */
selectDate: function(elem, target) {
this.selectDateOrig(elem, target);
var inst = this._getInst(elem);
if (!inst.inline && $.fn.validate) {
var validation = $(elem).parents('form').validate();
if (validation) {
validation.element('#' + elem.id);
}
}
},
/** Correct error placement for validation errors - after any trigger.
@param {jQuery} error The error message.
@param {jQuery} element The field in error.
@example $('form').validate({
...,
errorPlacement: $.datepick.errorPlacement
}) */
errorPlacement: function(error, element) {
var inst = $.datepick._getInst(element);
if (!$.isEmptyObject(inst)) {
error[inst.get('isRTL') ? 'insertBefore' : 'insertAfter'](
inst.trigger.length > 0 ? inst.trigger : element);
}
else {
error.insertAfter(element);
}
},
/** Format a validation error message involving dates.
@param {string} source The error message, which may include substitution points, '{0}', '{1}', etc.
@param {Date[]} params The dates, which are formatted with the current
{@linkcode module:Datepick~regionalOptions|dateFormat} before being inserted
into the message at the indexed substitution point.
@return {string} The formatted message.
@example $.datepick.errorFormat(messages.validateDateMinMax, [minDate, maxDate]) */
errorFormat: function(source, params) {
var format = ($.datepick.curInst ?
$.datepick.curInst.get('dateFormat') :
$.datepick.defaultOptions.dateFormat);
$.each(params, function(index, value) {
source = source.replace(new RegExp('\\{' + index + '\\}', 'g'),
$.datepick.formatDate(format, value) || 'nothing');
});
return source;
}
});
var lastElement = null;
/** Extensions to jQuery Validation for the {@linkcode module:Datepick|Datepick} plugin.
@module Datepick-validation */
/** Validate a date field.
Apply all the validations applicable from the datepicker,
i.e. minimum and maximum allowed dates and individual date selectability.
The start and end dates in a date range, and all dates in a multi-select datepicker are va lidated.
@name dpDate
@example rules: {
fieldName1: 'dpDate',
fieldName2: {
required: true,
dpDate: true
}
} */
$.validator.addMethod('dpDate', function(value, element) {
lastElement = element;
return this.optional(element) || validateEach(value, element);
},
function() {
var inst = $.datepick._getInst(lastElement);
var minDate = inst.get('minDate');
var maxDate = inst.get('maxDate');
var messages = $.datepick.defaultOptions;
return (minDate && maxDate ?
$.datepick.errorFormat(messages.validateDateMinMax, [minDate, maxDate]) :
(minDate ? $.datepick.errorFormat(messages.validateDateMin, [minDate]) :
(maxDate ? $.datepick.errorFormat(messages.validateDateMax, [maxDate]) :
messages.validateDate)));
});
/* And allow as a class rule. */
$.validator.addClassRules('dpDate', {dpDate: true});
var comparisons = {equal: 'eq', same: 'eq', notEqual: 'ne', notSame: 'ne',
lessThan: 'lt', before: 'lt', greaterThan: 'gt', after: 'gt',
notLessThan: 'ge', notBefore: 'ge', notGreaterThan: 'le', notAfter: 'le'};
/** Cross-validate date fields.
The parameters for this rule should be an array with
Date
or another field selector/element/jQueryOR an object with one attribute with a name of the comparison to apply -
'eq', 'ne', 'lt', 'gt', 'le', 'ge' or synonyms -
and a value of 'today' or a date string or a Date
or another field selector/element/jQuery
OR a string with 'eq', 'ne', 'lt', 'gt', 'le', 'ge' or synonyms followed by 'today' or a date string in the current {@linkcode module:Datepick~regionalOptions|dateFormat} or a jQuery selector
@name dpCompareDate @example rules: { validBeforePicker: { dpCompareDate: ['before', '#validAfterPicker'] }, validAfterPicker: { dpCompareDate: {after: '#validBeforePicker'} }, validTodayPicker: { dpCompareDate: 'ne today' }, validSpecificPicker: { dpCompareDate: 'notBefore 01/01/2012' } } */ $.validator.addMethod('dpCompareDate', function(value, element, params) { if (this.optional(element)) { return true; } params = normaliseParams(params); var thisDate = $(element).datepick('getDate'); var thatDate = extractOtherDate(element, params[1]); if (thisDate.length === 0 || thatDate.length === 0) { return true; } lastElement = element; var finalResult = true; for (var i = 0; i < thisDate.length; i++) { switch (comparisons[params[0]] || params[0]) { case 'eq': finalResult = (thisDate[i].getTime() === thatDate[0].getTime()); break; case 'ne': finalResult = (thisDate[i].getTime() !== thatDate[0].getTime()); break; case 'lt': finalResult = (thisDate[i].getTime() < thatDate[0].getTime()); break; case 'gt': finalResult = (thisDate[i].getTime() > thatDate[0].getTime()); break; case 'le': finalResult = (thisDate[i].getTime() <= thatDate[0].getTime()); break; case 'ge': finalResult = (thisDate[i].getTime() >= thatDate[0].getTime()); break; default: finalResult = true; } if (!finalResult) { break; } } return finalResult; }, function(params) { var messages = $.datepick.defaultOptions; var inst = $.datepick._getInst(lastElement); params = normaliseParams(params); var thatDate = extractOtherDate(lastElement, params[1], true); thatDate = (params[1] === 'today' ? messages.validateDateToday : (thatDate.length ? $.datepick.formatDate(inst.get('dateFormat'), thatDate[0], inst.getConfig()) : messages.validateDateOther)); return messages.validateDateCompare.replace(/\{0\}/, messages['validateDate' + (comparisons[params[0]] || params[0]).toUpperCase()]). replace(/\{1\}/, thatDate); }); } })(jQuery);