function SasoEventticketsValidator_WC_frontend($, phpObject) { const { __, _x, _n, sprintf } = wp.i18n; let _self = this; let inputTypes = [phpObject.inputType, "text", "value"]; function init() { _addHandlerToTheCodeFields(); _addHandlerToAddToCartButtons(); // for shop and product pages } function addStyleCode(content) { let c = document.createElement('style'); c.innerHTML = content; document.getElementsByTagName("head")[0].appendChild(c); } function getPidFromAddToCartButton(btn){ return btn.data('product_id') || btn.attr('data-product_id') || btn.val() || null; } function _addHandlerToAddToCartButtons() { if (!phpObject.fieldKey) return; if (!phpObject.has_daychooser) return; // only if at least one product has a date picker function findDateForPid(pid, $btn){ let input_id = phpObject.fieldKey+'_'+pid; return $(document.body) .find('input[data-input-type="daychooser"][data-plugin="event"][data-is-shop-page="1"][data-cart-item-id="'+input_id+'"]') .first(); } function checkDate(btn, e) { var pid = getPidFromAddToCartButton(btn); if (!pid) return true; // manche Themes weichen ab; dann greift serverseitige Prüfung var input = findDateForPid(pid, btn); if (!input || !input.length) return true; // kein Feld gefunden, dann greift serverseitige Prüfung var val = (input && input.val ? (input.val()||'').trim() : ''); if (!val) { if (e) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); } // Kurzes Feedback (ersetze gern durch eigenes Notice-System) alert(phpObject.daychooser_warning ? phpObject.daychooser_warning : __('Please choose a valid date.', 'event-tickets-with-ticket-scanner')); return false; } return true; } // product page $(document).on('click', '.single_add_to_cart_button', function(e){ var btn = $(this); if (!checkDate(btn, e)) { return false; } }); // product page with AJAX add to cart var form = document.querySelector('form.cart'); if (form) { form.addEventListener('submit', function(e){ var btn = $(form).find('.single_add_to_cart_button').first(); if (!btn || btn.length == 0) return; // no button found, then server side check if (!checkDate(btn, e)) { return false; } var pid = getPidFromAddToCartButton(btn); if (!pid) return; // manche Themes weichen ab; dann greift serverseitige Prüfung var $input = findDateForPid(pid, btn); if ($input && $input.length > 0) { var val = ($input && $input.val ? ($input.val()||'').trim() : ''); if (val == "") return false; // add hidden input field to the form if not already present var hiddenInput = form.querySelector('input[name="'+phpObject.fieldKey+'"]'); if (!hiddenInput) { hiddenInput = document.createElement('input'); hiddenInput.type = 'hidden'; hiddenInput.name = phpObject.fieldKey; form.appendChild(hiddenInput); } hiddenInput.value = val ? val : ''; // indicate that a day chooser was used var hiddenInputIndicator = form.querySelector('input[name="'+phpObject.fieldDayChooserIndicator+'"]'); if (!hiddenInputIndicator) { hiddenInputIndicator = document.createElement('input'); hiddenInputIndicator.type = 'hidden'; hiddenInputIndicator.name = phpObject.fieldDayChooserIndicator; form.appendChild(hiddenInputIndicator); } hiddenInputIndicator.value = 1; } }); } // shop/archive page: intercept ANY add-to-cart link with data-product_id // Works with both WC Classic (ajax_add_to_cart) and WC Blocks (plain link) $(document.body).on('click', 'a[data-product_id]', function(e){ var btn = $(this); var pid = getPidFromAddToCartButton(btn); if (!pid) return; var $input = findDateForPid(pid, btn); if (!$input || !$input.length) return; // not a daychooser product — let WC handle normally var val = ($input.val() || '').trim(); if (!val) { e.preventDefault(); e.stopImmediatePropagation(); alert(phpObject.daychooser_warning ? phpObject.daychooser_warning : __('Please choose a valid date.', 'event-tickets-with-ticket-scanner')); return false; } // For AJAX buttons (classic WC): let the adding_to_cart handler below add data if (btn.hasClass('ajax_add_to_cart')) return; // For non-AJAX buttons (WC Blocks / plain links): append date to URL e.preventDefault(); e.stopImmediatePropagation(); var url = btn.attr('href'); if (!url) return; url += (url.indexOf('?') > -1 ? '&' : '?'); url += encodeURIComponent(phpObject.fieldKey) + '=' + encodeURIComponent(val); url += '&' + encodeURIComponent(phpObject.fieldDayChooserIndicator) + '=1'; var nonce = document.querySelector('input[name="'+phpObject.nonceKey+'"]'); if (nonce) url += '&' + encodeURIComponent(phpObject.nonceKey) + '=' + encodeURIComponent(nonce.value); window.location.href = url; }); // shop page with AJAX add to cart (classic WC) $(document.body).on('adding_to_cart', function(e, $button, data){ var pid = getPidFromAddToCartButton($button); if (!pid) return; var $input = findDateForPid(pid, $button); if ($input && $input.length > 0) { var val = ($input && $input.val ? ($input.val()||'').trim() : ''); if (val == "") return false; data[phpObject.fieldKey] = val ? val : ''; data[phpObject.fieldDayChooserIndicator] = 1; } var nonce = document.querySelector('input[name="'+phpObject.nonceKey+'"]'); data[phpObject.nonceKey] = nonce ? nonce.value : ''; }); } function _addHandlerToTheCodeFields() { let isStoring = false; let waitingTimeout = null; let isChanged = false; function sendCode(elem, code, type) { //clearWaitingTimeout(); if (!isStoring) { $('div[class="woocommerce"]').block({ //message: '...loading...', message: null, overlayCSS: { background: '#fff', opacity: 0.6 } }); isStoring = true; let cart_item_id = elem.attr('data-cart-item-id'); let cart_item_count = elem.attr('data-cart-item-count'); let nonce = phpObject.nonce; $.ajax( { type: 'POST', url: phpObject.ajaxurl, data: { action: phpObject.action, a: 'updateSerialCodeToCartItem', security: nonce, cart_item_id: cart_item_id, cart_item_count: cart_item_count, type: type, code: code }, success: function( response ) { $('div[class="woocommerce"]').unblock(); $('.cart_totals').unblock(); if (response.success) { elem.val(response.code); } else { if (response.msg) alert(response.msg); } isStoring = false; //window.location.reload(); } } ) } } function clearWaitingTimeout() { clearTimeout(waitingTimeout); } function setWaitingTimeout(elem, code) { clearWaitingTimeout(); waitingTimeout = setTimeout(()=>{ if (isChanged) { isChanged = false; sendCode(elem, code); } }, 2500); } // finde die code text inputs // eventcoderrestriction is no longer used, but still in the code $('body').find('input[data-input-type="eventcoderestriction"][data-plugin="event"]') .on('keydown',function(e){ if (e.which === 13) { e.preventDefault(); let elem = $(this); isChanged = true; sendCode(elem, elem.val().trim(), "saso_eventtickets_request_name_per_ticket"); } }) .on('paste', event=>{ isStoring = false; let elem = $(event.srcElement); let code = (event.clipboardData || window.clipboardData).getData('text'); if (typeof code == "string") { code = code.trim(); isChanged = true; sendCode(elem, code, "saso_eventtickets_request_name_per_ticket"); } else { alert("no text"); } }) .on('change',function(){ let elem = $(this); let code = elem.val().trim(); //let cart_item_id = elem.data('cart-item-id'); //let d = document.querySelector('input[data-cart-item-id="'+cart_item_id+'"]').value isChanged = true; sendCode(elem, code, "saso_eventtickets_request_name_per_ticket"); }) /* .on('blur',function(){ let elem = $(this); let code = elem.val().trim(); if (code != "" && isChanged) { let cart_item_id = elem.data('cart-item-id'); //let d = document.querySelector('input[data-cart-item-id="'+cart_item_id+'"]').value sendCode(elem, code, "saso_eventtickets_request_name_per_ticket"); } }) */ .removeAttr('disabled'); $('body').find('input[data-input-type="text"][data-plugin="event"]') .on('keydown',function(e){ if (e.which === 13) { e.preventDefault(); let elem = $(this); isChanged = true; sendCode(elem, elem.val().trim(), "saso_eventtickets_request_name_per_ticket"); } }) .on('paste', event=>{ isStoring = false; let elem = $(event.srcElement); let code = (event.clipboardData || window.clipboardData).getData('text'); if (typeof code == "string") { code = code.trim(); isChanged = true; sendCode(elem, code, "saso_eventtickets_request_name_per_ticket"); } else { alert("no text"); } }) .on('change',function(){ let elem = $(this); let code = elem.val().trim(); isChanged = true; sendCode(elem, code, "saso_eventtickets_request_name_per_ticket"); }) .removeAttr('disabled'); $('body').find('select[data-input-type="value"][data-plugin="event"]') .on('change',function(){ let elem = $(this); let code = elem.val().trim(); isChanged = true; sendCode(elem, code, "saso_eventtickets_request_value_per_ticket"); }) .removeAttr('disabled'); // Resolve a min/max value (number-of-days, YYYY-MM-DD, or empty) → JS Date or null. function _sasoResolveLimitDate(opt) { if (opt == null || opt === '') return null; if (typeof opt === 'number' && !isNaN(opt)) { let d = new Date(); d.setHours(0, 0, 0, 0); d.setDate(d.getDate() + opt); return d; } let s = String(opt).trim(); let m = s.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/); if (m) return new Date(parseInt(m[1], 10), parseInt(m[2], 10) - 1, parseInt(m[3], 10)); let n = parseInt(s, 10); if (!isNaN(n)) { let d = new Date(); d.setHours(0, 0, 0, 0); d.setDate(d.getDate() + n); return d; } return null; } // Selectability rules — kept in sync with beforeShowDay below intentionally. function _sasoIsDateSelectable(date, attrs) { if (attrs.excludeWdays) { let excludedDays = attrs.excludeWdays.split(','); if (excludedDays.indexOf(date.getDay().toString()) !== -1) return false; } let today = new Date(); today.setHours(0, 0, 0, 0); let chk = new Date(date.getFullYear(), date.getMonth(), date.getDate()); if (chk < today) return false; if (attrs.excludeDates) { let excludedDates = attrs.excludeDates.split(','); let m = date.getMonth() + 1; let d = date.getDate(); let dateStr = date.getFullYear() + '-' + (m < 10 ? '0' : '') + m + '-' + (d < 10 ? '0' : '') + d; if (excludedDates.indexOf(dateStr) !== -1) return false; } return true; } // Scan up to 24 months forward from `from` for the first selectable day. // Used as datepicker.defaultDate so the picker opens on the first month // that actually has free slots — instead of an all-grey current month. function _sasoFindFirstSelectableDate(attrs, from, until) { let cap = 24; let start = from ? new Date(from.getFullYear(), from.getMonth(), from.getDate()) : (function () { let t = new Date(); t.setHours(0, 0, 0, 0); return t; })(); let scanFrom = new Date(start); for (let i = 0; i < cap; i++) { let monthEnd = new Date(scanFrom.getFullYear(), scanFrom.getMonth() + 1, 0); let lastDay = (until && monthEnd > until) ? until : monthEnd; for (let d = new Date(scanFrom); d <= lastDay; d.setDate(d.getDate() + 1)) { if (_sasoIsDateSelectable(d, attrs)) return new Date(d); } scanFrom = new Date(scanFrom.getFullYear(), scanFrom.getMonth() + 1, 1); if (until && scanFrom > until) return null; } return null; } $('body').find('input[data-input-type="daychooser"][data-plugin="event"]') .on('keydown', function(e) { e.preventDefault(); }) .on('paste', function(e) { e.preventDefault(); }) .each((idx, input) => { let elem_intern = $(input); let dateFormat = elem_intern.attr('placeholder'); dateFormat = dateFormat != null ? dateFormat.trim() : ''; dateFormat = dateFormat ? dateFormat : 'YYYY-MM-DD'; dateFormat = 'YYYY-MM-DD'; elem_intern.attr('placeholder', __(dateFormat)); let data_offset_start = 0; let data_offset_end = 0; try { data_offset_start = parseInt(elem_intern.attr('data-offset-start')); } catch (error) { //console.log(error); } if (elem_intern.attr('min') && elem_intern.attr('min').length > 0) { data_offset_start = elem_intern.attr('min'); } try { data_offset_end = parseInt(elem_intern.attr('data-offset-end')); } catch (error) { //console.log(error); } if (elem_intern.attr('max') && elem_intern.attr('max').length > 0) { data_offset_end = elem_intern.attr('max'); } //let today = new Date(); //let start = new Date(today.getFullYear(), today.getMonth(), today.getDate() + data_offset_start); //let end = new Date(today.getFullYear(), today.getMonth(), today.getDate() + data_offset_end); let _saso_attrs = { excludeWdays: elem_intern.attr('data-exclude-wdays') || '', excludeDates: elem_intern.attr('data-exclude-dates') || '' }; let _saso_minAbs = _sasoResolveLimitDate(data_offset_start); let _saso_maxAbs = _sasoResolveLimitDate(data_offset_end); let _saso_default = _sasoFindFirstSelectableDate(_saso_attrs, _saso_minAbs, _saso_maxAbs); elem_intern.datepicker({ dateFormat: 'yy-mm-dd', //dateFormat: dateFormat, showWeek: true, firstDay: 1, hideIfNoPrevNext : true, minDate: data_offset_start, maxDate: data_offset_end, defaultDate: _saso_default, beforeShow: function(input, options) { this._sasoevent_input_field = $(input); }, beforeShowDay: function(date) { // https://api.jqueryui.com/datepicker/#option-beforeShow let day = date.getDay(); let data_exclude_wdays = this._sasoevent_input_field.attr('data-exclude-wdays'); let selectable = true; let cssClass = ''; let toolTipp = ''; if (data_exclude_wdays && data_exclude_wdays.length > 0) { let excludedDays = data_exclude_wdays.split(','); selectable = excludedDays.indexOf(day.toString()) == -1; cssClass = selectable ? '' : 'ui-datepicker-unselectable'; toolTipp = selectable ? '' : __('This day is not selectable'); } if (selectable) { // check if the date is in the past let today = new Date(); let y = date.getFullYear(); let m = date.getMonth() + 1; let d = date.getDate(); let dateStr = y + '-' + (m < 10 ? '0' : '') + m + '-' + (d < 10 ? '0' : '') + d; let todayStr = today.getFullYear() + '-' + (today.getMonth() + 1 < 10 ? '0' : '') + (today.getMonth() + 1) + '-' + (today.getDate() < 10 ? '0' : '') + today.getDate(); if (dateStr < todayStr) { selectable = false; cssClass = 'ui-datepicker-unselectable'; toolTipp = __('This day is not selectable'); } } if (selectable) { let data_exclude_dates = this._sasoevent_input_field.attr('data-exclude-dates'); if (data_exclude_dates && data_exclude_dates.length > 0) { let excludedDates = data_exclude_dates.split(','); let y = date.getFullYear(); let m = date.getMonth() + 1; let d = date.getDate(); let dateStr = y + '-' + (m < 10 ? '0' : '') + m + '-' + (d < 10 ? '0' : '') + d; selectable = excludedDates.indexOf(dateStr) == -1; cssClass = selectable ? '' : 'ui-datepicker-unselectable'; toolTipp = selectable ? '' : __('This day is not selectable'); } } return [selectable, cssClass, toolTipp]; //return [true, '']; } }); }) .on('change',event=>{ let elem_intern = $(event.target); if (elem_intern.attr('data-is-shop-page') == "1") return; // only on cart and checkout page //console.log('change datepicker', elem_intern.attr('id')); let date_value = elem_intern.val().trim(); if (elem_intern.attr('data-previous-value') == date_value) return; // no change elem_intern.attr('data-previous-value', date_value); if (date_value) { sendCode(elem_intern, date_value, "saso_eventtickets_request_daychooser"); isChanged = true; let to_be_changed = []; // update the other date pickers if no value is set to use this date let data_cart_item_id = elem_intern.attr('data-cart-item-id'); $('body').find('input[data-input-type="daychooser"][data-plugin="event"][id^="saso_eventtickets_request_daychooser['+data_cart_item_id+']"]').each((idx, input_to_update) => { //console.log('update datepicker', input_to_update); let input_elem = $(input_to_update); let v = input_elem.val().trim(); if (!v) { //console.log(input_elem.attr("id")+' set value to: '+date_value); input_elem.val(date_value); // is not working somehow, so skip this step for now. The value is shown and send to the server, but on the checkout the other fields are empty to_be_changed.push(input_elem); } }); // remove the related error message on the cart //let data_cart_item_count = elem.attr('data-cart-item-count'); //$('li[data-cart-item-id="'+data_cart_item_id+'"][data-cart-item-count="'+data_cart_item_count+'"]').remove(); // send data to the server let counter = 0; let wait = 0; to_be_changed.forEach(input => { //console.log(input.attr("id")+'send code: '+date_value); //console.log(input); window.setTimeout(()=>{ sendCode(input, date_value, "saso_eventtickets_request_daychooser"); }, wait); counter++; if (wait == 0) wait = 250; if (counter == to_be_changed.length) { $(document.body).trigger('update_checkout'); } }); } // end date_value }) .each(function() { // Only remove disabled if not in a locked container (seats selected) if (!$(this).closest('.saso-datepicker-locked').length) { $(this).removeAttr('disabled'); } }); //addStyleCode('#ui-datepicker-div > table {background-color: white;}'); } init(); return { _addHandlerToTheCodeFields: _addHandlerToTheCodeFields, }; } (function($){ $(document).ready(function(){ window.SasoEventticketsValidator_WC_frontend = SasoEventticketsValidator_WC_frontend($, SasoEventticketsValidator_phpObject); }); })(jQuery);