1 2 3<!DOCTYPE html> 4<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> 5<head> 6
6<script> 7 var isAppWebView = false; 8 9 try { 10 var ua = navigator.userAgent || ''; 11 if (ua.includes('wv') || ua.includes('WebView') || ua.includes('BrustersApp')) { 12 isAppWebView = true; 13 } 14 } catch(e){} 15 </script>
15 16 17
17<script> 18 19 20 21 window.dataLayer = window.dataLayer || []; 22 function gtag(){dataLayer.push(arguments);} 23 24 if(!isAppWebView){ 25 gtag('consent','default',{ 26 ad_user_data:'denied', 27 ad_personalization:'denied', 28 ad_storage:'denied', 29 analytics_storage:'denied', 30 wait_for_update:500 31 }); 32 } 33 </script>
33 34 35 <!-- Google Tag Manager --> 36
36<script>
vendor: 364 bytes, lines 36-40
36(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 37 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 38 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 39 ' https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 40 })(window,document,'script','dataLayer','
40GTM-WH48QBD
vendor: 3 bytes, line 40
40');
40</script>
40 41 <!-- End Google Tag Manager --> 42 43
43<script> 44 if (!isAppWebView) { 45 var s = document.createElement('script'); 46 s.id = 'Cookiebot'; 47 s.src = 'https://consent.cookiebot.com/uc.js'; 48 s.async = true; 49 s.setAttribute('data-cbid', '43aabde7-00fb-4fc3-839c-309c4d1aa2fb'); 50 document.head.appendChild(s); 51 } 52 </script>
52 53 54 55 56 <meta property="og:description" content="Order online from your local Bruster's Ice Cream! From custom ice cream cakes to sundaes, order your favorite treats today." /><meta property="og:title" content="Bruster's - Order Ice Cream Online" /><meta property="og:image" content="https://brustersazure.blob.core.windows.net/onlineproductimages/1400x800_Category-SundaesV2.png" /><meta property="og:type" content="website" /><meta property="og:url" content="https://brustersonline.com/BrustersCom/Cart.aspx" /><meta property="og:site_name" content="Bruster's Real Ice Cream" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="Bruster's - Order Online" /><meta name="twitter:description" content="Order online from your local Bruster's Ice Cream! From custom ice cream cakes to sundaes, order your favorite treats today." /><meta name="twitter:image" content="https://brustersazure.blob.core.windows.net/onlineproductimages/1400x800_Category-SundaesV2.png" /><meta name="description" content="Order online from your local Bruster's Ice Cream! Cakes, sundaes, and more available for pickup." /><meta name="robots" content="index, follow" /><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title> 57 Bruster's - Order Ice Cream Online 58</title><link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" /><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" /><link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet" /><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@eonasdan/[email protected]/dist/css/tempus-dominus.min.css" crossorigin="anonymous" /> 59
59<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
59 60
60<script src="https://web.squarecdn.com/v1/square.js"></script>
60 61
61<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" crossorigin="anonymous"></script>
61 62
62<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
62 63
63<script src="https://cdn.jsdelivr.net/npm/@eonasdan/[email protected]/dist/js/tempus-dominus.min.js" crossorigin="anonymous"></script>
63 64
64<script src="/BrustersCom/js/locationsearchV2.js?v=11"></script>
64 65 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> 66
66<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
66 67 <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css" /> 68
68<script src="js/onlineordering-square-sdk-v2.js?v=118"></script>
68 69 <link href="BrustersCom/css/sqPaymentForm.css" rel="stylesheet" /> 70
70<script src="js/gtm.js?v=12"></script>
70 71 <style> 72 /* ADA - improved link contrast */ 73.btn-link { 74 color: #0367FC; 75} 76 77/* ADA - visible keyboard focus */ 78.btn:focus-visible { 79 outline: 3px solid #0367FC; 80 outline-offset: 2px; 81 box-shadow: 0 0 0 0.25rem rgba(3, 103, 252, 0.25); 82} 83 84 .gift-card-warning { 85 color: #990000; 86} 87 88 .header-logo-link:focus { 89 box-shadow: none; 90} 91 92.header-logo-link:focus-visible { 93 outline: none; 94 outline-offset: 3px; 95} 96 .menu-section button:focus { 97 box-shadow: none; 98} 99 100.menu-section button:focus:not(:focus-visible) { 101 outline: none; 102} 103 104.promo-label { 105 display: block; 106 margin-top: 4px; 107 font-size: 11px; 108 font-weight: 600; 109 color: #c0392b; 110} 111 112.promo-original { 113 font-weight: 400; 114 color: #555; 115} 116 117 .modal-header, .modal-footer { 118 background-color: #990000; 119 color: #ffffff; 120 } 121 .modal-header .btn-close { 122 filter: invert(1); /* This inverts the color of the close button to make it visible on the dark background */ 123 } 124.flavor-group-heading { 125 background-color: #f8f9fa; 126 color: #990000; 127 font-weight: bold; 128 padding: 10px; 129 border-radius: 5px; 130 margin-bottom: 10px; 131 text-align: center; 132 font-size: 1.2em; 133} 134 135 .fa-eye { 136 cursor: pointer; 137 } 138 .menu-section { 139 margin-bottom: 2rem; 140 } 141 /* Default shadow for all menu-img */ 142/* Default styling for all menu-img */ 143.menu-img { 144 max-width: 325px; 145 height: auto; 146 display: block; 147 margin-left: auto; 148 margin-right: auto; 149 cursor: pointer; 150 border-radius: 12px; 151 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Subtle shadow */ 152 transition: transform 0.3s ease, box-shadow 0.3s ease; 153} 154 155.menu-img:hover { 156 transform: scale(1.05); 157 box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); 158} 159 160/* Styling for menu-img inside the modal */ 161.modal .menu-img { 162 box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); /* Increased shadow */ 163 background-color: #f7f7f7; /* Set the background color */ 164 padding: 10px; /* Add some padding to make the background more visible */ 165 border-radius: 12px; /* Ensure rounded corners are still applied */ 166} 167 168.modal .menu-img:hover { 169 box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3); /* Even more shadow on hover */ 170} 171 172/* Styling for menu-img inside the modal */ 173.card-img-top { 174 box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); /* Increased shadow */ 175 background-color: #f7f7f7; /* Set the background color */ 176 padding: 10px; /* Add some padding to make the background more visible */ 177 border-radius: 12px; /* Ensure rounded corners are still applied */ 178 transition: transform 0.3s ease, box-shadow 0.3s ease; 179} 180 181.card-img-top:hover { 182 box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3); /* Even more shadow on hover */ 183} 184 185 186 187 .selected-modifier { 188 background-color: lightgreen; 189 } 190 .section-links { 191 white-space: nowrap; 192 margin-bottom: 1rem; 193 overflow-x: auto; /* Enable horizontal scrolling */ 194 -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */ 195 scrollbar-width: auto; /* Ensure scrollbar is visible in Firefox */ 196 scrollbar-color: #990000 #f1f1f1; /* Customize scrollbar color in Firefox */ 197 } 198 199 .section-links::-webkit-scrollbar { 200 height: 8px; /* WebKit-based browsers */ 201 } 202 203 .section-links::-webkit-scrollbar-thumb { 204 background-color: #990000; /* WebKit-based browsers */ 205 border-radius: 4px; /* WebKit-based browsers */ 206 } 207 208 .section-links::-webkit-scrollbar-track { 209 background-color: #f1f1f1; /* WebKit-based browsers */ 210 border-radius: 4px; /* WebKit-based browsers */ 211 } 212 213 @media (max-width: 768px) { 214 .header-logo { 215 max-width: 150px; 216 } 217 } 218 219 .section-link { 220 display: inline-block; 221 padding: 0.5rem 1rem; 222 margin-right: 0.5rem; 223 background-color: #f8f9fa; 224 border: 1px solid #dee2e6; 225 border-radius: 0.25rem; 226 cursor: pointer; 227 text-decoration: none; 228 color: #000; 229 } 230 .cart { 231 position: fixed; 232 top: 100px; 233 right: 20px; 234 width: 300px; 235 background-color: white; 236 box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); 237 padding: 15px; 238 z-index: 1000; 239 } 240 .cart-header { 241 font-weight: bold; 242 margin-bottom: 10px; 243 } 244 .cart-item { 245 display: flex; 246 justify-content: space-between; 247 align-items: center; 248 margin-bottom: 10px; 249 } 250 .cart-item-details { 251 flex: 1; 252 padding-right: 10px; 253 } 254 .cart-item button { 255 align-self: flex-start; 256 } 257 .cart-subtotal { 258 font-weight: bold; 259 margin-top: 10px; 260 padding-top: 10px; 261 border-top: 1px solid #dee2e6; 262 padding-left: 15px; 263 } 264 .cart-tax { 265 font-weight: bold; 266 margin-top: 10px; 267 padding-top: 10px; 268 border-top: 1px solid #dee2e6; 269 padding-left: 15px; 270 } 271 .cart-total { 272 font-weight: bold; 273 margin-top: 10px; 274 padding-top: 10px; 275 border-top: 1px solid #dee2e6; 276 padding-left: 15px; 277 } 278 .popper-image { 279 background: white; 280 border: 1px solid #ccc; 281 padding: 10px; 282 max-width: 300px; 283 z-index: 2000; 284 text-align: center; 285 position: fixed; 286 top: 50%; 287 left: 50%; 288 transform: translate(-50%, -50%); 289 } 290 .popper-image img { 291 max-width: 100%; 292 height: auto; 293 } 294 @media (min-width: 768px) { 295 .popper-image { 296 max-width: 300px; 297 } 298 } 299 @media (max-width: 767px) { 300 .popper-image { 301 max-width: 200px; 302 } 303 } 304 .popper-close { 305 position: absolute; 306 top: 5px; 307 right: 5px; 308 cursor: pointer; 309 } 310 .popper-alert { 311 position: absolute; 312 z-index: 1055; /* higher than Bootstrap's modal z-index of 1050 */ 313 background-color: #dc3545; /* Bootstrap danger color */ 314 color: white; 315 padding: 10px; 316 border-radius: 5px; 317 } 318 .hidden-items { 319 display: none; 320 } 321 .header-custom { 322 background-color: #990000; 323 display: flex; 324 align-items: center; 325 justify-content: space-between; 326 padding: 10px; 327 color: white; 328 } 329 .header-logo { 330 max-width: 200px; 331 height: auto; 332 } 333 334 @media (max-width: 768px) { 335 .header-logo { 336 max-width: 150px; 337 } 338 } 339 340 .cart-icon { 341 position: relative; 342 color: white; 343 padding: 0; 344 padding-right: 20px; 345 margin: 0; 346 background: none; 347 border: none; 348 font: inherit; 349 appearance: none; 350 -webkit-appearance: none; 351 cursor: pointer; 352 } 353 354 .cart-icon:focus { 355 box-shadow: none; 356 } 357 358 .cart-icon:focus:not(:focus-visible) { 359 outline: none; 360 } 361 .badge-custom { 362 position: absolute; 363 top: 0; 364 start: 100%; 365 transform: translate(-50%, -50%); 366 background-color: white; 367 color: #990000; 368 } 369 .address-display { 370 font-size: 1.2em; 371 font-weight: bold; 372 color: #990000; 373 padding: 10px; 374 border: 2px solid #990000; 375 border-radius: 5px; 376 text-align: center; 377 background-color: #fff5f5; 378 margin-bottom:8px; 379 } 380 /* Optional: Additional custom styling for the rounded header logo */ 381 .header-custom.rounded { 382 border-radius: 0.4rem; /* Adjust as needed */ 383 } 384 .monthly-features img { 385 margin-bottom: 1rem; /* Add space between the images and below */ 386} 387 388.bg-image { 389 background-image: url('https://brusters.imgix.net/SMPost_24Jul_CookieJarCrunch.jpg?fit=clamp&w=1920&h=1080'); 390 height: 100vh; 391 width: 100vw; 392 opacity: 0.5; /* Adjust opacity for watermark effect */ 393 background-size: cover; /* Ensure the background image covers the entire element */ 394 background-position: center top; /* Center the background image horizontally */ 395 background-repeat: no-repeat; /* Prevent the background image from repeating */ 396 position: absolute; 397 top: 0; 398 left: 0; 399 z-index: -1; /* Ensure the background image is behind the content */ 400} 401 402.landing-header-custom { 403 background-color: #990000; /* Same color as the header-custom div */ 404 color: white; 405 text-align: center; 406 padding: 10px 0; 407 width: 100%; 408 position: relative; 409 z-index: 2; 410} 411 412.landing-header-custom h2 { 413 margin: 0; 414 font-size: 2rem; /* Adjust the font size as needed */ 415} 416 417.landing-content { 418 position: absolute; 419 top: 50%; 420 left: 50%; 421 transform: translate(-50%, -50%); 422 text-align: center; 423 color: white; 424 z-index: 1; 425} 426 427.landing-content img { 428 max-width: 200px; 429 margin-bottom: 20px; 430} 431 432.landing-content .search-section { 433 background-color: rgba(0, 0, 0, 0.5); 434 padding: 20px; 435 border-radius: 10px; 436} 437 438.landing-content .search-section input, 439.landing-content .search-section button { 440 margin: 10px 0; 441} 442 443.landing-content .search-section .or-separator { 444 margin: 10px 0; 445} 446 447.landing-page { 448 display: block; /* Ensure the landing page is visible */ 449} 450.img-thumbnail { 451 max-width: 150px; 452 height: auto; 453} 454 455#divstores .list-group-item { 456 padding: 20px; 457 background-color: #fff; 458 border: 1px solid #ddd; 459 border-radius: 10px; 460 margin-bottom: 10px; 461} 462 463 464 .main-content { 465 display: none; 466 } 467 468 /* Absolute Center Spinner */ 469.loading { 470 position: fixed; 471 z-index: 999; 472 height: 2em; 473 width: 2em; 474 overflow: show; 475 margin: auto; 476 top: 0; 477 left: 0; 478 bottom: 0; 479 right: 0; 480} 481 482 /* Transparent Overlay */ 483 .loading:before { 484 content: ''; 485 display: block; 486 position: fixed; 487 top: 0; 488 left: 0; 489 width: 100%; 490 height: 100%; 491 background-color: rgba(0,0,0,0.3); 492 } 493 494 /* :not(:required) hides these rules from IE9 and below */ 495 .loading:not(:required) { 496 /* hide "loading..." text */ 497 font: 0/0 a; 498 color: transparent; 499 text-shadow: none; 500 background-color: transparent; 501 border: 0; 502 } 503 504 .loading:not(:required):after { 505 content: ''; 506 display: block; 507 font-size: 10px; 508 width: 1em; 509 height: 1em; 510 margin-top: -0.5em;
511 -webkit-animation: spinner 1500ms infinite linear; 512 -moz-animation: spinner 1500ms infinite linear; 513 -ms-animation: spinner 1500ms infinite linear; 514 -o-animation: spinner 1500ms infinite linear; 515 animation: spinner 1500ms infinite linear; 516 border-radius: 0.5em; 517 -webkit-box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.5) -1.5em 0 0 0, rgba(0, 0, 0, 0.5) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0; 518 box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) -1.5em 0 0 0, rgba(0, 0, 0, 0.75) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0; 519 } 520 521 522 523/* Animation */ 524 525@-webkit-keyframes spinner { 526 0% { 527 -webkit-transform: rotate(0deg); 528 -moz-transform: rotate(0deg); 529 -ms-transform: rotate(0deg); 530 -o-transform: rotate(0deg); 531 transform: rotate(0deg); 532 } 533 534 100% { 535 -webkit-transform: rotate(360deg); 536 -moz-transform: rotate(360deg); 537 -ms-transform: rotate(360deg); 538 -o-transform: rotate(360deg); 539 transform: rotate(360deg); 540 } 541} 542 543@-moz-keyframes spinner { 544 0% { 545 -webkit-transform: rotate(0deg); 546 -moz-transform: rotate(0deg); 547 -ms-transform: rotate(0deg); 548 -o-transform: rotate(0deg); 549 transform: rotate(0deg); 550 } 551 552 100% { 553 -webkit-transform: rotate(360deg); 554 -moz-transform: rotate(360deg); 555 -ms-transform: rotate(360deg); 556 -o-transform: rotate(360deg); 557 transform: rotate(360deg); 558 } 559} 560 561@-o-keyframes spinner { 562 0% { 563 -webkit-transform: rotate(0deg); 564 -moz-transform: rotate(0deg); 565 -ms-transform: rotate(0deg); 566 -o-transform: rotate(0deg); 567 transform: rotate(0deg); 568 } 569 570 100% { 571 -webkit-transform: rotate(360deg); 572 -moz-transform: rotate(360deg); 573 -ms-transform: rotate(360deg); 574 -o-transform: rotate(360deg); 575 transform: rotate(360deg); 576 } 577} 578 579@keyframes spinner { 580 0% { 581 -webkit-transform: rotate(0deg); 582 -moz-transform: rotate(0deg); 583 -ms-transform: rotate(0deg); 584 -o-transform: rotate(0deg); 585 transform: rotate(0deg); 586 } 587 588 100% { 589 -webkit-transform: rotate(360deg); 590 -moz-transform: rotate(360deg); 591 -ms-transform: rotate(360deg); 592 -o-transform: rotate(360deg); 593 transform: rotate(360deg); 594 } 595} 596 597/* Add styles for the storelist-page */ 598.storelist-page .bg-image { 599 background-image: url('https://brusters.imgix.net/SMPost_24Jul_CookieJarCrunch.jpg?fit=clamp&w=1920&h=1080'); 600 height: 100vh; 601 width: 100vw; 602 opacity: 0.5; 603 background-size: cover; 604 background-position: center top; 605 background-repeat: no-repeat; 606 position: absolute; 607 top: 0; 608 left: 0; 609 z-index: -1; 610} 611 612.storelist-page .landing-header-custom { 613 background-color: #990000; 614 color: white; 615 text-align: center; 616 padding: 10px 0; 617 width: 100%; 618 position: relative; 619 z-index: 1; 620 border-radius: 10px; 621} 622 623.storelist-page .landing-header-custom h2 { 624 margin: 0; 625 font-size: 2rem; 626} 627 628#divstores { 629 position: relative; 630 z-index: 1; 631 padding: 20px; 632} 633 634.small-font { 635 font-size: 0.8em; 636 } 637 .font-weight-bold { 638 font-weight: bold; 639 } 640 .featured-product-card { 641 max-width: 200px; 642 flex: 0 0 auto; 643 } 644 645 @media (min-width: 768px) { 646 .d-flex.flex-nowrap.overflow-auto { 647 flex-wrap: wrap; 648 justify-content: space-around; 649 } 650 651 .featured-product-card { 652 flex: 1 1 calc(25% - 1rem); /* Adjust the card width and margin */ 653 margin: 0.5rem; 654 } 655 } 656 657 658 659 </style> 660</head> 661<body> 662 <!-- Google Tag Manager (noscript) -->
vendor: 74 bytes, lines 662-663
662 663 <noscript><iframe src=" https://www.googletagmanager.com/ns.html?id=
663GTM-WH48QBD
vendor: 94 bytes, lines 663-665
663 " 664 height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 665
665<!-- End Google Tag Manager (noscript) --> 666 <form method="post" action="./" id="form1"> 667<div class="aspNetHidden"> 668<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> 669<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" /> 670<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTcwMjU4MDc2NA8WAh4TVmFsaWRhdGVSZXF1ZXN0TW9kZQIBZGRRCn7+/lAXA4OB8fqlj9UqzOa9Jw1dZtHeiJlpD9Noyw==" /> 671</div> 672
673<script type="text/javascript"> 674//<![CDATA[ 675var theForm = document.forms['form1']; 676if (!theForm) { 677 theForm = document.form1; 678} 679function __doPostBack(eventTarget, eventArgument) { 680 if (!theForm.onsubmit || (theForm.onsubmit() != false)) { 681 theForm.__EVENTTARGET.value = eventTarget; 682 theForm.__EVENTARGUMENT.value = eventArgument; 683 theForm.submit(); 684 } 685} 686//]]> 687</script>
687 688 689 690<div class="aspNetHidden"> 691 692 <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="15ECB9E0" /> 693 <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAFekkV+DNU9eNxtxHNgjOdjtwMbEJOwoVsSLDYPCE3G5HAAHZ+oVTfVxaqyuu9G/+n/3yreNFS8E/KpKYycKDEdkArt8ewAGDocERgSDWjTDRDWGGcLRc5vhLnlg3wW1MFTiE0/pr75ubVHBQmRK1hekzJtHovtyCS8eyTZCoFS3Q33Q/QLw+64IfQ/VF988mXffk3WNBwaNsoC/U/z/8VHjxEKD3SGWH/spCzyg42d8Qnj5SxjWapftyn1PJ53zbTwsnjzlxcwhPvvR2JwBgH13gHgEtgrdorlL4mGUyycpDzEqinOUKRa9CHfcuTgKbTEUS6BsSCxR7BYkWQs/wefeBMhqnz1crXMAb0Kt86cXIw8X1WIY7quvlGDtamUrNe7rHWS9YE1KiDIm00kjm894XfWKPZQ5yuO46xuKz5yPKEe8PZtCZYA/Ig4wO40kojCUenwv0sI6O+cV9KaCOcSyU9en8BECpqsW//1zoNRaa7Hc5KAJlSXv8PyjU4Lm2KYTUmVOiL8gXVPuefeS9AeUKNRpLLqEymPEWA+FRoPlLXOQRruLm4QuDEHoNUBPfjYAtv9olsPBTOgeh76nLyhxBbT1WKbwmYt2RCi9JBdL7Sp9ewXDxw1y+IUtOYkW8Ec1HENTVCXJKzEHUkHCxUfsRP15AOtlKL443Sqtx+vT8L58wD/vUMbhFGiCoAUsFVM5CAisSSg7eREwOgNwDB9x4zVTVHJuoads5e5hpNZ6nC/PUU09b7gWsldi1Lp4dQaX/glL6PCPewB4sRc8wptly3nvt2biflv6NfnwLpd4nCNpj3CUe4FAg9xquwgSMaHYpEJmN2nKi265p+50r4zGydHldIiRntTcaDKHRM1LtQNIAPeML3ZloNoImF+Y++5WRILd7tsFn06Mxiu9xp7Pi/TUeKbumH0eckUmQVSXfkvbXHZ1IxGG21l9t53Ya2SA28+1OaivfuaUakd/8ZMv8/jOOml9MQSHhqjIXNMVDi2612+Vyn+hKpiiwKdGIZ0i3tIIsOPjiXQSqQ9LgLqJaFJ6YBb7MV/smKXs+uzf/XHoLm1pbNQ1Wf+ZGjiDSeI6wUW+5WkWQTH+RHB2bAmnRe/6TpoJYPK1ETJ6muL1E6dp/EngC+ZspMFa+zUCHPu1KRIgO+mlYqmtLv4aUDzYJCO7kWocGf1EFvmssudlM5PhTOLX1wxlXnZRUHPivIDmjMswxyGe9ovM0HMWABz4pjsUVclid8USf95gJT5HLD+aPFeX46kNIe/FLwmImBtWB499TNKmHmHpwQxpKO9IuJQ9vkk4qmyriwDEG2ibJLQL/vQXEEKqTBAvV8opg/CYRUJtm3/JgaF0vghvSdUfO+ry4ztPVndwnoGJi5z93TqzQ/psN0pqmEr0IY1BTviieQhio2oswFtUAIWcBdVHqcfY9lCGHJffKo+cU2sURbmkvSpp4hlkykV2ruMKnBGsMyqYsBZMw4mHg2REVMkfFHFtjT3D6oO/VqUGKaMSrFA/MAwvnR3yYR4gIaAR1u+7gZ52+RmS45olUWTd5dZ4cGGdlbIs+C2SCtzpH8PtakI+3fo5gtDLzlRFUEzotEHAzZKyUmM/yqZ57c9IYiRff+lUzBflnd13gQJb2HUnjebZEypAjanRHVKRYwAIKvsz4oVEIuJfzufJUaY24FjgmUCXO/JNwTrexzlBqwjJnEEsf/ad8ONqeh+s7YGol6Y7HTZJrbYSQgaVr2+6HNjTES8hyJTnu9R/DaSAEbCthL5CCTaygXvrU88QYi2tZx7LeJk0RbFLah31K3kUbjeT2T8zLrQopZYSyTsy+Xy+qKbBe5mC5hM3L6RZ8jbuYsOpISLofszdfnSA2suj9bqkcxpqzs64WlejnBxYfYzJ1A==" /> 694</div> 695 <input type="hidden" name="hfKiosk" id="hfKiosk" value="False" /> 696 <input type="hidden" name="hfNumber" id="hfNumber" /> 697 <input type="hidden" name="hfEvoAuthKey" id="hfEvoAuthKey" /> 698 <input type="hidden" name="hfEvoCode" id="hfEvoCode" /> 699 <input type="hidden" name="hfRoundedPaymentAmount" id="hfRoundedPaymentAmount" /> 700 <input type="hidden" name="hfExistingYum" id="hfExistingYum" value="False" /> 701 <input type="hidden" name="hfYumBalance" id="hfYumBalance" /> 702 <input type="hidden" name="hfYumLoadOnly" id="hfYumLoadOnly" value="False" /> 703 <input type="hidden" name="hfYumDiscountOnLoad" id="hfYumDiscountOnLoad" /> 704 <input type="hidden" name="hfAddBalanceAmount" id="hfAddBalanceAmount" value="
7040" /> 705 <input type="hidden" name="hfPin" id="hfPin" /> 706 <input type="hidden" name="hfSquareResponse" id="hfSquareResponse" /> 707 <input type="hidden" name="hfIsEvent" id="hfIsEvent" value="False" /> 708 <input type="hidden" name="hfEventDate" id="hfEventDate" /> 709 <input type="hidden" name="hfEventTime" id="hfEventTime" /> 710 <input type="hidden" name="hfEventArray" id="hfEventArray" /> 711 <input type="hidden" name="hfExtras" id="hfExtras" /> 712 <input type="hidden" name="hfItemList" id="hfItemList" value="[]" /> 713 <input type="hidden" name="hfTipOnly" id="hfTipOnly" value="False" /> 714 <input type="hidden" name="hfState" id="hfState" /> 715 <input type="hidden" name="hfOnlineFundraising" id="hfOnlineFundraising" /> 716 <input type="hidden" name="hfPieCrusts" id="hfPieCrusts" value="[]" /> 717 <input type="hidden" name="hfPickupOnly" id="hfPickupOnly" value="Yes" /> 718 <input type="hidden" name="hfDeliveryDate" id="hfDeliveryDate" /> 719 <input type="hidden" name="hfDeliveryInstructions" id="hfDeliveryInstructions" /> 720 <input type="hidden" name="hfOrphanCakes" id="hfOrphanCakes" value="False" /> 721 <input type="hidden" name="hfGifts" id="hfGifts" value="False" /> 722 <input type="hidden" name="hfSundaes" id="hfSundaes" value="False" /> 723 <input type="hidden" name="hfShakes" id="hfShakes" value="False" /> 724 <input type="hidden" name="hfKits" id="hfKits" value="False" /> 725 <input type="hidden" name="hfTakeHome" id="hfTakeHome" value="False" /> 726 <input type="hidden" name="hfNathans" id="hfNathans" value="False" /> 727 <input type="hidden" name="hfCustomCakes" id="hfCustomCakes" value="False" /> 728 <input type="hidden" name="hfCurbside" id="hfCurbside" value="False" /> 729 <input type="hidden" name="hfCurbsideSelected" id="hfCurbsideSelected" value="False" /> 730 <input type="hidden" name="hfVehicleDescription" id="hfVehicleDescription" /> 731 <input type="hidden" name="hfFlavorImages" id="hfFlavorImages" /> 732 <input type="hidden" name="hfEGiftLive" id="hfEGiftLive" value="false" /> 733 <input type="hidden" name="hfEGiftStoreLocation" id="hfEGiftStoreLocation" /> 734 <input type="hidden" name="hfEGiftStoreApplicationId" id="hfEGiftStoreApplicationId" /> 735 <input type="hidden" name="hfCustomerId" id="hfCustomerId" /> 736 <input type="hidden" name="hfEGiftOrderId" id="hfEGiftOrderId" value="False" /> 737 <input type="hidden" name="hfActivePromo" id="hfActivePromo" /> 738 <input type="hidden" name="hfPromoProducts" id="hfPromoProducts" value="[]" /> 739 <input type="hidden" name="hfTimeZoneOffset" id="hfTimeZoneOffset" /> 740<div style="display:none !important" class="loading">Loading…</div> 741 742 743 744 <div id="pnlLive"> 745 746 747 <div style="display:none" class="squarepaymentform"> 748 <div id="form-container"> 749 <div class="container"> 750 <div class="landing-header-custom"> 751 <h1>Online Ordering</h1> 752 </div> 753 <!-- Credit Card Payment Form - START --> 754 <div class="row"> 755 <div class="col-12 col-lg-8"> 756 <div class="card"> 757 <div class="card-header"> 758 <div class="row"> 759 <div class="col"> 760 <img class="img-fluid" src="https://brustersonline.com/brustersCom/images/paywithsquare.png" alt="Pay with Square"> 761 </div> 762 </div> 763 </div> 764 <div class="card-body"> 765 <div class="row"> 766 <div id="paymentmessage" class="col-12"></div> 767 </div> 768 <div class="row" id="sqpaymentcontainer"></div> 769 <div class="row" id="sqgcpaymentcontainer"></div> 770 <div id="payment-status-container"></div> 771 </div> 772 <div class="card-footer"> 773 <div class="row"> 774 <div class="col-12">
775 <span class="storedisclaimer"></span> 776 <div style="display:flex;flex-direction:column;justify-content:space-between" class="paymentcontainer"> 777 <div class="creditcardpaymentbutton"><button type="button" id="sq-creditcard" class="button-credit-card paymentform">Pay Now</button></div> 778 <div class="creditcardpayment"> <div id="sq-google-pay" class="button-google-pay2 paymentform"></div></div> 779 <div class="applepaycontainer creditcardpayment"> <button 780 type="button" 781 class="apple-pay-button2 apple-pay-button-black paymentform" 782 id="sq-apple-pay" 783 aria-label="Pay with Apple Pay"> 784 </button></div> 785 <!-- â Disclaimer --> 786 <div class="mt-3 text-center"> 787 <small class="gift-card-warning fw-semibold"> 788 Gift Cards are not able to be redeemed for online ordering. 789 </small> 790 </div> 791 </div> 792 <input type="hidden" id="cardNonce" name="nonce" /> 793 <div class="text-danger" id="errors"></div> 794 <div class="text-primary" id="success"></div> 795 <div id="hidebutton" style="display:none"> 796 <input type="button" name="btnTest" value="Button" onclick="javascript:__doPostBack('btnTest','')" id="btnTest" class="testme" /> 797 </div> 798 <input type="hidden" name="hfNonce" id="hfNonce" /> 799 </div> 800 </div> 801 </div> 802 </div> 803 </div> 804 <div class="col-12 col-lg-4"> 805 <div class="card"> 806 <div class="card-header"> 807 <div class="row"> 808 <div class="col-12"> 809 <h3>ORDER DETAILS</h3> 810 </div> 811 </div> 812 </div> 813 <div class="card-body"> 814 <div class="row"> 815 <div id="customerInfo" class="col-12"></div> 816 </div> 817 <div class="row"> 818 819 <div id="orderDetailsCheckout" class="col-12"></div> 820 </div> 821 </div> 822 <div class="card-footer"> 823 <div class="row"></div> 824 </div> 825 </div> 826 </div> 827 </div> 828 <!-- Credit Card Payment Form - END --> 829 </div> 830 </div> 831</div> 832 833 834<div id="divstoresclone" style="display:none" class="list-group"> 835 <div class="list-group-item d-flex flex-column flex-md-row"> 836 <div class="d-flex flex-shrink-0"> 837 <img class="img-fluid img-thumbnail" alt="Map showing the location of [STORE NAME]" src="[MAPADDRESS]" style="max-width: 150px;"> 838 </div> 839 <div class="flex-grow-1 ms-3 position-relative"> 840 <h2 class="mb-1" style="font-weight:bold;font-size:1.5em">[STORE NAME]</h2> 841 <p class="mb-1" style="font-weight:bold;font-size:1.0em">[STORE ADDRESS]</p> 842 <p class="mb-1" style="font-weight:bold;font-size:1.2em">[STORE PHONE]</p> 843 <p class="mb-1" style="font-weight:bold;font-size:1.0em">[MILES AWAY]</p> 844 <!-- Button for larger screens --> 845 <a href="[ONLINEURL]" class="btn btn-primary position-absolute top-0 end-0 mt-2 me-2 d-none d-md-block">Order</a> 846 <!-- Button for smaller screens --> 847 <a href="[ONLINEURL]" class="btn btn-primary mt-2 d-block d-md-none">Order</a> 848 </div> 849 </div> 850</div> 851 852 853<div id="landingPage" class="landing-page"> 854 <div class="bg-image" id="bg-image"></div> 855 <div class="landing-header-custom"> 856 <h1>Online Ordering</h1> 857 </div> 858 <div class="landing-content"> 859 <a class="header-logo-link" href="/BrustersCom/CartV2.aspx" 860 aria-label="Restart online ordering"> 861 <img src="https://bic.blob.core.windows.net/departments/marketing/Graphics/Logos/brusterslogo-twocolor.png" 862 alt="Bruster's Logo" 863 class="header-logo" /> 864</a> 865 <div class="search-section"> 866 <h2>Find My Bruster's</h2> 867 <label for="zipcode">Zip Code</label> 868 869 <input type="text" 870 style="text-align:center" 871 id="zipcode" 872 class="form-control" 873 placeholder="Zip Code"> 874 <button id="searchButton" 875 type="button" 876 class="btn btn-secondary"> 877 Search 878 </button> 879 <div class="or-separator">OR</div> 880 <button id="useMyLocation" type="button" class="btn btn-primary">Use My Location</button> 881 </div> 882 </div> 883</div> 884 885<div id="storelist" class="storelist-page container" style="display:none"> 886 <div class="landing-header-custom"> 887 <h1>Order Online</h1> 888 </div> 889 <div id="divstores" class="list-group"></div> 890</div> 891
892 <main id="mainContent" class="main-content"> 893 <h1 class="visually-hidden">Bruster's Online Ordering Menu</h1> 894 <div class="container mt-1"> 895 <div class="header-custom mb-2 rounded"> 896 <a class="header-logo-link" href="/BrustersCom/CartV2.aspx" 897 aria-label="Restart online ordering"> 898 <img src="https://bic.blob.core.windows.net/departments/marketing/Graphics/Logos/brusterslogo-twocolor.png" 899 alt="Bruster's Logo" 900 class="header-logo" /> 901</a> 902 <button type="button" 903 id="cartIcon" 904 class="cart-icon" 905 aria-label="View shopping cart"> 906 <i class="fas fa-shopping-cart fa-2x" aria-hidden="true"></i> 907 <span id="cartItemCount" 908 class="badge rounded-pill badge-custom" 909 aria-hidden="true">0</span> 910 </button> 911 </div> 912 <div id="addressDisplay" class="address-display mt-1"></div> 913 914 915 <div class="mt-1 mb-3"> 916 <h2 class="text-center">Featured Products</h2> 917 <div class="d-flex flex-nowrap overflow-auto"> 918 919 </div> 920</div> 921 922 923 924 <div id="section-links" class="section-links text-center"></div> 925 <div id="menu-container" class="row row-cols-1 row-cols-md-3 g-4"></div> 926 </div> 927 928 <input type="hidden" name="hiddenFieldMenuData" id="hiddenFieldMenuData" /> 929 <input type="hidden" name="hiddenFieldAddress" id="hiddenFieldAddress" /> 930 <input type="hidden" name="hfStoreLocation" id="hfStoreLocation" /> 931 <input type="hidden" name="hfStoreLevelPromotion" id="hfStoreLevelPromotion" /> 932 <input type="hidden" name="hiddenFieldLeadTime" id="hiddenFieldLeadTime" /> 933 <input type="hidden" name="hfAllowHighRiskPayments" id="hfAllowHighRiskPayments" /> 934 <input type="hidden" name="hfOnlineCorpPromo" id="hfOnlineCorpPromo" /> 935 <input type="hidden" name="hfCorporatePromo" id="hfCorporatePromo" /> 936 <input type="hidden" name="hfStoreClosed" id="hfStoreClosed" /> 937 <input type="hidden" name="hfClosedMessage" id="hfClosedMessage" /> 938 <input type="hidden" name="hfDisabledTimeIntervals" id="hfDisabledTimeIntervals" /> 939 <input type="hidden" name="hfDaysDisabled" id="hfDaysDisabled" /> 940 <input type="hidden" name="hfMaxDateTime" id="hfMaxDateTime" /> 941 <input type="hidden" name="hfNextOpenDateTime" id="hfNextOpenDateTime" /> 942 <input type="hidden" name="hfDatesDisabled" id="hfDatesDisabled" /> 943 <input type="hidden" name="hfCurrentStoreTime" id="hfCurrentStoreTime" /> 944 <input type="hidden" name="hfIsClosed" id="hfIsClosed" /> 945 <input type="hidden" name="hfOpenTimeString" id="hfOpenTimeString" /> 946 <input type="hidden" name="hfOpenHour" id="hfOpenHour" /> 947 <input type="hidden" name="hfClosedHour" id="hfClosedHour" /> 948 <input type="hidden" name="hfEnabledHours" id="hfEnabledHours" /> 949 <input type="hidden" name="hfDisabledHours" id="hfDisabledHours" /> 950 <input type="hidden" name="hfJsonOrder" id="hfJsonOrder" value="[]" /> 951 <input type="hidden" name="hfSquareId" id="hfSquareId" /> 952 <input type="hidden" name="hfSquareLocationId" id="hfSquareLocationId" /> 953 <input type="hidden" name="hfFinalPaymentAmount" id="hfFinalPaymentAmount" /> 954 <input type="hidden" name="hfApplePay" id="hfApplePay" /> 955 <input type="hidden" name="hfSquareAccessToken" id="hfSquareAccessToken" /> 956 <input type="hidden" name="hfMandatoryItems" id="hfMandatoryItems" value="[]" /> 957 <input type="hidden" name="hfDisclaimer" id="hfDisclaimer" /> 958 <input type="hidden" name="hfOrderResponse" id="hfOrderResponse" /> 959 <input type="hidden" name="hfCustomerName" id="hfCustomerName" /> 960 <input type="hidden" name="hfOrderId" id="hfOrderId" /> 961 <input type="hidden" name="hfPaymentAmount" id="hfPaymentAmount" /> 962 <input type="hidden" name="hfSquareFees" id="hfSquareFees" /> 963 <input type="hidden" name="hfTotalDiscountAmount" id="hfTotalDiscountAmount" value="
9630" /> 964 <input type="hidden" name="hfDiscountType" id="hfDiscountType" /> 965 <input type="hidden" name="hfBackgroundImage" id="hfBackgroundImage" value="https://brustersazure.blob.core.windows.net/onlineproductimages/OLO_mainpage-backgroundv2.jpg" /> 966 </main> 967 <footer> 968 <div class="container"> 969 <div class="card border-0 mondelezfooter"> 970 <div class="card-footer small text-center"> 971 OREO, NILLA and NUTTER BUTTER are trademarks of MondelÄz International group, used with permission. 972 </div> 973</div> 974 975</div> 976 </footer> 977 <div class="container my-4"> 978 <div class="card border-danger allergenfooter"> 979 980 <!-- Header --> 981 <div class="card-header text-center fw-bold text-uppercase text-white bg-danger"> 982 Allergen Statement 983 </div> 984 985 <!-- Body --> 986 <div class="card-body text-center"> 987 <p class="mb-3"> 988 Brusterâs products, except non-dairy sorbets and ices, contain 989 <strong class="text-danger">MILK</strong> products. 990 Some products also contain 991 <strong class="text-danger"> 992 EGGS, WHEAT, TREE NUTS, PEANUTS, SOY AND SESAME 993 </strong>. 994 </p> 995 996 <p class="mb-3"> 997 Many of our products are produced or distributed on shared equipment, 998 creating a possibility that 999 <strong class="text-danger text-uppercase"> 1000 any flavor may contain allergens 1001 </strong>. 1002 </p> 1003 1004 <p class="mb-0"> 1005 Full nutritional and ingredient information for each flavor 1006 is available at 1007 <a href="https://www.brusters.com" target="_blank" rel="noopener"> 1008 Brusters.com 1009 </a>. 1010 </p> 1011 </div> 1012 1013 </div> 1014</div> 1015 1016 1017</div> 1018 1019 <div class="modal fade" id="promoModal" tabindex="-1" aria-labelledby="promoModalLabel" aria-hidden="true"> 1020 <div class="modal-dialog modal-dialog-centered"> 1021 <div class="modal-content"> 1022 <div class="modal-header"> 1023 <h5 class="modal-title" id="promoModalLabel">ð Special Promotion!</h5> 1024 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 1025 </div> 1026 <div class="modal-body"> 1027 1028 </div> 1029 </div> 1030 </div> 1031 </div> 1032 1033 <!-- Modal Custom Image --> 1034<div class="modal fade" id="modifierImageModal" tabindex="-1"> 1035 <div class="modal-dialog modal-dialog-centered modal-lg"> 1036 <div class="modal-content"> 1037 <div class="modal-header"> 1038 <img class="img-fluid img-headerimage" style="float:right" src="https://brustersonline.com/Images/brusterslogo_headerimage.png" alt="Brusters Logo" /> 1039 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 1040 </div> 1041 <div class="modal-body text-center"> 1042 <img id="modifierFullImage" src="" alt="modifier image" style="width:100%; height:auto; object-fit:contain;" /> 1043 </div> 1044 </div> 1045 </div> 1046</div> 1047 1048 1049 <!-- Modal Pie --> 1050<div class="modal fade" 1051 id="modalPie1" 1052 tabindex="-1" 1053 aria-labelledby="modalPie1Label" 1054 aria-describedby="modalPie1Description" 1055 aria-hidden="true"> 1056 1057 <div class="modal-dialog"> 1058 <div class="modal-content"> 1059 1060 <div class="modal-header"> 1061 <h2 id="modalPie1Label" class="modal-title visually-hidden"> 1062 Customize Your Ice Cream Pie 1063 </h2> 1064 1065 <img 1066 class="img-fluid img-headerimage" 1067 style="float: right;" 1068 src="https://brustersonline.com/Images/brusterslogo_headerimage.png" 1069 alt="Bruster's Real Ice Cream"> 1070 1071 <button 1072 type="button" 1073 class="btn-close" 1074 data-bs-dismiss="modal" 1075 aria-label="Close customize your ice cream pie dialog"> 1076 </button> 1077 </div> 1078
1079 <div class="modal-body modal-greeting"> 1080 1081 <p id="modalPie1Description" class="visually-hidden"> 1082 Select the flavor, crust, filling, writing color, and optional special message for your ice cream pie. 1083 </p> 1084 1085 <div id="pieheader"> 1086 <img 1087 src="https://brusters.azurewebsites.net/brusterscom/cakeanatomy.jpg" 1088 class="img-fluid pieimage menu-img mb-3 rounded" 1089 alt="Diagram showing the parts of a customizable ice cream pie"> 1090 </div> 1091 1092 <div id="pieselect"> 1093 <div class="form-group"> 1094 1095 <label for="pieicecreamselect" class="visually-hidden"> 1096 Ice cream flavor 1097 </label> 1098 <select 1099 class="form-control" 1100 id="pieicecreamselect" 1101 name="pieicecreamselect"> 1102 <option value="" selected disabled> 1103 Select Flavor 1104 </option> 1105 </select> 1106 1107 <br> 1108 1109 <label for="piecrustselect" class="visually-hidden"> 1110 Pie crust 1111 </label> 1112 <select 1113 class="form-control" 1114 id="piecrustselect" 1115 name="piecrustselect"> 1116 <option value="" selected disabled> 1117 Select Crust 1118 </option> 1119 </select> 1120 1121 <br> 1122 1123 <label for="piefillingselect" class="visually-hidden"> 1124 Pie filling 1125 </label> 1126 <select 1127 class="form-control" 1128 id="piefillingselect" 1129 name="piefillingselect"> 1130 <option value="" selected disabled> 1131 Select Filling 1132 </option> 1133 </select> 1134 1135 <br> 1136 1137 <label for="piecolorselect" class="visually-hidden"> 1138 Color of writing 1139 </label> 1140 <select 1141 class="form-control prattville" 1142 id="piecolorselect" 1143 name="piecolorselect"> 1144 <option value="" selected disabled> 1145 Select Color of Writing 1146 </option> 1147 </select> 1148 1149 <br> 1150 1151 <label for="piemessage" class="visually-hidden"> 1152 Special message 1153 </label> 1154 <textarea 1155 class="form-control restrictemojis" 1156 rows="5" 1157 maxlength="30" 1158 placeholder="Special Message" 1159 id="piemessage" 1160 name="piemessage" 1161 aria-describedby="piemessageHelp"></textarea> 1162 1163 <div id="piemessageHelp" class="visually-hidden"> 1164 Enter an optional special message of no more than 30 characters. 1165 </div> 1166 1167 </div> 1168 </div> 1169 </div> 1170 1171 <div class="modal-footer"> 1172 1173 <div 1174 id="modalpiefootertext" 1175 role="status" 1176 aria-live="polite"> 1177 </div> 1178 1179 <button 1180 type="button" 1181 class="btn btn-danger" 1182 data-bs-dismiss="modal"> 1183 Close 1184 </button> 1185 1186 <button 1187 type="button" 1188 class="btn btn-primary btn-pie-step1"> 1189 Continue 1190 </button> 1191 1192 </div> 1193 </div> 1194 </div> 1195</div> 1196 1197
1198 <!--Cake Modal --> 1199<div class="modal fade" 1200 id="modalCake1" 1201 tabindex="-1" 1202 aria-labelledby="modalCake1Label" 1203 aria-describedby="modalCake1Description" 1204 aria-hidden="true"> 1205 1206 <div class="modal-dialog"> 1207 <div class="modal-content"> 1208 1209 <div class="modal-header"> 1210 <h2 id="modalCake1Label" class="modal-title visually-hidden"> 1211 Customize Your Ice Cream Cake 1212 </h2> 1213 1214 <img class="img-fluid img-headerimage" 1215 style="float:right" 1216 src="https://brustersonline.com/Images/brusterslogo_headerimage.png" 1217 alt="Bruster's Real Ice Cream"> 1218 1219 <button type="button" 1220 class="btn-close" 1221 data-bs-dismiss="modal" 1222 aria-label="Close customize your ice cream cake dialog"> 1223 </button> 1224 </div> 1225 1226 <div class="modal-body modal-greeting"> 1227 1228 <p id="modalCake1Description" class="visually-hidden"> 1229 Select the flavor, cake, filling, writing color, and optional special message for your ice cream cake. 1230 </p> 1231 1232 <div id="cakeheader"> 1233 <img src="https://brusters.azurewebsites.net/brusterscom/cakeanatomy.jpg" 1234 class="img-fluid cakeimage menu-img mb-3 rounded" 1235 alt="Diagram showing the parts of a customizable ice cream cake"> 1236 </div> 1237 1238 <div id="cakeselect"> 1239 <div class="form-group"> 1240 1241 <label for="cakeicecreamselect" class="visually-hidden"> 1242 Ice cream flavor 1243 </label> 1244 <select class="form-control" 1245 id="cakeicecreamselect" 1246 name="cakeicecreamselect"> 1247 <option value="" selected disabled> 1248 Select Flavor 1249 </option> 1250 </select> 1251 1252 <br /> 1253 1254 <label for="cakecakeselect" class="visually-hidden"> 1255 Cake flavor 1256 </label> 1257 <select class="form-control" 1258 id="cakecakeselect" 1259 name="cakecakeselect"> 1260 <option value="" selected disabled> 1261 Select Cake 1262 </option> 1263 </select> 1264 1265 <br /> 1266 1267 <label for="cakefillingselect" class="visually-hidden"> 1268 Filling 1269 </label> 1270 <select class="form-control" 1271 id="cakefillingselect" 1272 name="cakefillingselect"> 1273 <option value="" selected disabled> 1274 Select Filling 1275 </option> 1276 </select> 1277 1278 <br /> 1279 1280 <label for="cakecolorselect" class="visually-hidden"> 1281 Color of writing 1282 </label> 1283 <select class="form-control prattville" 1284 id="cakecolorselect" 1285 name="cakecolorselect"> 1286 <option value="" selected disabled> 1287 Select Color of Writing 1288 </option> 1289 </select> 1290 1291 <br /> 1292 1293 <label for="cakemessage" class="visually-hidden"> 1294 Special message 1295 </label> 1296 <textarea class="form-control restrictemojis" 1297 rows="5" 1298 maxlength="35" 1299 placeholder="Special Message" 1300 id="cakemessage" 1301 name="cakemessage" 1302 aria-describedby="cakemessageHelp"></textarea> 1303 1304 <div id="cakemessageHelp" class="visually-hidden"> 1305 Enter an optional special message of no more than 35 characters. 1306 </div> 1307 1308 </div> 1309 </div> 1310 1311 </div> 1312 1313 <div class="modal-footer"> 1314 1315 <div id="modalcakefootertext" 1316 role="status"
1317 aria-live="polite"> 1318 </div> 1319 1320 <button type="button" 1321 class="btn btn-danger" 1322 data-bs-dismiss="modal"> 1323 Close 1324 </button> 1325 1326 <button type="button" 1327 class="btn btn-primary btn-cake-step1"> 1328 Continue 1329 </button> 1330 1331 </div> 1332 1333 </div> 1334 </div> 1335</div> 1336 1337 1338 <!-- Item Modal --> 1339 <div class="modal fade" id="itemModal" tabindex="-1" aria-labelledby="itemModalLabel" aria-hidden="true"> 1340 <div class="modal-dialog modal-lg"> 1341 <div class="modal-content"> 1342 <div class="modal-header"> 1343 <h5 class="modal-title" id="itemModalLabel">Item Details</h5> 1344 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 1345 </div> 1346 <div class="modal-body"> 1347 <!-- Top Buttons Section in Modal --> 1348 <!-- Top Buttons Section in Modal --> 1349 <div id="top-buttons" class="d-flex mt-3"> 1350 <button type="button" class="btn btn-danger me-2" id="backButtonTop" style="display: none;">Back</button> 1351 <button type="button" class="btn btn-primary me-2" id="nextModifierGroupButtonTop">Continue</button> 1352 <button type="button" class="btn btn-primary" id="addToCartButtonTop" style="display: none;">Add To Cart</button> 1353 </div> 1354 1355 <!-- Content will be injected by JavaScript --> 1356 <div id="modal-content"></div> 1357 </div> 1358 <div class="modal-footer"> 1359 <button type="button" class="btn btn-danger" id="backButton" style="display: none;">Back</button> 1360 <button type="button" class="btn btn-primary" id="nextModifierGroupButton">Continue</button> 1361 <button type="button" class="btn btn-primary" id="addToCartButton" style="display: none;">Add To Cart</button> 1362 </div> 1363 </div> 1364 </div> 1365 </div> 1366 1367 <!-- Shopping Cart Modal --> 1368 <div class="modal fade" id="cartModal" tabindex="-1" aria-labelledby="cartModalLabel" aria-hidden="true"> 1369 <div class="modal-dialog"> 1370 <div class="modal-content"> 1371 <div class="modal-header"> 1372 <h5 class="modal-title" id="cartModalLabel">Your Order</h5> 1373 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 1374 </div> 1375 <div class="modal-body" id="cartModalBody"> 1376 <p>Your cart is empty.</p> 1377 </div> 1378 <div class="cart-subtotal" id="cartSubtotal"> 1379 Subtotal: $0.00 1380 </div> 1381 <div class="cart-tax" id="cartTax"> 1382 Tax: $0.00 1383 </div> 1384 <div class="cart-total" id="cartTotal"> 1385 Total: $0.00 1386 </div> 1387 <div class="modal-footer"> 1388 <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> 1389 <button type="button" class="btn btn-primary" id="checkoutButton">Checkout</button> 1390 </div> 1391 </div> 1392 </div> 1393 </div> 1394 1395 <!--Promo Modal --> 1396<div class="modal fade" 1397 id="modalPromo" 1398 tabindex="-1" 1399 aria-labelledby="modalPromoLabel" 1400 aria-hidden="true"> 1401 1402 <div class="modal-dialog modal-dialog-centered"> 1403 <div class="modal-content"> 1404 1405 <div class="modal-header"> 1406 <h2 id="modalPromoLabel" class="modal-title visually-hidden"> 1407 Promotional Offer 1408 </h2> 1409 1410 <button type="button" 1411 class="btn-close" 1412 data-bs-dismiss="modal" 1413 aria-label="Close promotional offer"> 1414 </button> 1415 </div> 1416 1417 <div class="modal-body text-center"> 1418 <img class="img-fluid" 1419 src="" 1420 alt="Current promotional offer"> 1421 </div> 1422 1423 </div> 1424 </div> 1425</div> 1426 1427 1428 <!-- Rewards Modal --> 1429<div class="modal fade" 1430 id="modalRewards" 1431 tabindex="-1" 1432 aria-labelledby="modalRewardsLabel" 1433 aria-describedby="modalRewardsDescription" 1434 aria-hidden="true"> 1435 1436 <div class="modal-dialog modal-lg modal-dialog-centered"> 1437 <div class="modal-content"> 1438 1439 <div class="modal-header"> 1440 1441 <h2 id="modalRewardsLabel" class="modal-title visually-hidden"> 1442 Loyalty Rewards 1443 </h2> 1444 1445 <img class="img-responsive img-headerimage" 1446 style="float:right" 1447 src="https://brustersonline.com/Images/brusterslogo_headerimage.png" 1448 alt="Bruster's Real Ice Cream"> 1449 1450 <button type="button" 1451 class="btn-close" 1452 data-bs-dismiss="modal" 1453 aria-label="Close loyalty rewards dialog"> 1454 </button> 1455 1456 </div> 1457
1458 <div class="modal-body modal-greeting"> 1459 1460 <div id="modalRewardsDescription"> 1461 <h3>You have a reward available. Please select one.</h3> 1462 1463 <div id="rewardoptions" class="list-group"> 1464 <a class="list-group-item list-group-item-danger rewardoptions nothanks" 1465 data-wallet="0" 1466 data-type="No Thanks" 1467 href="#"> 1468 No Thanks - I'll Use Later 1469 </a> 1470 </div> 1471 1472 <div id="rewarddisclaimer"> 1473 <p> 1474 *Only one discount may be applied per transaction. 1475 If you have entered a discount code and choose to use 1476 your loyalty reward, the loyalty reward will be applied 1477 instead of your discount code. 1478 </p> 1479 </div> 1480 </div> 1481 1482 </div> 1483 1484 <div class="modal-footer"> 1485 <!-- Optional footer content --> 1486 </div> 1487 1488 </div> 1489 </div> 1490</div> 1491 1492 1493 1494<!-- Checkout Modal --> 1495<div class="modal fade" id="checkoutModal" tabindex="-1" aria-labelledby="checkoutModalLabel" aria-hidden="true"> 1496 <div class="modal-dialog"> 1497 <div class="modal-content"> 1498 <div class="modal-header"> 1499 <h2 class="modal-title" id="checkoutModalLabel">Checkout</h2> 1500 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 1501 </div> 1502 <div id="checkoutForm" class="modal-body"> 1503 <!-- Order Details Section --> 1504 <div id="orderDetails" class="small-font mb-3"> 1505 <!-- Order details will be injected here by JavaScript --> 1506 </div> 1507 <!-- Total Amount Section --> 1508 <div id="totalAmount" class="small-font mb-3 font-weight-bold"> 1509 <!-- Total amount will be injected here by JavaScript --> 1510 </div> 1511 <!-- Existing form elements --> 1512 <input type="hidden" id="cartTotalAmount" value="
15120"> 1513 <input type="hidden" id="cartSubTotalAmount" value="0"> 1514 <div class="mb-3"> 1515 <label for="name" class="form-label">Name</label> 1516 <input type="text" class="form-control" id="name"> 1517 </div> 1518 <div class="mb-3"> 1519 <label for="cellPhone" class="form-label">Cell Phone <small>(*loyalty number)</small></label> 1520 <input 1521 type="tel" 1522 maxlength="10" 1523 placeholder="10 digit cell phone" 1524 class="form-control" 1525 id="cellPhone" 1526 autocomplete="tel" 1527 inputmode="numeric" /> 1528 <div style="color:red" id="error-message" class="animate__animated"></div> 1529 </div> 1530 <div class="mb-3"> 1531 <h6 class="form-label">Pickup Time</h6> 1532 <fieldset> 1533 <legend class="visually-hidden">Pickup Time</legend> 1534 1535 <div class="form-check"> 1536 <input class="form-check-input" type="radio" name="pickupTimeOption" id="pickupAsap" value="ASAP" checked> 1537 <label class="form-check-label radio-inline" for="pickupAsap">ASAP</label> 1538 </div> 1539 1540 <div class="form-check"> 1541 <input class="form-check-input" type="radio" name="pickupTimeOption" id="pickupDateTime" value="SelectDateTime"> 1542 <label class="form-check-label radio-inline" for="pickupDateTime">Select Date/Time</label> 1543 </div> 1544 1545 </fieldset> 1546 <label for="pickupDateTimeInput" class="visually-hidden"> 1547 Pickup Date and Time 1548 </label> 1549 1550 <div class="input-group d-none mt-2" id="pickupDateTimePicker"> 1551 <input 1552 id="pickupDateTimeInput" 1553 type="text" 1554 class="form-control" 1555 data-td-target="#pickupDateTimePicker" 1556 /> 1557 1558 <span 1559 class="input-group-text" 1560 data-td-target="#pickupDateTimePicker" 1561 data-td-toggle="datetimepicker"> 1562 <i class="fa fa-calendar"></i> 1563 </span> 1564 </div> 1565 </div> 1566 <div class="mb-3"> 1567 <label for="discountCode" class="form-label">Discount Code <small>(Optional)</small></label> 1568 <input type="text" class="form-control" id="discountCode"> 1569 </div> 1570 <div class="mb-3"> 1571 <label for="additionalInstructions" class="form-label">Additional Instructions</label> 1572 <textarea class="form-control" id="additionalInstructions" rows="3"></textarea> 1573 </div> 1574 <div class="mb-3"> 1575 <h6 class="form-label">Scooper Tip <small>(Optional)</small></h6> 1576 <fieldset> 1577 <legend class="visually-hidden">Select a scooper tip amount</legend> 1578 1579 <div class="tip-group"> 1580 <div class="form-check form-check-inline"> 1581 <input class="form-check-input" type="radio" name="scooperTip" id="tip10" value="15%"> 1582 <label class="form-check-label" for="tip10">15%</label> 1583 </div> 1584 1585 <div class="form-check form-check-inline"> 1586 <input class="form-check-input" type="radio" name="scooperTip" id="tip15" value="20%"> 1587 <label class="form-check-label" for="tip15">20%</label> 1588 </div> 1589 1590 <div class="form-check form-check-inline"> 1591 <input class="form-check-input" type="radio" name="scooperTip" id="tip18" value="25%"> 1592 <label class="form-check-label" for="tip18">25%</label> 1593 </div> 1594 1595 <div class="form-check form-check-inline"> 1596 <input class="form-check-input" type="radio" name="scooperTip" id="tipOther" value="Other"> 1597 <label class="form-check-label" for="tipOther">Other</label> 1598 </div> 1599 1600 <label for="tipOtherAmount" class="visually-hidden"> 1601 Tip amount 1602 </label> 1603 1604 <input 1605 type="text" 1606 class="form-control d-none" 1607 id="tipOtherAmount" 1608 placeholder="Enter amount" /> 1609 </div> 1610 </fieldset> 1611 </div> 1612 <div class="form-check mb-3"> 1613 <input class="form-check-input" 1614 type="checkbox" 1615 id="promotionalTextMessages" 1616 checked> 1617 1618 <label class="form-check-label small text-muted" for="promotionalTextMessages"> 1619 By checking this box, you consent to receive recurring automated marketing and informational text messages from Bruster's Real Ice Cream at the mobile number provided. Consent is not a condition of purchase. Message frequency varies. Message and data rates may apply. Reply STOP to cancel or HELP for help. View our 1620 <a href="https://brusters.com/privacy-policy/" 1621 target="_blank" 1622 rel="noopener noreferrer">Privacy Policy</a>. 1623 For support, call 724-774-4250. 1624 </label> 1625 </div> 1626 </div> 1627 <div class="modal-footer"> 1628 <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> 1629 <button type="button" class="btn btn-primary" id="submitCheckoutButton">Submit</button> 1630 <div style="color:white" id="error-message2" class="animate__animated"></div> 1631 </div> 1632 </div> 1633 </div> 1634</div> 1635 1636 1637 1638 <!-- Confirmation Modal --> 1639<div class="modal fade" id="confirmationModal" tabindex="-1" aria-labelledby="confirmationModalLabel" aria-hidden="true"> 1640 <div class="modal-dialog"> 1641 <div class="modal-content"> 1642 <div class="modal-header"> 1643 <h5 class="modal-title" id="confirmationModalLabel">Item added to cart</h5> 1644 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 1645 </div> 1646 <div class="modal-body"> 1647 <p>The item has been added to your cart.</p> 1648 </div> 1649 <div class="modal-footer"> 1650 <button type="button" class="btn btn-secondary" id="continueShoppingButton">Continue Shopping</button> 1651 <button type="button" class="btn btn-primary" id="checkoutButtonConfirmation">Checkout</button> 1652 </div> 1653 </div> 1654 </div> 1655</div> 1656 1657 1658 <div id="popper-container"></div> 1659 1660
1660<script type="text/javascript"> 1661 1662 function updateCheckoutErrors() { 1663 var errorMessage = ''; 1664 1665 if (!$('#name').val().trim().length) { 1666 errorMessage += 'Name is required.<br/>'; 1667 } 1668 1669 var cellPhone = $('#cellPhone').val().trim(); 1670 1671 if (!cellPhone.length) { 1672 errorMessage += 'Cell phone is required.<br/>'; 1673 } 1674 else if (!/^\d{10}$/.test(cellPhone)) { 1675 errorMessage += 'Please enter a valid 10-digit cell phone number.<br/>'; 1676 } 1677 1678 if (errorMessage) { 1679 $('#error-message2') 1680 .html(errorMessage) 1681 .show(); 1682 } 1683 else { 1684 $('#error-message2') 1685 .html('') 1686 .hide(); 1687 } 1688 } 1689 1690 $(document).on('input', '#name', function () { 1691 if ($(this).val().trim().length) { 1692 $(this).css('border', ''); 1693 updateCheckoutErrors(); 1694 } 1695 }); 1696 1697 $(document).on('input', '#cellPhone', function () { 1698 var cellPhone = $(this).val().trim(); 1699 1700 if (/^\d{10}$/.test(cellPhone)) { 1701 $(this).css('border', ''); 1702 updateCheckoutErrors(); 1703 } 1704 }); 1705 1706 1707 function markPromotionalProducts() { 1708 1709 var promoJson = document.getElementById("hfPromoProducts")?.value; 1710 1711 if (!promoJson) return; 1712 1713 var promoItems; 1714 1715 try { 1716 1717 promoItems = JSON.parse(promoJson); 1718 1719 } catch (e) { 1720 1721 console.error("Invalid hfPromoProducts JSON", e); 1722 1723 return; 1724 } 1725 1726 // Build quick lookup: ItemId â OriginalPrice 1727 var promoMap = {}; 1728 1729 promoItems.flat().forEach(function (p) { 1730 1731 promoMap[String(p.ItemId)] = p.OriginalPrice; 1732 1733 }); 1734 1735 document.querySelectorAll("[data-item-id]").forEach(function (el) { 1736 1737 var raw = el.getAttribute("data-item-id"); 1738 1739 if (!raw) return; 1740 1741 // 412-3611089_9374 â 3611089 1742 var match = raw.match(/-(\d+)_/); 1743 1744 if (!match) return; 1745 1746 var itemId = match[1]; 1747 1748 if (!(itemId in promoMap)) return; 1749 1750 var container = 1751 el.closest(".menu-item") || 1752 el.closest(".item") || 1753 el.closest(".product") || 1754 el.closest("li") || 1755 el.closest("div"); 1756 1757 if (!container) return; 1758 1759 if (container.querySelector(".promo-label")) return; 1760 1761 var originalPrice = promoMap[itemId]; 1762 1763 var label = document.createElement("small"); 1764 1765 label.className = "promo-label"; 1766 1767 label.innerHTML = 1768 `Promotional Product · <span class="promo-original">Originally $${originalPrice.toFixed(2)}</span>`; 1769 1770 container.appendChild(label); 1771 1772 }); 1773 1774} 1775 1776 1777 function displayError(error) { 1778 const errorDiv = document.getElementById('errors'); 1779 errorDiv.innerText = error.message || error.toString(); 1780 } 1781 1782 1783 function setButtonWidths() { 1784 const googlePayButton = document.querySelector("#sq-google-pay div"); 1785 const applePayButton = document.querySelector("#sq-apple-pay"); 1786 1787 if (googlePayButton) { 1788 googlePayButton.style.width = "100%"; 1789 } 1790 1791 if (applePayButton) { 1792 applePayButton.style.width = "100%"; 1793 } 1794 } 1795 1796 function setBackgroundImage() { 1797 const bgImageElement = document.getElementById('bg-image'); 1798 const hfBackgroundImage = document.getElementById('hfBackgroundImage').value; 1799 const viewportWidth = window.innerWidth; 1800 let imgixUrl; 1801 1802 if (viewportWidth <= 320) { // Extra small devices 1803 imgixUrl = `${hfBackgroundImage}?fit=clamp&w=320&h=480`; 1804 } else if (viewportWidth <= 480) { // Small mobile devices
1805 imgixUrl = `${hfBackgroundImage}?fit=clamp&w=480&h=720`; 1806 } else if (viewportWidth <= 768) { // Tablets 1807 imgixUrl = `${hfBackgroundImage}?fit=clamp&w=768&h=1024`; 1808 } else if (viewportWidth <= 1024) { // Small desktops 1809 imgixUrl = `${hfBackgroundImage}?fit=clamp&w=1024&h=768`; 1810 } else if (viewportWidth <= 1440) { // Medium desktops 1811 imgixUrl = `${hfBackgroundImage}?fit=clamp&w=1440&h=900`; 1812 } else { // Large desktops 1813 imgixUrl = `${hfBackgroundImage}?fit=clamp&w=1920&h=1080`; 1814 } 1815 1816 bgImageElement.style.backgroundImage = `url('${imgixUrl}')`; 1817 } 1818 1819 1820 // Set background image on initial load 1821 setBackgroundImage(); 1822 1823 // Update background image on window resize 1824 window.addEventListener('resize', setBackgroundImage); 1825 1826 </script>
1826 1827 1828
1828<script type="text/javascript"> 1829 1830 const cartStoreNumber = new URLSearchParams(window.location.search).get('number'); 1831 const cartStorageKey = `brustersCart-${cartStoreNumber}`; 1832 1833 let cart = JSON.parse( 1834 sessionStorage.getItem(cartStorageKey) || '[]' 1835 ); 1836 1837 function saveCart() { 1838 sessionStorage.setItem( 1839 cartStorageKey, 1840 JSON.stringify(cart) 1841 ); 1842 } 1843 1844 window.addEventListener('pageshow', function () { 1845 try { 1846 cart = JSON.parse( 1847 sessionStorage.getItem(cartStorageKey) 1848 ) || []; 1849 } catch (error) { 1850 console.error('Unable to restore cart:', error); 1851 cart = []; 1852 } 1853 1854 renderCart(); 1855 }); 1856 1857 let currentItem = null; 1858 let currentModifierGroupIndex = 0; 1859 let isCake = false; 1860 let isPie = false; 1861 1862 function changeQuantity(action, id, maximum_amount) { 1863 const input = document.getElementById(`quantity-${id}`); 1864 let currentValue = parseInt(input.value); 1865 1866 if (action === 'plus') { 1867 if (currentValue < maximum_amount) { 1868 currentValue += 1; 1869 } 1870 } else if (action === 'minus' && currentValue > 0) { 1871 currentValue -= 1; 1872 } 1873 1874 input.value = currentValue; 1875 } 1876 1877function handleFlavorQtyChange(groupName, maximumAmount, sectionName, event) { 1878 const checkbox = event.target; 1879 const li = checkbox.closest("li"); 1880 const controls = li.querySelector(".flavor-qty-controls"); 1881 const qtyInput = li.querySelector(".flavor-qty"); 1882 1883 const showQtyControls = 1884 sectionName !== "Take Home" && maximumAmount > 1; 1885 1886 const visibleCard = checkbox.closest(".card"); 1887 1888 const allChecked = Array.from( 1889 visibleCard.querySelectorAll(".flavor-checkbox:checked") 1890 ); 1891 1892 if (checkbox.checked) { 1893 1894 if (showQtyControls) { 1895 controls.style.display = "flex"; 1896 } 1897 1898 if (allChecked.length === 1) { 1899 checkbox.dataset.flavorQty = maximumAmount; 1900 qtyInput.value = maximumAmount; 1901 } else { 1902 let donor = allChecked.find(cb => 1903 cb !== checkbox && Number(cb.dataset.flavorQty) > 1 1904 ); 1905 1906 if (!donor) { 1907 checkbox.checked = false; 1908 checkbox.dataset.flavorQty = 0; 1909 qtyInput.value = 0; 1910 controls.style.display = "none"; 1911 1912 showPopperAlert(`You have already allocated all ${maximumAmount} flavors.`, checkbox); 1913 return; 1914 } 1915 1916 donor.dataset.flavorQty = Number(donor.dataset.flavorQty) - 1; 1917 donor.closest("li").querySelector(".flavor-qty").value = donor.dataset.flavorQty; 1918 1919 checkbox.dataset.flavorQty = 1; 1920 qtyInput.value = 1; 1921 } 1922 } else { 1923 const removedQty = Number(checkbox.dataset.flavorQty || 0); 1924 1925 checkbox.dataset.flavorQty = 0; 1926 qtyInput.value = 0; 1927 controls.style.display = "none"; 1928 1929 let remaining = allChecked.filter(cb => cb !== checkbox); 1930 1931 if (remaining.length > 0) { 1932 remaining[0].dataset.flavorQty = 1933 Number(remaining[0].dataset.flavorQty) + removedQty; 1934 1935 remaining[0].closest("li").querySelector(".flavor-qty").value = 1936 remaining[0].dataset.flavorQty; 1937 } 1938 } 1939 1940 if (!showQtyControls) { 1941 controls.style.display = "none"; 1942 } 1943} 1944 1945 function changeFlavorQty(event, amount) { 1946 event.preventDefault(); 1947 event.stopPropagation(); 1948 1949 const li = event.target.closest("li"); 1950 const checkbox = li.querySelector(".flavor-checkbox"); 1951 1952 if (!checkbox || !checkbox.checked) return; 1953 1954 const visibleCard = checkbox.closest(".card"); 1955 1956 const allChecked = Array.from( 1957 visibleCard.querySelectorAll(".flavor-checkbox:checked") 1958 ); 1959
1960 let currentQty = Number(checkbox.dataset.flavorQty || 0); 1961 1962 if (amount === 1) { 1963 1964 let donor = allChecked.find(cb => 1965 cb !== checkbox && 1966 Number(cb.dataset.flavorQty) > 1 1967 ); 1968 1969 if (!donor) return; 1970 1971 donor.dataset.flavorQty = 1972 Number(donor.dataset.flavorQty) - 1; 1973 1974 donor.closest("li") 1975 .querySelector(".flavor-qty") 1976 .value = donor.dataset.flavorQty; 1977 1978 checkbox.dataset.flavorQty = currentQty + 1; 1979 } 1980 1981 if (amount === -1) { 1982 1983 if (currentQty <= 1) return; 1984 1985 let receiver = allChecked.find(cb => cb !== checkbox); 1986 1987 if (!receiver) return; 1988 1989 receiver.dataset.flavorQty = 1990 Number(receiver.dataset.flavorQty) + 1; 1991 1992 receiver.closest("li") 1993 .querySelector(".flavor-qty") 1994 .value = receiver.dataset.flavorQty; 1995 1996 checkbox.dataset.flavorQty = currentQty - 1; 1997 } 1998 1999 li.querySelector(".flavor-qty").value = 2000 checkbox.dataset.flavorQty; 2001} 2002 2003 2004 function handleModifierChange(groupName, minimumAmount, maximumAmount, event) { 2005 let currentGroup; 2006 2007 var blnNestedFlavors = false; 2008 if (getQueryStringParameter('number') != '999999'){ 2009 blnNestedFlavors = true; 2010 } 2011 2012 if (groupName === "Flavors" || (groupName === "Flavor #1" && blnNestedFlavors) ||( groupName === "Flavor #2" && blnNestedFlavors) ||( groupName === "Flavor #3" && blnNestedFlavors)) { 2013 var modifiedGroupName = groupName; 2014 currentGroup = currentItem.modifier_groups.find(group => group.name === modifiedGroupName); 2015 }else { 2016 currentGroup = currentItem.modifier_groups.find(group => group.name === groupName); 2017 } 2018 2019 if (!currentGroup) { 2020 console.error(`Modifier group with name ${groupName} not found.`); 2021 return; 2022 } 2023 2024 let selectedCount = 0; 2025 console.log(groupName); 2026 2027 if (groupName === "Flavors" || groupName === "Select Flavor" || (groupName === "Flavor #1" && blnNestedFlavors) || ( groupName === "Flavor #2" && blnNestedFlavors) || ( groupName === "Flavor #3" && blnNestedFlavors)) { 2028 console.log('checking here'); 2029 currentGroup.modifiers.forEach(flavorGroup => { 2030 if (flavorGroup.modifier_groups && flavorGroup.modifier_groups.length > 0) { 2031 const visibleCard = Array.from(document.querySelectorAll('#modal-content .card.mt-3')) 2032 .find(card => card.offsetParent !== null); 2033 2034 if (visibleCard) { 2035 flavorGroup.modifier_groups[0].modifiers.forEach(modifier => { 2036 const escapedId = CSS.escape(`modifier-${flavorGroup.name}-${modifier.id}`); 2037 const checkbox = visibleCard.querySelector(`#${escapedId}`); 2038 if (checkbox && checkbox.checked) { 2039 selectedCount++; 2040 } 2041 }); 2042 } 2043 } 2044 2045 }); 2046 } else { 2047 console.log('checking the reg'); 2048 currentGroup.modifiers.forEach(modifier => { 2049 const checkbox = document.getElementById(`modifier-${groupName}-${modifier.id}`); 2050 if (checkbox && checkbox.checked) { 2051 selectedCount++; 2052 } 2053 }); 2054 } 2055 2056 if (selectedCount > maximumAmount) { 2057 showPopperAlert(`You can select a maximum of ${maximumAmount} ${groupName}.`, event.target); 2058 event.target.checked = false; // Uncheck the checkbox that triggered the event 2059 } 2060 } 2061 2062 function showPopperAlert(message, element) { 2063 console.log('popper alert'); 2064 console.log(message); 2065 console.log(element); 2066 2067 const popperContainer = document.getElementById('popper-container'); 2068 2069 popperContainer.innerHTML = ` 2070 <div class="popper-alert" 2071 role="alert"
2072 aria-live="assertive" 2073 aria-atomic="true"> 2074 ${message} 2075 </div> 2076 `; 2077 2078 const popperAlert = popperContainer.querySelector('.popper-alert'); 2079 2080 Popper.createPopper(element, popperAlert, { 2081 placement: 'top', 2082 modifiers: [{ 2083 name: 'offset', 2084 options: { 2085 offset: [0, 8] 2086 } 2087 }] 2088 }); 2089 2090 setTimeout(() => { 2091 popperContainer.innerHTML = ''; 2092 }, 3000); 2093} 2094 2095 function toggleItemsVisibility(index) { 2096 const itemsDiv = document.getElementById(`items-${index}`); 2097 const button = document.querySelector( 2098 `[aria-controls="items-${index}"]` 2099 ); 2100 2101 if (itemsDiv.classList.contains('hidden-items')) { 2102 itemsDiv.classList.remove('hidden-items'); 2103 2104 if (button) { 2105 button.setAttribute('aria-expanded', 'true'); 2106 } 2107 } else { 2108 itemsDiv.classList.add('hidden-items'); 2109 2110 if (button) { 2111 button.setAttribute('aria-expanded', 'false'); 2112 } 2113 } 2114} 2115 2116 function buildGA4Items(cartItem) { 2117 var items = []; 2118 2119 var variantParts = []; 2120 if (cartItem.modifiers && cartItem.modifiers.length) { 2121 for (var i = 0; i < cartItem.modifiers.length; i++) { 2122 variantParts.push( 2123 cartItem.modifiers[i].group + ": " + cartItem.modifiers[i].name 2124 ); 2125 } 2126 } 2127 2128 // Base item 2129 items.push({ 2130 item_id: cartItem.id, 2131 item_name: cartItem.name, 2132 item_category: "Menu Item", 2133 item_variant: variantParts.join(" | "), 2134 price: cartItem.price, 2135 quantity: cartItem.quantity 2136 }); 2137 2138 // Paid extras 2139 if (cartItem.extras && cartItem.extras.length) { 2140 for (var j = 0; j < cartItem.extras.length; j++) { 2141 var extra = cartItem.extras[j]; 2142 items.push({ 2143 item_id: "EXTRA_" + extra.id, 2144 item_name: extra.name, 2145 item_category: "Extras", 2146 item_variant: extra.group, 2147 price: extra.price, 2148 quantity: extra.quantity 2149 }); 2150 } 2151 } 2152 2153 return items; 2154 } 2155 2156 2157 function buildGA4ItemsFromCart(cart) { 2158 var items = []; 2159 2160 if (!cart || !cart.length) return items; 2161 2162 for (var i = 0; i < cart.length; i++) { 2163 items = items.concat(buildGA4Items(cart[i])); 2164 } 2165 2166 return items; 2167 } 2168 2169 2170 function buildGA4ItemsFromOrder(orderItems) { 2171 var items = []; 2172 2173 if (!orderItems || !orderItems.length) return items; 2174 2175 for (var i = 0; i < orderItems.length; i++) { 2176 var order = orderItems[i]; 2177 2178 var qty = parseInt(order.qty, 10) || 1; 2179 var totalPrice = parseFloat(order.totalprice) || 0; 2180 2181 // â Convert extended price -> unit price for GA4 2182 var unitPrice = qty > 0 ? (totalPrice / qty) : totalPrice; 2183 2184 var variantParts = []; 2185 if (order.flavors && order.flavors.length) { 2186 for (var f = 0; f < order.flavors.length; f++) { 2187 variantParts.push( 2188 order.flavors[f].group + ": " + order.flavors[f].name 2189 ); 2190 } 2191 } 2192 2193 // Base item 2194 items.push({ 2195 item_id: order.itemid, 2196 item_name: order.itemname, 2197 item_category: "Menu Item", 2198 item_variant: variantParts.join(" | "), 2199 price: unitPrice, 2200 quantity: parseInt(order.qty, 10) || 1 2201 }); 2202 2203 // Extras 2204 if (order.extras && order.extras.length) { 2205 for (var e = 0; e < order.extras.length; e++) { 2206 var extra = order.extras[e]; 2207 items.push({ 2208 item_id: extra.itemid, 2209 item_name: extra.itemName, 2210 item_category: "Extras", 2211 item_variant: "Extra", 2212 price: extra.price, 2213 quantity: parseInt(extra.Qty, 10) || 1 2214 }); 2215 } 2216 } 2217 } 2218 2219 return items; 2220 } 2221 2222 2223 2224 function removeFromCart(index) { 2225 2226 if (window.console && console.log) { 2227 console.log("Removing item at index: " + index); 2228 } 2229 2230 var cartItem = cart[index]; 2231 2232 if (!cartItem) { 2233 console.warn("Cart item not found at index: " + index); 2234 return; 2235 } 2236 2237 /* 2238 * Analytics must never prevent the cart from updating. 2239 */ 2240 try { 2241 var ga4Items = buildGA4Items(cartItem) || []; 2242 2243 var totalValue = 0; 2244 2245 for (var i = 0; i < ga4Items.length; i++) { 2246 totalValue += ( 2247 Number(ga4Items[i].price || 0) * 2248 Number(ga4Items[i].quantity || 0) 2249 ); 2250 } 2251 2252 if ( 2253 window.GTM && 2254 typeof window.GTM.pushEvent === "function" 2255 ) { 2256 window.GTM.pushEvent({ 2257 event: "remove_from_cart", 2258 store_number: getStoreNumber(), 2259 ecommerce: { 2260 currency: "USD", 2261 value: Number(totalValue.toFixed(2)), 2262 items: ga4Items 2263 } 2264 }); 2265 } else { 2266 console.warn( 2267 "GTM remove_from_cart event skipped because " + 2268 "window.GTM.pushEvent is unavailable." 2269 ); 2270 } 2271 } catch (error) { 2272 console.error( 2273 "Unable to send GTM remove_from_cart event:", 2274 error 2275 ); 2276 } 2277 2278 /*
2279 * Always remove and re-render, even when analytics fails. 2280 */ 2281 cart.splice(index, 1); 2282 renderCart(); 2283} 2284 2285 2286 2287 document.addEventListener("DOMContentLoaded", function () { 2288 const hiddenField = $("#hiddenFieldMenuData"); 2289 const menuData = JSON.parse(hiddenField.val()); 2290 2291 // Display the value of the hiddenFieldAddress below the header 2292 var hiddenFieldAddress = $("#hiddenFieldAddress"); 2293 var addressDisplay = $("#addressDisplay"); 2294 addressDisplay.text(hiddenFieldAddress.val()); 2295 2296 const hiddenFieldLeadTime = $("#hiddenFieldLeadTime"); 2297 const leadTimeHours = parseInt(hiddenFieldLeadTime.val(), 10); 2298 2299 2300 // Function to create the lead time text 2301 function createLeadTimeText() { 2302 return `<p class="text-center text-muted">Lead Time Required: ${leadTimeHours} hours</p>`; 2303 } 2304 2305 const pickupAsapRadio = document.getElementById("pickupAsap"); 2306 const pickupDateTimeRadio = document.getElementById("pickupDateTime"); 2307 const pickupDateTimePicker = document.getElementById("pickupDateTimePicker"); 2308 2309 pickupAsapRadio.addEventListener("change", function () { 2310 if (this.checked) { 2311 pickupDateTimePicker.classList.add("d-none"); 2312 } 2313 }); 2314 2315 pickupDateTimeRadio.addEventListener("change", function () { 2316 if (this.checked) { 2317 pickupDateTimePicker.classList.remove("d-none"); 2318 } 2319 }); 2320 2321 function showPreviousModifierGroup() { 2322 if (currentModifierGroupIndex > 0) { 2323 currentModifierGroupIndex--; 2324 showNextModifierGroup(); 2325 } 2326 } 2327 2328function createFlavorModifierGroup(modifierGroup, sectionName) { 2329 2330 const showQtyControls = 2331 sectionName !== "Take Home" && modifierGroup.maximum_amount > 1; 2332 2333 let flavorGroups = modifierGroup.modifiers.map(flavorGroup => { 2334 const isNonDairyOatMilk = flavorGroup.name === "Non-Dairy w/ OAT MILK"; 2335 2336 let flavors = flavorGroup.modifier_groups[0].modifiers.map(modifier => { 2337 let modifierName = modifier.name; 2338 2339 if (isNonDairyOatMilk && modifierName.startsWith("Non-Dairy")) { 2340 modifierName = modifierName.replace( 2341 "Non-Dairy", 2342 `<span style="color: #047303; font-weight:bold">Non Dairy</span>` 2343 ); 2344 } 2345 2346 if (modifier.hasOwnProperty("vegan") && modifier.vegan === true) { 2347 modifierName += ` <sup style="color: #047303; font-weight:bold">V</sup>`; 2348 } 2349 2350 return ` 2351 <li class="list-group-item d-flex justify-content-between align-items-center"> 2352 2353 <div class="d-flex align-items-center"> 2354 2355 <input type="checkbox" 2356 id="modifier-${flavorGroup.name}-${modifier.id}" 2357 name="modifier-${flavorGroup.name}-${modifier.id}" 2358 class="modifier-checkbox flavor-checkbox me-2" 2359 data-flavor-qty="${modifier.selected ? modifierGroup.maximum_amount : 0}" 2360 ${modifier.selected ? 'checked' : ''} 2361 onchange="handleFlavorQtyChange('${modifierGroup.name}', ${modifierGroup.maximum_amount}, '${sectionName}', event);"> 2362 2363 <label class="mb-0" 2364 for="modifier-${flavorGroup.name}-${modifier.id}"> 2365 ${modifierName} 2366 </label> 2367 2368 ${modifier.image_url ? ` 2369 <button type="button" 2370 class="btn p-0 border-0 ms-1 flavor-image-button" 2371 onclick="showFlavorImage('${modifier.name}', '${modifier.image_url}', 'modifier-${flavorGroup.name}-${modifier.id}', event); return false;" 2372 aria-label="View image of ${modifier.n
2372ame}"> 2373 <i class="fas fa-eye" aria-hidden="true"></i> 2374 </button>` 2375 : ''} 2376 2377 </div> 2378 2379 <div class="d-flex align-items-center"> 2380 2381 <div class="input-group input-group-sm flavor-qty-controls" 2382 style=" 2383 width:100px; 2384 ${modifier.selected ? '' : 'display:none;'} 2385 ${!showQtyControls ? 'display:none;' : ''} 2386 "> 2387 2388 <button class="btn btn-outline-secondary" 2389 type="button" 2390 aria-label="Decrease ${modifier.name} quantity" 2391 onclick="changeFlavorQty(event, -1)"> 2392 - 2393 </button> 2394 2395 <label for="flavor-qty-${modifier.id}" 2396 class="visually-hidden"> 2397 ${modifier.name} quantity 2398 </label> 2399 2400 <input type="text" 2401 id="flavor-qty-${modifier.id}" 2402 class="form-control text-center flavor-qty" 2403 value="${modifier.selected ? modifierGroup.maximum_amount : 0}" 2404 readonly> 2405 2406 <button class="btn btn-outline-secondary" 2407 type="button" 2408 aria-label="Increase ${modifier.name} quantity" 2409 onclick="changeFlavorQty(event, 1)"> 2410 + 2411 </button> 2412 2413 </div> 2414 2415 ${modifier.price > 0 ? ` 2416 <span class="badge bg-primary rounded-pill ms-2"> 2417 $${modifier.price.toFixed(2)} 2418 </span> 2419 ` : ''} 2420 2421 </div> 2422 2423 </li> 2424 `; 2425 }).join(''); 2426 2427 return ` 2428 <div> 2429 <h6 class="flavor-group-heading">${flavorGroup.name}</h6> 2430 <ul class="list-group"> 2431 ${flavors} 2432 </ul> 2433 </div> 2434 `; 2435 }).join(''); 2436 2437 return ` 2438 <div class="card mt-3"> 2439 <div class="card-header"> 2440 <h5 class="mb-0">${modifierGroup.name}</h5> 2441 </div> 2442 <div class="card-body"> 2443 ${flavorGroups} 2444 </div> 2445 </div> 2446 `; 2447} 2448 2449 2450 function createModifierGroup(modifierGroup,sectionName) { 2451 if (!modifierGroup.modifiers || modifierGroup.modifiers.length === 0) { 2452 return ''; // Skip empty modifier groups 2453 } 2454 2455 if (modifierGroup.name === "Flavors" || modifierGroup.name === "Select Flavor" || modifierGroup.name === "Flavor #1" || modifierGroup.name === "Flavor #2" ||modifierGroup.name === "Flavor #3" ) { 2456 return createFlavorModifierGroup(modifierGroup,sectionName); 2457 } 2458 2459 let modifiers; 2460 if (modifierGroup.name === "Extras") { 2461 modifiers = modifierGroup.modifiers.map(modifier => ` 2462 <li class="list-group-item d-flex justify-content-between align-items-center"> 2463 <div> 2464 <span ${modifier.tax ? `data-tax="${modifier.tax}"` : ''}>${modifier.name}</span> 2465 ${modifier.price > 0 ? `<span class="badge bg-primary rounded-pill ms-2">$${modifier.price.toFixed(2)}</span>` : ''} 2466 </div> 2467 <div class="input-group input-group-sm" style="max-width: 100px;"> 2468 <button class="btn btn-outline-secondary" type="button" onclick="changeQuantity('minus', '${modifierGroup.name}-${modifier.id}',${modifier.maximum_amount})">-</button> 2469 <input type="text" class="form-control text-center" value="
24690" id="quantity-${modifierGroup.name}-${modifier.id}" readonly> 2470 <button class="btn btn-outline-secondary" type="button" onclick="changeQuantity('plus', '${modifierGroup.name}-${modifier.id}',${modifier.maximum_amount})">+</button> 2471 </div> 2472 </li> 2473 `).join(''); 2474 } else { 2475 modifiers = modifierGroup.modifiers.map(modifier => { 2476 if (modifier.inputType) { 2477 let formElement = ''; 2478 if (modifier.inputType === 'textarea') { 2479 formElement = `<textarea id="modifier-${modifierGroup.name}-${modifier.id}" class="form-control"></textarea>`; 2480 } else if (modifier.inputType === 'file') { 2481 formElement = `<input type="file" id="modifier-${modifierGroup.name}-${modifier.id}" class="form-control" />`; 2482 } else { 2483 formElement = `<input type="text" id="modifier-${modifierGroup.name}-${modifier.id}" class="form-control" />`; 2484 } 2485 2486 return ` 2487 <li class="list-group-item d-flex justify-content-between align-items-center"> 2488 <div> 2489 <label for="modifier-${modifierGroup.name}-${modifier.id}">${modifier.name}</label> 2490 ${formElement} 2491 </div> 2492 ${modifier.price > 0 ? `<span class="badge bg-primary rounded-pill">$${modifier.price.toFixed(2)}</span>` : ''} 2493 </li> 2494 `; 2495 } else { 2496 return ` 2497 <li class="list-group-item d-flex justify-content-between align-items-start"> 2498 <div class="me-3"> 2499 <div class="form-check"> 2500 <input type="checkbox" 2501 id="modifier-${modifierGroup.name}-${modifier.id}" 2502 name="modifier-${modifierGroup.name}-${modifier.id}" 2503 class="form-check-input modifier-checkbox" 2504 ${modifier.selected ? 'checked' : ''} 2505 onchange="handleModifierChange('${modifierGroup.name}', ${modifierGroup.minimum_amount}, ${modifierGroup.maximum_amount}, event);"> 2506 <label class="form-check-label" for="modifier-${modifierGroup.name}-${modifier.id}"> 2507 ${modifier.name} 2508 </label> 2509 </div> 2510 2511 ${modifier.customImg ? ` 2512 <div class="mt-2"> 2513 <img src="${modifier.image_url}" 2514 class="clickable-modifier-img" 2515 data-full="${modifier.image_url.split('?')[0]}" 2516 style="width:120px; height:auto; object-fit:cover; cursor:pointer; 2517 border:1px solid #dee2e6; border-radius:.25rem; padding:.25rem;"> 2518 <div><small class="text-muted">Actual Design</small></div> 2519 </div> 2520` : ''} 2521 </div> 2522 2523 ${modifier.price > 0 ? `<span class="badge bg-primary rounded-pill">$${modifier.price.toFixed(2)}</span>` : ''} 2524 </li> 2525`; 2526 } 2527 }).join(''); 2528 2529 } 2530 2531 return ` 2532 <div class="card mt-3"> 2533 <div class="card-header"> 2534 <h5 class="mb-0"> 2535 ${modifierGroup.name} 2536 </h5> 2537 </div> 2538 <div class="card-body"> 2539 <ul class="list-group">${modifiers}</ul> 2540 </div> 2541 </div> 2542 `; 2543 } 2544 2545 2546 function createMenuItem(item, index, sectionName) { 2547 // Replace line breaks with <br> and escape HTML special characters in the description 2548 const itemDescription = item.description 2549 .replace(/(?:\r\n|\r|\n)/g, '<br>') // Replace line breaks 2550 .replace(/&/g, '&') // Escape & 2551 .replace(/</g, '<') // Escape < 2552 .replace(/>/g, '>') // Escape > 2553 .replace(/"/g, '"') // Escape " 2554 .replace(/'/g, '''); // Escape ' 2555 2556 return ` 2557 <div class="card mt-3"> 2558 <div class="card-header"> 2559 <h5 class="mb-0"> 2560 <button class="btn btn-link" type="button" 2561 data-item-id="${item.id}" 2562 data-item-name="${item.name}" 2563 data-item-description="${itemDescription}" 2564 data-item-price="${item.price.toFixed(2)}" 2565 data-item-tax-rate="${item.tax_rate}" 2566 data-item-image="${item.image_url}" 2567 data-section-index="${index}" 2568 data-section-name="${sectionName}"> 2569 ${item.name} - $${item.price.toFixed(2)} 2570 </button> 2571 </h5> 2572 </div> 2573 </div> 2574 `; 2575 } 2576 2577 2578 function createMenuSection(section, index) { 2579 function createMenuItemWithIndex(item) { 2580 return createMenuItem(item, index, section.name); 2581 } 2582 2583 // Apply the same name transformation logic 2584 var displayName; 2585 2586 if (section.name === "Cakes") { 2587 displayName = "Custom Ice Cream Cakes"; 2588 } else if (section.name === "Pies") { 2589 displayName = "Custom Ice Cream Pies"; 2590 } else if (section.name === "Cakes & Pies") { 2591 displayName = "Ready Made Cakes & Pies"; 2592 } 2593 else if (section.name === "Miscellaneous") { 2594 displayName = "Other"; 2595 } 2596 else if (section.name === "Blasts") { 2597 displayName = "Bruster's Blasts"; 2598 } 2599 else if (section.name === "Shakes") { 2600 displayName = "Hand-Spun Shakes"; 2601 } 2602 else if (section.name === "Cups / Dishes") { 2603 displayName = "Cups"; 2604 } 2605 else if (section.name === "Take Home") { 2606 displayName = "Hand-Packed To Go"; 2607 } 2608 else if (section.name === "Kids") { 2609 displayName = "Kids' Stuff"; 2610 } 2611 else if (section.name === "Freeze") { 2612 displayName = "Freezes"; 2613 } 2614 else if (section.name === "Oreo Sandwiches") { 2615 displayName = "Ice Cream Sandwich Made With OREO® Wafers"; 2616 } 2617 else { 2618 displayName = section.name; // Fallback for all other cases 2619 } 2620 2621 const items = section.items.map(createMenuItemWithIndex).join(''); 2622 const additionalText = section.name === "Cakes & Pies" ? '<p class="text-center text-muted">Available For Same Day Pick Up</p>' : ''; 2623 const leadTimeText = section.items.some(item => item.leadTimeRequired) ? createLeadTimeText() : ''; 2624 2625 return ` 2626 <div class="col" id="section-${index}"> 2627 <div class="card menu-section"> 2628 <div class="card-header"> 2629 <h2>${displayName}</h2> 2630 </div> 2631 2632 <div class="card-body"> 2633 <button type="button" 2634 class="btn p-0 border-0 w-100" 2635 onclick="toggleItemsVisibility(${index})" 2636 aria-label="Show ${displayName} items" 2637 aria-controls="items-${index}" 2638 aria-expanded="false"> 2639 <img src="${section.image_url}" 2640 alt="${displayName}" 2641 class="img-fluid menu-img mb-3 rounded"> 2642 </button> 2643 2644 ${additionalText} 2645 ${leadTimeText} 2646 2647 <div class="items hidden-items" 2648 id="items-${index}"> 2649 ${items} 2650 </div> 2651 </div> 2652 </div> 2653 </div> 2654`; 2655 } 2656 2657 function createSectionLink(section, index) { 2658 var displayName; 2659 2660 if (section.name === "Cakes") { 2661 displayName = "Custom Ice Cream Cakes"; 2662 } else if (section.name === "Pies") { 2663 displayName = "Custom Ice Cream Pies"; 2664 } else if (section.name === "Cakes & Pies") { 2665 displayName = "Ready Made Cakes & Pies"; 2666 } 2667 else if (section.name === "Miscellaneous") { 2668 displayName = "Other"; 2669 } 2670 else if (section.name === "Blasts") { 2671 displayName = "Bruster's Blasts"; 2672 } 2673 else if (section.name === "Shakes") { 2674 displayName = "Hand-Spun Shakes"; 2675 } 2676 else if (section.name === "Cups / Dishes") { 2677 displayName = "Cups"; 2678 } 2679 else if (section.name === "Take Home") { 2680 displayName = "Hand-Packed To Go"; 2681 } 2682 else if (section.name === "Kids") { 2683 displayName = "Kids' Stuff"; 2684 } 2685 else if (section.name === "Freeze") { 2686 displayName = "Freezes"; 2687 } 2688 else if (section.name === "Oreo Sandwiches") { 2689 displayName = "Ice Cream Sandwich Made With OREO® Wafers"; 2690 } 2691 else { 2692 displayName = section.name; // Fallback for all other cases 2693 } 2694 2695 return ` 2696 <a href="#section-${index}" class="section-link">${displayName}</a> 2697 `; 2698 } 2699 2700 2701 function renderMenu(menuData) { 2702 const menuContainer = document.getElementById('menu-container'); 2703 const sectionLinksContainer = document.getElementById('section-links'); 2704 const sections = menuData[0].menu_sections.map((section, index) => createMenuSection(section, index)).join(''); 2705 const sectionLinks = menuData[0].menu_sections.map((section, index) => createSectionLink(section, index)).join(''); 2706 menuContainer.innerHTML = sections; 2707 sectionLinksContainer.innerHTML = sectionLinks; 2708 2709 2710 function populateSelectLists(item, type) { 2711 const flavorMap = new Map(); 2712 const cakeFlavorMap = new Map(); 2713 const fillingMap = new Map(); 2714 const colorMap = new Map(); 2715 const crustFlavorMap = new Map(); 2716 2717 let hasFlavor = false; 2718 let hasCake = false; 2719 let hasFilling = false; 2720 let hasColor = false; 2721 let hasCrust = false; 2722 2723 if (item.modifier_groups && item.modifier_groups.length > 0) { 2724 item.modifier_groups.forEach(modifierGroup => { 2725 if (modifierGroup.name === 'Select Flavor' || modifierGroup.name === 'Flavors') { 2726 hasFlavor = true; 2727 if (modifierGroup.modifiers) { 2728 modifierGroup.modifiers.forEac
2728h(category => { 2729 if (category.modifier_groups && category.modifier_groups.length > 0) { 2730 category.modifier_groups.forEach(flavorGroup => { 2731 if (flavorGroup.modifiers) { 2732 flavorGroup.modifiers.forEach(flavor => { 2733 flavorMap.set(flavor.name, { 2734 name: flavor.name, 2735 itemId: flavor.id 2736 }); 2737 }); 2738 } 2739 }); 2740 } 2741 }); 2742 } 2743 } 2744 2745 if (modifierGroup.name === 'Cake Flavor') { 2746 hasCake = true; 2747 if (modifierGroup.modifiers) { 2748 modifierGroup.modifiers.forEach(modifier => { 2749 cakeFlavorMap.set(modifier.name, { 2750 name: modifier.name, 2751 itemId: modifier.id 2752 }); 2753 }); 2754 } 2755 } 2756 2757 if (modifierGroup.name === 'Crust Flavor') { 2758 hasCrust = true; 2759 if (modifierGroup.modifiers) { 2760 modifierGroup.modifiers.forEach(modifier => { 2761 crustFlavorMap.set(modifier.name, { 2762 name: modifier.name, 2763 itemId: modifier.id 2764 }); 2765 }); 2766 } 2767 } 2768 2769 if (modifierGroup.name === 'Filling Flavor') { 2770 hasFilling = true; 2771 if (modifierGroup.modifiers) { 2772 modifierGroup.modifiers.forEach(modifier => { 2773 fillingMap.set(modifier.name, { 2774 name: modifier.name, 2775 itemId: modifier.id 2776 }); 2777 }); 2778 } 2779 } 2780 2781 if (modifierGroup.name === 'Writing Color') { 2782 hasColor = true; 2783 if (modifierGroup.modifiers) { 2784 modifierGroup.modifiers.forEach(modifier => { 2785 colorMap.set(modifier.name, { 2786 name: modifier.name, 2787 itemId: modifier.id 2788 }); 2789 }); 2790 } 2791 } 2792 }); 2793 } 2794 2795 if (type === 'cake') { 2796 populateDropdown('cakeicecreamselect', flavorMap, 'Select Flavor', hasFlavor); 2797 populateDropdown('cakecakeselect', cakeFlavorMap, 'Select Cake', hasCake); 2798 populateDropdown('cakefillingselect', fillingMap, 'Select Filling', hasFilling); 2799 populateDropdown('cakecolorselect', colorMap, 'Select Color of Writing', hasColor); 2800 2801 // â Add this line at the end of this function 2802 toggleMessageBox(item.modifier_groups); 2803 2804 } else if (type === 'pie') { 2805 populateDropdown('pieicecreamselect', flavorMap, 'Select Flavor', hasFlavor); 2806 populateDropdown('piecrustselect', crustFlavorMap, 'Select Crust', hasCrust); // Crust = Cake flavor 2807 populateDropdown('piefillingselect', fillingMap, 'Select Filling', hasFilling); 2808 populateDropdown('piecolorselect', colorMap, 'Select Color of Writing', hasColor); 2809 } 2810 } 2811 2812 function populateModifiers(modifiers, selectId) { 2813 const selectElement = document.getElementById(selectId);
2814 modifiers.forEach(modifier => { 2815 const option = document.createElement('option'); 2816 option.value = modifier.name; 2817 option.text = modifier.name; 2818 option.setAttribute('data-item-id', modifier.itemId); // Set data-item-id attribute 2819 selectElement.appendChild(option); 2820 }); 2821 } 2822 2823 function populateDropdown(selectId, map, defaultText, groupExists) { 2824 console.log(selectId); 2825 console.log(map); 2826 console.log(groupExists); 2827 const select = document.getElementById(selectId); 2828 if (!select) return; 2829 2830 select.innerHTML = ""; 2831 2832 const defaultOption = document.createElement("option"); 2833 defaultOption.value = ""; 2834 defaultOption.textContent = defaultText; 2835 select.appendChild(defaultOption); 2836 2837 map.forEach(value => { 2838 const option = document.createElement("option"); 2839 option.value = value.name; 2840 option.textContent = value.name; 2841 option.setAttribute('data-item-id', value.itemId); // Set data-item-id attribute 2842 select.appendChild(option); 2843 }); 2844 2845 // Only hide the dropdown if the modifier group doesn't exist at all 2846 select.style.display = groupExists ? "" : "none"; 2847 } 2848 2849 function toggleMessageBox(modifierGroups) { 2850 const hasCustomMessage = modifierGroups.some(group => 2851 group.name.toLowerCase().includes("message") 2852 ); 2853 2854 const messageBox = document.getElementById("cakemessage"); 2855 if (messageBox) { 2856 messageBox.style.display = hasCustomMessage ? "" : "none"; 2857 } 2858 } 2859 2860 2861 2862 2863 document.querySelectorAll('.btn-link').forEach(button => { 2864 button.addEventListener('click', function () { 2865 const itemId = this.getAttribute('data-item-id'); 2866 const itemName = this.getAttribute('data-item-name'); 2867 const itemDescription = this.getAttribute('data-item-description').replace(/(?:\r\n|\r|\n)/g, '<br>'); // Replace line breaks with <br> 2868 const itemPrice = this.getAttribute('data-item-price'); 2869 const itemTaxRate = this.getAttribute('data-item-tax-rate'); 2870 const itemImage = this.getAttribute('data-item-image'); 2871 const sectionName = this.getAttribute('data-section-name'); // Get the section name 2872 2873 isCake = false; 2874 isPie = false; 2875 2876 console.log(sectionName); 2877 2878 const item = menuData[0].menu_sections.flatMap(section => section.items).find(i => i.id == itemId && i.name == itemName); 2879 2880 console.log(item); 2881 2882 currentItem = item; 2883 currentModifierGroupIndex = 0; 2884 2885 const modifierGroups = item.modifier_groups 2886 .filter(group => group.modifiers && group.modifiers.length > 0) 2887 .map(group => createModifierGroup(group, sectionName)) 2888 .filter(groupHtml => groupHtml !== ''); 2889 2890 console.log(modifierGroups.length); 2891 2892 // Add Quantity Input with Validation 2893 const modalContent = ` 2894 <h3>${itemName}</h3> 2895 <p>${itemDescription}</p> 2896 <img src="${itemImage}" alt="${itemName}" class="img-fluid menu-img mb-3 rounded"> 2897 <h4>Price: $${itemPrice}</h4> 2898 <div class="mt-3"> 2899 <label for="itemQuantity" class="d-block">Quantity:</label> 2900 <input type="text" id="itemQuantity" class="form-control" value="1" inputmode="numeric" style="width: 80px; -moz-appearance: textfield;"> 2901 <small id="quantityError" class="text-danger" style="display: none;">Please enter a valid quantity.</small> 2902 </div> 2903 <div id="modifier-groups">${modifierGroups.join('')}</div> 2904 `; 2905
2906 document.querySelector('#modal-content').innerHTML = modalContent; 2907 2908 // Quantity Input Validation 2909 const quantityInput = document.getElementById('itemQuantity'); 2910 const quantityError = document.getElementById('quantityError'); 2911 2912 quantityInput.addEventListener('input', function () { 2913 const value = quantityInput.value; 2914 if (!/^\d+$/.test(value) || parseInt(value, 10) < 1) { 2915 quantityError.style.display = 'block'; 2916 } else { 2917 quantityError.style.display = 'none'; 2918 } 2919 }); 2920 2921 2922 2923 if (sectionName == 'Cakes') { 2924 if (modifierGroups.length < 6) { 2925 $(".btn-cake-step1").text('Add To Cart'); 2926 } 2927 isCake = true; 2928 currentModifierGroupIndex = 4; 2929 //populateSelectLists(menuData, 'cake'); 2930 populateSelectLists(item, 'cake'); 2931 $(".cakedescription").remove(); 2932 $(".cakeimage").before('<p class="text-muted cakedescription">' + itemDescription + '</p>'); 2933 $(".cakeimage").attr('src', itemImage); 2934 $("#modalCake1").modal('show'); 2935 } else if (sectionName == 'Pies') { 2936 if (modifierGroups.length < 6) { 2937 $(".btn-pie-step1").text('Add To Cart'); 2938 } 2939 isPie = true; 2940 currentModifierGroupIndex = 4; 2941 //populateSelectLists(menuData, 'pie'); 2942 populateSelectLists(item, 'pie'); 2943 $(".piedescription").remove(); 2944 $(".pieimage").before('<p class="text-muted piedescription">' + itemDescription + '</p>'); 2945 $(".pieimage").attr('src', itemImage); 2946 $("#modalPie1").modal('show'); 2947 } else { 2948 const nextModifierGroupButtonTop = document.querySelector('#nextModifierGroupButtonTop'); 2949 const nextModifierGroupButton = document.querySelector('#nextModifierGroupButton'); 2950 const addToCartButtonTop = document.querySelector('#addToCartButtonTop'); 2951 const addToCartButton = document.querySelector('#addToCartButton'); 2952 2953 if (modifierGroups.length === 0) { 2954 if (nextModifierGroupButtonTop) nextModifierGroupButtonTop.style.display = 'none'; 2955 if (nextModifierGroupButton) nextModifierGroupButton.style.display = 'none'; 2956 if (addToCartButtonTop) addToCartButtonTop.style.display = 'block'; 2957 if (addToCartButton) addToCartButton.style.display = 'block'; 2958 } else { 2959 if (nextModifierGroupButtonTop) nextModifierGroupButtonTop.style.display = 'block'; 2960 if (nextModifierGroupButton) nextModifierGroupButton.style.display = 'block'; 2961 if (addToCartButtonTop) addToCartButtonTop.style.display = 'none'; 2962 if (addToCartButton) addToCartButton.style.display = 'none'; 2963 showNextModifierGroup(); 2964 } 2965 2966 const itemModal = new bootstrap.Modal(document.getElementById('itemModal')); 2967 itemModal.show(); 2968 } 2969 2970 }); 2971 }); 2972 2973 } 2974 2975 let originalTopButtonsParent = null; 2976 2977 // Update the showNextModifierGroup function to handle Back button visibility 2978 function showNextModifierGroup() { 2979 const modifierGroups = Array.from(document.querySelectorAll('#modifier-groups .card')).filter(group => group.querySelector('.list-group-item')); 2980 const topButtonsContainer = document.getElementById('top-buttons'); 2981 const modifierGroupsDiv = document.getElementById('modifier-groups'); 2982 2983 // Store the original parent of the top buttons if not already stored 2984 if (!originalTopButtonsParent) { 2985 originalTopButtonsParent = topButtonsContainer.parentNode; 2986 } 2987 // Move the top buttons to the new position 2988 if (topButtonsContainer && modifierGroupsDiv) { 2989 modifierGroupsDiv.parentNode.insertBefore(topButtonsContainer, modifierGroupsDiv); 2990 } 2991 2992 if (currentModifierGroupIndex < modifierGroups.length) { 2993 modifierGroups.forEach((group, index) => { 2994 if (index === currentModifierGroupIndex) { 2995 group.style.display = 'block'; 2996 // Scroll to the top-buttons element 2997 topButtonsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); 2998 } else { 2999 group.style.display = 'none'; 3000 } 3001 }); 3002 3003 const nextModifierGroupButtonTop = document.querySelector('#nextModifierGroupButtonTop'); 3004 const nextModifierGroupButton = document.querySelector('#nextModifierGroupButton'); 3005 const addToCartButtonTop = document.querySelector('#addToCartButtonTop'); 3006 const addToCartButton = document.querySelector('#addToCartButton'); 3007 const backButtonTop = document.querySelector('#backButtonTop'); 3008 const backButton = document.querySelector('#backButton'); 3009 3010 if (currentModifierGroupIndex === modifierGroups.length - 1) { 3011 if (nextModifierGroupButtonTop) nextModifierGroupButtonTop.style.display = 'none'; 3012 if (nextModifierGroupButton) nextModifierGroupButton.style.display = 'none'; 3013 if (addToCartButtonTop) addToCartButtonTop.style.display = 'block'; 3014 if (addToCartButton) addToCartButton.style.display = 'block'; 3015 } else { 3016 if (nextModifierGroupButtonTop) nextModifierGroupButtonTop.style.display = 'block'; 3017 if (nextModifierGroupButton) nextModifierGroupButton.style.display = 'block'; 3018 if (addToCartButtonTop) addToCartButtonTop.style.display = 'none'; 3019 if (addToCartButton) addToCartButton.style.display = 'none'; 3020 } 3021 3022 if (currentModifierGroupIndex > 0) { 3023 if (backButtonTop) backButtonTop.style.display = 'block'; 3024 if (backButton) backButton.style.display = 'block'; 3025 } else { 3026 if (backButtonTop) backButtonTop.style.display = 'none'; 3027 if (backButton) backButton.style.display = 'none'; 3028 } 3029 } 3030 } 3031 3032 // Function to move the top buttons back to their original position 3033 function moveTopButtonsBack() { 3034 const topButtonsContainer = document.getElementById('top-buttons'); 3035 if (originalTopButtonsParent) { 3036 originalTopButtonsParent.appendChild(topButtonsContainer); 3037 } 3038 } 3039 3040 function checkEmptyCake() { 3041 let somethingEmpty = false; 3042 3043 const selectors = [ 3044 '#cakeicecreamselect', 3045 '#cakefillingselect', 3046 '#cakecakeselect', 3047 '#cakecolorselect' 3048 ]; 3049
3050 selectors.forEach(selector => { 3051 const $el = $(selector); 3052 if ($el.is(':visible') && $el.val() === '') { 3053 $el.animateCss('jello'); 3054 somethingEmpty = true; 3055 } 3056 }); 3057 3058 return somethingEmpty; 3059 } 3060 3061 3062 function checkEmptyPie() { 3063 let somethingEmpty = false; 3064 3065 if ($('#pieicecreamselect').val() == '') { 3066 $('#pieicecreamselect').animateCss('jello'); 3067 somethingEmpty = true; 3068 } 3069 3070 if ($('#piefillingselect').val() == '') { 3071 $('#piefillingselect').animateCss('jello'); 3072 somethingEmpty = true; 3073 } 3074 3075 //if ($('#piecrustselect').val() == '' && $("input[type='hidden'][id='hfProductSelected']").val() != 'Valentines Day Pie' && $("input[type='hidden'][id='hfProductSelected']").val() != 'Pink Valentines Day Pie' && $("input[type='hidden'][id='hfProductSelected']").val() != 'Red Valentines Day Pie') { 3076 // $('#piecrustselect').animateCss('animated jello'); 3077 // somethingEmpty = true; 3078 //} 3079 3080 if ($('#piecrustselect').val() == '') { 3081 $('#piecrustselect').animateCss('jello'); 3082 somethingEmpty = true; 3083 } 3084 3085 if ($('#piecolorselect').val() == '') { 3086 $('#piecolorselect').animateCss('jello'); 3087 somethingEmpty = true; 3088 } 3089 3090 return somethingEmpty; 3091 } 3092 3093 //cake and pie continue listeners 3094 $(document).on('click', '.btn-cake-step1', function () { 3095 var $btn = $(this); // wrap this in jQuery to use jQuery methods 3096 var btnText = $btn.text(); // get the button's text value 3097 3098 if (!checkEmptyCake()) { 3099 $("#modalCake1").modal('hide'); 3100 3101 3102 if (btnText != 'Add To Cart') { 3103 $("#backButtonTop").hide(); 3104 $("#backButton").hide(); 3105 currentModifierGroupIndex++; 3106 const itemModal = new bootstrap.Modal(document.getElementById('itemModal')); 3107 itemModal.show(); 3108 showNextModifierGroup(); 3109 } 3110 else { 3111 //add it to the cart 3112 addToCart(); 3113 } 3114 3115 } 3116 3117 3118 3119 }); 3120 3121 //cake and pie continue listeners 3122 $(document).on('click', '.btn-pie-step1', function () { 3123 var $btn = $(this); // wrap this in jQuery to use jQuery methods 3124 var btnText = $btn.text(); // get the button's text value 3125 3126 if (!checkEmptyPie()) { 3127 3128 $("#modalPie1").modal('hide'); 3129 if (btnText != 'Add To Cart') { 3130 $("#backButtonTop").hide(); 3131 $("#backButton").hide(); 3132 currentModifierGroupIndex++; 3133 const itemModal = new bootstrap.Modal(document.getElementById('itemModal')); 3134 itemModal.show(); 3135 showNextModifierGroup(); 3136 } 3137 else { 3138 //add it to the cart 3139 addToCart(); 3140 } 3141 } 3142 3143 3144 }); 3145 3146 // Event listeners for the Back buttons 3147 document.getElementById('backButton').addEventListener('click', function () { 3148 showPreviousModifierGroup(); 3149 }); 3150 3151 document.getElementById('backButtonTop').addEventListener('click', function () { 3152 showPreviousModifierGroup(); 3153 }); 3154 3155 // Event listeners for the buttons 3156 document.getElementById('nextModifierGroupButton').addEventListener('click', function () { 3157 if (validateModifierGroups(this)) { 3158 currentModifierGroupIndex++; 3159 moveTopButtonsBack(); 3160 showNextModifierGroup(); 3161 } 3162 }); 3163 3164 document.getElementById('nextModifierGroupButtonTop').addEventListener('click', function () { 3165 if (validateModifierGroups(this)) { 3166 currentModifierGroupIndex++; 3167 moveTopButtonsBack(); 3168 showNextModifierGroup(); 3169 } 3170 }); 3171 3172 document.getElementById('addToCartButton').addEventListener('click', function () { 3173 if (validateModifierGroups(this)) { 3174 addToCart(); 3175 moveTopButtonsBack(); 3176 } 3177 }); 3178 3179 document.getElementById('addToCartButtonTop').addEventListener('click', function () { 3180 if (validateModifierGroups(this)) { 3181 addToCart(); 3182 moveTopButtonsBack(); 3183 } 3184 }); 3185 3186 // Event listener for the modal close event to call moveTopButtonsBack() 3187 document.getElementById('itemModal').addEventListener('hidden.bs.modal', function () { 3188 moveTopButtonsBack(); 3189 }); 3190 3191 function validateModifierGroups(buttonElement) { 3192 if (!currentItem || !Array.isArray(currentItem.modifier_groups)) { 3193 console.error('Invalid currentItem or modifier_groups not an array'); 3194 return false; 3195 } 3196 3197 // No modifier groups, skip validation 3198 if (currentItem.modifier_groups.length === 0) { 3199 return true; 3200 } 3201 3202 // Check if currentModifierGroupIndex is valid 3203 if (typeof currentModifierGroupIndex !== 'number' || 3204 currentModifierGroupIndex < 0 || 3205 currentModifierGroupIndex >= currentItem.modifier_groups.length) { 3206 console.error('Invalid modifier group index'); 3207 return false; 3208 } 3209 3210 const currentGroup = currentItem.modifier_groups[currentModifierGroupIndex]; 3211 3212 if (!currentGroup || !Array.isArray(currentGroup.modifiers)) { 3213 console.error('Invalid currentGroup or modifiers not an array'); 3214 return false; 3215 } 3216 console.log(currentGroup); 3217 console.log(currentGroup.modifiers); 3218 3219 let selectedCount = 0; 3220 let firstElement = null; 3221 let skipValidation = false; 3222 3223 // Check if the current group is a nested flavor group 3224 if (currentGroup.name === "Flavors" || currentGroup.name === "Select Flavor" || currentGroup.name === 'Flavor #1' || currentGroup.name === 'Flavor #2' || currentGroup.name === 'Flavor #3' ) {
3225 currentGroup.modifiers.forEach(flavorGroup => { 3226 if (flavorGroup.modifier_groups && flavorGroup.modifier_groups.length > 0) { 3227 flavorGroup.modifier_groups[0].modifiers.forEach(modifier => { 3228 const checkbox = document.getElementById(`modifier-${flavorGroup.name}-${modifier.id}`); 3229 if (!firstElement) { 3230 firstElement = checkbox; 3231 } 3232 if (checkbox && checkbox.checked) { 3233 selectedCount++; 3234 } 3235 }); 3236 } 3237 }); 3238 } else { 3239 currentGroup.modifiers.forEach(modifier => { 3240 if (modifier.inputType === 'textarea') { 3241 // Skip validation for optional input types like textarea 3242 console.log('skip validation'); 3243 skipValidation = true; 3244 return; 3245 } 3246 3247 if (currentGroup.name !== "Extras") { 3248 const checkbox = document.getElementById(`modifier-${currentGroup.name}-${modifier.id}`); 3249 if (!firstElement) { 3250 firstElement = checkbox; 3251 } 3252 if (checkbox && checkbox.checked) { 3253 selectedCount++; 3254 } 3255 } else { 3256 const quantity = parseInt(document.getElementById(`quantity-${currentGroup.name}-${modifier.id}`).value); 3257 if (!firstElement) { 3258 firstElement = document.getElementById(`quantity-${currentGroup.name}-${modifier.id}`); 3259 } 3260 if (quantity > 0) { 3261 selectedCount += quantity; 3262 } 3263 } 3264 }); 3265 } 3266 3267 if (skipValidation) { 3268 return true; 3269 } 3270 3271 if (selectedCount < currentGroup.minimum_amount) { 3272 showPopperAlert(`Please select at least ${currentGroup.minimum_amount} ${currentGroup.name}.`, buttonElement); 3273 return false; 3274 } else if (selectedCount > currentGroup.maximum_amount) { 3275 showPopperAlert(`Please select no more than ${currentGroup.maximum_amount} ${currentGroup.name}.`, buttonElement); 3276 return false; 3277 } 3278 3279 return true; 3280 } 3281 3282 3283 const confirmationModalElement = document.getElementById('confirmationModal'); 3284 const continueShoppingButton = document.getElementById('continueShoppingButton'); 3285 const checkoutButtonConfirmation = document.getElementById('checkoutButtonConfirmation'); 3286 const itemModalElement = document.getElementById('itemModal'); 3287 const cartModalElement = document.getElementById('cartModal'); 3288 let confirmationModal; 3289 let itemModal; 3290 let cartModal; 3291 3292 // Show confirmation modal 3293 function showConfirmationModal() { 3294 confirmationModal = new bootstrap.Modal(confirmationModalElement); 3295 confirmationModal.show(); 3296 } 3297 3298 // Add event listeners for modal buttons 3299 continueShoppingButton.addEventListener('click', function () { 3300 confirmationModal.hide(); 3301 //window.scrollTo({ top: 0, behavior: 'smooth' }); 3302 }); 3303 3304 checkoutButtonConfirmation.addEventListener('click', function () { 3305 confirmationModal.hide(); 3306 cartModal = new bootstrap.Modal(cartModalElement); 3307 cartModal.show(); 3308 }); 3309 3310 // Add to cart function 3311 function addToCart() { 3312 var selectedModifiers = []; 3313 var selectedExtras = []; 3314 3315 var blnNestedFlavors = false; 3316 if (getQueryStringParameter('number') != '999999'){ 3317 blnNestedFlavors = true; 3318 } 3319 3320 var parentQty = $("#itemQuantity").val() && parseInt($("#itemQuantity").val()) > 0 3321 ? parseInt($("#itemQuantity").val()) 3322 : 1; 3323 3324 if (!isPie && !isCake) { 3325 currentItem.modifier_groups.forEach(group => { 3326 3327 if ( 3328 group.name === "Flavors" || 3329 group.name === "Select Flavor" || 3330 (group.name === "Flavor #1" && blnNestedFlavors) || 3331 (group.name === "Flavor #2" && blnNestedFlavors) || 3332 (group.name === "Flavor #3" && blnNestedFlavors) 3333 ) { 3334
3335 group.modifiers.forEach(flavorGroup => { 3336 3337 if (flavorGroup.modifier_groups && flavorGroup.modifier_groups.length > 0) { 3338 3339 flavorGroup.modifier_groups[0].modifiers.forEach(modifier => { 3340 3341 const checkbox = document.getElementById( 3342 `modifier-${flavorGroup.name}-${modifier.id}` 3343 ); 3344 3345 if (checkbox && checkbox.checked) { 3346 3347 const flavorQty = parseInt( 3348 checkbox.dataset.flavorQty || "1", 3349 10 3350 ); 3351 3352 for (let i = 0; i < flavorQty; i++) { 3353 3354 selectedModifiers.push({ 3355 id: modifier.id, 3356 name: modifier.name, 3357 price: modifier.price, 3358 tax: currentItem.tax_rate, 3359 group: group.name, 3360 quantity: 1 3361 }); 3362 3363 } 3364 } 3365 }); 3366 } 3367 }); 3368 3369 } else { 3370 group.modifiers.forEach(modifier => { 3371 if (modifier.inputType === 'textarea') { 3372 const textarea = document.getElementById(`modifier-${group.name}-${modifier.id}`); 3373 if (textarea && textarea.value.trim() !== "") { 3374 selectedModifiers.push({ 3375 id: modifier.id, 3376 name: textarea.value.trim(), 3377 value: textarea.value.trim(), // Capture the textarea value 3378 price: modifier.price, 3379 tax: currentItem.tax_rate, 3380 group:group.name, 3381 quantity: 1 3382 }); 3383 } 3384 } else if (group.name !== "Extras") { 3385 const checkbox = document.getElementById(`modifier-${group.name}-${modifier.id}`); 3386 if (checkbox && checkbox.checked) { 3387 selectedModifiers.push({ 3388 id: modifier.id, 3389 name: modifier.name, 3390 price: modifier.price, 3391 tax: currentItem.tax_rate, 3392 group:group.name, 3393 quantity: 1 3394 }); 3395 } 3396 } else { 3397 const quantity = parseInt(document.getElementById(`quantity-${group.name}-${modifier.id}`).value); 3398 if (quantity > 0) { 3399 //changing to include actual extra tax here 3400 //change tax from currentItem.tax_rate to modifier.tax 3401 3402 selectedExtras.push({ 3403 id: modifier.id, 3404 name: modifier.name, 3405 price: modifier.price, 3406 tax: modifier.tax, 3407 group:group.name, 3408 quantity: quantity 3409 }); 3410 } 3411 } 3412 }); 3413 } 3414 }); 3415 } 3416 else if (isCake) { 3417 //get the modifiers from the modal 3418 // Iterate over the range 3419 for (var i = 0; i < 6; i++) { 3420 switch (i) { 3421 case 0: 3422 // flavor 3423 console.log("Case 0"); 3424 // Your logic for case 0 3425 var icecreamflavor = $('#cakeicecreamselect').val(); 3426 var itemid = $('#cakeicecreamselect').attr('data-item-id'); 3427 selectedModifiers.push({ 3428 id: itemid, 3429 name: icecreamflavor, 3430 price: 0, 3431 tax: 0, 3432 group: 'Ice Cream Flavor', 3433 quantity: 1 3434 }); 3435 break; 3436 case 1: 3437 // cake type 3438 console.log("Case 1"); 3439 // Your logic for case 1 3440 var icecreamflavor = $('#cakecakeselect').val(); 3441 var itemid = $('#cakecakeselect').attr('data-item-id'); 3442 selectedModifiers.push({ 3443 id: itemid, 3444 name: icecreamflavor, 3445 price: 0, 3446 tax: 0, 3447 group: 'Cake Flavor', 3448 quantity: 1 3449 }); 3450 break; 3451 case 2: 3452 // filling type 3453 console.log("Case 2"); 3454 // Your logic for case 2 3455 var icecreamflavor = $('#cakefillingselect').val(); 3456 var itemid = $('#cakefillingselect').attr('data-item-id'); 3457 selectedModifiers.push({ 3458 id: itemid, 3459 name: icecreamflavor, 3460 price: 0, 3461 tax: 0, 3462 group: 'Filling Flavor', 3463 quantity: 1 3464 }); 3465 break; 3466 case 3: 3467 // color type 3468 console.log("Case 3"); 3469 // Your logic for case 3 3470 var icecreamflavor = $('#cakecolorselect').val(); 3471 var itemid = $('#cakecolorselect').attr('data-item-id'); 3472 selectedModifiers.push({ 3473 id: itemid, 3474 name: icecreamflavor, 3475 price: 0, 3476 tax: 0, 3477 group: 'Cake Color', 3478 quantity: 1 3479 }); 3480 break; 3481 case 4: 3482 // message 3483 console.log("Case 4"); 3484 // Your logic for case 4 3485 var icecreamflavor = $("textarea[id='cakemessage']").val(); 3486 var itemid = $('#cakeicecreamselect').attr('data-item-id'); 3487 selectedModifiers.push({ 3488 id: 'cakemessage', 3489 name: icecreamflavor, 3490 price: 0, 3491 tax: 0, 3492 group: 'Cake Message', 3493 quantity: 1 3494 }); 3495 break; 3496 case 5: 3497 // grab the extras now if any... 3498 console.log("Case 5"); 3499 // Your logic for case 4 3500 currentItem.modifier_groups.forEach(group => { 3501 if (group.name === "Extras") {
3502 group.modifiers.forEach(modifier => { 3503 const quantity = parseInt(document.getElementById(`quantity-${group.name}-${modifier.id}`).value); 3504 if (quantity > 0) { 3505 3506 //changing to include actual extra tax here 3507 //change tax from currentItem.tax_rate to modifier.tax 3508 // If user selects "All Ice Cream Upcharge", remove "Cake Flavor" from selectedModifiers 3509 console.log(modifier); 3510 if (modifier.name === "All Ice Cream Upcharge") { 3511 console.log('removing cake'); 3512 selectedModifiers = selectedModifiers.filter(mod => mod.group !== "Cake Flavor"); 3513 } 3514 3515 selectedExtras.push({ 3516 id: modifier.id, 3517 name: modifier.name, 3518 price: modifier.price, 3519 tax: currentItem.tax_rate, 3520 group: group.name, quantity: quantity 3521 }); 3522 } 3523 3524 }); 3525 } 3526 }); 3527 break; 3528 default: 3529 // Handle default case if needed 3530 console.log("Default case"); 3531 break; 3532 } 3533 } 3534 3535 3536 } 3537 else if (isPie) { 3538 ///get the modifiers from the modal 3539 for (var i = 0; i < 6; i++) { 3540 switch (i) { 3541 case 0: 3542 // flavor 3543 console.log("Case 0"); 3544 // Your logic for case 0 3545 var icecreamflavor = $('#pieicecreamselect').val(); 3546 var itemid = $('#pieicecreamselect').attr('data-item-id'); 3547 selectedModifiers.push({ 3548 id: itemid, 3549 name: icecreamflavor, 3550 price: 0, 3551 tax: 0, 3552 group: 'Ice Cream Flavor', 3553 quantity: 1 3554 }); 3555 break; 3556 case 1: 3557 // cake type 3558 console.log("Case 1"); 3559 // Your logic for case 1 3560 var icecreamflavor = $('#piecrustselect').val(); 3561 var itemid = $('#piecrustselect').attr('data-item-id'); 3562 selectedModifiers.push({ 3563 id: itemid, 3564 name: icecreamflavor, 3565 price: 0, 3566 tax: 0, 3567 group: 'Crust Flavor', 3568 quantity: 1 3569 }); 3570 break; 3571 case 2: 3572 // filling type 3573 console.log("Case 2"); 3574 // Your logic for case 2 3575 var icecreamflavor = $('#piefillingselect').val(); 3576 var itemid = $('#piefillingselect').attr('data-item-id'); 3577 selectedModifiers.push({ 3578 id: itemid, 3579 name: icecreamflavor, 3580 price: 0, 3581 tax: 0, 3582 group: 'Filling Flavor', 3583 quantity: 1 3584 }); 3585 break; 3586 case 3: 3587 // color type 3588 console.log("Case 3"); 3589 // Your logic for case 3 3590 var icecreamflavor = $('#piecolorselect').val(); 3591 var itemid = $('#piecolorselect').attr('data-item-id'); 3592 selectedModifiers.push({ 3593 id: itemid, 3594 name: icecreamflavor, 3595 price: 0, 3596 tax: 0, 3597 group: 'Pie Color', 3598 quantity: 1 3599 }); 3600 break; 3601 case 4: 3602 // message 3603 console.log("Case 4"); 3604 // Your logic for case 4 3605 var icecreamflavor = $("textarea[id='piemessage']").val(); 3606 var itemid = $('#cakeicecreamselect').attr('data-item-id'); 3607 selectedModifiers.push({ 3608 id: 'piemessage', 3609 name: icecreamflavor, 3610 price: 0, 3611 tax: 0, 3612 group: 'Pie Message', 3613 quantity: 1 3614 }); 3615 break; 3616 case 5: 3617 // grab the extras now if any... 3618 console.log("Case 5"); 3619 // Your logic for case 4 3620 currentItem.modifier_groups.forEach(group => { 3621 if (group.name === "Extras") {
3622 group.modifiers.forEach(modifier => { 3623 const quantity = parseInt(document.getElementById(`quantity-${group.name}-${modifier.id}`).value); 3624 if (quantity > 0) { 3625 selectedExtras.push({ 3626 id: modifier.id, 3627 name: modifier.name, 3628 price: modifier.price, 3629 tax: currentItem.tax_rate, 3630 group: group.name, 3631 quantity: quantity 3632 }); 3633 } 3634 3635 }); 3636 } 3637 }); 3638 break; 3639 default: 3640 // Handle default case if needed 3641 console.log("Default case"); 3642 break; 3643 } 3644 } 3645 } 3646 3647 3648 for (var i = 0; i < parentQty; i++) { 3649 3650 cart.push({ 3651 id: currentItem.id, 3652 name: currentItem.name, 3653 price: currentItem.price, 3654 taxRate: currentItem.tax_rate, 3655 quantity: 1, // Each line is a single item 3656 modifiers: JSON.parse(JSON.stringify(selectedModifiers)), // deep copy 3657 extras: JSON.parse(JSON.stringify(selectedExtras)), // deep copy 3658 leadTimeRequired: currentItem.leadTimeRequired, 3659 location: $("#hiddenFieldAddress").val().replace('Ordering for Pick Up From: ','') 3660 }); 3661 3662 } 3663 3664 3665 // Persist the updated cart 3666 saveCart(); 3667 3668 var newCartItems = cart.slice(-parentQty); 3669 3670 try { 3671 var ga4Items = []; 3672 3673 newCartItems.forEach(function (item) { 3674 ga4Items = ga4Items.concat(buildGA4Items(item)); 3675 }); 3676 3677 var totalValue = ga4Items.reduce(function (sum, item) { 3678 return sum + (Number(item.price || 0) * Number(item.quantity || 0)); 3679 }, 0); 3680 3681 if ( 3682 window.GTM && 3683 typeof window.GTM.pushEvent === "function" 3684 ) { 3685 window.GTM.pushEvent({ 3686 event: "add_to_cart", 3687 store_number: getStoreNumber(), 3688 ecommerce: { 3689 currency: "USD", 3690 value: Number(totalValue.toFixed(2)), 3691 items: ga4Items 3692 } 3693 }); 3694 } else { 3695 console.warn( 3696 "GTM add_to_cart event skipped because window.GTM.pushEvent is unavailable." 3697 ); 3698 } 3699 } catch (error) { 3700 console.error("Unable to send GTM add_to_cart event:", error); 3701 } 3702 3703 /* 3704 * Cart functionality must continue even when analytics fails. 3705 */ 3706 renderCart(); 3707 3708 try { 3709 const itemModal = bootstrap.Modal.getInstance( 3710 document.getElementById("itemModal") 3711 ); 3712 3713 if (itemModal) { 3714 itemModal.hide(); 3715 } 3716 } catch (error) { 3717 console.error("Error hiding itemModal:", error); 3718 } 3719 3720 showConfirmationModal(); 3721 } 3722 3723 document.getElementById('cartIcon').addEventListener('click', function () { 3724 const cartModal = new bootstrap.Modal(document.getElementById('cartModal')); 3725 cartModal.show(); 3726 }); 3727 3728 renderMenu(menuData); 3729 }); 3730 3731 function showFlavorImage(flavorName, imageUrl, checkboxId, event) { 3732 // Prevent default action and stop propagation 3733 event.preventDefault(); 3734 event.stopPropagation(); 3735 3736 const popperContainer = document.getElementById('popper-container'); 3737 popperContainer.innerHTML = `<div class="popper-image" id="flavorImagePopper"> 3738 <div class="popper-close" onclick="closePopper()">Ã</div> 3739 <strong>${flavorName}</strong> 3740 <img src="${imageUrl}" alt="${flavorName}"> 3741 </div>`; 3742 3743 const flavorImagePopper = document.getElementById('flavorImagePopper'); 3744 const checkboxElement = document.getElementById(checkboxId); 3745 3746 Popper.createPopper(checkboxElement, flavorImagePopper, { 3747 placement: 'top', 3748 modifiers: [{ 3749 name: 'offset', 3750 options: { 3751 offset: [0, 8], 3752 }, 3753 }], 3754 }); 3755 } 3756 3757 function closePopper() { 3758 const popperContainer = document.getElementById('popper-container'); 3759 popperContainer.innerHTML = ''; 3760 } 3761 3762 function renderCart() { 3763 const cartModalBody = document.getElementById('cartModalBody'); 3764 const cartItemCount = document.getElementById('cartItemCount'); 3765 const cartSubtotal = document.getElementById('cartSubtotal'); 3766 const cartTax = document.getElementById('cartTax'); 3767 const cartTotal = document.getElementById('cartTotal'); 3768 3769 if (cart.length === 0) { 3770 cartModalBody.innerHTML = '<p>Your cart is empty.</p>'; 3771 cartItemCount.textContent = '0'; 3772 cartSubtotal.textContent = 'Subtotal: $0.00'; 3773 cartTax.textContent = 'Tax: $0.00'; 3774 cartTotal.textContent = 'Total: $0.00'; 3775 return; 3776 } 3777 3778 let subtotal = 0; 3779 let tax = 0; 3780 3781 const cartItemsHtml = cart.map((item, index) => { 3782 const itemSubtotal = item.price * item.quantity; 3783 let itemTax = itemSubtotal * (item.taxRate / 100); 3784 3785 const modifiersHtml = item.modifiers.map(modifier => { 3786 const modifierSubtotal = modifier.price * modifier.quantity; 3787 const modifierTax = modifierSubtotal * (modifier.tax / 100); 3788 subtotal += modifierSubtotal; 3789 tax += modifierTax; 3790 return ` 3791 <div class="cart-item-modifier">
3791${modifier.name} ${modifier.price > 0 ? `- $${modifier.price.toFixed(2)}` : ''}</div> 3792 `; 3793 }).join(''); 3794 3795 const extrasHtml = item.extras.map(extra => { 3796 const extraSubtotal = extra.price * extra.quantity; 3797 const extraTax = extraSubtotal * (extra.tax / 100); 3798 subtotal += extraSubtotal; 3799 tax += extraTax; 3800 return ` 3801 <div class="cart-item-extra">${extra.quantity} x ${extra.name} ${extra.price > 0 ? `- $${extra.price.toFixed(2)}` : ''}</div> 3802 `; 3803 }).join(''); 3804 3805 subtotal += itemSubtotal; 3806 tax += itemTax; 3807 3808 return ` 3809 <div class="cart-item"> 3810 <div class="cart-item-details"> 3811 <div class="cart-item-name"> 3812 ${item.name} ${item.price > 0 ? `- $${item.price.toFixed(2)}` : ''} 3813 ${item.quantity > 1 ? `<div class="cart-item-quantity">Quantity: ${item.quantity}</div>` : ''} 3814 </div> 3815 ${modifiersHtml} 3816 ${extrasHtml} 3817 </div> 3818 <button class="btn btn-danger btn-sm" onclick="removeFromCart(${index})">Remove</button> 3819 </div> 3820 `; 3821 }).join(''); 3822 3823 cartModalBody.innerHTML = cartItemsHtml; 3824 cartItemCount.textContent = cart.length.toString(); 3825 cartSubtotal.textContent = `Subtotal: $${subtotal.toFixed(2)}`; 3826 cartTax.textContent = `Tax: $${tax.toFixed(2)}`; 3827 cartTotal.textContent = `Total: $${(subtotal + tax).toFixed(2)}`; 3828 3829 // Pass the total amount to the hidden field 3830 document.getElementById('cartTotalAmount').value = (subtotal + tax).toFixed(2); 3831 } 3832 3833 3834 3835 3836 3837 // Event listener for the checkout button 3838 document.getElementById('checkoutButton').addEventListener('click', function () { 3839 3840 // Inject mandatory items into cart only if they don't already exist 3841 try { 3842 const mandatoryItemsJson = document.getElementById('hfMandatoryItems').value; 3843 const mandatoryItems = JSON.parse(mandatoryItemsJson); 3844 3845 mandatoryItems.forEach(item => { 3846 const alreadyInCart = cart.some(cartItem => cartItem.id === item.ItemId); 3847 3848 if (!alreadyInCart) { 3849 cart.push({ 3850 id: item.ItemId, 3851 name: item.ItemName, 3852 price: item.Price, 3853 taxRate: item.Tax, 3854 quantity: item.Qty, 3855 modifiers: [], 3856 extras: [], 3857 leadTimeRequired: false, 3858 location: $("#hiddenFieldAddress").val().replace('Ordering for Pick Up From: ', '') 3859 }); 3860 } 3861 }); 3862 } catch (err) { 3863 console.error("Failed to parse mandatory items:", err); 3864 } 3865 3866 const cartModal = bootstrap.Modal.getInstance(document.getElementById('cartModal')); 3867 cartModal.hide(); 3868 const checkoutModal = new bootstrap.Modal(document.getElementById('checkoutModal')); 3869 3870 // Pass the total amount to the checkout modal 3871 const totalAmount = calculateTotalAmount(); 3872 document.getElementById('cartTotalAmount').value = totalAmount.toFixed(2); 3873 3874 // Populate order details 3875 const orderDetailsContainer = document.getElementById('orderDetails'); 3876 orderDetailsContainer.innerHTML = generateOrderDetailsHtml(cart); 3877 3878 // Populate total amount 3879 const totalAmountContainer = document.getElementById('totalAmount'); 3880 totalAmountContainer.innerHTML = `Total Amount: $${totalAmount.toFixed(2)}`; 3881 3882 checkoutModal.show(); 3883 }); 3884 3885 function generateOrderDetailsHtml(cart) { 3886 let mainItemSubtotal = 0; 3887 let extrasSubtotal = 0; 3888 3889 let location = ""; 3890 let pickupTime = ""; 3891 let orderTip = 0; 3892 3893 let orderTax = 0; 3894 let orderTotal = 0; 3895 let orderDiscount = 0; 3896 3897 let useBackendTotals = false; 3898 3899 // ð¥ Get backend JSON 3900 let detailsJson = $("input[type='hidden'][id$='hfJsonOrder']").val(); 3901 3902 let details = []; 3903 3904 try { 3905 details = JSON.parse(detailsJson || "[]"); 3906 } catch (e) { 3907 console.log("Invalid JSON in hfJsonOrder."); 3908 } 3909 3910 // ð¥ Determine if backend totals exist 3911 if ( 3912 details.length > 0 && 3913 typeof details[0].order_total === "number" 3914 ) { 3915 location = details[0]?.location || ""; 3916 pickupTime = details[0]?.datetime || ""; 3917 orderTip = parseFloat(details[0]?.tip) || 0; 3918 3919 orderTax = parseFloat(details[0]?.order_tax) || 0; 3920 orderTotal = parseFloat(details[0]?.order_total) || 0; 3921 orderDiscount = parseFloat(details[0]?.order_discount) || 0; 3922 3923 useBackendTotals = true; 3924 } 3925 3926 // ð¥ Build item + extras UI 3927 const cartHtml = cart.map(item => { 3928 3929 const itemPrice = parseFloat(item.price) || 0; 3930 const itemQuantity = parseInt(item.quantity, 10) || 0; 3931 const itemSubtotal = itemPrice * itemQuantity; 3932 3933 mainItemSubtotal += itemSubtotal; 3934 3935 const modifiersHtml = (item.modifiers || []).map(modifier => { 3936 return `<div>
3936${modifier.name} ${modifier.price > 0 ? `- $${modifier.price.toFixed(2)}` : ''}</div>`; 3937 }).join(''); 3938 3939 const extrasHtml = (item.extras || []).map(extra => { 3940 const extraPrice = parseFloat(extra.price) || 0; 3941 const extraQuantity = parseInt(extra.quantity, 10) || 0; 3942 const extraSubtotal = extraPrice * extraQuantity; 3943 3944 extrasSubtotal += extraSubtotal; 3945 3946 return `<div>${extraQuantity} x ${extra.name} ${extraPrice > 0 ? `- $${extraPrice.toFixed(2)}` : ''}</div>`; 3947 }).join(''); 3948 3949 return ` 3950 <div> 3951 <strong>${item.name}</strong> ${itemPrice > 0 ? `- $${itemPrice.toFixed(2)}` : ''} 3952 ${itemQuantity > 1 ? `<div>Quantity: ${itemQuantity}</div>` : ''} 3953 ${modifiersHtml} 3954 ${extrasHtml} 3955 </div> 3956 <hr> 3957 `; 3958 }).join(''); 3959 3960 const subtotal = mainItemSubtotal + extrasSubtotal; 3961 3962 // ð¥ FRONTEND fallback calculation (only when backend not available) 3963 if (!useBackendTotals) { 3964 let tempTax = 0; 3965 3966 cart.forEach(item => { 3967 const itemPrice = parseFloat(item.price) || 0; 3968 const itemQuantity = parseInt(item.quantity, 10) || 0; 3969 const itemTaxRate = parseFloat(item.taxRate) || 0; 3970 3971 const itemSubtotal = itemPrice * itemQuantity; 3972 tempTax += itemSubtotal * (itemTaxRate / 100); 3973 3974 (item.extras || []).forEach(extra => { 3975 const extraPrice = parseFloat(extra.price) || 0; 3976 const extraQuantity = parseInt(extra.quantity, 10) || 0; 3977 const extraTaxRate = parseFloat(extra.tax) || 0; 3978 3979 const extraSubtotal = extraPrice * extraQuantity; 3980 tempTax += extraSubtotal * (extraTaxRate / 100); 3981 }); 3982 }); 3983 3984 orderTax = tempTax; 3985 orderDiscount = 0; 3986 orderTotal = subtotal + orderTax + orderTip; 3987 } 3988 3989 // ð¥ IMPORTANT: if backend total already includes tip, DO NOT add again 3990 const finalTotal = orderTotal; // adjust if needed 3991 3992 const summaryHtml = ` 3993 <div><strong>Subtotal:</strong> $${subtotal.toFixed(2)}</div> 3994 ${orderDiscount > 0 ? `<div><strong>Discount:</strong> -$${orderDiscount.toFixed(2)}</div>` : ''} 3995 <div><strong>Tax:</strong> $${orderTax.toFixed(2)}</div> 3996 ${orderTip > 0 ? `<div><strong>Tip:</strong> $${orderTip.toFixed(2)}</div>` : ''} 3997 <div><strong>Total:</strong> $${finalTotal.toFixed(2)}</div> 3998 `; 3999 4000 let pickupHtml = ""; 4001 4002 if (location) { 4003 pickupHtml += `<hr><br/><div><strong>Pickup Location:</strong> ${location}</div>`; 4004 } 4005 4006 if (pickupTime) { 4007 pickupHtml += `<div><strong>Pickup Time:</strong> ${pickupTime}</div>`; 4008 } 4009 4010 return cartHtml + summaryHtml + pickupHtml; 4011} 4012 4013 4014 4015 4016 document.getElementsByName('scooperTip').forEach(tipRadio => { 4017 tipRadio.addEventListener('change', function () { 4018 const tipOtherAmount = document.getElementById('tipOtherAmount'); 4019 //const totalAmount = parseFloat(document.getElementById('cartTotalAmount').value); // Get the total amount passed from the ShoppingCart modal 4020 const totalAmount = parseFloat(document.getElementById('cartSubTotalAmount').value); // Get the total amount passed from the ShoppingCart modal 4021 if (this.value === 'Other') { 4022 tipOtherAmount.classList.remove('d-none'); 4023 tipOtherAmount.value = ''; // Clear the input when 'Other' is selected 4024 tipOtherAmount.focus(); 4025 } else { 4026 tipOtherAmount.classList.remove('d-none'); 4027 let tipPercentage = 0; 4028 if (this.value === '15%') { 4029 tipPercentage = 0.15; 4030 } else if (this.value === '20%') { 4031 tipPercentage = 0.20; 4032 } else if (this.value === '25%') { 4033 tipPercentage = 0.25; 4034 } 4035 tipOtherAmount.value = (totalAmount * tipPercentage).toFixed(2); 4036 } 4037 }); 4038 }); 4039 4040 // Function to calculate the total amount 4041 function calculateTotalAmount() { 4042 let subtotal = 0; 4043 let tax = 0; 4044 4045 cart.forEach(item => { 4046 subtotal += item.price * item.quantity; 4047 tax += (item.price * item.quantity) * (item.taxRate / 100); 4048
4049 item.modifiers.forEach(modifier => { 4050 subtotal += modifier.price * modifier.quantity; 4051 tax += (modifier.price * modifier.quantity) * (modifier.tax / 100); 4052 }); 4053 4054 item.extras.forEach(extra => { 4055 subtotal += extra.price * extra.quantity; 4056 tax += (extra.price * extra.quantity) * (extra.tax / 100); 4057 }); 4058 }); 4059 document.getElementById('cartSubTotalAmount').value = subtotal.toFixed(2); 4060 return subtotal + tax; 4061 } 4062 4063 </script>
4063 4064
4064<script type="text/javascript"> 4065 4066 function getQueryStringParameter(name) { 4067 const urlParams = new URLSearchParams(window.location.search); 4068 return urlParams.get(name); 4069 } 4070 4071 const inputElement = document.getElementById('tipOtherAmount'); 4072 4073 inputElement.addEventListener('input', function (e) { 4074 // Remove invalid characters (anything that's not a digit or a single decimal point) 4075 const value = e.target.value; 4076 4077 // Allow only numbers and a single decimal point 4078 const validValue = value 4079 .replace(/[^0-9.]/g, '') // Remove non-numeric characters except '.' 4080 .replace(/(\..*?)\./g, '$1'); // Allow only one '.' 4081 4082 e.target.value = validValue; 4083 }); 4084 4085 4086 var picker; 4087 4088 function validateLeadTime() { 4089 4090 if (!picker || !picker.dates) return false; 4091 4092 var selectedDateTime = (picker && picker.dates) ? picker.dates.lastPicked : null; 4093 var dateSelected = null; 4094 4095 // ð¥ Try picker first 4096 if (selectedDateTime && typeof selectedDateTime.toJSDate === "function") { 4097 dateSelected = selectedDateTime.toJSDate(); 4098 } 4099 4100 // ð¥ Fallback to input (VERY important in WebView) 4101 if (!dateSelected) { 4102 var inputEl = document.querySelector('#pickupDateTimePicker input'); 4103 var inputVal = inputEl ? inputEl.value : null; 4104 4105 if (inputVal) { 4106 dateSelected = new Date(inputVal); 4107 } 4108 } 4109 4110 // ð¥ Final guard 4111 if (!dateSelected || isNaN(dateSelected.getTime())) { 4112 console.log("Invalid dateSelected"); 4113 return false; 4114 } 4115 4116 var leadTimeHours = parseInt($("input[type='hidden'][id$='hiddenFieldLeadTime']").val(), 10); 4117 4118 if (isNaN(leadTimeHours) || leadTimeHours <= 0) { 4119 console.log("Invalid leadTimeHours"); 4120 return false; 4121 } 4122 4123 var currentDateTime = new Date(); 4124 var leadTimeDate = new Date(currentDateTime.getTime() + (leadTimeHours * 60 * 60 * 1000)); 4125 4126 console.log("Selected:", dateSelected); 4127 console.log("Required after:", leadTimeDate); 4128 4129 return dateSelected >= leadTimeDate; 4130} 4131 4132 $(document).ready(function () { 4133 4134 $(document).on('show.bs.modal', '.modal', function (event) { 4135 this._modalTrigger = event.relatedTarget || document.activeElement; 4136 }); 4137 4138 $(document).on('hidden.bs.modal', '.modal', function () { 4139 const trigger = this._modalTrigger; 4140 4141 if (trigger && document.contains(trigger)) { 4142 trigger.focus(); 4143 } 4144 4145 this._modalTrigger = null; 4146 }); 4147 4148 4149 $(document).on('input', 'textarea[id^="modifier-Custom Message"]', function () { 4150 var maxLength = 30; 4151 var currentVal = $(this).val(); 4152 if (currentVal.length > maxLength) { 4153 $(this).val(currentVal.substring(0, maxLength)); 4154 } 4155 }); 4156 4157 4158 //if (!getQueryStringParameter('number')){ 4159 // var footer = document.querySelector('.mondelezfooter'); 4160 // if (footer) { 4161 // footer.style.position = 'fixed'; 4162 // footer.style.bottom = '5px'; 4163 // footer.style.left = '0'; 4164 // footer.style.right = '0'; 4165 // footer.style.margin = '0 auto'; 4166 // footer.style.width = '100%'; 4167 // footer.style.zIndex = '1030'; 4168 // } 4169 //} 4170 //else { 4171 // var footer = document.querySelector('.mondelezfooter'); 4172 // if (footer) { 4173 // footer.removeAttribute('style'); 4174 // } 4175 //} 4176 4177 function pinFooters(selectors) { 4178 var bottomOffset = 5; // starting offset from bottom 4179 var spacing = 50; // vertical space per footer 4180
4181 selectors.forEach(function (selector) { 4182 var el = document.querySelector(selector); 4183 if (!el) return; 4184 4185 el.style.position = 'fixed'; 4186 el.style.bottom = bottomOffset + 'px'; 4187 el.style.left = '0'; 4188 el.style.right = '0'; 4189 el.style.margin = '0 auto'; 4190 el.style.width = '100%'; 4191 el.style.zIndex = '1030'; 4192 4193 bottomOffset += spacing; 4194 }); 4195 } 4196 4197 function resetFooters(selectors) { 4198 selectors.forEach(function (selector) { 4199 var el = document.querySelector(selector); 4200 if (el) el.removeAttribute('style'); 4201 }); 4202 } 4203 4204 var hasNumber = getQueryStringParameter('number'); 4205 4206 var pinnedFooters = [ 4207 '.mondelezfooter' 4208 ]; 4209 4210 var allergen = document.querySelector('.allergenfooter'); 4211 4212 if (!hasNumber) { 4213 // â Pin ONLY Mondelez footer 4214 pinFooters(pinnedFooters); 4215 4216 // â Hide allergen footer 4217 if (allergen) allergen.style.display = 'none'; 4218 } else { 4219 // Restore everything 4220 resetFooters([ 4221 '.mondelezfooter', 4222 '.allergenfooter' 4223 ]); 4224 4225 if (allergen) allergen.style.display = ''; 4226 } 4227 4228 4229 4230 4231 4232 $('#pickupDateTimePicker input').attr('readonly', true); // Prevent manual typing 4233 4234 $('.restrictemojis').on('input paste', function () { 4235 // Delay the handling to ensure the pasted content is processed 4236 setTimeout(() => { 4237 var value = $(this).val(); 4238 var cleanValue = value.replace(/[\u{1F600}-\u{1F6FF}\u{2600}-\u{26FF}\u{1F300}-\u{1F5FF}\u{1F900}-\u{1F9FF}\u{1F700}-\u{1F77F}\u{1F780}-\u{1F7FF}\u{1F800}-\u{1F8FF}\u{2700}-\u{27BF}\u{1F1E0}-\u{1F1FF}\u{1F900}-\u{1F9FF}\u{1FA70}-\u{1FAFF}\u{1F600}-\u{1F64F}\u{1F680}-\u{1F6FF}\u{1F700}-\u{1F77F}\u{1F780}-\u{1F7FF}\u{1F800}-\u{1F8FF}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA6F}\u{1FA70}-\u{1FAFF}\u{1FB00}-\u{1FBFF}\u{1FC00}-\u{1FCFF}\u{1FD00}-\u{1FDFF}\u{1FE00}-\u{1FE0F}\u{1FE10}-\u{1FE1F}\u{1FE30}-\u{1FE4F}\u{1FE50}-\u{1FE6F}\u{1FE70}-\u{1FEFF}\u{1FF00}-\u{1FFEF}\u{1FFF0}-\u{1FFFF}]/gu, ''); 4239 $(this).val(cleanValue); 4240 }, 0); 4241 }); 4242 4243 $.fn.extend({ 4244 animateCss: function (animationName, callback) { 4245 const animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'; 4246 this.addClass('animate__animated animate__' + animationName).one(animationEnd, function () { 4247 $(this).removeClass('animate__animated animate__' + animationName); 4248 if (callback) { 4249 callback(); 4250 } 4251 }); 4252 return this; 4253 } 4254 }); 4255 4256 4257 $('#searchButton').on('click', function (e) { 4258 e.preventDefault(); 4259 $(".loading").show(); 4260 var search = $("input[type='text'][id='zipcode']").val(); 4261 var searchurl = '/API/OnlineOrderStoreSearchZip.aspx?search=' + search; 4262 4263 $.getJSON(searchurl, function (data) { 4264 $("#divstores").empty(); 4265 $(".list-group-item", $("#divstores")).remove(); 4266 var outerHtml = $("#divstoresclone .list-group-item").prop('outerHTML'); 4267 if (data.length > 0) { 4268 var checkOlo = 0; 4269 var currentPageUrl = window.location.href.split('?')[0]; // Get the current page URL without query string 4270 4271 // Function to get query parameter value by name (compatible with older browsers) 4272 function getQueryParamValue(name) { 4273 var query = window.location.search.substring(1); 4274 var vars = query.split('&'); 4275 for (var i = 0; i < vars.length; i++) { 4276 var pair = vars[i].split('='); 4277 if (decodeURIComponent(pair[0]) === name) { 4278 return decodeURIComponent(pair[1] || ''); 4279 } 4280 } 4281 return null; 4282 } 4283 4284 // Function to construct the new URL with preserved query parameters and hash 4285 function constructNewUrl(baseUrl, newParamName, newParamValue) { 4286 var baseParts = baseUrl.indexOf('http') === 0 ? baseUrl.split('?')[0] : window.location.origin + '/' + baseUrl;
4287 var currentParams = window.location.search.substring(1); 4288 var queryParams = []; 4289 4290 if (currentParams) { 4291 var paramArray = currentParams.split('&'); 4292 for (var i = 0; i < paramArray.length; i++) { 4293 var keyValue = paramArray[i].split('='); 4294 if (decodeURIComponent(keyValue[0]) !== newParamName) { 4295 queryParams.push(paramArray[i]); 4296 } 4297 } 4298 } 4299 4300 // Add the new/updated parameter 4301 queryParams.push(encodeURIComponent(newParamName) + '=' + encodeURIComponent(newParamValue)); 4302 4303 var finalUrl = baseParts + '?' + queryParams.join('&'); 4304 4305 if (window.location.hash) { 4306 finalUrl += window.location.hash; 4307 } 4308 4309 return finalUrl; 4310 } 4311 4312 for (var i = 0; i < data.length; i++) { 4313 if (data[i].OnlineUrl) { 4314 checkOlo = 1; 4315 var htmltoadd = outerHtml; 4316 var baseUrl = data[i].isNewOLO ? 'BrustersCom/Cart.aspx' : 'BrustersCom/ShoppingCart.aspx'; 4317 var newUrl = constructNewUrl(baseUrl, 'number', data[i].Number); // Construct the new URL with query string 4318 4319 htmltoadd = htmltoadd.replace('[LAT]', data[i].lat) 4320 .replace('[LNG]', data[i].lng) 4321 .replaceAll('[ONLINEURL]', newUrl) 4322 .replace('[STORE PHONE]', data[i].phone) 4323 .replaceAll('[STORE NAME]', data[i].Name) 4324 .replace('[STORE ADDRESS]', data[i].MAPADDRESS) 4325 .replace('[MILES AWAY]', data[i].Distance) 4326 .replace('[MAPADDRESS]', data[i].googleMapImage) 4327 .replace('[MAPADDRESS2]', data[i].googleMapImage); 4328 4329 4330 $("#divstores").append(htmltoadd); 4331 } 4332 } 4333 4334 if (checkOlo === 0) { 4335 $("#divstores").append('<p>We are sorry. No participating stores in your area.</p>'); 4336 } 4337 } else { 4338 $("#divstores").append('<p>We are sorry. No participating stores in your area.</p>'); 4339 } 4340 4341 }).done(function () { 4342 // Hide landing page and show store list page 4343 $("#landingPage").hide(); 4344 $("#storelist").show(); 4345 $(".loading").hide(); 4346 }); 4347 }); 4348 4349 4350 4351 4352 const number = getQueryStringParameter('number'); 4353 4354 if (number) { 4355 $('#mainContent').show(); 4356 $('#landingPage').hide(); 4357 4358 //need to call an API for order processing stuff with line items 4359 $(".loading").show(); 4360 $.ajax({
4361 type: "POST", 4362 url: "../Service.asmx/getOnlineFlavors", 4363 data: JSON.stringify({ 'number': number, 'fundraiser': 0 }), 4364 contentType: "application/json; charset=utf-8", 4365 //dataType: "json", 4366 success: function (data) { 4367 $(".loading").hide(); 4368 console.log(data.d); 4369 var mydata = JSON.parse(data.d); 4370 $("input[type='hidden'][id$='hfItemList']").val(mydata.Items); 4371 4372 // ð¥ IMPORTANT: wait until DOM is updated/rendered 4373 setTimeout(function () { 4374 markPromotionalProducts(); 4375 }, 0); 4376 4377 }}); 4378 4379 } else { 4380 $('#mainContent').hide(); 4381 $('#landingPage').show(); 4382 } 4383 4384 var enabled = $("input[type='hidden'][id$='hfEnabledHours']").val(); 4385 var disabled = $("input[type='hidden'][id$='hfDisabledHours']").val(); 4386 var disabledDates = $("input[type='hidden'][id$='hfDaysDisabled']").val(); 4387 var datesDisabled = $("input[type='hidden'][id$='hfDatesDisabled']").val(); 4388 var closedHour = parseInt($("input[type='hidden'][id$='hfClosedHour']").val()); 4389 var startCloseHour = closedHour - 1; 4390 var startCloseMinute = 45; 4391 var strOpenTime = $("input[type='hidden'][id$='hfOpenTimeString']").val(); 4392 var hfIsClosed = $("input[type='hidden'][id$='hfIsClosed']").val(); 4393 var hfNextOpenDateTime = $("input[type='hidden'][id$='hfNextOpenDateTime']").val(); 4394 var hfMaxDateTime = $("input[type='hidden'][id$='hfMaxDateTime']").val(); 4395 var hfCurrentStoreTime = $("input[type='hidden'][id$='hfCurrentStoreTime']").val(); 4396 4397 var eArray = enabled ? JSON.parse(enabled) : []; 4398 var dArray = disabled ? JSON.parse(disabled) : []; 4399 var daysDisabled = disabledDates ? JSON.parse(disabledDates) : []; 4400 var datesDisabledArray = datesDisabled ? JSON.parse(datesDisabled) : []; 4401 4402 var startCloseDateTime = new tempusDominus.DateTime(new Date().setHours(startCloseHour, startCloseMinute)); 4403 var endCloseDateTime = new tempusDominus.DateTime(new Date().setHours(closedHour - 1, 45)); 4404 4405 console.log(endCloseDateTime); 4406 4407 var d = new Date(); 4408 var n = d.getHours(); 4409 var m = d.getMinutes(); 4410 4411 var setDate; 4412 var isPM = strOpenTime.toLowerCase().indexOf('pm') != -1; 4413 if (strOpenTime.toLowerCase().indexOf('closed') == -1 || hfIsClosed.toLowerCase() == 'true') { 4414 strOpenTime = strOpenTime.replace(/AM|PM|am|pm|\s/g, ''); 4415 var splitTime = strOpenTime.split(/:/); 4416 var openMinutes = splitTime[1]; 4417 var thisOpenHour = parseInt(splitTime[0]); 4418 4419 if (isPM && thisOpenHour < 12) { 4420 thisOpenHour += 12; 4421 } else if (!isPM && thisOpenHour === 12) { 4422 thisOpenHour = 0; 4423 } 4424 4425 var openD = new Date(hfCurrentStoreTime); 4426 if (openD.getHours() < thisOpenHour) { 4427 setDate = new Date(openD.setHours(thisOpenHour, openMinutes, 0)); 4428 } 4429 else if (hfNextOpenDateTime){ 4430 console.log('next day'); 4431 setDate = new Date(hfNextOpenDateTime); 4432 } 4433 else { 4434 setDate = new Date(); 4435 } 4436 } 4437 4438 console.log(eArray); 4439 console.log(dArray); 4440 4441 // Create the DateTime objects for the disabled time intervals 4442 const disabledFrom = new tempusDominus.DateTime(new Date(hfCurrentStoreTime)); 4443 disabledFrom.hours = startCloseHour; 4444 disabledFrom.minutes = startCloseMinute; 4445 4446 const disabledTo = new tempusDominus.DateTime(new Date(hfCurrentStoreTime)); 4447 disabledTo.hours = closedHour; 4448 disabledTo.minutes = 0; 4449 4450 const pickerElement = document.getElementById('pickupDateTimePicker');
4451 picker = new tempusDominus.TempusDominus(pickerElement, { 4452 localization: { 4453 today: 'Go to today', 4454 clear: 'Clear selection', 4455 close: 'Close the picker', 4456 selectMonth: 'Select Month', 4457 previousMonth: 'Previous Month', 4458 nextMonth: 'Next Month', 4459 selectYear: 'Select Year', 4460 previousYear: 'Previous Year', 4461 nextYear: 'Next Year', 4462 selectDecade: 'Select Decade', 4463 previousDecade: 'Previous Decade', 4464 nextDecade: 'Next Decade', 4465 previousCentury: 'Previous Century', 4466 nextCentury: 'Next Century', 4467 pickHour: 'Pick Hour', 4468 incrementHour: 'Increment Hour', 4469 decrementHour: 'Decrement Hour', 4470 pickMinute: 'Pick Minute', 4471 incrementMinute: 'Increment Minute', 4472 decrementMinute: 'Decrement Minute', 4473 pickSecond: 'Pick Second', 4474 incrementSecond: 'Increment Second', 4475 decrementSecond: 'Decrement Second', 4476 toggleMeridiem: 'Toggle Meridiem', 4477 selectTime: 'Select Time', 4478 selectDate: 'Select Date', 4479 dayViewHeaderFormat: { month: 'long', year: '2-digit' }, 4480 locale: 'en', 4481 startOfTheWeek: 0, 4482 hourCycle: undefined, 4483 dateFormats: { 4484 LTS: 'h:mm:ss T', 4485 LT: 'h:mm T', 4486 L: 'MM/dd/yyyy', 4487 LL: 'MMMM d, yyyy', 4488 LLL: 'MMMM d, yyyy h:mm T', 4489 LLLL: 'dddd, MMMM d, yyyy h:mm T' 4490 }, 4491 ordinal: (n) => n, 4492 format: 'LLL' 4493 }, 4494 allowInputToggle: false, 4495 container: undefined, 4496 dateRange: false, 4497 debug: false, 4498 defaultDate: setDate, 4499 display: { 4500 icons: { 4501 type: 'icons', 4502 time: 'fa-solid fa-clock', 4503 date: 'fa-solid fa-calendar', 4504 up: 'fa-solid fa-arrow-up', 4505 down: 'fa-solid fa-arrow-down', 4506 previous: 'fa-solid fa-chevron-left', 4507 next: 'fa-solid fa-chevron-right', 4508 today: 'fa-solid fa-calendar-check', 4509 clear: 'fa-solid fa-trash', 4510 close: 'fa-solid fa-xmark' 4511 }, 4512 sideBySide: false, 4513 calendarWeeks: false, 4514 viewMode: 'calendar', 4515 toolbarPlacement: 'bottom', 4516 keepOpen: false, 4517 buttons: { 4518 today: false, 4519 clear: false, 4520 close: false 4521 }, 4522 components: { 4523 calendar: true, 4524 date: true, 4525 month: true, 4526 year: true, 4527 decades: true, 4528 clock: true, 4529 hours: true, 4530 minutes: true, 4531 seconds: false, 4532 useTwentyfourHour: undefined 4533 }, 4534 inline: false, 4535 theme: 'auto' 4536 }, 4537 keepInvalid: false, 4538 meta: {}, 4539 multipleDates: false, 4540 multipleDatesSeparator: '; ', 4541 promptTimeOnDateChange: false, 4542 promptTimeOnDateChangeTransitionDelay: 200, 4543 restrictions: { 4544 minDate: hfNextOpenDateTime ? new tempusDominus.DateTime(new Date(hfNextOpenDateTime)) : new tempusDominus.DateTime(), 4545 maxDate: hfMaxDateTime ? new tempusDominus.DateTime(new Date(hfMaxDateTime)) : undefined, 4546 disabledDates: datesDisabledArray.map(date => new tempusDominus.DateTime(new Date(date))), 4547 enabledDates: [], 4548 daysOfWeekDisabled: daysDisabled, 4549 disabledTimeIntervals: [ 4550 { from: disabledFrom, to: disabledTo } 4551 ], 4552 disabledHours: dArray, 4553 enabledHours: eArray 4554 }, 4555 stepping: 1, 4556 useCurrent: true, 4557 viewDate: new tempusDominus.DateTime() 4558 }); 4559 4560 pickerElement.addEventListener('change.td', function (e) { 4561 //code used to check for day specific closing times 4562 console.log(e.detail); 4563 console.log('triggered date change'); 4564 if (!e.detail || !e.detail.date) return; 4565 4566 const d = (typeof e.detail.date.toDate === "function") 4567 ? e.detail.date.toDate() 4568 : new Date(e.detail.date); 4569 4570 const old = e.detail.oldDate 4571 ? ((typeof e.detail.oldDate.toDate === "function") 4572 ? e.detail.oldDate.toDate() 4573 : new Date(e.detail.oldDate)) 4574 : null; 4575 4576 if (!old) return; 4577 4578 // only run when DATE changes (not time) 4579 if (old.toDateString() === d.toDateString()) return; 4580
4581 const disabledIntervalsRaw = $("input[type='hidden'][id$='hfDisabledTimeIntervals']").val(); 4582 if (!disabledIntervalsRaw) return; 4583 4584 const mydates = JSON.parse(disabledIntervalsRaw); 4585 const dayofweek = d.getDay(); 4586 4587 for (let i = 0; i < mydates.length; i++) { 4588 4589 if (dayofweek !== mydates[i].dayofweek) continue; 4590 4591 const beghour = mydates[i].starthour; 4592 const begmin = mydates[i].startminute; 4593 const endhour = mydates[i].endhour; 4594 const endmin = mydates[i].endminute; 4595 4596 // ð compute closing window (same logic you had) 4597 const startCloseHour = endhour - 1; 4598 const startCloseMinute = 45; 4599 4600 // build enabled / disabled hours 4601 const allhours = [...Array(24).keys()]; 4602 const openhours = []; 4603 const closedhours = []; 4604 4605 for (let h of allhours) { 4606 if (h >= beghour && h < endhour) { 4607 openhours.push(h); 4608 } else { 4609 closedhours.push(h); 4610 } 4611 } 4612 4613 // set the time to opening time for the new date 4614 d.setHours(beghour, begmin, 0, 0); 4615 4616 // update picker value 4617 picker.dates.setValue( 4618 new tempusDominus.DateTime(d) 4619 ); 4620 4621 // update picker restrictions dynamically 4622 picker.updateOptions({ 4623 restrictions: { 4624 enabledHours: openhours, 4625 disabledHours: closedhours, 4626 disabledTimeIntervals: [ 4627 { 4628 from: new tempusDominus.DateTime( 4629 new Date(d.getFullYear(), d.getMonth(), d.getDate(), startCloseHour, startCloseMinute) 4630 ), 4631 to: new tempusDominus.DateTime( 4632 new Date(d.getFullYear(), d.getMonth(), d.getDate(), endhour, endmin) 4633 ) 4634 } 4635 ] 4636 } 4637 }); 4638 4639 break; 4640 } 4641 }); 4642 4643 4644 4645 var isClosed = false; 4646 var openMinutes = 0; 4647 var openHour = parseInt($("input[type='hidden'][id$='hfOpenHour']").val()) 4648 var closedHour = parseInt($("input[type='hidden'][id$='hfClosedHour']").val()) 4649 4650 var isStoreClosed = $("input[type='hidden'][id$='hfIsClosed']").val(); 4651 //console.log(n); 4652 4653 if (n < openHour || n >= closedHour || (n == parseInt(closedHour - 1) && m >= 45) || isStoreClosed == 'True') { 4654 isClosed = true; 4655 } 4656 4657 4658 4659 4660 if (isClosed == true) { 4661 $("input[type='radio'][name='pickupTimeOption']").eq(0).hide(); 4662 $("input[type='radio'][name='pickupTimeOption']").eq(1).show(); 4663 $(".radio-inline").eq(0).hide(); 4664 $(".radio-inline").eq(1).show(); 4665 $("input[type='radio'][name='pickupTimeOption']").eq(1).prop('checked', true); 4666 $("#pickupDateTimePicker").removeClass('d-none') 4667 } 4668 4669 $("input[name='pickupTimeOption']").change(function () { 4670 if ($(this).val() === "SelectDateTime") { 4671 $('#pickupDateTimePicker').removeClass('d-none'); 4672 } else { 4673 $('#pickupDateTimePicker').addClass('d-none'); 4674 } 4675 }); 4676 4677 // Step 1: Check if the query string contains "&prepacked=true" 4678 if (window.location.search.indexOf("prepacked=true") !== -1) { 4679
4680 // Step 2: Find the anchor with the text "Hand-Packed To Go" 4681 var handPackedLink = $("a.section-link").filter(function() { 4682 return $(this).text().trim() === "Hand-Packed To Go"; 4683 }); 4684 4685 if (handPackedLink.length) { 4686 // Step 3: Click the link to scroll to the section 4687 handPackedLink[0].click(); 4688 4689 // Step 4: After a short delay, find and click the image that toggles items 4690 setTimeout(function () { 4691 // Find the section number dynamically from the link href 4692 var sectionHref = handPackedLink.attr("href"); // e.g., "#section-4" 4693 var sectionNumber = sectionHref.replace("#section-", ""); // e.g., "4" 4694 4695 // Find the image with the matching onclick toggleItemsVisibility(sectionNumber) 4696 var toggleImage = $("img[onclick='toggleItemsVisibility(" + sectionNumber + ")']"); 4697 4698 if (toggleImage.length) { 4699 toggleImage[0].click(); // Simulate the click 4700 } 4701 }, 500); // Adjust delay if needed 4702 } 4703 } 4704 4705 4706 }); 4707 4708 </script>
4708 4709 4710
4710<script> 4711 4712 $(document).on('click', '.section-link', function (event) { 4713 event.preventDefault(); 4714 4715 const section = document.querySelector(this.getAttribute('href')); 4716 4717 if (section) { 4718 section.scrollIntoView({ 4719 behavior: 'smooth', 4720 block: 'start' 4721 }); 4722 4723 const categoryButton = section.querySelector( 4724 'button[aria-controls]' 4725 ); 4726 4727 if (categoryButton) { 4728 categoryButton.focus({ 4729 preventScroll: true 4730 }); 4731 } 4732 } 4733}); 4734 4735 4736 let lastOpenedModal = null; 4737 4738 4739 document.addEventListener('click', function(e) { 4740 if (e.target.classList.contains('clickable-modifier-img')) { 4741 4742 const fullImageUrl = e.target.getAttribute('data-full'); 4743 document.getElementById('modifierFullImage').src = fullImageUrl; 4744 4745 // store the modal we are coming from (if any) 4746 const currentModal = e.target.closest('.modal'); 4747 if (currentModal) { 4748 lastOpenedModal = bootstrap.Modal.getInstance(currentModal); 4749 lastOpenedModal.hide(); 4750 } else { 4751 lastOpenedModal = null; 4752 } 4753 4754 // open image modal 4755 const fullModal = new bootstrap.Modal(document.getElementById('modifierImageModal')); 4756 fullModal.show(); 4757 } 4758 }); 4759 4760 document.getElementById('modifierImageModal').addEventListener('hidden.bs.modal', function () { 4761 if (lastOpenedModal) { 4762 lastOpenedModal.show(); 4763 } 4764 }); 4765 4766 4767</script>
4767 4768 4769<input type="hidden" name="hfShowInvalidKioskModal" id="hfShowInvalidKioskModal" /> 4770<div class="modal fade" id="invalidKioskModal" tabindex="-1" aria-hidden="true" 4771 data-bs-backdrop="static" data-bs-keyboard="false"> 4772 <div class="modal-dialog modal-dialog-centered"> 4773 <div class="modal-content text-center p-4"> 4774 <h4 class="mb-3">This on-lot link is no longer active.</h4> 4775 <p>Please scan the QR code if you are currently at the store.</p> 4776 <p class="mb-4">Or click below to visit the storeâs online store:</p> 4777 <a id="btnGoToOnlineStore" class="btn btn-primary btn-lg" href="#">Visit Online Store</a> 4778 </div> 4779 </div> 4780</div> 4781 4782 4783 4784</form> 4785</body> 4786</html> 4787
4788<script> 4789document.addEventListener("DOMContentLoaded", function () { 4790 var show = document.getElementById("hfShowInvalidKioskModal").value; 4791 if (show === "True") { 4792 var modal = new bootstrap.Modal(document.getElementById('invalidKioskModal')); 4793 modal.show(); 4794 4795 // Always redirect to store's online ordering using the number from querystring 4796 var storeNumber = ''; 4797 document.getElementById("btnGoToOnlineStore").href = "https://brustersonline.com/store/" + storeNumber; 4798 } 4799}); 4800</script>
4800 4801
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.