1/** 2 * Coupon Purchase Form Handler 3 * Handles amount selection, custom input toggle, and form validation 4 */ 5 6window._couponTurnstileVerified = false; 7window.onCouponTurnstileSuccess = function(token) { window._couponTurnstileVerified = true; $('#coupon-turnstile-error').hide(); }; 8window.onCouponTurnstileExpired = function() { window._couponTurnstileVerified = false; }; 9window.onCouponTurnstileError = function() { window._couponTurnstileVerified = false; }; 10 11$(document).ready(function() { 12 // Only run if we're on the coupon page 13 if (!$('#couponForm').length) return; 14 15 // Sticky Steps Navigation Handler 16 const stepsBar = $('#stepsSummaryBar'); 17 const stepsBarOffset = stepsBar.length ? stepsBar.offset().top : 0; 18 const stepsBarHeight = stepsBar.length ? stepsBar.outerHeight() : 0; 19 const infoSidebar = $('.coupon-info-sidebar'); 20 let currentStep = 1; 21 let stickyPlaceholder = null; 22 23 // Handle scroll for sticky navigation 24 function handleStickySteps() { 25 if (!stepsBar.length) return; 26 27 const scrollTop = $(window).scrollTop(); 28 29 if (scrollTop > stepsBarOffset) { 30 if (!stepsBar.hasClass('sticky')) { 31 stepsBar.addClass('sticky'); 32 33 // Create placeholder to prevent content jump 34 if (!stickyPlaceholder) { 35 stickyPlaceholder = $('<div class="sticky-placeholder"></div>'); 36 stickyPlaceholder.css('height', stepsBarHeight + 'px'); 37 stepsBar.after(stickyPlaceholder); 38 } 39 40 // Adjust sidebar top position to account for sticky bar 41 if (infoSidebar.length) { 42 const stickyBarHeight = stepsBar.outerHeight(); 43 infoSidebar.css('top', (stickyBarHeight + 20) + 'px'); 44 } 45 } 46 } else { 47 stepsBar.removeClass('sticky'); 48 49 // Remove placeholder 50 if (stickyPlaceholder) { 51 stickyPlaceholder.remove(); 52 stickyPlaceholder = null; 53 } 54 55 // Reset sidebar top position 56 if (infoSidebar.length) { 57 infoSidebar.css('top', '20px'); 58 } 59 } 60 } 61 62 // Update step progress based on scroll position 63 function updateStepProgress() { 64 const scrollTop = $(window).scrollTop(); 65 const windowHeight = $(window).height(); 66 67 // Find which step is currently in view 68 $('.step-number-circle').each(function(index) { 69 const stepElement = $(this).closest('.mb-4'); 70 if (stepElement.length) { 71 const stepTop = stepElement.offset().top; 72 const stepBottom = stepTop + stepElement.outerHeight(); 73 const viewportMiddle = scrollTop + (windowHeight / 3); 74 75 if (viewportMiddle >= stepTop && viewportMiddle < stepBottom) { 76 const newStep = index + 1; 77 if (newStep !== currentStep) { 78 currentStep = newStep; 79 markStepAsActive(currentStep); 80 } 81 } 82 } 83 }); 84 } 85 86 // Mark step as active and previous steps as completed 87 function markStepAsActive(stepNumber) { 88 $('.summary-item').each(function() { 89 const itemStep = parseInt($(this).data('step')); 90 91 $(this).removeClass('active completed'); 92 93 if (itemStep < stepNumber) { 94 $(this).addClass('completed'); 95 } else if (itemStep === stepNumber) { 96 $(this).addClass('active'); 97 } 98 }); 99 } 100 101 // Initialize on page load 102 handleStickySteps(); 103 updateStepProgress(); 104 105 // Handle scroll events 106 $(window).on('scroll', function() { 107 handleStickySteps(); 108 updateStepProgress(); 109 }); 110 111 // Gift checkbox toggle - show/hide receiver section 112 const isGiftCheckbox = $('#is_gift'); 113 const receiverSection = $('#receiverSection'); 114 const customerNameInput = $('#customer_name'); 115 const customerEmailInput = $('#customer_email'); 116 117 if (isGiftCheckbox.length && receiverSection.length) { 118 // Set initial state immediately (no animation) 119 if (isGiftCheckbox.prop('checked')) { 120 receiverSection.show(); 121 customerNameInput.attr('required', 'required'); 122 customerEmailInput.attr('required', 'required'); 123 } else { 124 receiverSection.hide(); 125 customerNameInput.removeAttr('required'); 126 customerEmailInput.removeAttr('required'); 127 } 128 129 // Handle checkbox change with animation 130 isGiftCheckbox.on('change', function() { 131 if ($(this).prop('checked')) { 132 receiverSection.slideDown(300); 133 customerNameInput.attr('required', 'required'); 134 customerEmailInput.attr('required', 'required'); 135 } else {
136 receiverSection.slideUp(300); 137 customerNameInput.removeAttr('required'); 138 customerEmailInput.removeAttr('required'); 139 } 140 }); 141 } 142 143 const customAmountWrapper = $('.custom-amount-input-wrapper'); 144 const customAmountInput = $('.custom-amount-input'); 145 const amountInfoBox = $('#amountInfoBox'); 146 const selectedAmountDisplay = $('#selectedAmountDisplay'); 147 148 // Function to show/hide custom amount input 149 function toggleCustomInput(show) { 150 if (show) { 151 customAmountWrapper.show(); 152 setTimeout(function() { 153 customAmountInput.focus(); 154 }, 300); 155 } else { 156 customAmountWrapper.hide(); 157 } 158 } 159 160 // Function to update amount info box 161 function updateAmountInfoBox(amount) { 162 if (amount > 0) { 163 selectedAmountDisplay.text('â¬' + amount); 164 amountInfoBox.css('display', 'flex'); 165 } else { 166 amountInfoBox.hide(); 167 } 168 } 169 170 // Handle amount type selection 171 $('.amount-type-radio').on('change', function() { 172 const amountType = $(this).val(); 173 174 if (amountType === 'custom') { 175 // Uncheck all preset radios and re-enable them for future selection 176 $('.amount-type-radio[value="preset"]').each(function() { 177 $(this).prop('checked', false).prop('disabled', false); 178 }); 179 180 toggleCustomInput(true); 181 182 // If custom amount is already filled, show info box 183 const customValue = customAmountInput.val(); 184 if (customValue) { 185 updateAmountInfoBox(parseFloat(customValue)); 186 } else { 187 updateAmountInfoBox(0); 188 } 189 190 // Disable all preset amount inputs 191 $('.preset-amount-input').prop('disabled', true); 192 } else { 193 // Uncheck custom radio and re-enable it for future selection 194 $('.amount-type-radio[value="custom"]').prop('checked', false).prop('disabled', false); 195 196 toggleCustomInput(false); 197 198 // Clear custom amount value when switching to preset 199 customAmountInput.val(''); 200 201 // Show info box with preset amount 202 const amount = $(this).data('amount'); 203 if (amount) { 204 updateAmountInfoBox(parseFloat(amount)); 205 } 206 207 // Update hidden preset amount inputs - enable only the selected one 208 $('.preset-amount-input').each(function() { 209 $(this).prop('disabled', $(this).val() != amount); 210 }); 211 } 212 }); 213 214 // Handle custom amount input changes 215 customAmountInput.on('input', function() { 216 const amount = parseFloat($(this).val()); 217 if (amount > 0) { 218 updateAmountInfoBox(amount); 219 } else { 220 updateAmountInfoBox(0); 221 } 222 }); 223 224 customAmountInput.on('focus', function() { 225 // Only auto-select custom if not already selected 226 if (!$('.amount-type-radio[value="custom"]').prop('checked')) { 227 // Uncheck all preset radios and re-enable them 228 $('.amount-type-radio[value="preset"]').each(function() { 229 $(this).prop('checked', false).prop('disabled', false); 230 }); 231 // Check custom radio 232 $('.amount-type-radio[value="custom"]').prop('checked', true); 233 // Disable all preset inputs 234 $('.preset-amount-input').prop('disabled', true); 235 toggleCustomInput(true); 236 } 237 }); 238 239 // Handle clicking on amount cards to ensure proper radio selection 240 $('.amount-option').on('click', function(e) { 241 const radio = $(this).find('.amount-type-radio'); 242 if (radio.length) { 243 // Trigger the radio button if not already checked 244 if (!radio.prop('checked')) { 245 radio.prop('checked', true).trigger('change'); 246 } 247 } 248 }); 249 250 // Handle clicking on custom amount link 251 $('.custom-amount-link-wrapper').on('click', function(e) { 252 e.preventDefault(); 253 const radio = $(this).find('.amount-type-radio'); 254 if (radio.length && !radio.prop('checked')) { 255 radio.prop('checked', true).trigger('change'); 256 } 257 }); 258 259 // Custom Country Code Dropdown Functionality 260 const countryDropdown = { 261 init: function() { 262 const trigger = $('.country-code-trigger'); 263 const menu = $('.country-code-menu'); 264 const search = $('.country-search'); 265 const items = $('.country-item'); 266 const hiddenInput = $('#phone_country_code'); 267 268 // Toggle dropdown - use mousedown to prevent form control focus issues 269 trigger.on('mousedown', function(e) { 270 e.preventDefault(); 271 e.stopPropagation(); 272 }); 273 274 trigger.on('click', function(e) { 275 e.preventDefault(); 276 e.stopPropagation(); 277 const isOpen = menu.hasClass('show'); 278 279 if (isOpen) { 280 countryDropdown.close(); 281 } else { 282 countryDropdown.open(); 283 } 284 }); 285 286 // Search functionality 287 search.on('input', function() { 288 const searchTerm = $(this).val().toLowerCase(); 289 290 items.each(function() { 291 const countryName = $(this).find('.country-name').text().toLowerCase(); 292 const countryCode = $(this).find('.country-code').text().toLowerCase(); 293 294 if (countryName.includes(searchTerm) || countryCode.includes(searchTerm)) { 295 $(this).removeClass('hidden'); 296 } else { 297 $(this).addClass('hidden'); 298 } 299 }); 300 }); 301 302 // Select country 303 items.on('click', function(e) { 304 e.preventDefault(); 305 e.stopPropagation(); 306 307 const code = $(this).data('code'); 308 const iso = $(this).data('iso'); 309 310 // Update hidden input 311 hiddenInput.val(code); 312 313 // Update trigger button 314 trigger.find('img.flag').attr('class', 'flag flag-' + iso); 315 trigger.find('.country-code-value').text(code); 316 317 // Update active state 318 items.removeClass('active'); 319 $(this).addClass('active'); 320 321 // Close dropdown 322 countryDropdown.close(); 323 324 // Focus phone number input 325 $('#customer_phone_number').focus(); 326 }); 327 328 // Close on outside click 329 $(document).on('click', function(e) { 330 if (!$(e.target).closest('.country-code-dropdown').length) { 331 countryDropdown.close(); 332 } 333 }); 334 335 // Close on escape key 336 $(document).on('keydown', function(e) { 337 if (e.key === 'Escape') { 338 countryDropdown.close(); 339 } 340 }); 341 342 // Prevent dropdown from closing when clicking inside 343 menu.on('click', function(e) { 344 e.stopPropagation(); 345 }); 346 }, 347 348 open: function() { 349 const trigger = $('.country-code-trigger'); 350 const menu = $('.country-code-menu'); 351 const search = $('.country-search'); 352 353 console.log('Opening dropdown, menu element:', menu); 354 console.log('Has show class before:', menu.hasClass('show')); 355 menu.addClass('show'); 356 console.log('Has show class after:', menu.hasClass('show')); 357 console.log('Menu display:', menu.css('display')); 358 trigger.attr('aria-expanded', 'true'); 359 360 // Focus search input 361 setTimeout(function() { 362 search.focus(); 363 }, 100); 364 }, 365 366 close: function() { 367 const trigger = $('.country-code-trigger'); 368 const menu = $('.country-code-menu'); 369 const search = $('.country-search'); 370 371 menu.removeClass('show'); 372 trigger.attr('aria-expanded', 'false'); 373 374 // Clear search 375 search.val(''); 376 $('.country-item').removeClass('hidden'); 377 } 378 }; 379 380 // Initialize country dropdown 381 countryDropdown.init(); 382 383 // Format phone number input 384 $('#customer_phone_number').on('input', function() { 385 let value = $(this).val().replace(/[^\d\s\-]/g, ''); 386 $(this).val(value); 387 }); 388 389 // Form validation 390 $('#couponForm').on('submit', function(e) { 391 const amountType = $('.amount-type-radio:checked'); 392 393 if (!amountType.length) { 394 e.preventDefault(); 395 alert('PraÅ¡ome pasirinkti kortelÄs sumÄ '); 396 return false;
397 } 398 399 if (!window._couponTurnstileVerified) { 400 e.preventDefault(); 401 $('#coupon-turnstile-error').show(); 402 return false; 403 } 404 405 if (amountType.val() === 'custom') { 406 const customAmount = parseFloat(customAmountInput.val()); 407 const minAmount = parseFloat(customAmountInput.attr('min')); 408 const maxAmount = parseFloat(customAmountInput.attr('max')); 409 410 if (!customAmount || customAmount < minAmount || customAmount > maxAmount) { 411 e.preventDefault(); 412 alert(`Suma turi bÅ«ti nuo â¬${minAmount} iki â¬${maxAmount}`); 413 return false; 414 } 415 } 416 417 // Show loading state 418 const submitBtn = $(this).find('button[type="submit"]'); 419 submitBtn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin me-2"></i>Kraunama...'); 420 }); 421});
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.