1(function($) { 2 let selectedTickets = {}; // Memory to store selected ticket IDs and quantities 3 4 function updateSubtotal() { 5 let subtotal = 0; 6 let enableContinue = false; 7 8 $('#ticketSelectionForm .ticket-item').each(function() { 9 const $item = $(this); 10 const $input = $item.find('input[type="number"]'); 11 12 if ($input.is(':disabled')) return; 13 14 const price = parseFloat($item.data('price')) || 0; 15 const quantity = parseInt($input.val(), 10) || 0; 16 const minPerOrder = parseInt($input.data('min-per-order'), 10) || 1; 17 18 subtotal += quantity * price; 19 20 if (quantity >= minPerOrder) { 21 enableContinue = true; 22 } 23 }); 24 25 $('#subtotal-amount').text('â¬' + subtotal.toFixed(2)); 26 $('#continue-to-step-2').prop('disabled', !enableContinue); 27 } 28 29 // Trigger initial update when modal opens 30 $('#ticketModal').on('shown.bs.modal', function () { 31 updateSubtotal(); 32 attachQuantityListeners(); // Rebind in case modal content was updated 33 }); 34 35 function updateButtonStates($input) { 36 const $parent = $input.closest('.quantity-selector'); 37 const $plus = $parent.find('.plus'); 38 const $minus = $parent.find('.minus'); 39 const max = parseInt($input.attr('max')) || 999; 40 const quantity = parseInt($input.val(), 10) || 0; 41 42 $minus.prop('disabled', quantity <= 0); 43 $plus.prop('disabled', quantity >= max); 44 } 45 46 function attachQuantityListeners() { 47 // Always unbind to avoid duplicates 48 $('#ticketSelectionForm').off('click', '.plus'); 49 $('#ticketSelectionForm').off('click', '.minus'); 50 $('#ticketSelectionForm').off('change', 'input[type="number"]'); 51 52 $('#ticketSelectionForm').on('click', '.plus', function () { 53 const $input = $(this).closest('.quantity-selector').find('input[type="number"]'); 54 let value = parseInt($input.val(), 10) || 0; 55 const max = parseInt($input.attr('max')) || 999; 56 const minPerOrder = parseInt($input.data('min-per-order'), 10) || 1; 57 58 if (value === 0) { 59 value = minPerOrder; 60 } else if (value < max) { 61 value++; 62 } 63 64 $input.val(value).trigger('change'); 65 }); 66 67 $('#ticketSelectionForm').on('click', '.minus', function () { 68 const $input = $(this).closest('.quantity-selector').find('input[type="number"]'); 69 let value = parseInt($input.val(), 10) || 0; 70 const minPerOrder = parseInt($input.data('min-per-order'), 10) || 1; 71 72 value = (value <= minPerOrder) ? 0 : value - 1; 73 $input.val(value).trigger('change'); 74 }); 75 76 $('#ticketSelectionForm').on('change', 'input[type="number"]', function () { 77 const $input = $(this); 78 const ticketId = $input.data('ticket-id'); 79 let value = parseInt($input.val(), 10) || 0; 80 const minPerOrder = parseInt($input.data('min-per-order'), 10) || 1; 81 const max = parseInt($input.attr('max')) || 999; 82 83 if (value > 0 && value < minPerOrder) value = minPerOrder; 84 if (value > max) value = max; 85 86 $input.val(value); 87 88 if (value > 0) { 89 selectedTickets[ticketId] = value; 90 } else { 91 delete selectedTickets[ticketId]; 92 } 93 94 updateButtonStates($input); 95 updateSubtotal(); 96 }); 97 } 98 99 // --------------------------------------------------------------- 100 // Ticket Tailor â Tickets button 101 // Uses data attributes + delegated handler instead of inline onclick. 102 // iOS Safari does not reliably honour "return false" in inline onclick 103 // handlers on anchor tags, causing the browser to follow the href 104 // and redirect to the event page instead of opening the TT widget. 105 // --------------------------------------------------------------- 106 $(document).on('click', '.jnp-tt-ticket-btn', function(e) { 107 if (window.TTWidget && typeof TTWidget.loadEventOccurrence === 'function') { 108 e.preventDefault(); 109 TTWidget.loadEventOccurrence( 110 $(this).data('boxoffice'), 111 $(this).data('tt-id') 112 ); 113 } 114 // If TTWidget is not loaded, fall through to href (fallback URL) 115 }); 116 117 // --------------------------------------------------------------- 118 // Ticket Tailor â Tavoli (tables) button 119 // Same fix: data attributes instead of inline onclick. 120 // --------------------------------------------------------------- 121 $(document).on('click', '.jnp-tt-tables-btn', function(e) { 122 if (window.TTWidget && typeof TTWidget.loadEventOccurrence === 'function') { 123 e.preventDefault(); 124 TTWidget.loadEventOccurrence( 125 $(this).data('boxoffice'), 126 $(this).data('tt-id') 127 ); 128 } 129 }); 130 131 // --------------------------------------------------------------- 132 // Internal ticket modal trigger (non-TT events) 133 // --------------------------------------------------------------- 134 $(document).on('click', '.open-ticket-modal', function(e) { 135 e.preventDefault(); 136 137 const eventId = $(this).data('event-id'); 138 const occurrence = $(this).data('occurrence'); 139 140 if (!eventId || !occurrence) return; 141 142 const baseUrl = window.location.href.split('?')[0]; 143 window.location.href = `${baseUrl}?modal=tickets&occurrence=${occurrence}`; 144 }); 145 146 $('#continue-to-step-2').on('click', function() { 147 if ($.isEmptyObject(selectedTickets)) { 148 alert('Please select at least one ticket.'); 149 return; 150 } 151 152 const eventId = $('#ticketSelectionForm').data('event-id'); 153 const occurrenceTs = $('#ticketSelectionForm').data('occurrence-ts'); 154 155 if (!eventId || !occurrenceTs) { 156 alert('Event information missing. Please refresh the page.'); 157 return; 158 } 159 160 const referer = encodeURIComponent(window.location.href); 161 let query = `?event_id=${eventId}&occurrence_ts=${occurrenceTs}&product_type=event_ticket&referer=${referer}`; 162 $.each(selectedTickets, function(ticketId, quantity) { 163 query += `&selected_tickets[${ticketId}]=${quantity}`; 164 }); 165 166 window.location.href = '/checkout/' + query; 167 }); 168 169 // Initialize 170 attachQuantityListeners(); 171 updateSubtotal(); 172})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.