// Functions loaded to enhance jQuery UI Datepicker // Note: This code was originally found in https://www.hawaiifun.org/reservation/common/calendar_js.jsp?jsversion=20151110 (function( $, undefined ) { $.fn.datepicker_async = function(options) { this.each(function() { var $target = $(this); var inst; options = $.extend({}, options, { onChangeMonthYear: prepareMonth }); $target.datepicker(options); inst = getDatepickerInstance($target); var initialDate = $target.datepicker('getDate'); if (initialDate == null) initialDate = Date(); prepareMonth(initialDate.getFullYear(), 1 + initialDate.getMonth(), inst); }); return this; }; function prepareMonth(year, month, inst) { inst.asyncPrepareMonthPromise = null; setCalendarMask(inst, null); if (!inst.settings.onPrepareMonthBegin) { return; } var promise = inst.settings.onPrepareMonthBegin(year, month, inst); if (promise) { inst.asyncPrepareMonthPromise = promise; setCalendarMask(inst, 'loading'); // The mask we created earlier will be deleted because the datepicker HTML // will be completely recreated *after* the onChangeMonthYear callback // returns. So we'll reapply the mask with a timer. // (Note that this issue doesn't exist when the calendar is being created.) var reapplyMaskFunc = function() { applyCalendarMask(inst); }; var applyMaskTimeout = setTimeout(reapplyMaskFunc, 0); // Masking with a timer works reliably but creates flicker. So we also try // a less reliable method to get control immediately after the datepicker HTML // is regenerated: binding to a 'click' event that is be the most often used // (or even the only) method of switching month. // (Note that this doesn't work in IE7/8 as the 'click' event is not bubbled.) $(getDatepickerElement(inst)).one('click', reapplyMaskFunc); promise.done(function() { clearTimeout(applyMaskTimeout); $(getDatepickerElement(inst)).off('click', reapplyMaskFunc); if (inst.asyncPrepareMonthPromise != promise) return; inst.asyncPrepareMonthPromise = null; setCalendarMask(inst, null); $(getDatepickerElement(inst)).datepicker('refresh'); }); promise.fail(function() { clearTimeout(applyMaskTimeout); $(getDatepickerElement(inst)).off('click', reapplyMaskFunc); if (inst.asyncPrepareMonthPromise != promise) return; inst.asyncPrepareMonthPromise = null; setCalendarMask(inst, 'failed'); }); } } function setCalendarMask(inst, mask) { inst.asyncRequestedMask = mask; applyCalendarMask(inst); } function applyCalendarMask(inst) { // We try to prevent unnecessary mask re-application by checking // if the currently existing mask matches the requested mask. var appliedMask = getExistingCalendarMask(inst); if (appliedMask && appliedMask != inst.asyncRequestedMask) { unmaskCalendar(inst); } if (inst.asyncRequestedMask && inst.asyncRequestedMask != appliedMask) { switch (inst.asyncRequestedMask) { case 'loading': maskCalendarLoading(inst); break; case 'failed': maskCalendarFailed(inst); break; } } } function unmaskCalendar(inst) { $(getDatepickerElement(inst)).find(".datepicker-async-mask").remove(); $(getDatepickerElement(inst)).find("table.ui-datepicker-calendar tbody").css( { visibility: 'inherit' }); } function maskCalendarLoading(inst) { $(getDatepickerElement(inst)).find("table.ui-datepicker-calendar").each(function() { var $calendar = $(this).find("tbody"); $calendar.css({ visibility: 'hidden' }); var $mask = $("
") .addClass('datepicker-async-mask').data('mask', 'loading') .css({ position: isPositionFixed($calendar) ? 'fixed' : 'absolute' }) .insertAfter($(this)); $("
Loading...
") .css({ marginTop: '3.5em', width: '100%', textAlign: 'center' }) .appendTo($mask); coverWithMask($calendar, $mask); }); } function maskCalendarFailed(inst) { $(getDatepickerElement(inst)).find("table.ui-datepicker-calendar").each(function() { var $calendar = $(this).find("tbody"); $calendar.css({ visibility: 'hidden' }); var $mask = $("
") .addClass('datepicker-async-mask').data('mask', 'failed') .css({ position: isPositionFixed($calendar) ? 'fixed' : 'absolute' }) .insertAfter($(this)); $("
Failed to load
") .css({ marginTop: '3.5em', width: '100%', textAlign: 'center' }) .appendTo($mask); coverWithMask($calendar, $mask); }); } function getExistingCalendarMask(inst) { return $(getDatepickerElement(inst)).find(".datepicker-async-mask").data('mask'); } function isPositionFixed($element) { var isFixed = false; $element.parents().each(function() { isFixed |= $(this).css("position") === "fixed"; return !isFixed; }); return isFixed; } function coverWithMask($target, $mask) { var targetOffset = $target.offset(); var maskOriginalOffset = $mask.offset(); $mask.css({ width: $target.width() + 'px', height: $target.height() + 'px', marginLeft: targetOffset.left - maskOriginalOffset.left, marginTop: targetOffset.top - maskOriginalOffset.top }); } function getDatepickerInstance($target) { return $target.data('datepicker'); // Widget factory would use 'ui-datepicker', but as of 1.10.2 datepicker // is not a factory's widget. } function getDatepickerElement(inst) { return inst.input; // Widget factory would use '.element'. } })(jQuery); function reservationcalendar_getBaseUrlWithReservation() { var url = 'https://www.hawaiifun.org/reservation/'; if (/reservation\/$/.test(url) || /reservation_test\/$/.test(url) || /reservation-alt\/$/.test(url)) { // Leave as is. } else if (/perfecthawaiivacationguide.com$/.test(window.location.hostname) || /^(\w+\.)*hawaiifun\.org$/.test(window.location.hostname) || /^(\w+\.)*hawaiifunfusion\.com/.test(window.location.hostname)) { url = 'https://www.hawaiifun.org/reservation/'; } else { url += 'reservation/'; } return url; } (function($) { var reservationcalendar_availability = []; var reservationcalendar_datepickerPanel = null; window.calendar = function(activityId, field, local, minavailability, numberOfMonths) { showAvailabilityCalendar2(activityId, field, { local: local, webBooking: true, minavailability: minavailability, numberOfMonths: numberOfMonths }); }; window.showSimpleCalendar = function(fieldId, anchorId, options) { var $field = $('#' + fieldId); var $anchor = (anchorId ? $('#' + anchorId) : $field); if ($field.length == 0) return; if ($anchor.length == 0) $anchor = $field; var $panel = reservationcalendar_createDatepickerPanel($anchor); var calendarOptions = { showOn: 'none', showAnim: '' }; if (typeof options != 'undefined') { $.extend(calendarOptions, options) } $panel.datepicker(calendarOptions); // Add class for compatibility with the old (modified) availability datepicker. $panel.find(".ui-datepicker").addClass('ui-datepicker_availability'); var initialDateSeconds = Date.parse($field.val()); $panel.datepicker('setDate', initialDateSeconds ? new Date(initialDateSeconds) : null); reservationcalendar_placePanelNearAnchor($panel, $anchor); reservationcalendar_connectDatepickerToField($panel, $field); $panel.css({ visibility: 'inherit' }); reservationcalendar_datepickerPanel = $panel.get(0); }; // Replaced by showAvailabilityCalendar2 but is used in old external/agencyexternal code. window.showAvailabilityCalendar = function(activityId, agencyId, fieldId, anchorId, local) { if (local == null) local = true; if (!/^\d+$/.test(activityId) || !activityId) { alert('Please select activity'); return; } showAvailabilityCalendar2(activityId, fieldId, { agencyId: agencyId, anchorId: anchorId, local: local }); }; window.showAvailabilityCalendar2 = function(activityId, fieldId, options) { var agencyId = (options['agencyId'] != null ? options['agencyId'] : 0); var blocksOnly = (options['blocksOnly'] != null ? options['blocksOnly'] : false); var anchorId = (options['anchorId'] != null ? options['anchorId'] : null); var local = (options['local'] != null ? options['local'] : false); var webBooking = (options['webBooking'] != null ? options['webBooking'] : true); var hawaiifunBooking = (options['hawaiifunBooking'] != null ? options['hawaiifunBooking'] : false); var agencyBooking = (options['agencyBooking'] != null ? options['agencyBooking'] : false); var minAvailability = (options['minavailability'] != null ? options['minavailability'] : undefined); var numberOfMonths = (options['numberOfMonths'] != null ? options['numberOfMonths'] : undefined); var activityIdArray = $.makeArray(activityId); var haveBadActivityId = $.grep(activityIdArray, function(i) { return !/^\d+$/.test(i) || !i }).length; if (activityIdArray.length == 0 || haveBadActivityId) { alert('Please select activity'); return; } var baseurl = (local ? "" : reservationcalendar_getBaseUrlWithReservation()); var $field = $('#' + fieldId); var $anchor = (anchorId ? $('#' + anchorId) : $field); var minAvailabilityStr = typeof(minAvailability) == 'undefined' ? '' : JSON.stringify(minAvailability); var activityIdStr = $.makeArray(activityId).join('|'); if ($field.length == 0) { console.log('showAvailabilityCalendar2: No data for field ' + fieldId); return; } if ($anchor.length == 0) $anchor = $field; if (typeof(numberOfMonths) == 'undefined') numberOfMonths = 1; var $panel = reservationcalendar_createDatepickerPanel($anchor); $panel.datepicker_async({ numberOfMonths: typeof(numberOfMonths) == 'undefined' ? 1 : numberOfMonths, onPrepareMonthBegin: function (year, month, inst) { var yms = []; for (var i = 0, y = year, m = month; i < numberOfMonths; ++i, ++m) { if (m > 12) { m -= 12; y++; } if (!(activityIdStr + '_' + agencyId + '_' + y + '_' + m + '_' + minAvailabilityStr in reservationcalendar_availability)) { yms.push(y + '_' + m); } } if (yms.length == 0) { return null; // All months are already loaded, no need for asynchronous handling. } var deferred = $.Deferred(); $.ajax({ type: 'GET', url: baseurl + 'companyservlet', dataType: 'jsonp', data: { action: 'COMMON_AVAILABILITYCHECKJSON', activityid: activityIdStr, agencyid: agencyId, blocksonly: blocksOnly, year_months: yms.join('|'), webbooking: webBooking, hawaiifunbooking: hawaiifunBooking, agencybooking: agencyBooking, minavailability: minAvailabilityStr } }) .done(function(data) { for (i = 0; i < yms.length; i++) { reservationcalendar_availability[activityIdStr + '_' + agencyId + '_' + yms[i] + '_' + minAvailabilityStr] = data['yearmonth_' + yms[i]]; } deferred.resolve(); }) .fail(function(xhr, textStatus, errorThrown) { alert(baseurl + ' ' + textStatus + ' ' + errorThrown); deferred.reject(); }); return deferred.promise(); }, beforeShowDay: function(date) { var m = date.getMonth() + 1; var d = date.getDate(); var y = date.getFullYear(); var idx = activityIdStr + '_' + agencyId + '_' + y + '_' + m + '_' + minAvailabilityStr; if (!(idx in reservationcalendar_availability) || !('d' + d in reservationcalendar_availability[idx])) { return [false]; } else if (reservationcalendar_availability[idx]['d' + d] <= 0) { return [false, 'un', 'Not Available']; } else { return [true]; } } }); // Add class for compatibility with the old (modified) availability datepicker. $panel.find(".ui-datepicker").addClass('ui-datepicker_availability'); var initialDateSeconds = Date.parse($field.val()); $panel.datepicker('setDate', initialDateSeconds ? new Date(initialDateSeconds) : null); reservationcalendar_placePanelNearAnchor($panel, $anchor); reservationcalendar_connectDatepickerToField($panel, $field); $panel.css({ visibility: 'inherit' }); reservationcalendar_datepickerPanel = $panel.get(0); }; window.reservationcalendar_createDatepickerPanel = function($anchor) { if (reservationcalendar_datepickerPanel) { var $oldPanel = $(reservationcalendar_datepickerPanel); if ($oldPanel.data('datepicker')) { $oldPanel.datepicker('destroy'); $oldPanel.remove(); } reservationcalendar_datepickerPanel = null; } var isFixed = false; $anchor.parents().each(function() { isFixed |= $(this).css("position") === "fixed"; return !isFixed; }); return $("
") .css({ paddingTop: '1px', paddingBottom: '1px', position: isFixed ? 'fixed' : 'absolute', visibility: 'hidden', zIndex: $anchor.zIndex() // zIndex() is from jquery-ui core }) .appendTo($anchor.closest("body")); }; window.reservationcalendar_placePanelNearAnchor = function($panel, $anchor) { $panel.css({ left: 0, top: 0 }); var originalPanelPosition = $panel.offset(); var panelSize = { width: $panel.outerWidth(), height: $panel.outerHeight() }; var anchorPosition = $anchor.offset(); var anchorSize = { width: $anchor.outerWidth(false), height: $anchor.outerHeight(false) }; var $document = $($panel.get(0).ownerDocument); var $window = $(reservationcalendar_getElementWindow($panel)); var viewDimensions = { left: $document.scrollLeft(), top: $document.scrollTop(), width: $window.width(), height: $window.height() }; // Fit position vertically. var primaryPanelPosition = { left: anchorPosition.left, top: anchorPosition.top + anchorSize.height }; var altPanelPosition = { left: anchorPosition.left, top: anchorPosition.top - panelSize.height }; var primaryVerticallyVisible = reservationcalendar_checkVerticalVisibility(primaryPanelPosition, panelSize, viewDimensions); var altVerticallyVisible = reservationcalendar_checkVerticalVisibility(altPanelPosition, panelSize, viewDimensions); var selectedPanelPosition = (!primaryVerticallyVisible && altVerticallyVisible ? altPanelPosition : primaryPanelPosition); // Adjust position horizontally. if (selectedPanelPosition.left + panelSize.width > viewDimensions.left + viewDimensions.width) { selectedPanelPosition.left = Math.max( viewDimensions.left, viewDimensions.left + viewDimensions.width - panelSize.width); } $panel.css({ left: (selectedPanelPosition.left - originalPanelPosition.left) + 'px', top: (selectedPanelPosition.top - originalPanelPosition.top) + 'px' }); }; window.reservationcalendar_connectDatepickerToField = function($panel, $field) { var setupTimeout = null; var lastInputValue = null; var destroyDatepicker = function() { if (setupTimeout) clearTimeout(setupTimeout); $field.off('input', onFieldChange); $field.off('onpropertychange', onFieldChange); $field.off('keyup', onFieldChange); $panel.off('click', onPanelClick); $panel.closest("html").off('click', onDocumentClick); $panel.datepicker('destroy'); $panel.remove(); }; var onFieldChange = function(ev) { var inputValue = $field.val(); if (inputValue === lastInputValue) return; var dateSeconds = Date.parse(inputValue); $panel.datepicker('setDate', dateSeconds ? new Date(dateSeconds) : null); lastInputValue = inputValue; }; var onDocumentClick = function(ev) { destroyDatepicker(); }; var onPanelClick = function(ev) { ev.stopPropagation(); }; $panel.datepicker('option', 'onSelect', function(dateText, inst) { $field.val(dateText); $field.change(); destroyDatepicker(); }); // Bind events that are fired when input is changed in various browsers: $field.on('input', onFieldChange); $field.on('onpropertychange', onFieldChange); $field.on('keyup', onFieldChange); setupTimeout = setTimeout(function() { // We set up click handlers asynchronously to prevent onDocumentClick from // being called immediately when bubbling click event, *if* we are creating // a datepicker in response to this very event. $panel.on('click', onPanelClick); $panel.closest("html").on('click', onDocumentClick); }, 0); }; window.reservationcalendar_getElementWindow = function($element) { var doc = $element.get(0).ownerDocument; return doc.defaultView || doc.parentWindow; }; window.reservationcalendar_checkVerticalVisibility = function(panelPosition, panelSize, viewDimensions) { return (panelPosition.top >= viewDimensions.top && panelPosition.top + panelSize.height <= viewDimensions.top + viewDimensions.height); }; })(jQuery);