Bugfix for ui.datepicker.js (1.7.2) Fix when datepicker is used in iframe ( and jquery + ui is loaded in the parent window )

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