Bugfix for ui.datepicker.js (1.7.2) Fix when datepicker is used in iframe ( and jquery + ui is loaded in the parent window )
Hi, jQuery UI Team,
I'm using jquery + UI in our web application. Our application uses a
main window that loads all the jquery +UI files.
Then it opens lightbox-like windows with iframes and uses the jquery
classes from the main window. This gives problems in your datepicker
code. I have modified it to work in my context. I hope you can use
these fixes for your development.
Kinds Regards,
Samuel Lim
/*
* jQuery UI Datepicker 1.7.2
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Datepicker
*
* Depends:
* ui.core.js
*/
(function($) { // hide the namespace
$.extend($.ui, { datepicker: { version: "1.7.2" } });
var PROP_NAME = 'datepicker';
/* Date picker manager.
Use the singleton instance of this class, $.datepicker, to interact
with the date picker.
Settings for (groups of) date pickers are maintained in an instance
object,
allowing multiple different settings on the same page. */
function Datepicker() {
this.debug = false; // Change this to true to start debugging
this._curInst = null; // The current instance in use
this._keyEvent = false; // If the last event was a key event
this._disabledInputs = []; // List of date picker inputs that have
been disabled
this._datepickerShowing = false; // True if the popup picker is
showing , false if not
this._inDialog = false; // True if showing within a "dialog", false
if not
this._mainDivId = 'ui-datepicker-div'; // The ID of the main
datepicker division
this._inlineClass = 'ui-datepicker-inline'; // The name of the inline
marker class
this._appendClass = 'ui-datepicker-append'; // The name of the append
marker class
this._triggerClass = 'ui-datepicker-trigger'; // The name of the
trigger marker class
this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog
marker class
this._disableClass = 'ui-datepicker-disabled'; // The name of the
disabled covering marker class
this._unselectableClass = 'ui-datepicker-unselectable'; // The name
of the unselectable cell marker class
this._currentClass = 'ui-datepicker-current-day'; // The name of the
current day marker class
this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of
the day hover marker class
this.regional = []; // Available regional settings, indexed by
language code
this.regional[''] = { // Default regional settings
closeText: 'Done', // Display text for close link
prevText: 'Prev', // Display text for previous month link
nextText: 'Next', // Display text for next month link
currentText: 'Today', // Display text for current month link
monthNames: ['January','February','March','April','May','June',
'July','August','September','October','November','December'], //
Names of months for drop-down and formatting
monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday'], // For formatting
dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], //
For formatting
dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column
headings for days starting at Sunday
dateFormat: 'mm/dd/yy', // See format options on parseDate
firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
isRTL: false // True if right-to-left language, false if left-to-
right
};
this._defaults = { // Global defaults for all the date picker
instances
showOn: 'focus', // 'focus' for popup on focus,
// 'button' for trigger button, or 'both' for either
showAnim: 'show', // Name of jQuery animation for popup
showOptions: {}, // Options for enhanced animations
defaultDate: null, // Used when field is blank: actual date,
// +/-number for offset from today, null for today
appendText: '', // Display text following the input box, e.g.
showing the format
buttonText: '...', // Text for trigger button
buttonImage: '', // URL for trigger button image
buttonImageOnly: false, // True if the image appears alone, false if
it appears on a button
hideIfNoPrevNext: false, // True to hide next/previous month links
// if not applicable, false to just disable them
navigationAsDateFormat: false, // True if date formatting applied to
prev/today/next links
gotoCurrent: false, // True if today link goes back to current
selection instead
changeMonth: false, // True if month can be selected directly, false
if only prev/next
changeYear: false, // True if year can be selected directly, false
if only prev/next
showMonthAfterYear: false, // True if the year select precedes
month, false for month then year
yearRange: '-10:+10', // Range of years to display in drop-down,
// either relative to current year (-nn:+nn) or absolute
(nnnn:nnnn)
showOtherMonths: false, // True to show dates in other months, false
to leave blank
calculateWeek: this.iso8601Week, // How to calculate the week of the
year,
// takes a Date and returns the number of the week for it
shortYearCutoff: '+10', // Short year values < this are in the
current century,
// > this are in the previous century,
// string value starting with '+' for current year + value
minDate: null, // The earliest selectable date, or null for no limit
maxDate: null, // The latest selectable date, or null for no limit
duration: 'normal', // Duration of display/closure
beforeShowDay: null, // Function that takes a date and returns an
array with
// [0] = true if selectable, false if not, [1] = custom CSS class
name(s) or '',
// [2] = cell title (optional), e.g. $.datepicker.noWeekends
beforeShow: null, // Function that takes an input field and
// returns a set of custom settings for the date picker
onSelect: null, // Define a callback function when a date is
selected
onChangeMonthYear: null, // Define a callback function when the
month or year is changed
onClose: null, // Define a callback function when the datepicker is
closed
numberOfMonths: 1, // Number of months to show at a time
showCurrentAtPos: 0, // The position in multipe months at which to
show the current month (starting at 0)
stepMonths: 1, // Number of months to step back/forward
stepBigMonths: 12, // Number of months to step back/forward for the
big links
altField: '', // Selector for an alternate field to store selected
dates into
altFormat: '', // The date format to use for the alternate field
constrainInput: true, // The input is constrained by the current
date format
showButtonPanel: false // True to show button panel, false to not
show it
};
$.extend(this._defaults, this.regional['']);
this.dpDiv = $('<div id="' + this._mainDivId + '" class="ui-
datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-
all ui-helper-hidden-accessible"></div>');
}
$.extend(Datepicker.prototype, {
/* Class name added to elements to indicate already configured with a
date picker. */
markerClassName: 'hasDatepicker',
/* Debug logging (if enabled). */
log: function () {
if (this.debug)
console.log.apply('', arguments);
},
/* Override the default settings for all instances of the date
picker.
@param settings object - the new settings to use as defaults
(anonymous object)
@return the manager object */
setDefaults: function(settings) {
extendRemove(this._defaults, settings || {});
return this;
},
/* Attach the date picker to a jQuery selection.
@param target element - the target input field or division or
span
@param settings object - the new settings to use for this date
picker instance (anonymous) */
_attachDatepicker: function(target, settings) {
// check for settings on the control itself - in namespace 'date:'
var inlineSettings = null;
for (var attrName in this._defaults) {
var attrValue = target.getAttribute('date:' + attrName);
if (attrValue) {
inlineSettings = inlineSettings || {};
try {
inlineSettings[attrName] = eval(attrValue);
} catch (err) {
inlineSettings[attrName] = attrValue;
}
}
}
var nodeName = target.nodeName.toLowerCase();
var inline = (nodeName == 'div' || nodeName == 'span');
if (!target.id)
target.id = 'dp' + (++this.uuid);
var inst = this._newInst($(target), inline);
inst.settings = $.extend({}, settings || {}, inlineSettings || {});
if (nodeName == 'input') {
this._connectDatepicker(target, inst);
} else if (inline) {
this._inlineDatepicker(target, inst);
}
},
/* Create a new instance object. */
_newInst: function(target, inline) {
var id = target[0].id.replace(/([:\[\]\.])/g, '\\\\$1'); // escape
jQuery meta chars
return {id: id, input: target, // associated target
selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current
selection
drawMonth: 0, drawYear: 0, // month being drawn
inline: inline, // is datepicker inline or not
dpDiv: (!inline ? this.dpDiv : // presentation div
$('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-
widget-content ui-helper-clearfix ui-corner-all"></div>'))};
},
/* Attach the date picker to an input field. */
_connectDatepicker: function(target, inst) {
var input = $(target);
inst.append = $([]);
inst.trigger = $([]);
if (input.hasClass(this.markerClassName))
return;
var appendText = this._get(inst, 'appendText');
var isRTL = this._get(inst, 'isRTL');
if (appendText) {
inst.append = $('<span class="' + this._appendClass + '">' +
appendText + '</span>');
input[isRTL ? 'before' : 'after'](inst.append);
}
var showOn = this._get(inst, 'showOn');
if (showOn == 'focus' || showOn == 'both') // pop-up date picker
when in the marked field
input.focus(this._showDatepicker);
if (showOn == 'button' || showOn == 'both') { // pop-up date picker
when button clicked
var buttonText = this._get(inst, 'buttonText');
var buttonImage = this._get(inst, 'buttonImage');
inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
$('<img/>', target.ownerDocument ).addClass(this._triggerClass).
attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
$('<button type="button"></button>',
target.ownerDocument ).addClass(this._triggerClass).
html(buttonImage == '' ? buttonText : $('<img/>').attr(
{ src:buttonImage, alt:buttonText, title:buttonText })));
input[isRTL ? 'before' : 'after'](inst.trigger);
inst.trigger.click(function() {
if ($.datepicker._datepickerShowing && $.datepicker._lastInput ==
target)
$.datepicker._hideDatepicker();
else
$.datepicker._showDatepicker(target);
return false;
});
}
input.addClass(this.markerClassName).keydown
(this._doKeyDown).keypress(this._doKeyPress).
bind("setData.datepicker", function(event, key, value) {
inst.settings[key] = value;
}).bind("getData.datepicker", function(event, key) {
return this._get(inst, key);
});
$.data(target, PROP_NAME, inst);
},
/* Attach an inline date picker to a div. */
_inlineDatepicker: function(target, inst) {
var divSpan = $(target);
if (divSpan.hasClass(this.markerClassName))
return;
divSpan.addClass(this.markerClassName).append(inst.dpDiv).
bind("setData.datepicker", function(event, key, value){
inst.settings[key] = value;
}).bind("getData.datepicker", function(event, key){
return this._get(inst, key);
});
$.data(target, PROP_NAME, inst);
this._setDate(inst, this._getDefaultDate(inst));
this._updateDatepicker(inst);
this._updateAlternate(inst);
},
/* Pop-up the date picker in a "dialog" box.
@param input element - ignored
@param dateText string - the initial date to display (in the
current format)
@param onSelect function - the function(dateText) to call when a
date is selected
@param settings object - update the dialog date picker
instance's settings (anonymous object)
@param pos int[2] - coordinates for the dialog's position
within the screen or
event - with x/y coordinates or
leave empty for default (screen centre)
@return the manager object */
_dialogDatepicker: function(input, dateText, onSelect, settings, pos)
{
var inst = this._dialogInst; // internal instance
if (!inst) {
var id = 'dp' + (++this.uuid);
this._dialogInput = $('<input type="text" id="' + id +
'" size="1" style="position: absolute; top: -100px;"/>',
DP_jQuery.datepicker._lastInput.ownerDocument);
this._dialogInput.keydown(this._doKeyDown);
$('body').append(this._dialogInput);
inst = this._dialogInst = this._newInst(this._dialogInput, false);
inst.settings = {};
$.data(this._dialogInput[0], PROP_NAME, inst);
}
extendRemove(inst.settings, settings || {});
this._dialogInput.val(dateText);
this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) :
null);
if (!this._pos) {
var browserWidth = window.innerWidth ||
document.documentElement.clientWidth || document.body.clientWidth;
var browserHeight = window.innerHeight ||
document.documentElement.clientHeight || document.body.clientHeight;
var scrollX = document.documentElement.scrollLeft ||
document.body.scrollLeft;
var scrollY = document.documentElement.scrollTop ||
document.body.scrollTop;
this._pos = // should use actual width/height below
[(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 +
scrollY];
}
// move input on screen for focus, but hidden behind dialog
this._dialogInput.css('left', this._pos[0] + 'px').css('top',
this._pos[1] + 'px');
inst.settings.onSelect = onSelect;
this._inDialog = true;
this.dpDiv.addClass(this._dialogClass);
this._showDatepicker(this._dialogInput[0]);
if ($.blockUI)
$.blockUI(this.dpDiv);
$.data(this._dialogInput[0], PROP_NAME, inst);
return this;
},
/* Detach a datepicker from its control.
@param target element - the target input field or division or
span */
_destroyDatepicker: function(target) {
var $target = $(target);
var inst = $.data(target, PROP_NAME);
if (!$target.hasClass(this.markerClassName)) {
return;
}
var nodeName = target.nodeName.toLowerCase();
$.removeData(target, PROP_NAME);
if (nodeName == 'input') {
inst.append.remove();
inst.trigger.remove();
$target.removeClass(this.markerClassName).
unbind('focus', this._showDatepicker).
unbind('keydown', this._doKeyDown).
unbind('keypress', this._doKeyPress);
} else if (nodeName == 'div' || nodeName == 'span')
$target.removeClass(this.markerClassName).empty();
},
/* Enable the date picker to a jQuery selection.
@param target element - the target input field or division or
span */
_enableDatepicker: function(target) {
var $target = $(target);
var inst = $.data(target, PROP_NAME);
if (!$target.hasClass(this.markerClassName)) {
return;
}
var nodeName = target.nodeName.toLowerCase();
if (nodeName == 'input') {
target.disabled = false;
inst.trigger.filter('button').
each(function() { this.disabled = false; }).end().
filter('img').css({opacity: '1.0', cursor: ''});
}
else if (nodeName == 'div' || nodeName == 'span') {
var inline = $target.children('.' + this._inlineClass);
inline.children().removeClass('ui-state-disabled');
}
this._disabledInputs = $.map(this._disabledInputs,
function(value) { return (value == target ? null : value); }); //
delete entry
},
/* Disable the date picker to a jQuery selection.
@param target element - the target input field or division or
span */
_disableDatepicker: function(target) {
var $target = $(target);
var inst = $.data(target, PROP_NAME);
if (!$target.hasClass(this.markerClassName)) {
return;
}
var nodeName = target.nodeName.toLowerCase();
if (nodeName == 'input') {
target.disabled = true;
inst.trigger.filter('button').
each(function() { this.disabled = true; }).end().
filter('img').css({opacity: '0.5', cursor: 'default'});
}
else if (nodeName == 'div' || nodeName == 'span') {
var inline = $target.children('.' + this._inlineClass);
inline.children().addClass('ui-state-disabled');
}
this._disabledInputs = $.map(this._disabledInputs,
function(value) { return (value == target ? null : value); }); //
delete entry
this._disabledInputs[this._disabledInputs.length] = target;
},
/* Is the first field in a jQuery collection disabled as a
datepicker?
@param target element - the target input field or division or
span
@return boolean - true if disabled, false if enabled */
_isDisabledDatepicker: function(target) {
if (!target) {
return false;
}
for (var i = 0; i < this._disabledInputs.length; i++) {
if (this._disabledInputs[i] == target)
return true;
}
return false;
},
/* Retrieve the instance data for the target control.
@param target element - the target input field or division or
span
@return object - the associated instance data
@throws error if a jQuery problem getting data */
_getInst: function(target) {
try {
return $.data(target, PROP_NAME);
}
catch (err) {
throw 'Missing instance data for this datepicker';
}
},
/* Update or retrieve the settings for a date picker attached to an
input field or division.
@param target element - the target input field or division or
span
@param name object - the new settings to update or
string - the name of the setting to change or
retrieve,
when retrieving also 'all' for all instance
settings or
'defaults' for all global defaults
@param value any - the new value for the setting
(omit if above is an object or to retrieve a
value) */
_optionDatepicker: function(target, name, value) {
var inst = this._getInst(target);
if (arguments.length == 2 && typeof name == 'string') {
return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
(inst ? (name == 'all' ? $.extend({}, inst.settings) :
this._get(inst, name)) : null));
}
var settings = name || {};
if (typeof name == 'string') {
settings = {};
settings[name] = value;
}
if (inst) {
if (this._curInst == inst) {
this._hideDatepicker(null);
}
var date = this._getDateDatepicker(target);
extendRemove(inst.settings, settings);
this._setDateDatepicker(target, date);
this._updateDatepicker(inst);
}
},
// change method deprecated
_changeDatepicker: function(target, name, value) {
this._optionDatepicker(target, name, value);
},
/* Redraw the date picker attached to an input field or division.
@param target element - the target input field or division or
span */
_refreshDatepicker: function(target) {
var inst = this._getInst(target);
if (inst) {
this._updateDatepicker(inst);
}
},
/* S