Current File : /home/inlingua/public_html/sensoriumpsychologists.com/js/jquery.date-dropdowns.js |
/*
* jQuery Date Dropdowns - v1.0.0
* A simple, customisable date select plugin
*
* Made by Chris Brown
* Under MIT License
*/
;(function ($, window, document, undefined) {
'use strict';
// Create the defaults once
var pluginName = 'dateDropdowns',
pluginDefaults = {
defaultDate: null,
defaultDateFormat: 'yyyy-mm-dd',
displayFormat: 'dmy',
submitFormat: 'yyyy-mm-dd',
minAge: 0,
maxAge: 120,
minYear: null,
maxYear: null,
submitFieldName: 'date',
wrapperClass: 'date-dropdowns',
dropdownClass: null,
daySuffixes: true,
monthSuffixes: true,
monthFormat: 'long',
required: false,
dayLabel: 'Day',
monthLabel: 'Month',
yearLabel: 'Year',
monthLongValues: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
monthShortValues: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
initialDayMonthYearValues: ['Day', 'Month', 'Year'],
daySuffixValues: ['st', 'nd', 'rd', 'th']
};
// The actual plugin constructor
function Plugin(element, options) {
this.element = element; // Element handle
this.$element = $(element); // jQuery element handle
this.config = $.extend({}, pluginDefaults, options); // Plugin options
this.internals = { // Internal variables
objectRefs: {}
};
this.init();
return this;
}
// Avoid Plugin.prototype conflicts
$.extend(Plugin.prototype, {
/**
* Initialise the plugin
*/
init: function () {
this.checkForDuplicateElement();
this.setInternalVariables();
this.setupMarkup();
this.buildDropdowns();
this.attachDropdowns();
this.bindChangeEvent();
if (this.config.defaultDate) {
this.populateDefaultDate();
}
},
/**
* Check whether an element exists with the same name attribute. If so, throw an error
*/
checkForDuplicateElement: function () {
if ($('input[name="' + this.config.submitFieldName + '"]').length) {
$.error('Duplicate element found');
return false;
}
return true;
},
/**
* Set the plugin's internal variables
*/
setInternalVariables: function () {
var date = new Date();
this.internals.currentDay = date.getDate();
this.internals.currentMonth = date.getMonth() + 1;
this.internals.currentYear = date.getFullYear();
},
/**
* Set the container which will hold the date dropdowns.
*
* - If the element on which the plugin was called is an input of type text or hidden, set it to
* be hidden and wrap it in a div. This outer div will become the container.
* - If the element was an HTML element (e.g. <div/>), create a hidden text field within it, and use
* the div as the container.
*/
setupMarkup: function () {
var wrapper, hiddenField;
if (this.element.tagName.toLowerCase() === 'input') {
if (!this.config.defaultDate) {
this.config.defaultDate = this.element.value;
}
// Configure the input element and wrap
hiddenField = this.$element.attr('type', 'hidden')
.wrap('<div class="' + this.config.wrapperClass + '"></div>');
var customFieldNameProvided = (this.config.submitFieldName !== pluginDefaults.submitFieldName),
fieldHasName = this.element.hasAttribute('name');
// Set the name attribute of the submit input
if (!fieldHasName && !customFieldNameProvided) {
this.$element.attr('name', pluginDefaults.submitFieldName);
} else if (customFieldNameProvided) {
this.$element.attr('name', this.config.submitFieldName);
}
wrapper = this.$element.parent();
} else {
// Build a hidden input and set this.$element as the wrapper
hiddenField = $('<input/>', {
type: 'hidden',
name: this.config.submitFieldName
});
this.$element.append(hiddenField).addClass(this.config.wrapperClass);
wrapper = this.$element;
}
// Store a reference to the wrapper and hidden field elements for later use
this.internals.objectRefs.pluginWrapper = wrapper;
this.internals.objectRefs.hiddenField = hiddenField;
return true;
},
/**
* Generate the Day, Month and Year dropdowns
*/
buildDropdowns: function () {
var $dayDropdown, $monthDropdown, $yearDropdown;
Plugin.message = {
day: this.config.initialDayMonthYearValues[0],
month: this.config.initialDayMonthYearValues[1],
year: this.config.initialDayMonthYearValues[2]
};
// Build the day dropdown element
$dayDropdown = this.buildDayDropdown();
this.internals.objectRefs.dayDropdown = $dayDropdown;
$monthDropdown = this.buildMonthDropdown();
this.internals.objectRefs.monthDropdown = $monthDropdown;
$yearDropdown = this.buildYearDropdown();
this.internals.objectRefs.yearDropdown = $yearDropdown;
return true;
},
/**
* Attach the generated dropdowns to the container
*/
attachDropdowns: function () {
var $element = this.internals.objectRefs.pluginWrapper,
$daySelect = this.internals.objectRefs.dayDropdown,
$monthSelect = this.internals.objectRefs.monthDropdown,
$yearSelect = this.internals.objectRefs.yearDropdown;
switch (this.config.displayFormat) {
case 'mdy':
$element.append($monthSelect, $daySelect, $yearSelect);
break;
case 'ymd':
$element.append($yearSelect, $monthSelect, $daySelect);
break;
case 'dmy':
default:
$element.append($daySelect, $monthSelect, $yearSelect);
break;
}
return true;
},
/**
* Bind the change event to the generated dropdowns
*/
bindChangeEvent: function () {
var $daySelect = this.internals.objectRefs.dayDropdown,
$monthSelect = this.internals.objectRefs.monthDropdown,
$yearSelect = this.internals.objectRefs.yearDropdown,
pluginHandle = this,
objectRefs = this.internals.objectRefs;
objectRefs.pluginWrapper.on('change', 'select', function () {
var day = $daySelect.val(),
month = $monthSelect.val(),
year = $yearSelect.val(),
invalidDate,
newDate;
// Find out whether the change has made the date invalid (e.g. 31st Feb)
invalidDate = pluginHandle.checkDate(day, month, year);
// If invalid - add an error class to the day dropdown and return
if (invalidDate) {
objectRefs.dayDropdown.addClass('invalid');
return false;
}
if ('00' !== objectRefs.dayDropdown.val()) {
objectRefs.dayDropdown.removeClass('invalid');
}
// Empty the hidden field after each change
objectRefs.hiddenField.val('');
// Only format the submit date if a full date has been selected
if (!invalidDate && (day * month * year !== 0)) {
newDate = pluginHandle.formatSubmitDate(day, month, year);
objectRefs.hiddenField.val(newDate);
}
objectRefs.hiddenField.change();
});
},
/**
* Take a provided default date and populate both the hidden field and the
* dropdown elements with the relevant formatted values
*
* @returns {boolean}
*/
populateDefaultDate: function () {
var date = this.config.defaultDate,
parts = [],
day = '',
month = '',
year = '';
switch (this.config.defaultDateFormat) {
case 'yyyy-mm-dd':
default:
parts = date.split('-');
day = parts[2];
month = parts[1];
year = parts[0];
break;
case 'dd/mm/yyyy':
parts = date.split('/');
day = parts[0];
month = parts[1];
year = parts[2];
break;
case 'mm/dd/yyyy':
parts = date.split('/');
day = parts[1];
month = parts[0];
year = parts[2];
break;
case 'unix':
parts = new Date();
parts.setTime(date * 1000);
day = parts.getDate() + '';
month = (parts.getMonth() + 1) + '';
year = parts.getFullYear();
if (day.length < 2) {
day = '0' + day;
}
if (month.length < 2) {
month = '0' + month;
}
break;
}
// Set the values on the dropdowns
this.internals.objectRefs.dayDropdown.val(day);
this.internals.objectRefs.monthDropdown.val(month);
this.internals.objectRefs.yearDropdown.val(year);
this.internals.objectRefs.hiddenField.val(date);
if (true === this.checkDate(day, month, year)) {
this.internals.objectRefs.dayDropdown.addClass('invalid');
}
return true;
},
/**
* Build a generic dropdown element
*
* @param type
* @returns {*|HTMLElement}
*/
buildBaseDropdown: function (type) {
var classString = type;
if (this.config.dropdownClass) {
classString += ' ' + this.config.dropdownClass;
}
return $('<select></select>', {
class: classString,
name: this.config.submitFieldName + '_[' + type + ']',
required: this.config.required
});
},
/**
* Build the day dropdown element
*
* @returns {*|HTMLElement}
*/
buildDayDropdown: function () {
var day,
dropdown = this.buildBaseDropdown('day'),
option = document.createElement('option');
option.setAttribute('value', '');
option.appendChild(document.createTextNode(this.config.dayLabel));
dropdown.append(option);
// Days 1-9
for (var i = 1; i < 10; i++) {
if (this.config.daySuffixes) {
day = i + this.getSuffix(i);
} else {
day = '0' + i;
}
option = document.createElement('option');
option.setAttribute('value', '0' + i);
option.appendChild(document.createTextNode(day));
dropdown.append(option);
}
// Days 10-31
for (var j = 10; j <= 31; j++) {
day = j;
if (this.config.daySuffixes) {
day = j + this.getSuffix(j);
}
option = document.createElement('option');
option.setAttribute('value', j);
option.appendChild(document.createTextNode(day));
dropdown.append(option);
}
return dropdown;
},
/**
* Build the month dropdown element
*
* @returns {*|HTMLElement}
*/
buildMonthDropdown: function () {
var dropdown = this.buildBaseDropdown('month'),
option = document.createElement('option');
option.setAttribute('value', '');
option.appendChild(document.createTextNode(this.config.monthLabel));
dropdown.append(option);
// Populate the month values
for (var monthNo = 1; monthNo <= 12; monthNo++) {
var month;
switch (this.config.monthFormat) {
case 'short':
month = this.config.monthShortValues[monthNo - 1];
break;
case 'long':
month = this.config.monthLongValues[monthNo - 1];
break;
case 'numeric':
month = monthNo;
if (this.config.monthSuffixes) {
month += this.getSuffix(monthNo);
}
break;
}
if (monthNo < 10) {
monthNo = '0' + monthNo;
}
option = document.createElement('option');
option.setAttribute('value', monthNo);
option.appendChild(document.createTextNode(month));
dropdown.append(option);
}
return dropdown;
},
/**
* Build the year dropdown element.
*
* By default minYear and maxYear are null, however if provided they take precedence over
* the minAge and maxAge values.
*
* @returns {*|HTMLElement}
*/
buildYearDropdown: function () {
var minYear = this.config.minYear,
maxYear = this.config.maxYear,
dropdown = this.buildBaseDropdown('year'),
option = document.createElement('option');
option.setAttribute('value', '');
option.appendChild(document.createTextNode(this.config.yearLabel));
dropdown.append(option);
if (!minYear) {
minYear = this.internals.currentYear - (this.config.maxAge + 1);
}
if (!maxYear) {
maxYear = this.internals.currentYear - this.config.minAge;
}
for (var i = maxYear; i >= minYear; i--) {
option = document.createElement('option');
option.setAttribute('value', i);
option.appendChild(document.createTextNode(i));
dropdown.append(option);
}
return dropdown;
},
/**
* Get the relevant suffix for a day/month number
*
* @param number
* @returns {string}
*/
getSuffix: function (number) {
var suffix = '';
var st = this.config.daySuffixValues[0];
var nd = this.config.daySuffixValues[1];
var rd = this.config.daySuffixValues[2];
var th = this.config.daySuffixValues[3];
switch (number % 10) {
case 1:
suffix = (number % 100 === 11) ? th : st;
break;
case 2:
suffix = (number % 100 === 12) ? th : nd;
break;
case 3:
suffix = (number % 100 === 13) ? th : rd;
break;
default:
suffix = 'th';
break;
}
return suffix;
},
/**
* Check whether the date entered is invalid, e.g. 31st Feb
*
* @param day
* @param month
* @param year
* @returns {boolean}
*/
checkDate: function (day, month, year) {
var invalidDate;
if (month !== '00') {
var numDaysInMonth = (new Date(year, month, 0).getDate()),
selectedDayInt = parseInt(day, 10);
invalidDate = this.updateDayOptions(numDaysInMonth, selectedDayInt);
// If the date is invalid, empty the hidden field to prevent invalid submissions
if (invalidDate) {
this.internals.objectRefs.hiddenField.val('');
}
}
return invalidDate;
},
/**
* Update the day options available based on the month now selected
*
* @param numDaysInMonth
* @param selectedDayInt
* @returns {boolean}
*/
updateDayOptions: function (numDaysInMonth, selectedDayInt) {
var lastDayOption = parseInt(this.internals.objectRefs.dayDropdown.children(':last').val(), 10),
newDayOption = '',
newDayText = '',
invalidDay = false;
// If the selected month has less days than the Day dropdown currently contains - remove the extra days
if (lastDayOption > numDaysInMonth) {
while (lastDayOption > numDaysInMonth) {
this.internals.objectRefs.dayDropdown.children(':last').remove();
lastDayOption--;
}
// If the user-selected day is removed, indicate this so a message can be shown to the user
if (selectedDayInt > numDaysInMonth) {
invalidDay = true;
}
// If the month contains more days than the Day dropdown contains - add the missing options
} else if (lastDayOption < numDaysInMonth) {
while (lastDayOption < numDaysInMonth) {
newDayOption = ++lastDayOption;
newDayText = newDayOption;
// Add the suffix if required
if (this.config.daySuffixes) {
newDayText += this.getSuffix(lastDayOption);
}
// Build the option and append to the dropdown
var option = document.createElement('option');
option.setAttribute('value', newDayOption);
option.appendChild(document.createTextNode(newDayText));
this.internals.objectRefs.dayDropdown.append(option);
}
}
return invalidDay;
},
/**
* Format the selected date based on the submitFormat config value provided
*
* @param day
* @param month
* @param year
* @returns {*}
*/
formatSubmitDate: function (day, month, year) {
var formattedDate,
_date;
switch (this.config.submitFormat) {
case 'unix':
_date = new Date();
_date.setDate(day);
_date.setMonth(month - 1);
_date.setYear(year);
formattedDate = Math.round(_date.getTime() / 1000);
break;
default:
formattedDate = this.config.submitFormat
.replace('dd', day)
.replace('mm', month)
.replace('yyyy', year);
break;
}
return formattedDate;
},
/**
* Revert the changes applied by the plugin on the specified element
*/
destroy: function () {
var wrapperClass = this.config.wrapperClass;
if (this.$element.hasClass(wrapperClass)) {
this.$element.empty();
} else {
var $parent = this.$element.parent(),
$select = $parent.find('select');
this.$element.unwrap();
$select.remove();
}
}
});
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[pluginName] = function (options) {
this.each(function () {
if (typeof options === 'string') {
var args = Array.prototype.slice.call(arguments, 1);
var plugin = $.data(this, 'plugin_' + pluginName);
if (typeof plugin === 'undefined') {
$.error('Please initialize the plugin before calling this method.');
return false;
}
plugin[options].apply(plugin, args);
} else {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
}
}
});
// chain jQuery functions
return this;
};
})(jQuery, window, document);