1/** 2 * MultiStepBookingForm - A self-contained form module 3 * Safe for embedding in other websites 4 * Version 1.0.0 5 */ 6(function(window, document) { 7 'use strict'; 8 9 // Helper to parse query params 10 function getQueryParams() { 11 var params = {}; 12 var search = window.location.search.substring(1); 13 if (!search) return params; 14 search.split('&').forEach(function(pair) { 15 var parts = pair.split('='); 16 params[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1] || ''); 17 }); 18 return params; 19 } 20 21 // Add a method to fetch jlid data 22 function fetchJLIDData(jlid) { 23 // TODO: Replace with your real API endpoint 24 var url = 'https://your.api/lookup-jlid?jlid=' + encodeURIComponent(jlid); 25 return fetch(url) 26 .then(function(res) { return res.json(); }); 27 } 28 29 // Create a unique namespace 30 var BookingFormApp = { 31 // Initialize the form application 32 init: function(containerId) { 33 var self = this; 34 console.log('Initializing form with container ID:', containerId); 35 36 // Only initialize if the container exists 37 var container = document.getElementById(containerId || 'form-container'); 38 if (!container) { 39 console.warn('BookingFormApp: Container element not found'); 40 return; 41 } 42 43 // Check if already initialized using a more specific data attribute 44 if (container.getAttribute('data-booking-form-version') === '1.0.0') { 45 console.warn('BookingFormApp: Form already initialized'); 46 return; 47 } 48 49 // Mark as initialized with version 50 container.setAttribute('data-booking-form-version', '1.0.0'); 51 52 // --- JLID SUPPORT --- 53 var queryParams = getQueryParams(); 54 if (queryParams.jlid) { 55 fetchJLIDData(queryParams.jlid) 56 .then(function(jlidData) { 57 // Store for later use 58 self.jlidData = jlidData; 59 // Continue with country detection and form build 60 return self.detectCountry(); 61 }) 62 .then(function(countryData) { 63 self.userCountry = countryData; 64 return self.loadFontAwesome(); 65 }) 66 .then(function() { 67 self.injectStyles(); 68 self.buildForm(container); 69 // After form is built, prefill and auto-progress 70 self.prefillAndProgressWithJLID(container); 71 return self.detectCountry(); 72 }) 73 .then(function(countryData) { 74 self.userCountry = countryData; 75 self.updateCountryCodeDropdown(countryData); 76 }) 77 .catch(function(error) { 78 console.error('BookingFormApp: Error during initialization with jlid', error); 79 }); 80 return; 81 } 82 // --- END JLID SUPPORT --- 83 84 // First detect country, then build form 85 this.detectCountry() 86 .then(function(countryData) { 87 self.userCountry = countryData; 88 console.log('Country detected:', countryData); 89 90 // Now load Font Awesome and build form 91 return self.loadFontAwesome(); 92 }) 93 .then(function() { 94 self.injectStyles(); 95 self.buildForm(container); 96 97 // Detect country after the form is built 98 return self.detectCountry(); 99 }) 100 .then(function(countryData) { 101 self.userCountry = countryData; 102 103 // Update the country code dropdown 104 self.updateCountryCodeDropdown(countryData); 105 }) 106 .catch(function(error) { 107 console.error('BookingFormApp: Error during initialization', error); 108 }); 109 }, 110 111 // Check if Font Awesome is loaded 112 isFontAwesomeLoaded: function() { 113 // Try to detect if Font Awesome is available 114 var span = document.createElement('span'); 115 span.className = 'fa'; 116 span.style.display = 'none'; 117 document.body.appendChild(span); 118 119 var computedStyle = window.getComputedStyle(span); 120 var isFALoaded = computedStyle && computedStyle.fontFamily.indexOf('FontAwesome') !== -1; 121 122 document.body.removeChild(span); 123 return isFALoaded; 124 }, 125 126 // Load Font Awesome if needed 127 loadFontAwesome: function() { 128 var self = this; 129 return new Promise(function(resolve) { 130 if (document.querySelector('link[href*="font-awesome"]') || self.isFontAwesomeLoaded()) { 131 // Font Awesome is already loaded 132 resolve(); 133 return; 134 } 135 136 // Font Awesome is not loaded, let's add it 137 var link = document.createElement('link'); 138 link.rel = 'stylesheet'; 139 link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'; 140 link.onload = function() { 141 resolve(); 142 }; 143 link.onerror = function() { 144 console.warn('BookingFormApp: Failed to load Font Awesome, icons may not display correctly'); 145 resolve(); // Continue anyway 146 }; 147 148 document.head.appendChild(link); 149 }); 150 }, 151
152 // Inject scoped CSS styles to avoid conflicts 153 injectStyles: function() { 154 if (document.getElementById('booking-form-styles')) { 155 return; // Already injected 156 } 157 158 var styles = ` 159 /* Booking Form Styles - Scoped to avoid conflicts */ 160 #form-container *, 161 [id$="-form-container"] * { 162 box-sizing: border-box; 163 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 164 } 165 166 #form-container, 167 [id$="-form-container"] { 168 width: 100%; 169 padding: 0; 170 margin: 0; 171 } 172 173 #form-container .booking-container, 174 [id$="-form-container"] .booking-container { 175 display: flex; 176 flex-direction: column; 177 justify-content: center; 178 align-items: center; 179 width: 100%; 180 max-width: 100%; 181 margin: 0 auto; 182 } 183 184 #form-container .booking-frame-container, 185 [id$="-form-container"] .booking-frame-container { 186 width: 100%; 187 } 188 189 #form-container .booking-section, 190 [id$="-form-container"] .booking-section { 191 color: #8d959e; 192 font-size: 14px; 193 margin-bottom: 10px; 194 padding-left: 5px; 195 text-align: center; 196 } 197 198 #form-container .booking-frame, 199 [id$="-form-container"] .booking-frame { 200 background-color: white; 201 border-radius: 15px; 202 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 203 padding: 24px; 204 width: 100%; 205 min-height: auto; 206 overflow: hidden; 207 position: relative; 208 } 209 210 #form-container .booking-step-content, 211 [id$="-form-container"] .booking-step-content { 212 display: none; 213 animation: bookingFormFadeIn 0.3s ease-in-out; 214 width: 100%; 215 } 216 217 @keyframes bookingFormFadeIn { 218 from { opacity: 0; } 219 to { opacity: 1; } 220 } 221 222 #form-container .booking-step-content.active, 223 [id$="-form-container"] .booking-step-content.active { 224 display: block; 225 } 226 227 #form-container h2, 228 [id$="-form-container"] h2 { 229 font-size: 20px; 230 font-weight: 600; 231 margin-bottom: 20px; 232 text-align: center; 233 } 234 235 /* Progress Bar */ 236 #form-container .booking-progress-bar, 237 [id$="-form-container"] .booking-progress-bar { 238 display: flex; 239 justify-content: center; 240 gap: 15px; 241 margin-bottom: 25px; 242 width: 100%; 243 } 244 245 #form-container .booking-progress-item, 246 [id$="-form-container"] .booking-progress-item { 247 height: 5px; 248 flex: 1; 249 max-width: 70px; 250 background-color: #e0e0e0; 251 border-radius: 3px; 252 } 253 254 #form-container .booking-progress-item.active, 255 [id$="-form-container"] .booking-progress-item.active { 256 background-color: #ffcf00; 257 } 258 259 /* Form Elements */ 260 #form-container .booking-form-group, 261 [id$="-form-container"] .booking-form-group { 262 margin-bottom: 20px; 263 width: 100%; 264 } 265 266 #form-container .booking-form-group label, 267 [id$="-form-container"] .booking-form-group label { 268 display: block; 269 font-size: 16px; 270 font-weight: 500; 271 margin-bottom: 8px; 272 color: #333; 273 } 274 275 /* Phone input specific styles - style like the example */ 276 #form-container .booking-form-row, 277 [id$="-form-container"] .booking-form-row { 278 display: flex; 279 gap: 10px; 280 width: 100%; 281 flex-direction: row !important; /* Force row display */ 282 flex-wrap: nowrap !important; /* Prevent wrapping */ 283 } 284 285 /* Fix the phone row to ensure labels align */ 286 #form-container .booking-phone-row, 287 [id$="-form-container"] .booking-phone-row { 288 align-items: stretch; /* Stretch children to equal height */ 289 } 290 291 /* Make sure each column in the phone row is self-contained */ 292 #form-container .booking-phone-row > div, 293 [id$="-form-container"] .booking-phone-row > div { 294 display: flex; 295 flex-direction: column; 296 } 297 298 #form-container .booking-country-code, 299 [id$="-form-container"] .booking-country-code { 300 flex: 0 0 auto; 301 width: 90px; 302 min-width: 90px; 303 } 304 305 #form-container .booking-mobile-number, 306 [id$="-form-container"] .booking-mobile-number { 307 flex: 1; 308 min-width: 0; 309 } 310 311 #form-container .booking-select-wrapper, 312 [id$="-form-container"] .booking-select-wrapper { 313 position: relative; 314 border: 1px solid #ddd; 315 border-radius: 8px; 316 background-color: white; 317 width: 100%; 318 height: 100%; /* Fill the height of the container */ 319 } 320 321 #form-container .booking-select-wrapper::after, 322 [id$="-form-container"] .booking-select-wrapper::after { 323 content: ''; 324 position: absolute; 325 right: 8px; 326 top: 50%; 327 transform: translateY(-50%); 328 width: 0; 329 height: 0; 330 border-left: 5px solid transparent; 331 border-right: 5px solid transparent; 332 border-top: 5px solid #333; 333 pointer-events: none; 334 } 335 336 #form-container select, 337 [id$="-form-container"] select { 338 width: 100%; 339 height: 100%; /* Fill the height of the container */ 340 padding: 12px 6px; 341 border: none; 342 border-radius: 8px; 343 background-color: transparent; 344 appearance: none; 345 font-size: 14px; 346 font-weight: 500; 347 color: #333; 348 text-overflow: ellipsis; 349 white-space: nowrap; 350 overflow: hidden; 351 cursor: pointer; 352 } 353 354 #form-container select option, 355 [id$="-form-container"] select option { 356 font-size: 14px; 357 } 358 359 #form-container input[type="tel"], 360 #form-container input[type="text"], 361 #form-container input[type="email"], 362 [id$="-form-container"] input[type="tel"], 363 [id$="-form-container"] input[type="text"], 364 [id$="-form-container"] input[type="email"] { 365 width: 100%; 366 height: 100%; /* Fill the height of the container */ 367 min-height: 46px; /* Minimum height to match select height */ 368 padding: 12px; 369 border: 1px solid #ddd; 370 border-radius: 8px; 371 font-size: 16px; 372 color: #333; 373 } 374 375 #form-container .booking-help-text, 376 [id$="-form-container"] .booking-help-text { 377 font-size: 12px; 378 color: #666; 379 margin: 5px 0 15px; 380 } 381 382 /* Grade Grid - Responsive */ 383 #form-container .booking-grade-grid, 384 [id$="-form-container"] .booking-grade-grid { 385 display: grid; 386 grid-template-columns: repeat(4, 1fr); 387 gap: 10px; 388 width: 100%; 389 } 390 391 /* Date Grid - Responsive */ 392 #form-container .booking-date-grid, 393 [id$="-form-container"] .booking-date-grid { 394 display: grid; 395 grid-template-columns: repeat(3, 1fr); 396 gap: 10px; 397 width: 100%; 398 } 399 400 /* Time Grid - Responsive */ 401 #form-container .booking-time-grid, 402 [id$="-form-container"] .booking-time-grid { 403 display: grid; 404 grid-template-columns: repeat(4, 1fr); 405 gap: 10px; 406 width: 100%; 407 } 408 409 /* Buttons */ 410 #form-container .booking-primary-button, 411 [id$="-form-container"] .booking-primary-button { 412 width: 100%; 413 padding: 14px; 414 background-color: #ffcf00; 415 color: #333; 416 border: none; 417 border-radius: 10px; 418 font-size: 16px; 419 font-weight: 600; 420 cursor: pointer; 421 display: flex; 422 align-items: center; 423 justify-content: center; 424 gap: 10px; 425 margin: 15px 0; 426 box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); 427 } 428 429 #form-container .booking-secondary-button, 430 [id$="-form-container"] .booking-secondary-button { 431 width: 100%; 432 padding: 12px; 433 background-color: #f5f5f5; 434 color: #666; 435 border: 1px solid #ddd; 436 border-radius: 10px; 437 font-size: 14px; 438 font-weight: 500; 439 cursor: pointer; 440 display: flex; 441 align-items: center; 442 justify-content: center; 443 margin-bottom: 15px; 444 } 445 446 #form-container .booking-grade-btn, 447 [id$="-form-container"] .booking-grade-btn, 448 #form-container .booking-date-btn, 449 [id$="-form-container"] .booking-date-btn, 450 #form-container .booking-time-btn, 451 [id$="-form-container"] .booking-time-btn { 452 width: 100%; 453 padding: 10px; 454 border: 1px solid #ddd; 455 border-radius: 8px; 456 background-color: white; 457 cursor: pointer; 458 } 459 460 #form-container .booking-back-button, 461 [id$="-form-container"] .booking-back-button { 462 position: absolute; 463 left: 24px; 464 top: 25px; 465 font-size: 18px; 466 color: #333; 467 cursor: pointer; 468 z-index: 10; 469 } 470 471 #form-container .booking-option-buttons, 472 [id$="-form-container"] .booking-option-buttons { 473 display: flex; 474 flex-wrap: wrap; 475 gap: 10px; 476 width: 100%; 477 } 478 479 #form-container .booking-option-btn, 480 [id$="-form-container"] .booking-option-btn { 481 padding: 10px 15px; 482 border: 1px solid #ddd; 483 border-radius: 30px; 484 background-color: white; 485 cursor: pointer; 486 font-size: 14px; 487 } 488 489 /* Error messages */ 490 #form-container .booking-error-message, 491 [id$="-form-container"] .booking-error-message { 492 color: #e74c3c; 493 font-size: 12px; 494 margin-top: 5px; 495 } 496 497 /* Mobile Responsiveness */ 498 @media (max-width: 576px) { 499 #form-container .booking-grade-grid, 500 [id$="-form-container"] .booking-grade-grid { 501 grid-template-columns: repeat(3, 1fr); 502 } 503 504 #form-container .booking-date-grid, 505 [id$="-form-container"] .booking-date-grid { 506 grid-template-columns: repeat(2, 1fr); 507 } 508 509 #form-container .booking-time-grid, 510 [id$="-form-container"] .booking-time-grid { 511 grid-template-columns: repeat(2, 1fr); 512 gap: 8px; 513 } 514 515 #form-container .booking-time-btn, 516 [id$="-form-container"] .booking-time-btn { 517 padding: 12px 8px; 518 font-size: 14px; 519 white-space: nowrap; 520 min-height: 45px; 521 display: flex; 522 align-items: center; 523 justify-content: center; 524 } 525 526 #form-container h2, 527 [id$="-form-container"] h2 { 528 font-size: 18px; 529 padding-right: 15px; 530 padding-left: 15px; 531 margin-bottom: 15px; 532 } 533 534 #form-container .booking-frame, 535 [id$="-form-container"] .booking-frame { 536 padding: 16px 12px; 537 border-radius: 10px; 538 } 539 540 /* Keep form rows in row direction for phone input */ 541 #form-container .booking-form-row:not(.booking-phone-row), 542 [id$="-form-container"] .booking-form-row:not(.booking-phone-row) { 543 flex-direction: column; 544 gap: 10px; 545 } 546 547 #form-container .booking-country-code, 548 [id$="-form-container"] .booking-country-code { 549 width: 80px; 550 min-width: 80px; 551 } 552 } 553 554 /* Extra small devices */ 555 @media (max-width: 360px) { 556 #form-container .booking-grade-grid, 557 [id$="-form-container"] .booking-grade-grid, 558 #form-container .booking-time-grid, 559 [id$="-form-container"] .booking-time-grid { 560 grid-template-columns: repeat(2, 1fr); 561 gap: 6px; 562 } 563 564 #form-container .booking-time-btn, 565 [id$="-form-container"] .booking-time-btn { 566 padding: 10px 6px; 567 font-size: 13px; 568 } 569 570 #form-container .boo
570king-country-code, 571 [id$="-form-container"] .booking-country-code { 572 width: 70px; 573 min-width: 70px; 574 } 575 576 #form-container select, 577 [id$="-form-container"] select { 578 padding: 12px 4px; 579 font-size: 13px; 580 } 581 } 582 `; 583 584 var styleSheet = document.createElement('style'); 585 styleSheet.id = 'booking-form-styles'; 586 styleSheet.textContent = styles; 587 document.head.appendChild(styleSheet); 588 }, 589 590 // Build the form structure 591 buildForm: function(container) { 592 var self = this; 593 594 // Create the main container 595 var mainContainer = this.createElement('div', { className: 'booking-container' }); 596 597 var frameContainer = this.createElement('div', { className: 'booking-frame-container' }); 598 var frame = this.createElement('div', { className: 'booking-frame' }); 599 600 // Append elements 601 mainContainer.appendChild(frameContainer); 602 frameContainer.appendChild(frame); 603 container.appendChild(mainContainer); 604 605 // Create step 1: Book Now & Get Certified 606 var step1 = this.createStep1(1); 607 frame.appendChild(step1); 608 609 // Create step 2: Select Date & Time 610 var step2 = this.createStep2(2); 611 frame.appendChild(step2); 612 613 // Create step 3: Student Details 614 var step3 = this.createStep3(3); 615 frame.appendChild(step3); 616 617 // Set up event listeners 618 this.setupEventListeners(container); 619 }, 620 621 // Create step 1 622 createStep1: function(stepNumber) { 623 var step = this.createElement('div', { 624 className: 'booking-step-content active', 625 id: 'booking-step1' 626 }); 627 628 // Add title and progress bar 629 var title = this.createElement('h2', {}); 630 title.textContent = 'Book Now & Get Certified'; 631 step.appendChild(title); 632 633 var progressBar = this.createProgressBar(stepNumber); 634 step.appendChild(progressBar); 635 636 // Add the phone input group with required indicator but no asterisk 637 var phoneGroup = this.createPhoneInput(true); 638 step.appendChild(phoneGroup); 639 640 // Add grade selection with required indicator but no asterisk 641 var gradeGroup = this.createGradeSelection(true); 642 step.appendChild(gradeGroup); 643 644 // Add action button 645 var actionButton = this.createElement('button', { 646 className: 'booking-primary-button', 647 attributes: { 'data-step': stepNumber.toString() } 648 }); 649 actionButton.innerHTML = 'Book a Free Trial Class'; 650 step.appendChild(actionButton); 651 652 // Add terms text 653 654 655 return step; 656 }, 657 658 // Create step 2 659 createStep2: function(stepNumber) { 660 var step = this.createElement('div', { 661 className: 'booking-step-content', 662 id: 'booking-step2' 663 }); 664 665 // Add back button 666 var backButton = this.createElement('div', { className: 'booking-back-button' }); 667 backButton.innerHTML = 'â'; 668 backButton.style.fontSize = '18px'; 669 step.appendChild(backButton); 670 671 // Add title and progress bar 672 var title = this.createElement('h2', {}); 673 title.textContent = 'Select Date & Time'; 674 step.appendChild(title); 675 676 var progressBar = this.createProgressBar(stepNumber); 677 step.appendChild(progressBar); 678 679 // Add timezone selection before date selection 680 var timezoneSelect = this.createTimezoneSelect(); 681 step.appendChild(timezoneSelect); 682 683 // Add date selection without asterisk 684 var dateGroup = this.createElement('div', { className: 'booking-form-group' }); 685 var dateLabel = this.createElement('label', {}); 686 dateLabel.textContent = 'Select Date'; 687 dateGroup.appendChild(dateLabel); 688 689 var dateHelpText = this.createElement('p', { className: 'booking-help-text' }); 690 dateHelpText.textContent = 'Your class will be for 1 hour'; 691 dateGroup.appendChild(dateHelpText); 692 693 var dateGrid = this.createElement('div', { className: 'booking-date-grid' }); 694 695 // Get dates for the next 6 days 696 var today = new Date(); 697 var dates = []; 698 var daysToCheck = 1; // Start from tomorrow 699 700 // Dates to skip (format: 'YYYY-MM-DD') 701 var skipDates = [ 702 '2025-12-24', // Wednesday, 24 December 2025 703 '2025-12-25', // Thursday, 25 December 2025 704 '2025-12-31', // Wednesday, 31 December 2025 705 '2026-01-01', // Thursday, 1 January 2026 706 '2026-03-30' // Monday, 30 March 2026 â excluded from booking 707 ]; 708 709 // Helper function to check if date should be skipped 710 function shouldSkipDate(dateObj) { 711 var year = dateObj.getFullYear();
712 var month = String(dateObj.getMonth() + 1).padStart(2, '0'); 713 var day = String(dateObj.getDate()).padStart(2, '0'); 714 var dateKey = year + '-' + month + '-' + day; 715 return skipDates.indexOf(dateKey) !== -1; 716 } 717 718 // Continue until we have 6 dates 719 while (dates.length < 6) { 720 var date = new Date(today); 721 date.setDate(today.getDate() + daysToCheck); 722 723 // Skip this date if it's in the skip list 724 if (shouldSkipDate(date)) { 725 daysToCheck++; 726 continue; 727 } 728 729 var day = daysToCheck === 1 ? 'Tomorrow' : this.getDayName(date.getDay()); 730 var dateStr = date.getDate() + ' ' + this.getMonthShortName(date.getMonth()); 731 732 dates.push({ 733 day: day, 734 date: dateStr, 735 active: dates.length === 0 // First available date is active by default 736 }); 737 738 daysToCheck++; 739 } 740 741 var self = this; 742 dates.forEach(function(date) { 743 var dateBtn = self.createElement('button', { 744 className: 'booking-date-btn' + (date.active ? ' active' : ''), 745 attributes: { 'type': 'button' } 746 }); 747 748 var dayDiv = self.createElement('div', {}); 749 dayDiv.textContent = date.day; 750 751 var dateDiv = self.createElement('div', {}); 752 dateDiv.textContent = date.date; 753 754 dateBtn.appendChild(dayDiv); 755 dateBtn.appendChild(dateDiv); 756 dateGrid.appendChild(dateBtn); 757 }); 758 759 dateGroup.appendChild(dateGrid); 760 step.appendChild(dateGroup); 761 762 // Add time selection without asterisk 763 var timeGroup = this.createElement('div', { className: 'booking-form-group' }); 764 var timeLabel = this.createElement('label', {}); 765 timeLabel.textContent = 'Select Start Time'; 766 timeGroup.appendChild(timeLabel); 767 768 var timeHelpText = this.createElement('p', { className: 'booking-help-text' }); 769 timeHelpText.textContent = 'Class slots are limited. Please choose carefully.'; 770 timeGroup.appendChild(timeHelpText); 771 772 // Get tomorrow's date to check if it's a weekend 773 var tomorrow = new Date(); 774 tomorrow.setDate(tomorrow.getDate() + 1); 775 // Special case: Dec 30, 2025 (tomorrow) should have all slots open like a weekend 776 var isTomorrowSpecialDay = tomorrow.getDate() === 30 && tomorrow.getMonth() === 11 && tomorrow.getFullYear() === 2025; 777 var isWeekend = tomorrow.getDay() === 0 || tomorrow.getDay() === 6 || isTomorrowSpecialDay; 778 779 // Create time grid based on country and whether tomorrow is a weekend 780 var timeGrid = this.createElement('div', { className: 'booking-time-grid' }); 781 782 // Define time slots based on country and day 783 var timeSlots; 784 if (this.userCountry && this.userCountry.isIndia) { 785 if (isWeekend) { 786 // Weekend slots for India: 10:30 AM to 7:30 PM 787 timeSlots = [ 788 '10:30 AM', '11:30 AM', '12:30 PM', 789 '1:30 PM', '2:30 PM', '3:30 PM', 790 '4:30 PM', '5:30 PM', '6:30 PM', 791 '7:30 PM' 792 ]; 793 } else { 794 // Weekday slots for India: 4:30 PM to 7:30 PM 795 timeSlots = [ 796 '4:30 PM', '5:30 PM', '6:30 PM', '7:30 PM' 797 ]; 798 } 799 } else { 800 if (isWeekend) { 801 // Weekend slots for ROW: 8:00 AM to 8:00 PM 802 timeSlots = [ 803 '8:00 AM', '9:00 AM', '10:00 AM', '11:00 AM', 804 '12:00 PM', '1:00 PM', '2:00 PM', '3:00 PM', 805 '4:00 PM', '5:00 PM', '6:00 PM', '7:00 PM', 806 '8:00 PM' 807 ]; 808 } else { 809 // Weekday slots for ROW: 5:00 PM to 8:00 PM 810 timeSlots = [ 811 '5:00 PM', '6:00 PM', '7:00 PM', '8:00 PM' 812 ]; 813 } 814 } 815
816 timeSlots.forEach(function(time) { 817 var timeBtn = self.createElement('button', { 818 className: 'booking-time-btn', 819 attributes: { 'type': 'button' } 820 }); 821 timeBtn.textContent = time; 822 timeGrid.appendChild(timeBtn); 823 }); 824 825 timeGroup.appendChild(timeGrid); 826 step.appendChild(timeGroup); 827 828 // Add action buttons 829 var actionButton = this.createElement('button', { 830 className: 'booking-primary-button', 831 attributes: { 'data-step': stepNumber.toString() } 832 }); 833 actionButton.textContent = 'Confirm Class Time'; 834 step.appendChild(actionButton); 835 836 return step; 837 }, 838 839 // Create step 3 840 createStep3: function(stepNumber) { 841 var step = this.createElement('div', { 842 className: 'booking-step-content', 843 id: 'booking-step3' 844 }); 845 846 // Add back button 847 var backButton = this.createElement('div', { className: 'booking-back-button' }); 848 backButton.innerHTML = 'â'; 849 backButton.style.fontSize = '18px'; 850 step.appendChild(backButton); 851 852 // Add title and progress bar 853 var title = this.createElement('h2', {}); 854 title.textContent = 'Student Details'; 855 step.appendChild(title); 856 857 var progressBar = this.createProgressBar(stepNumber); 858 step.appendChild(progressBar); 859 860 // Add help text 861 var helpText = this.createElement('p', { className: 'booking-help-text' }); 862 helpText.textContent = 'Please enter the following details correctly for the accreditation certificate.'; 863 step.appendChild(helpText); 864 865 // Child's name field without asterisk 866 var childNameGroup = this.createFormField( 867 'Child\'s Name', 868 'text', 869 'Enter Child\'s Name', 870 'This will be used on your child\'s certificate', 871 true, 872 'child-name-input' // Add specific ID 873 ); 874 step.appendChild(childNameGroup); 875 876 // Parent's name field without asterisk 877 var parentNameGroup = this.createFormField( 878 'Parent\'s Name', 879 'text', 880 'Enter Parent\'s Name', 881 '', 882 true, 883 'parent-name-input' // Add specific ID 884 ); 885 step.appendChild(parentNameGroup); 886 887 // Email field without asterisk 888 var emailGroup = this.createFormField( 889 'Email ID', 890 'email', 891 'Enter Email ID', 892 null, 893 true 894 ); 895 step.appendChild(emailGroup); 896 897 // Add action buttons 898 var actionButton = this.createElement('button', { 899 className: 'booking-primary-button', 900 attributes: { 'data-step': stepNumber.toString() } 901 }); 902 actionButton.textContent = 'Confirm Booking'; 903 step.appendChild(actionButton); 904 905 906 907 return step; 908 }, 909 910 // Create step 4 911 createStep4: function(stepNumber) { 912 var step = this.createElement('div', { 913 className: 'booking-step-content', 914 id: 'booking-step4' 915 }); 916 917 // Add back button 918 var backButton = this.createElement('div', { className: 'booking-back-button' }); 919 backButton.innerHTML = 'â'; 920 backButton.style.fontSize = '18px'; 921 step.appendChild(backButton); 922 923 // Add title and progress bar 924 var title = this.createElement('h2', {}); 925 title.textContent = 'Help us customize your experience'; 926 step.appendChild(title); 927 928 var progressBar = this.createProgressBar(stepNumber); 929 step.appendChild(progressBar); 930 931 // Who are you? 932 var roleGroup = this.createElement('div', { className: 'booking-form-group' }); 933 var roleLabel = this.createElement('label', {}); 934 roleLabel.textContent = 'Who are you?'; 935 roleGroup.appendChild(roleLabel); 936 937 var roleButtons = this.createElement('div', { className: 'booking-option-buttons' }); 938 roleButtons.style.display = 'flex'; 939 roleButtons.style.flexWrap = 'wrap'; 940 roleButtons.style.gap = '10px'; 941 942 var roleOptions = [ 943 { text: 'Parent', active: true }, 944 { text: 'Student' }, 945 { text: 'Guardian' } 946 ]; 947 948 roleOptions.forEach(function(option) { 949 var btn = this.createElement('button', { 950 className: 'booking-option-btn' + (option.active ? ' active' : ''), 951 attributes: { 'type': 'button' } 952 }); 953 btn.style.padding = '10px 15px'; 954 btn.style.border = option.active ? '1px solid #ffcf00' : '1px solid #ddd'; 955 btn.style.borderRadius = '30px'; 956 btn.style.backgroundColor = option.active ? 'rgba(255, 207, 0, 0.1)' : 'white'; 957 btn.style.cursor = 'pointer'; 958 btn.style.fontSize = '14px'; 959 960 btn.textContent = option.text; 961 roleButtons.appendChild(btn); 962 }, this); 963 964 roleGroup.appendChild(roleButtons); 965 step.appendChild(roleGroup); 966 967 // Why booking? 968 var reasonGroup = this.createElement('div', { className: 'booking-form-group' }); 969 var reasonLabel = this.createElement('label', {}); 970 reasonLabel.textContent = 'Why are you booking this demo class?'; 971 reasonGroup.appendChild(reasonLabel); 972 973 var reasonButtons = this.createElement('div', { className: 'booking-option-buttons' }); 974 reasonButtons.style.display = 'flex'; 975 reasonButtons.style.flexWrap = 'wrap'; 976 reasonButtons.style.gap = '10px'; 977 978 var reasonOptions = [ 979 { text: 'Want to buy course', active: true }, 980 { text: 'Just want the free demo class' } 981 ]; 982 983 reasonOptions.forEach(function(option) { 984 var btn = this.createElement('button', { 985 className: 'booking-option-btn' + (option.active ? ' active' : ''), 986 attributes: { 'type': 'button' } 987 }); 988 btn.style.padding = '10px 15px'; 989 btn.style.border = option.active ? '1px solid #ffcf00' : '1px solid #ddd'; 990 btn.style.borderRadius = '30px'; 991 btn.style.backgroundColor = option.active ? 'rgba(255, 207, 0, 0.1)' : 'white'; 992 btn.style.cursor = 'pointer'; 993 btn.style.fontSize = '14px'; 994 995 btn.textContent = option.text; 996 reasonButtons.appendChild(btn); 997 }, this); 998 999 reasonGroup.appendChild(reasonButtons); 1000 step.appendChild(reasonGroup); 1001 1002 // When planning to buy? 1003 var timelineGroup = this.createElement('div', { className: 'booking-form-group' }); 1004 var timelineLabel = this.createElement('label', {}); 1005 timelineLabel.textContent = 'When are you planning to buy the course?'; 1006 timelineGroup.appendChild(timelineLabel); 1007 1008 var timelineButtons = this.createElement('div', { className: 'booking-option-buttons' }); 1009 timelineButtons.style.display = 'flex'; 1010 timelineButtons.style.flexWrap = 'wrap'; 1011 timelineButtons.style.gap = '10px'; 1012 1013 var timelineOptions = [ 1014 { text: 'After the demo', active: true }, 1015 { text: 'This week' }, 1016 { text: 'This month' }, 1017 { text: 'Just exploring' } 1018 ]; 1019 1020 timelineOptions.forEach(function(option) { 1021 var btn = this.createElement('button', { 1022 className: 'booking-option-btn' + (option.active ? ' active' : ''), 1023 attributes: { 'type': 'button' } 1024 }); 1025 btn.style.padding = '10px 15px'; 1026 btn.style.border = option.active ? '1px solid #ffcf00' : '1px solid #ddd'; 1027 btn.style.borderRadius = '30px'; 1028 btn.style.backgroundColor = option.active ? 'rgba(255, 207, 0, 0.1)' : 'white'; 1029 btn.style.cursor = 'pointer'; 1030 btn.style.fontSize = '14px'; 1031 1032 btn.textContent = option.text; 1033 timelineButtons.appendChild(btn); 1034 }, this); 1035 1036 timelineGroup.appendChild(timelineButtons); 1037 step.appendChild(timelineGroup); 1038 1039 // Add action buttons 1040 var actionButton = this.createElement('button', { 1041 className: 'booking-primary-button booking-submit-btn', 1042 attributes: { 'data-step': stepNumber.toString() } 1043 }); 1044 actionButton.innerHTML = 'â Submit'; 1045 actionButton.style.marginTop = '30px'; 1046 step.appendChild(actionButton); 1047 1048 1049 1050 return step; 1051 }, 1052 1053 // Helper functions to create form elements 1054 createProgressBar: function(currentStep) { 1055 var progressBar = this.createElement('div', { className: 'booking-progress-bar' }); 1056 1057 for (var i = 1; i <= 3; i++) { 1058 progressBar.appendChild( 1059 this.createElement('div', { 1060 className: 'booking-progress-item' + (i <= currentStep ? ' active' : '') 1061 }) 1062 ); 1063 } 1064 1065 return progressBar; 1066 }, 1067 1068 createPhoneInput: function(required) { 1069 var self = this; 1070 var formGroup = this.createElement('div', { className: 'booking-form-group' }); 1071 1072 // Create form row for phone input with labels on the same line 1073 var labelRow = this.createElement('div', { className: 'booking-form-row' }); 1074 1075 // Country code label 1076 var countryCodeLabelDiv = this.createElement('div', { className: 'booking-country-code' }); 1077 var countryCodeLabel = this.createElement('label', {}); 1078 countryCodeLabel.textContent = 'Country Code'; 1079 countryCodeLabelDiv.appendChild(countryCodeLabel); 1080 1081 // Mobile number label 1082 var mobileNumberLabelDiv = this.createElement('div', { className: 'booking-mobile-number' }); 1083 var mobileLabel = this.createElement('label', {}); 1084 mobileLabel.textContent = 'Mobile Number'; 1085 mobileNumberLabelDiv.appendChild(mobileLabel); 1086 1087 // Add labels to the label row 1088 labelRow.appendChild(countryCodeLabelDiv); 1089 labelRow.appendChild(mobileNumberLabelDiv); 1090 formGroup.appendChild(labelRow); 1091 1092 // Create input row for the actual input fields 1093 var inputRow = this.createElement('div', { className: 'booking-form-row booking-phone-row' }); 1094 1095 // Country code input 1096 var countryCodeDiv = this.createElement('div', { className: 'booking-country-code' }); 1097 var selectWrapper = this.createElement('div', { className: 'booking-select-wrapper' }); 1098 var select = this.createElement('select', {}); 1099 1100 // Add all countries to the dropdown 1101 this.countriesList.forEach(function(country) { 1102 var option = self.createElement('option', { 1103 attributes: { 1104 value: country.code, 1105 selected: self.userCountry && country.code === self.userCountry.code ? 'selected' : null 1106 } 1107 });
1108 option.textContent = country.dial_code; 1109 select.appendChild(option); 1110 }); 1111 1112 selectWrapper.appendChild(select); 1113 countryCodeDiv.appendChild(selectWrapper); 1114 1115 // Mobile number input 1116 var mobileNumberDiv = this.createElement('div', { className: 'booking-mobile-number' }); 1117 var mobileInput = this.createElement('input', { 1118 attributes: { 1119 type: 'tel', 1120 placeholder: 'Enter Mobile Number', 1121 required: required ? 'required' : null 1122 } 1123 }); 1124 1125 // Add input validation to allow only digits 1126 mobileInput.addEventListener('input', function(e) { 1127 e.target.value = e.target.value.replace(/\D/g, ''); // Remove non-digit characters 1128 }); 1129 1130 mobileNumberDiv.appendChild(mobileInput); 1131 1132 // Add error message container (hidden by default) 1133 var errorMessage = this.createElement('div', { className: 'booking-error-message' }); 1134 errorMessage.style.color = '#e74c3c'; 1135 errorMessage.style.fontSize = '12px'; 1136 errorMessage.style.marginTop = '5px'; 1137 errorMessage.style.display = 'none'; 1138 mobileNumberDiv.appendChild(errorMessage); 1139 1140 // Assemble the input row 1141 inputRow.appendChild(countryCodeDiv); 1142 inputRow.appendChild(mobileNumberDiv); 1143 formGroup.appendChild(inputRow); 1144 1145 // Add help text 1146 var helpText = this.createElement('p', { className: 'booking-help-text' }); 1147 helpText.textContent = 'Please share the number that you use for WhatsApp'; 1148 formGroup.appendChild(helpText); 1149 1150 return formGroup; 1151 }, 1152 1153 // Update createGradeSelection method to show ages instead of grades 1154 createGradeSelection: function(required) { 1155 var formGroup = this.createElement('div', { className: 'booking-form-group' }); 1156 1157 // Add label without asterisk 1158 var label = this.createElement('label', {}); 1159 label.textContent = 'Select Child\'s Age'; 1160 formGroup.appendChild(label); 1161 1162 // Create grade grid 1163 var gradeGrid = this.createElement('div', { className: 'booking-grade-grid' }); 1164 1165 // Add age buttons (starting from age 5, which corresponds to Grade 1) 1166 for (var i = 1; i <= 12; i++) { 1167 var ageValue = i + 4; // Grade 1 = Age 5, Grade 2 = Age 6, etc. 1168 var gradeBtn = this.createElement('button', { 1169 className: 'booking-grade-btn', 1170 attributes: { 1171 'type': 'button', 1172 'data-grade': i.toString(), // Keep original grade value for backend 1173 'data-age': ageValue.toString() // Add age data attribute 1174 } 1175 }); 1176 1177 var ageText = this.createElement('span', {}); 1178 ageText.textContent = 'Age '; 1179 1180 var ageNumber = this.createElement('span', {}); 1181 ageNumber.textContent = ageValue.toString(); 1182 1183 gradeBtn.appendChild(ageText); 1184 gradeBtn.appendChild(ageNumber); 1185 gradeGrid.appendChild(gradeBtn); 1186 } 1187 1188 formGroup.appendChild(gradeGrid); 1189 return formGroup; 1190 }, 1191 1192 // Add method to create form fields with error support 1193 createFormField: function(labelText, inputType, placeholder, note, required, inputId) { 1194 var formGroup = this.createElement('div', { className: 'booking-form-group' }); 1195 1196 // Create label without asterisk 1197 var label = this.createElement('label', {}); 1198 label.textContent = labelText; 1199 formGroup.appendChild(label); 1200 1201 // Create input with ID if provided 1202 var input = this.createElement('input', { 1203 attributes: { 1204 type: inputType, 1205 placeholder: placeholder || ('Enter ' + labelText), 1206 required: required ? 'required' : null, 1207 id: inputId || null // Add ID if provided 1208 } 1209 }); 1210 formGroup.appendChild(input); 1211 1212 // Add note if provided 1213 if (note) { 1214 var noteElement = this.createElement('small', {}); 1215 noteElement.textContent = note; 1216 noteElement.style.display = 'block'; 1217 noteElement.style.fontSize = '12px'; 1218 noteElement.style.color = '#666'; 1219 noteElement.style.marginTop = '5px'; 1220 formGroup.appendChild(noteElement); 1221 } 1222 1223 // Add error message container (hidden by default) 1224 var errorMessage = this.createElement('div', { className: 'booking-error-message' }); 1225 errorMessage.style.color = '#e74c3c'; 1226 errorMessage.style.fontSize = '12px'; 1227 errorMessage.style.marginTop = '5px'; 1228 errorMessage.style.display = 'none'; 1229 formGroup.appendChild(errorMessage); 1230 1231 return formGroup; 1232 }, 1233 1234 // Helper method to show error 1235 showError: function(container, field, message) { 1236 // Find the error message container in the field's parent 1237 var errorContainer = field.parentNode.querySelector('.booking-error-message'); 1238 if (errorContainer) { 1239 errorContainer.textContent = message; 1240 errorContainer.style.display = 'block'; 1241 1242 // Highlight the field 1243 field.style.borderColor = '#e74c3c'; 1244 } 1245 return false; // For convenience in validation chains 1246 }, 1247 1248 // Helper method to clear error 1249 clearError: function(field) { 1250 if (!field) return; 1251 1252 // Find the error message container in the field's parent 1253 var errorContainer = field.parentNode.querySelector('.booking-error-message'); 1254 if (errorContainer) { 1255 errorContainer.textContent = ''; 1256 errorContainer.style.display = 'none'; 1257 } 1258 1259 // Reset field styling 1260 field.style.borderColor = '#ddd'; 1261 }, 1262 1263 // Helper method to clear all errors in a container 1264 clearAllErrors: function(container) { 1265 var errorContainers = container.querySelectorAll('.booking-error-message');
1266 errorContainers.forEach(function(errorContainer) { 1267 errorContainer.textContent = ''; 1268 errorContainer.style.display = 'none'; 1269 }); 1270 1271 var fields = container.querySelectorAll('input, select'); 1272 fields.forEach(function(field) { 1273 field.style.borderColor = '#ddd'; 1274 }); 1275 }, 1276 1277 // Update setupEventListeners to handle age selection error messages 1278 setupEventListeners: function(container) { 1279 var self = this; 1280 1281 // Handle navigation between steps 1282 var primaryButtons = container.querySelectorAll('.booking-primary-button'); 1283 primaryButtons.forEach(function(button) { 1284 button.addEventListener('click', function() { 1285 var stepNumber = parseInt(this.getAttribute('data-step')); 1286 var nextStepNumber = stepNumber + 1; 1287 var currentStep = container.querySelector('#booking-step' + stepNumber); 1288 1289 // Clear all previous errors 1290 self.clearAllErrors(currentStep); 1291 1292 // Basic validation 1293 var isValid = true; 1294 if (stepNumber === 1) { 1295 var mobileInput = container.querySelector('input[type="tel"]'); 1296 var gradeSelected = container.querySelector('.booking-grade-btn.active'); 1297 if (!mobileInput || !mobileInput.value) { 1298 isValid = self.showError(currentStep, mobileInput, 'Please enter your mobile number (required)'); 1299 } 1300 if (!gradeSelected) { 1301 var gradeGrid = container.querySelector('.booking-grade-grid'); 1302 if (gradeGrid) { 1303 var errorMessage = self.createElement('div', { className: 'booking-error-message' }); 1304 errorMessage.style.color = '#e74c3c'; 1305 errorMessage.style.fontSize = '12px'; 1306 errorMessage.style.marginTop = '5px'; 1307 errorMessage.textContent = 'Please select an age (required)'; 1308 var existingError = gradeGrid.nextElementSibling; 1309 if (existingError && existingError.classList.contains('booking-error-message')) { 1310 existingError.remove(); 1311 } 1312 gradeGrid.parentNode.insertBefore(errorMessage, gradeGrid.nextSibling); 1313 isValid = false; 1314 } 1315 } 1316 } else if (stepNumber === 2) { 1317 var dateSelected = container.querySelector('.booking-date-btn.active'); 1318 var timeSelected = container.querySelector('.booking-time-btn.active'); 1319 if (!dateSelected) { 1320 var dateGrid = container.querySelector('.booking-date-grid'); 1321 if (dateGrid) { 1322 var errorMessage = self.createElement('div', { className: 'booking-error-message' }); 1323 errorMessage.style.color = '#e74c3c'; 1324 errorMessage.style.fontSize = '12px'; 1325 errorMessage.style.marginTop = '5px'; 1326 errorMessage.textContent = 'Please select a date (required)'; 1327 var existingError = dateGrid.nextElementSibling; 1328 if (existingError && existingError.classList.contains('booking-error-message')) { 1329 existingError.remove(); 1330 } 1331 dateGrid.parentNode.insertBefore(errorMessage, dateGrid.nextSibling); 1332 isValid = false; 1333 } 1334 } 1335 if (!timeSelected) { 1336 var timeGrid = container.querySelector('.booking-time-grid'); 1337 if (timeGrid) { 1338 var errorMessage = self.createElement('div', { className: 'booking-error-message' }); 1339 errorMessage.style.color = '#e74c3c'; 1340 errorMessage.style.fontSize = '12px'; 1341 errorMessage.style.marginTop = '5px'; 1342 errorMessage.textContent = 'Please select a time (required)'; 1343 var existingError = timeGrid.nextElementSibling; 1344 if (existingError && existingError.classList.contains('booking-error-message')) { 1345 existingError.remove(); 1346 } 1347 timeGrid.parentNode.insertBefore(errorMessage, timeGrid.nextSibling); 1348 isValid = false;
1349 } 1350 } 1351 } else if (stepNumber === 3) { 1352 var childName = container.querySelector('#child-name-input'); 1353 var parentName = container.querySelector('#parent-name-input'); 1354 var emailId = container.querySelector('input[type="email"]'); 1355 if (!childName || !childName.value) { 1356 isValid = self.showError(currentStep, childName, 'Please enter child\'s name (required)'); 1357 } 1358 if (!parentName || !parentName.value) { 1359 isValid = self.showError(currentStep, parentName, 'Please enter parent\'s name (required)'); 1360 } 1361 if (!emailId || !emailId.value) { 1362 isValid = self.showError(currentStep, emailId, 'Please enter email ID (required)'); 1363 } else if (!/^\S+@\S+\.\S+$/.test(emailId.value)) { 1364 isValid = self.showError(currentStep, emailId, 'Please enter a valid email address'); 1365 } 1366 } 1367 1368 if (!isValid) return; 1369 1370 // Collect form data for the current step 1371 var formData = self.collectFormData(container); 1372 1373 // Make the API call but don't wait for it to complete 1374 self.submitStepData(stepNumber, formData) 1375 .catch(function(error) { 1376 console.error('Error submitting step data:', error); 1377 // Log the error but don't show it to the user 1378 console.log('Error details:', { 1379 step: stepNumber, 1380 data: formData, 1381 error: error 1382 }); 1383 }); 1384 1385 if (stepNumber === 3) { 1386 // Step 3 is the final step - submit the form and redirect 1387 self.submitForm(container); 1388 return; 1389 } 1390 1391 // Proceed to next step immediately without waiting for API response 1392 var nextStep = container.querySelector('#booking-step' + nextStepNumber); 1393 if (currentStep && nextStep) { 1394 currentStep.classList.remove('active'); 1395 nextStep.classList.add('active'); 1396 } 1397 }); 1398 }); 1399 1400 // Handle input events to clear errors when user starts typing 1401 container.addEventListener('input', function(e) { 1402 if (e.target.tagName === 'INPUT') { 1403 self.clearError(e.target); 1404 } 1405 }); 1406 1407 // Handle back buttons 1408 var backButtons = container.querySelectorAll('.booking-back-button, .booking-back-btn'); 1409 backButtons.forEach(function(button) { 1410 button.addEventListener('click', function() { 1411 var stepNumber; 1412 1413 if (this.classList.contains('booking-back-btn')) { 1414 stepNumber = parseInt(this.getAttribute('data-step')); 1415 } else { 1416 var currentStep = container.querySelector('.booking-step-content.active'); 1417 var allSteps = container.querySelectorAll('.booking-step-content'); 1418 stepNumber = Array.from(allSteps).indexOf(currentStep) + 1; 1419 } 1420 1421 var prevStepNumber = stepNumber - 1; 1422 1423 if (prevStepNumber > 0) { 1424 var currentStep = container.querySelector('#booking-step' + stepNumber); 1425 var prevStep = container.querySelector('#booking-step' + prevStepNumber); 1426 1427 if (currentStep && prevStep) { 1428 currentStep.classList.remove('active'); 1429 prevStep.classList.add('active'); 1430 } 1431 } 1432 }); 1433 }); 1434 1435 // Handle grade (now age) selection 1436 container.addEventListener('click', function(e) { 1437 if (e.target.closest('.booking-grade-btn')) { 1438 var gradeBtn = e.target.closest('.booking-grade-btn'); 1439 var gradeBtns = container.querySelectorAll('.booking-grade-btn'); 1440
1441 gradeBtns.forEach(function(btn) { 1442 btn.classList.remove('active'); 1443 btn.style.border = '1px solid #ddd'; 1444 btn.style.backgroundColor = 'white'; 1445 }); 1446 1447 gradeBtn.classList.add('active'); 1448 gradeBtn.style.border = '1px solid #ffcf00'; 1449 gradeBtn.style.backgroundColor = 'rgba(255, 207, 0, 0.1)'; 1450 1451 // Clear any error messages 1452 var gradeGrid = container.querySelector('.booking-grade-grid'); 1453 if (gradeGrid) { 1454 var errorMessage = gradeGrid.nextElementSibling; 1455 if (errorMessage && errorMessage.classList.contains('booking-error-message')) { 1456 errorMessage.remove(); 1457 } 1458 } 1459 } 1460 }); 1461 1462 // Handle date selection 1463 container.addEventListener('click', function(e) { 1464 if (e.target.closest('.booking-date-btn')) { 1465 var dateBtn = e.target.closest('.booking-date-btn'); 1466 var dateBtns = container.querySelectorAll('.booking-date-btn'); 1467 1468 dateBtns.forEach(function(btn) { 1469 btn.classList.remove('active'); 1470 btn.style.border = '1px solid #ddd'; 1471 btn.style.backgroundColor = 'white'; 1472 }); 1473 1474 dateBtn.classList.add('active'); 1475 dateBtn.style.border = '1px solid #ffcf00'; 1476 dateBtn.style.backgroundColor = 'rgba(255, 207, 0, 0.1)'; 1477 1478 // Clear any error messages 1479 var dateGrid = container.querySelector('.booking-date-grid'); 1480 if (dateGrid) { 1481 var errorMessage = dateGrid.nextElementSibling; 1482 if (errorMessage && errorMessage.classList.contains('booking-error-message')) { 1483 errorMessage.remove(); 1484 } 1485 } 1486 1487 // Get the selected date and check if it's a weekend 1488 var dayText = dateBtn.querySelector('div:first-child').textContent; 1489 // Special case: Tuesday Dec 30, 2025 should have all slots open like a weekend 1490 var isWeekend = dayText === 'Saturday' || dayText === 'Sunday' || dayText === 'Tuesday'; 1491 1492 // Get the current timezone 1493 var timezoneSelect = container.querySelector('.booking-timezone-select'); 1494 var currentTimezone = timezoneSelect ? timezoneSelect.value : 'Asia/Kolkata'; 1495 1496 // Update time slots based on both timezone and weekend status 1497 self.updateTimeSlotsBasedOnTimezone(currentTimezone); 1498 } 1499 }); 1500 1501 // Handle time selection 1502 container.addEventListener('click', function(e) { 1503 if (e.target.closest('.booking-time-btn')) { 1504 var timeBtn = e.target.closest('.booking-time-btn'); 1505 var timeBtns = container.querySelectorAll('.booking-time-btn'); 1506 1507 timeBtns.forEach(function(btn) { 1508 btn.classList.remove('active'); 1509 btn.style.border = '1px solid #ddd'; 1510 btn.style.backgroundColor = 'white'; 1511 }); 1512 1513 timeBtn.classList.add('active'); 1514 timeBtn.style.border = '1px solid #ffcf00'; 1515 timeBtn.style.backgroundColor = 'rgba(255, 207, 0, 0.1)'; 1516 1517 // Clear any error messages 1518 var timeGrid = container.querySelector('.booking-time-grid'); 1519 if (timeGrid) { 1520 var errorMessage = timeGrid.nextElementSibling; 1521 if (errorMessage && errorMessage.classList.contains('booking-error-message')) { 1522 errorMessage.remove(); 1523 } 1524 } 1525 } 1526 }); 1527 1528 // Handle option buttons 1529 container.addEventListener('click', function(e) { 1530 if (e.target.closest('.booking-option-btn')) { 1531 var optionBtn = e.target.closest('.booking-option-btn'); 1532 var optionGroup = optionBtn.closest('.booking-option-buttons'); 1533 var optionBtns = optionGroup.querySelectorAll('.booking-option-btn'); 1534
1535 optionBtns.forEach(function(btn) { 1536 btn.classList.remove('active'); 1537 btn.style.border = '1px solid #ddd'; 1538 btn.style.backgroundColor = 'white'; 1539 }); 1540 1541 optionBtn.classList.add('active'); 1542 optionBtn.style.border = '1px solid #ffcf00'; 1543 optionBtn.style.backgroundColor = 'rgba(255, 207, 0, 0.1)'; 1544 } 1545 }); 1546 }, 1547 1548 // Helper function to collect device and browser information 1549 getDeviceBrowserData: function() { 1550 var data = { 1551 // Browser information 1552 user_agent: navigator.userAgent || '', 1553 browser_language: navigator.language || navigator.userLanguage || '', 1554 browser_languages: navigator.languages ? navigator.languages.join(',') : '', 1555 platform: navigator.platform || '', 1556 vendor: navigator.vendor || '', 1557 cookies_enabled: navigator.cookieEnabled, 1558 do_not_track: navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack || '', 1559 online: navigator.onLine, 1560 1561 // Screen information 1562 screen_width: screen.width || 0, 1563 screen_height: screen.height || 0, 1564 screen_avail_width: screen.availWidth || 0, 1565 screen_avail_height: screen.availHeight || 0, 1566 screen_color_depth: screen.colorDepth || 0, 1567 screen_pixel_ratio: window.devicePixelRatio || 1, 1568
1569 // Window/viewport information 1570 viewport_width: window.innerWidth || document.documentElement.clientWidth || 0, 1571 viewport_height: window.innerHeight || document.documentElement.clientHeight || 0, 1572 1573 // Timezone information 1574 timezone_offset: new Date().getTimezoneOffset(), 1575 timezone_name: Intl.DateTimeFormat().resolvedOptions().timeZone || '', 1576 1577 // Connection information (if available) 1578 connection_type: (navigator.connection && navigator.connection.effectiveType) || '', 1579 connection_downlink: (navigator.connection && navigator.connection.downlink) || '', 1580 1581 // Device memory (if available) 1582 device_memory: navigator.deviceMemory || '', 1583 hardware_concurrency: navigator.hardwareConcurrency || '', 1584 1585 // Touch capability 1586 touch_support: ('ontouchstart' in window) || (navigator.maxTouchPoints > 0), 1587 max_touch_points: navigator.maxTouchPoints || 0, 1588 1589 // Referrer information 1590 referrer: document.referrer || '', 1591 1592 // Local storage data 1593 local_storage_available: (function() { 1594 try { 1595 localStorage.setItem('test', 'test'); 1596 localStorage.removeItem('test'); 1597 return true; 1598 } catch(e) { 1599 return false; 1600 } 1601 })(), 1602 session_storage_available: (function() { 1603 try { 1604 sessionStorage.setItem('test', 'test'); 1605 sessionStorage.removeItem('test'); 1606 return true; 1607 } catch(e) { 1608 return false; 1609 } 1610 })() 1611 }; 1612 1613 // Collect relevant localStorage items (excluding sensitive data) 1614 var localStorageData = {}; 1615 try { 1616 if (data.local_storage_available) { 1617 for (var i = 0; i < localStorage.length; i++) { 1618 var key = localStorage.key(i); 1619 // Only collect booking/form related data, exclude sensitive items 1620 if (key && (key.indexOf('booking') !== -1 || key.indexOf('form') !== -1 || key.indexOf('utm') !== -1 || key.indexOf('ref') !== -1)) { 1621 localStorageData[key] = localStorage.getItem(key); 1622 } 1623 } 1624 } 1625 } catch(e) { 1626 console.log('Error reading localStorage:', e); 1627 } 1628 data.local_storage_data = localStorageData; 1629 1630 // Collect relevant sessionStorage items 1631 var sessionStorageData = {}; 1632 try { 1633 if (data.session_storage_available) { 1634 for (var i = 0; i < sessionStorage.length; i++) { 1635 var key = sessionStorage.key(i); 1636 // Only collect booking/form related data 1637 if (key && (key.indexOf('booking') !== -1 || key.indexOf('form') !== -1 || key.indexOf('utm') !== -1 || key.indexOf('ref') !== -1)) { 1638 sessionStorageData[key] = sessionStorage.getItem(key); 1639 } 1640 } 1641 } 1642 } catch(e) { 1643 console.log('Error reading sessionStorage:', e); 1644 } 1645 data.session_storage_data = sessionStorageData; 1646 1647 // Parse URL parameters for UTM tracking 1648 var urlParams = {}; 1649 try { 1650 var searchParams = new URLSearchParams(window.location.search); 1651 searchParams.forEach(function(value, key) { 1652 urlParams[key] = value; 1653 }); 1654 } catch(e) { 1655 console.log('Error parsing URL params:', e); 1656 }
1657 data.url_params = urlParams; 1658 1659 return data; 1660 }, 1661 1662 // Add a method to send step data to the server 1663 submitStepData: function(stepNumber, formData) { 1664 console.log('Starting submitStepData for step:', stepNumber); 1665 console.log('Initial formData:', formData); 1666 1667 // Format the data according to the required structure based on available data 1668 var formattedData = { 1669 step: stepNumber, 1670 page_url: window.location.href // Add page URL to API request 1671 }; 1672 1673 // Get timezone based on country code from step 1 1674 if (stepNumber >= 1) { 1675 var countrySelect = document.querySelector('.booking-country-code select'); 1676 if (countrySelect) { 1677 var countryCode = countrySelect.value; 1678 formattedData.timezone = this.getDefaultTimezone(countryCode); 1679 console.log('Using timezone from country code:', formattedData.timezone); 1680 } 1681 } 1682 1683 // Add IP address to the request 1684 if (stepNumber >= 1) { 1685 formattedData.ip_address = this.userCountry?.ip || ''; 1686 } 1687 1688 // Add device and browser data for step 1 1689 if (stepNumber === 1) { 1690 var deviceBrowserData = this.getDeviceBrowserData(); 1691 // Add IP address to device data 1692 deviceBrowserData.ip_address = this.userCountry?.ip || formattedData.ip_address || ''; 1693 formattedData.device_data = deviceBrowserData; 1694 console.log('Device and browser data collected:', deviceBrowserData); 1695 } 1696 1697 // Override timezone with user selection in step 2 1698 if (stepNumber >= 2) { 1699 var timezoneSelect = document.querySelector('.booking-timezone-select'); 1700 if (timezoneSelect && timezoneSelect.value) { 1701 formattedData.timezone = timezoneSelect.value; 1702 console.log('Using user selected timezone:', formattedData.timezone); 1703 } 1704 } 1705 1706 // Step 1: Phone and Age 1707 if (stepNumber >= 1) { 1708 console.log('Processing Step 1 data...'); 1709 var countrySelect = document.querySelector('.booking-country-code select'); 1710 var phoneInput = document.querySelector('input[type="tel"]'); 1711 console.log('Country select element:', countrySelect); 1712 console.log('Phone input element:', phoneInput); 1713 1714 // Check if phone number exists in formData first 1715 if (formData.phone_number) { 1716 formattedData.phone_number = formData.phone_number; 1717 console.log('Using phone number from formData:', formattedData.phone_number); 1718 } 1719 // If not in formData, try to get from input elements 1720 else if (countrySelect && phoneInput && phoneInput.value) { 1721 var countryCode = countrySelect.value; 1722 var phoneNumber = phoneInput.value; 1723 console.log('Country code:', countryCode); 1724 console.log('Phone number:', phoneNumber); 1725 1726 // Find the country dial code from the countriesList 1727 var country = this.countriesList.find(function(c) { 1728 return c.code.toLowerCase() === countryCode.toLowerCase(); 1729 }); 1730 console.log('Found country:', country); 1731 1732 // Format phone number with proper dial code 1733 if (country) { 1734 // Remove any existing country code from the phone number 1735 var cleanPhoneNumber = phoneNumber.replace(/^\+\d+/, '').replace(/\D/g, ''); 1736 formattedData.phone_number = country.dial_code + cleanPhoneNumber; 1737 } else { 1738 formattedData.phone_number = phoneNumber; 1739 } 1740 console.log('Formatted phone number:', formattedData.phone_number); 1741 } 1742 1743 // Get age from the selected grade button 1744 var selectedGrade = document.querySelector('.booking-grade-btn.active'); 1745 if (selectedGrade) { 1746 formattedData.age = selectedGrade.getAttribute('data-age'); 1747 console.log('Age:', formattedData.age); 1748 } 1749 } 1750 1751 // Step 2: Date and Time 1752 if (stepNumber >= 2) { 1753 console.log('Processing Step 2 data...'); 1754 if (formData.selectedDate) { 1755 formattedData.trial_date = formData.selectedDate; 1756 console.log('Trial date:', formattedData.trial_date); 1757 } 1758 if (formData.selectedTime) { 1759 formattedData.trial_time = formData.selectedTime; 1760 console.log('Trial time:', formattedData.trial_time); 1761 } 1762 } 1763 1764 // Step 3: Child and Parent Details 1765 if (stepNumber >= 3) { 1766 console.log('Processing Step 3 data...'); 1767 if (formData.childName) {
1768 formattedData.child_name = formData.childName; 1769 console.log('Child name:', formattedData.child_name); 1770 } 1771 if (formData.parentName) { 1772 formattedData.parent_name = formData.parentName; 1773 console.log('Parent name:', formattedData.parent_name); 1774 } 1775 if (formData.emailId) { 1776 formattedData.email = formData.emailId; 1777 console.log('Email:', formattedData.email); 1778 } 1779 } 1780 1781 // Step 4: Additional Information 1782 if (stepNumber >= 4) { 1783 console.log('Processing Step 4 data...'); 1784 formattedData.booked_by = formData.userRole || 'Parent'; 1785 formattedData.booking_reason = formData.bookingReason || 'Want to buy course'; 1786 formattedData.purchase_plan = formData.purchaseTiming || 'This week'; 1787 console.log('Step 4 data:', { 1788 booked_by: formattedData.booked_by, 1789 booking_reason: formattedData.booking_reason, 1790 purchase_plan: formattedData.purchase_plan, 1791 timezone: formattedData.timezone 1792 }); 1793 } 1794 1795 console.log('Final formatted data:', formattedData); 1796 1797 // Only make the API call if we have the required data 1798 if (stepNumber === 1 && !formattedData.phone_number) { 1799 console.error('Phone number is required for step 1'); 1800 return Promise.reject(new Error('Phone number is required')); 1801 } 1802 1803 // Only make the API call if we have some data to send 1804 if (Object.keys(formattedData).length > 2) { // More than just step and timezone 1805 console.log('Making API call to submit form data...'); 1806 console.log('Request URL:', 'https://live.jetlearn.com/hooks/submit-form/'); 1807 console.log('Request headers:', { 1808 'Content-Type': 'application/json', 1809 'Accept': 'application/json' 1810 }); 1811 console.log('Request body:', JSON.stringify(formattedData, null, 2)); 1812 1813 return fetch('https://live.jetlearn.com/hooks/submit-form/', { 1814 method: 'POST', 1815 mode: 'cors', 1816 headers: { 1817 'Content-Type': 'application/json', 1818 'Accept': 'application/json' 1819 }, 1820 body: JSON.stringify(formattedData) 1821 }) 1822 .then(function(response) { 1823 console.log('Received API response:', { 1824 status: response.status, 1825 statusText: response.statusText, 1826 headers: Object.fromEntries(response.headers.entries()) 1827 }); 1828 1829 return response.json().then(function(data) { 1830 console.log('API response data:', data); 1831 if (!response.ok || (data && data.status === 'error')) { 1832 throw new Error(data.message || 'Failed to submit form data'); 1833 } 1834 return data; 1835 }); 1836 }) 1837 .catch(function(error) { 1838 console.error('Error submitting form data:', error); 1839 console.log('Error details:', { 1840 step: stepNumber, 1841 data: formattedData, 1842 error: error.message, 1843 stack: error.stack 1844 }); 1845 throw error; 1846 }); 1847 } else { 1848 console.log('No data to send, resolving promise'); 1849 return Promise.resolve(); 1850 } 1851 }, 1852 1853 // Helper function to create DOM elements 1854 createElement: function(tagName, options) { 1855 options = options || {}; 1856 1857 var element = document.createElement(tagName); 1858 1859 if (options.className) { 1860 element.className = options.className; 1861 } 1862 1863 if (options.id) {
1864 element.id = options.id; 1865 } 1866 1867 if (options.attributes) { 1868 for (var key in options.attributes) { 1869 if (options.attributes.hasOwnProperty(key)) { 1870 element.setAttribute(key, options.attributes[key]); 1871 } 1872 } 1873 } 1874 1875 return element; 1876 }, 1877 1878 // Add country detection using IP 1879 detectCountry: function() { 1880 var self = this; 1881 1882 // Try to get cached country data from sessionStorage 1883 var cachedCountry = sessionStorage.getItem('bookingFormCountry'); 1884 if (cachedCountry) { 1885 console.log('Using cached country data'); 1886 var countryData = JSON.parse(cachedCountry); 1887 self.updateCountryCodeDropdown(countryData); 1888 return Promise.resolve(countryData); 1889 } 1890 1891 // Default country data (India as default for faster loading) 1892 var defaultCountry = { 1893 code: 'in', 1894 dial_code: '+91', 1895 name: 'India', 1896 isIndia: true, 1897 timezone: 'Asia/Kolkata' 1898 }; 1899 1900 // First set default country for immediate UI update 1901 self.updateCountryCodeDropdown(defaultCountry); 1902 1903 // Then fetch actual country and timezone in background 1904 return fetch('https://ipapi.co/json/?key=BZEnvu2Wh5giJT7yIUyoNpU95yPxX6QKXGfrmgLSzVCm9Rjiu7') 1905 .then(function(response) { 1906 if (!response.ok) { 1907 throw new Error('Network response was not ok'); 1908 } 1909 return response.json(); 1910 }) 1911 .then(function(data) { 1912 console.log('IP API Response:', data); 1913 1914 if (data && data.country_code) { 1915 var countryCode = data.country_code.toLowerCase(); 1916 var country = self.findCountryByCode(countryCode); 1917 var isIndia = countryCode === 'in'; 1918 1919 // Ensure we have a valid timezone 1920 var timezone = data.timezone; 1921 if (!timezone || timezone.trim() === '') { 1922 timezone = self.getDefaultTimezone(countryCode); 1923 console.log('Using default timezone for country:', timezone); 1924 } 1925 1926 // Store the IP address 1927 var detectedCountry = { 1928 code: countryCode, 1929 dial_code: country.dial_code, 1930 name: country.name, 1931 isIndia: isIndia, 1932 timezone: timezone, 1933 ip: data.ip 1934 }; 1935 1936 // Cache the country data 1937 sessionStorage.setItem('bookingFormCountry', JSON.stringify(detectedCountry)); 1938 self.userCountry = detectedCountry; 1939 self.updateCountryCodeDropdown(detectedCountry); 1940 return Promise.resolve(detectedCountry); 1941 } 1942 console.log('Using default country data'); 1943 return defaultCountry; 1944 }) 1945 .catch(function(error) { 1946 console.error('Error during country detection:', error); 1947 console.log('Using default country data due to error'); 1948 return defaultCountry; 1949 }); 1950 }, 1951 1952 // Add a new method to update the country code dropdown 1953 updateCountryCodeDropdown: function(country) { 1954 console.log('Updating country code dropdown with:', country); 1955 var countrySelect = document.querySelector('.booking-country-code select'); 1956 if (countrySelect) { 1957 countrySelect.value = country.code; 1958 1959 // Trigger the change event to update related UI (e.g., time slots) 1960 var event = new Event('change'); 1961 countrySelect.dispatchEvent(event); 1962 console.log('Country code dropdown updated successfully'); 1963 } else { 1964 console.warn('Country code dropdown not found'); 1965 } 1966 }, 1967 1968 // Find country by code in countries list 1969 findCountryByCode: function(code) { 1970 return this.countriesList.find(function(country) { 1971 return country.code.toLowerCase() === code.toLowerCase(); 1972 }); 1973 }, 1974 1975 // Add a countries list 1976 countriesList: [
1977 { name: 'Afghanistan', dial_code: '+93', code: 'af' }, 1978 { name: 'Albania', dial_code: '+355', code: 'al' }, 1979 { name: 'Algeria', dial_code: '+213', code: 'dz' }, 1980 { name: 'American Samoa', dial_code: '+1-684', code: 'as' }, 1981 { name: 'Andorra', dial_code: '+376', code: 'ad' }, 1982 { name: 'Angola', dial_code: '+244', code: 'ao' }, 1983 { name: 'Anguilla', dial_code: '+1-264', code: 'ai' }, 1984 { name: 'Antarctica', dial_code: '+672', code: 'aq' }, 1985 { name: 'Antigua and Barbuda', dial_code: '+1-268', code: 'ag' }, 1986 { name: 'Argentina', dial_code: '+54', code: 'ar' }, 1987 { name: 'Armenia', dial_code: '+374', code: 'am' }, 1988 { name: 'Aruba', dial_code: '+297', code: 'aw' }, 1989 { name: 'Australia', dial_code: '+61', code: 'au' }, 1990 { name: 'Austria', dial_code: '+43', code: 'at' }, 1991 { name: 'Azerbaijan', dial_code: '+994', code: 'az' }, 1992 { name: 'Bahamas', dial_code: '+1-242', code: 'bs' }, 1993 { name: 'Bahrain', dial_code: '+973', code: 'bh' }, 1994 { name: 'Bangladesh', dial_code: '+880', code: 'bd' }, 1995 { name: 'Barbados', dial_code: '+1-246', code: 'bb' }, 1996 { name: 'Belarus', dial_code: '+375', code: 'by' },
1997 { name: 'Belgium', dial_code: '+32', code: 'be' }, 1998 { name: 'Belize', dial_code: '+501', code: 'bz' }, 1999 { name: 'Benin', dial_code: '+229', code: 'bj' }, 2000 { name: 'Bermuda', dial_code: '+1-441', code: 'bm' }, 2001 { name: 'Bhutan', dial_code: '+975', code: 'bt' }, 2002 { name: 'Bolivia', dial_code: '+591', code: 'bo' }, 2003 { name: 'Bosnia and Herzegovina', dial_code: '+387', code: 'ba' }, 2004 { name: 'Botswana', dial_code: '+267', code: 'bw' }, 2005 { name: 'Brazil', dial_code: '+55', code: 'br' }, 2006 { name: 'British Indian Ocean Territory', dial_code: '+246', code: 'io' }, 2007 { name: 'Brunei', dial_code: '+673', code: 'bn' }, 2008 { name: 'Bulgaria', dial_code: '+359', code: 'bg' }, 2009 { name: 'Burkina Faso', dial_code: '+226', code: 'bf' }, 2010 { name: 'Burundi', dial_code: '+257', code: 'bi' }, 2011 { name: 'Cambodia', dial_code: '+855', code: 'kh' }, 2012 { name: 'Cameroon', dial_code: '+237', code: 'cm' }, 2013 { name: 'Canada', dial_code: '+1', code: 'ca' }, 2014 { name: 'Cape Verde', dial_code: '+238', code: 'cv' }, 2015 { name: 'Cayman Islands', dial_code: '+1-345', code: 'ky' }, 2016 { name: 'Central African Republic', dial_code: '+236', code: 'cf' }, 2017 { name: 'Chad', dial_code: '+235', code: 'td' },
2018 { name: 'Chile', dial_code: '+56', code: 'cl' }, 2019 { name: 'China', dial_code: '+86', code: 'cn' }, 2020 { name: 'Christmas Island', dial_code: '+61', code: 'cx' }, 2021 { name: 'Cocos (Keeling) Islands', dial_code: '+61', code: 'cc' }, 2022 { name: 'Colombia', dial_code: '+57', code: 'co' }, 2023 { name: 'Comoros', dial_code: '+269', code: 'km' }, 2024 { name: 'Congo', dial_code: '+242', code: 'cg' }, 2025 { name: 'Congo (Democratic Republic)', dial_code: '+243', code: 'cd' }, 2026 { name: 'Cook Islands', dial_code: '+682', code: 'ck' }, 2027 { name: 'Costa Rica', dial_code: '+506', code: 'cr' }, 2028 { name: 'Côte d\'Ivoire', dial_code: '+225', code: 'ci' }, 2029 { name: 'Croatia', dial_code: '+385', code: 'hr' }, 2030 { name: 'Cuba', dial_code: '+53', code: 'cu' }, 2031 { name: 'Curaçao', dial_code: '+599', code: 'cw' }, 2032 { name: 'Cyprus', dial_code: '+357', code: 'cy' }, 2033 { name: 'Czech Republic', dial_code: '+420', code: 'cz' }, 2034 { name: 'Denmark', dial_code: '+45', code: 'dk' }, 2035 { name: 'Djibouti', dial_code: '+253', code: 'dj' }, 2036 { name: 'Dominica', dial_code: '+1-767', code: 'dm' }, 2037 { name: 'Dominican Republic', dial_code: '+1-809', code: 'do' }, 2038 { name: 'Ecuador', dial_code: '+593', code: 'ec' },
2039 { name: 'Egypt', dial_code: '+20', code: 'eg' }, 2040 { name: 'El Salvador', dial_code: '+503', code: 'sv' }, 2041 { name: 'Equatorial Guinea', dial_code: '+240', code: 'gq' }, 2042 { name: 'Eritrea', dial_code: '+291', code: 'er' }, 2043 { name: 'Estonia', dial_code: '+372', code: 'ee' }, 2044 { name: 'Eswatini', dial_code: '+268', code: 'sz' }, 2045 { name: 'Ethiopia', dial_code: '+251', code: 'et' }, 2046 { name: 'Falkland Islands', dial_code: '+500', code: 'fk' }, 2047 { name: 'Faroe Islands', dial_code: '+298', code: 'fo' }, 2048 { name: 'Fiji', dial_code: '+679', code: 'fj' }, 2049 { name: 'Finland', dial_code: '+358', code: 'fi' }, 2050 { name: 'France', dial_code: '+33', code: 'fr' }, 2051 { name: 'French Guiana', dial_code: '+594', code: 'gf' }, 2052 { name: 'French Polynesia', dial_code: '+689', code: 'pf' }, 2053 { name: 'French Southern Territories', dial_code: '', code: 'tf' }, 2054 { name: 'Gabon', dial_code: '+241', code: 'ga' }, 2055 { name: 'Gambia', dial_code: '+220', code: 'gm' }, 2056 { name: 'Georgia', dial_code: '+995', code: 'ge' }, 2057 { name: 'Germany', dial_code: '+49', code: 'de' }, 2058 { name: 'Ghana', dial_code: '+233', code: 'gh' },
2059 { name: 'Gibraltar', dial_code: '+350', code: 'gi' }, 2060 { name: 'Greece', dial_code: '+30', code: 'gr' }, 2061 { name: 'Greenland', dial_code: '+299', code: 'gl' }, 2062 { name: 'Grenada', dial_code: '+1-473', code: 'gd' }, 2063 { name: 'Guadeloupe', dial_code: '+590', code: 'gp' }, 2064 { name: 'Guam', dial_code: '+1-671', code: 'gu' }, 2065 { name: 'Guatemala', dial_code: '+502', code: 'gt' }, 2066 { name: 'Guernsey', dial_code: '+44', code: 'gg' }, 2067 { name: 'Guinea', dial_code: '+224', code: 'gn' }, 2068 { name: 'Guinea-Bissau', dial_code: '+245', code: 'gw' }, 2069 { name: 'Guyana', dial_code: '+592', code: 'gy' }, 2070 { name: 'Haiti', dial_code: '+509', code: 'ht' }, 2071 { name: 'Heard Island and McDonald Islands', dial_code: '', code: 'hm' }, 2072 { name: 'Holy See (Vatican City State)', dial_code: '+379', code: 'va' }, 2073 { name: 'Honduras', dial_code: '+504', code: 'hn' }, 2074 { name: 'Hong Kong', dial_code: '+852', code: 'hk' }, 2075 { name: 'Hungary', dial_code: '+36', code: 'hu' }, 2076 { name: 'Iceland', dial_code: '+354', code: 'is' }, 2077 { name: 'India', dial_code: '+91', code: 'in' }, 2078 { name: 'Indonesia', dial_code: '+62', code: 'id' }, 2079 { name: 'Iran', dial_code: '+98', code: 'ir' }, 2080 { name: 'Iraq', dial_code: '+964', code: 'iq' }, 2081 { name: 'Ireland', dial_code: '+353', code: 'ie' }, 2082 { name: 'Isle of Man', dial_code: '+44', code: 'im' },
2083 { name: 'Israel', dial_code: '+972', code: 'il' }, 2084 { name: 'Italy', dial_code: '+39', code: 'it' }, 2085 { name: 'Jamaica', dial_code: '+1-876', code: 'jm' }, 2086 { name: 'Japan', dial_code: '+81', code: 'jp' }, 2087 { name: 'Jersey', dial_code: '+44', code: 'je' }, 2088 { name: 'Jordan', dial_code: '+962', code: 'jo' }, 2089 { name: 'Kazakhstan', dial_code: '+7', code: 'kz' }, 2090 { name: 'Kenya', dial_code: '+254', code: 'ke' }, 2091 { name: 'Kiribati', dial_code: '+686', code: 'ki' }, 2092 { name: 'North Korea', dial_code: '+850', code: 'kp' }, 2093 { name: 'South Korea', dial_code: '+82', code: 'kr' }, 2094 { name: 'Kuwait', dial_code: '+965', code: 'kw' }, 2095 { name: 'Kyrgyzstan', dial_code: '+996', code: 'kg' }, 2096 { name: 'Laos', dial_code: '+856', code: 'la' }, 2097 { name: 'Latvia', dial_code: '+371', code: 'lv' }, 2098 { name: 'Lebanon', dial_code: '+961', code: 'lb' }, 2099 { name: 'Lesotho', dial_code: '+266', code: 'ls' }, 2100 { name: 'Liberia', dial_code: '+231', code: 'lr' }, 2101 { name: 'Libya', dial_code: '+218', code: 'ly' }, 2102 { name: 'Liechtenstein', dial_code: '+423', code: 'li' }, 2103 { name: 'Lithuania', dial_code: '+370', code: 'lt' }, 2104 { name: 'Luxembourg', dial_code: '+352', code: 'lu' }, 2105 { name: 'Macao', dial_code: '+853', code: 'mo' }, 2106 { name: 'Madagascar', dial_code: '+261', code: 'mg' }, 2107 { name: 'Malawi', dial_code: '+265', code: 'mw' }, 2108 { name: 'Malaysia', dial_code: '+60', code: 'my' }, 2109 { name: 'Maldives', dial_code: '+960', code: 'mv' }, 2110 { name: 'Mali', dial_code: '+223', code: 'ml' }, 2111 { name: 'Malta', dial_code: '+356', code: 'mt' }, 2112 { name: 'Marshall Islands', dial_code: '+692', code: 'mh' }, 2113 { name: 'Martinique', dial_code: '+596', code: 'mq' }, 2114 { name: 'Mauritania', dial_code: '+222', code: 'mr' }, 2115 { name: 'Mauritius', dial_code: '+230', code: 'mu' },
2116 { name: 'Mayotte', dial_code: '+262', code: 'yt' }, 2117 { name: 'Mexico', dial_code: '+52', code: 'mx' }, 2118 { name: 'Micronesia', dial_code: '+691', code: 'fm' }, 2119 { name: 'Moldova', dial_code: '+373', code: 'md' }, 2120 { name: 'Monaco', dial_code: '+377', code: 'mc' }, 2121 { name: 'Mongolia', dial_code: '+976', code: 'mn' }, 2122 { name: 'Montenegro', dial_code: '+382', code: 'me' }, 2123 { name: 'Montserrat', dial_code: '+1-664', code: 'ms' }, 2124 { name: 'Morocco', dial_code: '+212', code: 'ma' }, 2125 { name: 'Mozambique', dial_code: '+258', code: 'mz' }, 2126 { name: 'Myanmar', dial_code: '+95', code: 'mm' }, 2127 { name: 'Namibia', dial_code: '+264', code: 'na' }, 2128 { name: 'Nauru', dial_code: '+674', code: 'nr' }, 2129 { name: 'Nepal', dial_code: '+977', code: 'np' }, 2130 { name: 'Netherlands', dial_code: '+31', code: 'nl' }, 2131 { name: 'New Caledonia', dial_code: '+687', code: 'nc' }, 2132 { name: 'New Zealand', dial_code: '+64', code: 'nz' }, 2133 { name: 'Nicaragua', dial_code: '+505', code: 'ni' }, 2134 { name: 'Niger', dial_code: '+227', code: 'ne' }, 2135 { name: 'Nigeria', dial_code: '+234', code: 'ng' },
2136 { name: 'Niue', dial_code: '+683', code: 'nu' }, 2137 { name: 'Norfolk Island', dial_code: '+672', code: 'nf' }, 2138 { name: 'North Macedonia', dial_code: '+389', code: 'mk' }, 2139 { name: 'Northern Mariana Islands', dial_code: '+1-670', code: 'mp' }, 2140 { name: 'Norway', dial_code: '+47', code: 'no' }, 2141 { name: 'Oman', dial_code: '+968', code: 'om' }, 2142 { name: 'Pakistan', dial_code: '+92', code: 'pk' }, 2143 { name: 'Palau', dial_code: '+680', code: 'pw' }, 2144 { name: 'Palestine', dial_code: '+970', code: 'ps' }, 2145 { name: 'Panama', dial_code: '+507', code: 'pa' }, 2146 { name: 'Papua New Guinea', dial_code: '+675', code: 'pg' }, 2147 { name: 'Paraguay', dial_code: '+595', code: 'py' }, 2148 { name: 'Peru', dial_code: '+51', code: 'pe' }, 2149 { name: 'Philippines', dial_code: '+63', code: 'ph' }, 2150 { name: 'Pitcairn', dial_code: '+64', code: 'pn' }, 2151 { name: 'Poland', dial_code: '+48', code: 'pl' }, 2152 { name: 'Portugal', dial_code: '+351', code: 'pt' }, 2153 { name: 'Puerto Rico', dial_code: '+1-787', code: 'pr' },
2154 { name: 'Qatar', dial_code: '+974', code: 'qa' }, 2155 { name: 'Réunion', dial_code: '+262', code: 're' }, 2156 { name: 'Romania', dial_code: '+40', code: 'ro' }, 2157 { name: 'Russia', dial_code: '+7', code: 'ru' }, 2158 { name: 'Rwanda', dial_code: '+250', code: 'rw' }, 2159 { name: 'Saint Barthélemy', dial_code: '+590', code: 'bl' }, 2160 { name: 'Saint Helena, Ascension and Tristan da Cunha', dial_code: '+290', code: 'sh' }, 2161 { name: 'Saint Kitts and Nevis', dial_code: '+1-869', code: 'kn' }, 2162 { name: 'Saint Lucia', dial_code: '+1-758', code: 'lc' }, 2163 { name: 'Saint Martin', dial_code: '+590', code: 'mf' }, 2164 { name: 'Saint Pierre and Miquelon', dial_code: '+508', code: 'pm' }, 2165 { name: 'Saint Vincent and the Grenadines', dial_code: '+1-784', code: 'vc' }, 2166 { name: 'Samoa', dial_code: '+685', code: 'ws' }, 2167 { name: 'San Marino', dial_code: '+378', code: 'sm' }, 2168 { name: 'Sao Tome and Principe', dial_code: '+239', code: 'st' }, 2169 { name: 'Saudi Arabia', dial_code: '+966', code: 'sa' }, 2170 { name: 'Senegal', dial_code: '+221', code: 'sn' }, 2171 { name: 'Serbia', dial_code: '+381', code: 'rs' }, 2172 { name: 'Seychelles', dial_code: '+248', code: 'sc' }, 2173 { name: 'Sierra Leone', dial_code: '+232', code: 'sl' }, 2174 { name: 'Singapore', dial_code: '+65', code: 'sg' }, 2175 { name: 'Sint Maarten', dial_code: '+1-721', code: 'sx' }, 2176 { name: 'Slovakia', dial_code: '+421', code: 'sk' }, 2177 { name: 'Slovenia', dial_code: '+386', code: 'si' }, 2178 { name: 'Solomon Islands', dial_code: '+677', code: 'sb' }, 2179 { name: 'Somalia', dial_code: '+252', code: 'so' }, 2180 { name: 'South Africa', dial_code: '+27', code: 'za' }, 2181 { name: 'South Georgia and the South Sandwich Islands', dial_code: '+500', code: 'gs' }, 2182 { name: 'South Sudan', dial_code: '+211', code: 'ss' }, 2183 { name: 'Spain', dial_code: '+34', code: 'es' }, 2184 { name: 'Sri Lanka', dial_code: '+94', code: 'lk' }, 2185 { name: 'Sudan', dial_code: '+249', code: 'sd' },
2186 { name: 'Suriname', dial_code: '+597', code: 'sr' }, 2187 { name: 'Svalbard and Jan Mayen', dial_code: '+47', code: 'sj' }, 2188 { name: 'Sweden', dial_code: '+46', code: 'se' }, 2189 { name: 'Switzerland', dial_code: '+41', code: 'ch' }, 2190 { name: 'Syria', dial_code: '+963', code: 'sy' }, 2191 { name: 'Taiwan', dial_code: '+886', code: 'tw' }, 2192 { name: 'Tajikistan', dial_code: '+992', code: 'tj' }, 2193 { name: 'Tanzania', dial_code: '+255', code: 'tz' }, 2194 { name: 'Thailand', dial_code: '+66', code: 'th' }, 2195 { name: 'Timor-Leste', dial_code: '+670', code: 'tl' }, 2196 { name: 'Togo', dial_code: '+228', code: 'tg' }, 2197 { name: 'Tokelau', dial_code: '+690', code: 'tk' }, 2198 { name: 'Tonga', dial_code: '+676', code: 'to' }, 2199 { name: 'Trinidad and Tobago', dial_code: '+1-868', code: 'tt' }, 2200 { name: 'Tunisia', dial_code: '+216', code: 'tn' }, 2201 { name: 'Turkey', dial_code: '+90', code: 'tr' }, 2202 { name: 'Turkmenistan', dial_code: '+993', code: 'tm' }, 2203 { name: 'Turks and Caicos Islands', dial_code: '+1-649', code: 'tc' },
2204 { name: 'Tuvalu', dial_code: '+688', code: 'tv' }, 2205 { name: 'Uganda', dial_code: '+256', code: 'ug' }, 2206 { name: 'Ukraine', dial_code: '+380', code: 'ua' }, 2207 { name: 'United Arab Emirates', dial_code: '+971', code: 'ae' }, 2208 { name: 'United Kingdom', dial_code: '+44', code: 'gb' }, 2209 { name: 'United States', dial_code: '+1', code: 'us' }, 2210 { name: 'Uruguay', dial_code: '+598', code: 'uy' }, 2211 { name: 'Uzbekistan', dial_code: '+998', code: 'uz' }, 2212 { name: 'Vanuatu', dial_code: '+678', code: 'vu' }, 2213 { name: 'Vatican City', dial_code: '+379', code: 'va' }, 2214 { name: 'Venezuela', dial_code: '+58', code: 've' }, 2215 { name: 'Vietnam', dial_code: '+84', code: 'vn' }, 2216 { name: 'Virgin Islands (British)', dial_code: '+1-284', code: 'vg' }, 2217 { name: 'Virgin Islands (U.S.)', dial_code: '+1-340', code: 'vi' }, 2218 { name: 'Wallis and Futuna', dial_code: '+681', code: 'wf' }, 2219 { name: 'Western Sahara', dial_code: '+212', code: 'eh' }, 2220 { name: 'Yemen', dial_code: '+967', code: 'ye' }, 2221 { name: 'Zambia', dial_code: '+260', code: 'zm' }, 2222 { name: 'Zimbabwe', dial_code: '+263', code: 'zw' }, 2223 { name: 'Ã land Islands', dial_code: '+358', code: 'ax' } 2224 ], 2225 2226 // Add timezone list with common timezones 2227 timezonesList: [ 2228 { name: 'UTC', value: 'UTC' }, 2229 { name: 'Africa/Abidjan', value: 'Africa/Abidjan' }, 2230 { name: 'Africa/Accra', value: 'Africa/Accra' }, 2231 { name: 'Africa/Addis Ababa', value: 'Africa/Addis_Ababa' }, 2232 { name: 'Africa/Algiers', value: 'Africa/Algiers' }, 2233 { name: 'Africa/Asmara', value: 'Africa/Asmara' }, 2234 { name: 'Africa/Bamako', value: 'Africa/Bamako' }, 2235 { name: 'Africa/Bangui', value: 'Africa/Bangui' }, 2236 { name: 'Africa/Banjul', value: 'Africa/Banjul' }, 2237 { name: 'Africa/Bissau', value: 'Africa/Bissau' }, 2238 { name: 'Africa/Blantyre', value: 'Africa/Blantyre' }, 2239 { name: 'Africa/Brazzaville', value: 'Africa/Brazzaville' }, 2240 { name: 'Africa/Bujumbura', value: 'Africa/Bujumbura' }, 2241 { name: 'Africa/Cairo', value: 'Africa/Cairo' }, 2242 { name: 'Africa/Casablanca', value: 'Africa/Casablanca' }, 2243 { name: 'Africa/Ceuta', value: 'Africa/Ceuta' }, 2244 { name: 'Africa/Conakry', value: 'Africa/Conakry' }, 2245 { name: 'Africa/Dakar', value: 'Africa/Dakar' }, 2246 { name: 'Africa/Dar es Salaam', value: 'Africa/Dar_es_Salaam' }, 2247 { name: 'Africa/Djibouti', value: 'Africa/Djibouti' }, 2248 { name: 'Africa/Douala', value: 'Africa/Douala' }, 2249 { name: 'Africa/El Aaiun', value: 'Africa/El_Aaiun' }, 2250 { name: 'Africa/Freetown', value: 'Africa/Freetown' }, 2251 { name: 'Africa/Gaborone', value: 'Africa/Gaborone' }, 2252 { name: 'Africa/Harare', value: 'Africa/Harare' }, 2253 { name: 'Africa/Johannesburg', value: 'Africa/Johannesburg' }, 2254 { name: 'Africa/Juba', value: 'Africa/Juba' }, 2255 { name: 'Africa/Kampala', value: 'Africa/Kampala' }, 2256 { name: 'Africa/Khartoum', value: 'Africa/Khartoum' }, 2257 { name: 'Africa/Kigali', value: 'Africa/Kigali' }, 2258 { name: 'Africa/Kinshasa', value: 'Africa/Kinshasa' }, 2259 { name: 'Africa/Lagos', value: 'Africa/Lagos' }, 2260 { name: 'Africa/Libreville', value: 'Africa/Libreville' }, 2261 { name: 'Africa/Lome', value: 'Africa/Lome' }, 2262 { name: 'Africa/Luanda', value: 'Africa/Luanda' }, 2263 { name: 'Africa/Lubumbashi', value: 'Africa/Lubumbashi' }, 2264 { name: 'Africa/Lusaka', value: 'Africa/Lusaka' }, 2265 { name: 'Africa/Malabo', value: 'Africa/Malabo' }, 2266 { name: 'Africa/Maputo', value: 'Africa/Maputo' }, 2267 { name: 'Africa/Maseru', value: 'Africa/Maseru' }, 2268 { name: 'Africa/Mbabane', value: 'Africa/Mbabane' }, 2269 { name: 'Africa/Mogadishu', value: 'Africa/Mogadishu' }, 2270 { name: 'Africa/Monrovia', value: 'Africa/Monrovia' }, 2271 { name: 'Africa/Nairobi', value: 'Africa/Nairobi' }, 2272 { name: 'Africa/Ndjamena', value: 'Africa/Ndjamena' }, 2273 { name: 'Africa/Niamey', value: 'Africa/Niamey' }, 2274 { name: 'Africa/Nouakchott', value: 'Africa/Nouakchott' }, 2275 { name: 'Africa/Ouagadougou', value: 'Africa/Ouagadougou' }, 2276 { name: 'Africa/Porto-Novo', value: 'Africa/Porto-Novo' }, 2277 { name: 'Africa/Sao Tome', value: 'Africa/Sao_Tome' }, 2278 { name: 'Africa/Tripoli', value: 'Africa/Tripoli' }, 2279 { name: 'Africa/Tunis', value: 'Africa/Tunis' }, 2280 { name: 'Africa/Windhoek', value: 'Africa/Windhoek' }, 2281 { name: 'America/Adak', value: 'America/Adak' }, 2282 { name: 'America/Anchorage', value: 'America/Anchorage' }, 2283 { name: 'America/Anguilla', value: 'America/Anguilla' }, 2284 { name: 'America/Antigua', value: 'America/Antigua' }, 2285 { name: 'America/Araguaina', value: 'America/Araguaina' }, 2286 { name: 'America/Argentina/Buenos Aires', value: 'America/Argentina/Buenos_Aires' }, 2287 { name: 'America/Argentina/Catamarca', value: 'America/Argentina/Catamarca' }, 2288 { name: 'America/Argentina/Cordoba', value: 'America/Argentina/Cordoba' }, 2289 { name: 'America/Argentina/Jujuy', value: 'America/Argentina/Jujuy' }, 2290 { name: 'America/Argentina/La Rioja', value: 'America/Argentina/La_Rioja' }, 2291 { name: 'America/Argentina/Mendoza', value: 'America/Argentina/Mendoza' }, 2292 { name: 'America/Argentina/Rio Gallegos', value: 'America/Argentina/Rio_Gallegos' }, 2293 { name: 'America/Argentina/Salta', value: 'America/Argentina/Salta' }, 2294 { name: 'America/Argentina/San Juan', value: 'America/Argentina/San_Juan' }, 2295 { name: 'America/Argentina/San Luis', value: 'America/Argentina/San_Luis' }, 2296 { name: 'America/Argentina/Tucuman', value: 'America/Argentina/Tucuman' }, 2297 { name: 'America/Argentina/Ushuaia', value: 'America/Argentina/Ushuaia' }, 2298 { name: 'America/Aruba', value: 'America/Aruba' }, 2299 { name: 'America/Asuncion', value: 'America/Asuncion' }, 2300 { name: 'America/Atikokan', value: 'America/Atikokan' }, 2301 { name: 'America/Bahia', value: 'America/Bahia' }, 2302 { name: 'America/Bahia Banderas', value: 'America/Bahia_Banderas' }, 2303 { name: 'America/Barbados', value: 'America/Barbados' }, 2304 { name: 'America/Belem', value: 'America/Belem' }, 2305 { name: 'America/Belize', value: 'America/Belize' }, 2306 { name: 'America/Blanc-Sablon', value: 'America/Blanc-Sablon' }, 2307 { name: 'America/Boa Vista', value: 'America/Boa_Vista' }, 2308 { name: 'America/Bogota', value: 'America/Bogota' }, 2309 { name: 'America/Boise', value: 'America/Boise' }, 2310 { name: 'America/Cambridge Bay', value: 'America/Cambridge_Bay' }, 2311 { name: 'America/Campo Grande', value: 'America/Campo_Grande' }, 2312 { name: 'America/Cancun', value: 'America/Cancun' }, 2313 { name: 'America/Caracas', value: 'America/Caracas' }, 2314 { name: 'America/Cayenne', value: 'America/Cayenne' }, 2315 { name: 'America/Cayman', value: 'America/Cayman' }, 2316 { name: 'America/Chicago', value: 'America/Chicago' }, 2317 { name: 'America/Chihuahua', value: 'America/Chihuahua' }, 2318 { name: 'America/Costa Rica', value: 'America/Costa_Rica' }, 2319 { name: 'America/Creston', value: 'America/Creston' }, 2320 { name: 'America/Cuiaba', value: 'America/Cuiaba' }, 2321 { name: 'America/Curacao', value: 'America/Curacao' }, 2322 { name: 'America/Danmarkshavn', value: 'America/Danmarkshavn' }, 2323 { name: 'America/Dawson', value: 'America/Dawson' }, 2324 { name: 'America/Dawson Creek', value: 'America/Dawson_Creek' }, 2325 { name: 'America/Denver', value: 'America/Denver' }, 2326 { name: 'America/Detroit', value: 'America/Detroit' }, 2327 { name: 'America/Dominica', value: 'America/Dominica' }, 2328 { name: 'America/Edmonton', value: 'America/Edmonton' }, 2329 { name: 'America/Eirunepe', value: 'America/Eirunepe' }, 2330 { name: 'America/El Salvador', value: 'America/El_Salvador' }, 2331 { name: 'America/Fort Nelson', value: 'America/Fort_Nelson' }, 2332 { name: 'America/Fortaleza', value: 'America/Fortaleza' }, 2333 { name: 'America/Glace Bay', value: 'America/Glace_Bay' }, 2334 { name: 'America/Godthab', value: 'America/Godthab' }, 2335 { name: 'America/Goose Bay', value: 'America/Goose_Bay' }, 2336 { name: 'America/Grand Turk', value: 'America/Grand_Turk' }, 2337 { name: 'America/Grenada', value: 'America/Grenada' }, 2338 { name: 'America/Guadeloupe', value: 'America/Guadeloupe' }, 2339 { name: 'America/Guatemala', value: 'America/Guatemala' }, 2340 { name: 'America/Guayaquil', value: 'America/Guayaquil' }, 2341 { name: 'America/Guyana', value: 'America/Guyana' }, 2342 { name: 'America/Halifax', value: 'America/Halifax' }, 2343 { name: 'America/Havana', value: 'America/Havana' }, 2344 { name: 'America/Hermosillo', value: 'America/Hermosillo' }, 2345 { name: 'America/Indiana/Indianapolis', value: 'America/Indiana/Indianapolis' }, 2346 { name: 'America/Indiana/Knox', value: 'America/Indiana/Knox' }, 2347 { name: 'America/Indiana/Marengo', value: 'America/Indiana/Marengo' }, 2348 { name: 'America/Indiana/Petersburg', value: 'America/Indiana/Petersburg' }, 2349 { name: 'America/Indiana/Tell City', value: 'America/Indiana/Tell_City' }, 2350 { name: 'America/Indiana/Vevay', value: 'America/Indiana/Vevay' }, 2351 { name: 'America/Indiana/Vincennes', value: 'America/Indiana/Vincennes' }, 2352 { name: 'America/Indiana/Winamac', value: 'America/Indiana/Winamac' }, 2353 { name: 'America/Inuvik', value: 'America/Inuvik' }, 2354 { name: 'America/Iqaluit', value: 'America/Iqaluit' }, 2355 { name: 'America/Jamaica', value: 'America/Jamaica' }, 2356 { name: 'America/Juneau', value: 'America/Juneau' }, 2357 { name: 'America/Kentucky/Louisville', value: 'America/Kentucky/Louisville' }, 2358 { name: 'America/Kentucky/Monticello', value: 'America/Kentucky/Monticello' }, 2359 { name: 'America/Kralendijk', value: 'America/Kralendijk' }, 2360 { name: 'America/La Paz', value: 'America/La_Paz' }, 2361 { name: 'America/Lima', value: 'America/Lima' }, 2362 { name: 'America/Los Angeles', value: 'America/Los_Angeles' }, 2363 { name: 'America/Lower Princes', value: 'America/Lower_Princes' }, 2364 { name: 'America/Maceio', value: 'America/Maceio' }, 2365 { name: 'America/Managua', value: 'America/Managua' }, 2366 { name: 'America/Manaus', value: 'America/Manaus' }, 2367 { name: 'America/Marigot', value: 'America/Marigot' }, 2368 { name: 'America/Martinique', value: 'America/Martinique' }, 2369 { name: 'America/Matamoros', value: 'America/Matamoros' }, 2370 { name: 'America/Mazatlan', value: 'America/Mazatlan' }, 2371 { name: 'America/Menominee', value: 'America/Menominee' }, 2372 { name: 'America/Merida', value: 'America/Merida' }, 2373 { name: 'America/Metlakatla', value: 'America/Metlakatla' }, 2374 { name: 'America/Mexico City', value: 'America/Mexico_City' }, 2375 { name: 'America/Miquelon', value: 'America/Miquelon' }, 2376 { name: 'America/Moncton', value: 'America/Moncton' }, 2377 { name: 'America/Monterrey', value: 'America/Monterrey' }, 2378 { name: 'America/Montevideo', value: 'America/Montevideo' }, 2379 { name: 'America/Montserrat', value: 'America/Montserrat' }, 2380 { name: 'America/Nassau', value: 'America/Nassau' },
2381 { name: 'America/New York', value: 'America/New_York' }, 2382 { name: 'America/Nipigon', value: 'America/Nipigon' }, 2383 { name: 'America/Nome', value: 'America/Nome' }, 2384 { name: 'America/Noronha', value: 'America/Noronha' }, 2385 { name: 'America/North Dakota/Beulah', value: 'America/North_Dakota/Beulah' }, 2386 { name: 'America/North Dakota/Center', value: 'America/North_Dakota/Center' }, 2387 { name: 'America/North Dakota/New Salem', value: 'America/North_Dakota/New_Salem' }, 2388 { name: 'America/Ojinaga', value: 'America/Ojinaga' }, 2389 { name: 'America/Panama', value: 'America/Panama' }, 2390 { name: 'America/Pangnirtung', value: 'America/Pangnirtung' }, 2391 { name: 'America/Paramaribo', value: 'America/Paramaribo' }, 2392 { name: 'America/Phoenix', value: 'America/Phoenix' }, 2393 { name: 'America/Port-au-Prince', value: 'America/Port-au-Prince' }, 2394 { name: 'America/Port of Spain', value: 'America/Port_of_Spain' }, 2395 { name: 'America/Porto Velho', value: 'America/Porto_Velho' }, 2396 { name: 'America/Puerto Rico', value: 'America/Puerto_Rico' }, 2397 { name: 'America/Punta Arenas', value: 'America/Punta_Arenas' }, 2398 { name: 'America/Rainy River', value: 'America/Rainy_River' }, 2399 { name: 'America/Rankin Inlet', value: 'America/Rankin_Inlet' }, 2400 { name: 'America/Recife', value: 'America/Recife' }, 2401 { name: 'America/Regina', value: 'America/Regina' }, 2402 { name: 'America/Resolute', value: 'America/Resolute' }, 2403 { name: 'America/Rio Branco', value: 'America/Rio_Branco' }, 2404 { name: 'America/Santarem', value: 'America/Santarem' }, 2405 { name: 'America/Santiago', value: 'America/Santiago' }, 2406 { name: 'America/Santo Domingo', value: 'America/Santo_Domingo' }, 2407 { name: 'America/Sao Paulo', value: 'America/Sao_Paulo' }, 2408 { name: 'America/Scoresbysund', value: 'America/Scoresbysund' }, 2409 { name: 'America/Sitka', value: 'America/Sitka' }, 2410 { name: 'America/St Barthelemy', value: 'America/St_Barthelemy' }, 2411 { name: 'America/St Johns', value: 'America/St_Johns' }, 2412 { name: 'America/St Kitts', value: 'America/St_Kitts' }, 2413 { name: 'America/St Lucia', value: 'America/St_Lucia' }, 2414 { name: 'America/St Thomas', value: 'America/St_Thomas' }, 2415 { name: 'America/St Vincent', value: 'America/St_Vincent' }, 2416 { name: 'America/Swift Current', value: 'America/Swift_Current' }, 2417 { name: 'America/Tegucigalpa', value: 'America/Tegucigalpa' }, 2418 { name: 'America/Thule', value: 'America/Thule' }, 2419 { name: 'America/Thunder Bay', value: 'America/Thunder_Bay' }, 2420 { name: 'America/Tijuana', value: 'America/Tijuana' }, 2421 { name: 'America/Toronto', value: 'America/Toronto' }, 2422 { name: 'America/Tortola', value: 'America/Tortola' }, 2423 { name: 'America/Vancouver', value: 'America/Vancouver' }, 2424 { name: 'America/Whitehorse', value: 'America/Whitehorse' }, 2425 { name: 'America/Winnipeg', value: 'America/Winnipeg' }, 2426 { name: 'America/Yakutat', value: 'America/Yakutat' }, 2427 { name: 'America/Yellowknife', value: 'America/Yellowknife' }, 2428 { name: 'Antarctica/Casey', value: 'Antarctica/Casey' }, 2429 { name: 'Antarctica/Davis', value: 'Antarctica/Davis' }, 2430 { name: 'Antarctica/DumontDUrville', value: 'Antarctica/DumontDUrville' }, 2431 { name: 'Antarctica/Macquarie', value: 'Antarctica/Macquarie' }, 2432 { name: 'Antarctica/Mawson', value: 'Antarctica/Mawson' }, 2433 { name: 'Antarctica/McMurdo', value: 'Antarctica/McMurdo' }, 2434 { name: 'Antarctica/Palmer', value: 'Antarctica/Palmer' }, 2435 { name: 'Antarctica/Rothera', value: 'Antarctica/Rothera' }, 2436 { name: 'Antarctica/Syowa', value: 'Antarctica/Syowa' }, 2437 { name: 'Antarctica/Troll', value: 'Antarctica/Troll' }, 2438 { name: 'Antarctica/Vostok', value: 'Antarctica/Vostok' }, 2439 { name: 'Arctic/Longyearbyen', value: 'Arctic/Longyearbyen' }, 2440 { name: 'Asia/Aden', value: 'Asia/Aden' }, 2441 { name: 'Asia/Almaty', value: 'Asia/Almaty' }, 2442 { name: 'Asia/Amman', value: 'Asia/Amman' }, 2443 { name: 'Asia/Anadyr', value: 'Asia/Anadyr' }, 2444 { name: 'Asia/Aqtau', value: 'Asia/Aqtau' }, 2445 { name: 'Asia/Aqtobe', value: 'Asia/Aqtobe' }, 2446 { name: 'Asia/Ashgabat', value: 'Asia/Ashgabat' }, 2447 { name: 'Asia/Atyrau', value: 'Asia/Atyrau' }, 2448 { name: 'Asia/Baghdad', value: 'Asia/Baghdad' }, 2449 { name: 'Asia/Bahrain', value: 'Asia/Bahrain' }, 2450 { name: 'Asia/Baku', value: 'Asia/Baku' }, 2451 { name: 'Asia/Bangkok', value: 'Asia/Bangkok' }, 2452 { name: 'Asia/Barnaul', value: 'Asia/Barnaul' }, 2453 { name: 'Asia/Beirut', value: 'Asia/Beirut' }, 2454 { name: 'Asia/Bishkek', value: 'Asia/Bishkek' }, 2455 { name: 'Asia/Brunei', value: 'Asia/Brunei' }, 2456 { name: 'Asia/Chita', value: 'Asia/Chita' }, 2457 { name: 'Asia/Choibalsan', value: 'Asia/Choibalsan' }, 2458 { name: 'Asia/Colombo', value: 'Asia/Colombo' }, 2459 { name: 'Asia/Damascus', value: 'Asia/Damascus' }, 2460 { name: 'Asia/Dhaka', value: 'Asia/Dhaka' }, 2461 { name: 'Asia/Dili', value: 'Asia/Dili' }, 2462 { name: 'Asia/Dubai', value: 'Asia/Dubai' }, 2463 { name: 'Asia/Dushanbe', value: 'Asia/Dushanbe' }, 2464 { name: 'Asia/Famagusta', value: 'Asia/Famagusta' }, 2465 { name: 'Asia/Gaza', value: 'Asia/Gaza' }, 2466 { name: 'Asia/Hebron', value: 'Asia/Hebron' }, 2467 { name: 'Asia/Ho Chi Minh', value: 'Asia/Ho_Chi_Minh' }, 2468 { name: 'Asia/Hong Kong', value: 'Asia/Hong_Kong' }, 2469 { name: 'Asia/Hovd', value: 'Asia/Hovd' }, 2470 { name: 'Asia/Irkutsk', value: 'Asia/Irkutsk' }, 2471 { name: 'Asia/Jakarta', value: 'Asia/Jakarta' }, 2472 { name: 'Asia/Jayapura', value: 'Asia/Jayapura' }, 2473 { name: 'Asia/Jerusalem', value: 'Asia/Jerusalem' }, 2474 { name: 'Asia/Kabul', value: 'Asia/Kabul' }, 2475 { name: 'Asia/Kamchatka', value: 'Asia/Kamchatka' }, 2476 { name: 'Asia/Karachi', value: 'Asia/Karachi' }, 2477 { name: 'Asia/Kathmandu', value: 'Asia/Kathmandu' }, 2478 { name: 'Asia/Khandyga', value: 'Asia/Khandyga' }, 2479 { name: 'Asia/Kolkata', value: 'Asia/Kolkata' }, 2480 { name: 'Asia/Krasnoyarsk', value: 'Asia/Krasnoyarsk' }, 2481 { name: 'Asia/Kuala Lumpur', value: 'Asia/Kuala_Lumpur' }, 2482 { name: 'Asia/Kuching', value: 'Asia/Kuching' }, 2483 { name: 'Asia/Kuwait', value: 'Asia/Kuwait' }, 2484 { name: 'Asia/Macau', value: 'Asia/Macau' }, 2485 { name: 'Asia/Magadan', value: 'Asia/Magadan' }, 2486 { name: 'Asia/Makassar', value: 'Asia/Makassar' }, 2487 { name: 'Asia/Manila', value: 'Asia/Manila' }, 2488 { name: 'Asia/Muscat', value: 'Asia/Muscat' }, 2489 { name: 'Asia/Nicosia', value: 'Asia/Nicosia' }, 2490 { name: 'Asia/Novokuznetsk', value: 'Asia/Novokuznetsk' }, 2491 { name: 'Asia/Novosibirsk', value: 'Asia/Novosibirsk' }, 2492 { name: 'Asia/Omsk', value: 'Asia/Omsk' }, 2493 { name: 'Asia/Oral', value: 'Asia/Oral' }, 2494 { name: 'Asia/Phnom Penh', value: 'Asia/Phnom_Penh' }, 2495 { name: 'Asia/Pontianak', value: 'Asia/Pontianak' }, 2496 { name: 'Asia/Pyongyang', value: 'Asia/Pyongyang' }, 2497 { name: 'Asia/Qatar', value: 'Asia/Qatar' }, 2498 { name: 'Asia/Qostanay', value: 'Asia/Qostanay' }, 2499 { name: 'Asia/Qyzylorda', value: 'Asia/Qyzylorda' }, 2500 { name: 'Asia/Riyadh', value: 'Asia/Riyadh' }, 2501 { name: 'Asia/Sakhalin', value: 'Asia/Sakhalin' }, 2502 { name: 'Asia/Samarkand', value: 'Asia/Samarkand' }, 2503 { name: 'Asia/Seoul', value: 'Asia/Seoul' }, 2504 { name: 'Asia/Shanghai', value: 'Asia/Shanghai' }, 2505 { name: 'Asia/Singapore', value: 'Asia/Singapore' }, 2506 { name: 'Asia/Srednekolymsk', value: 'Asia/Srednekolymsk' }, 2507 { name: 'Asia/Taipei', value: 'Asia/Taipei' }, 2508 { name: 'Asia/Tashkent', value: 'Asia/Tashkent' }, 2509 { name: 'Asia/Tbilisi', value: 'Asia/Tbilisi' }, 2510 { name: 'Asia/Tehran', value: 'Asia/Tehran' }, 2511 { name: 'Asia/Thimphu', value: 'Asia/Thimphu' }, 2512 { name: 'Asia/Tokyo', value: 'Asia/Tokyo' }, 2513 { name: 'Asia/Tomsk', value: 'Asia/Tomsk' }, 2514 { name: 'Asia/Ulaanbaatar', value: 'Asia/Ulaanbaatar' }, 2515 { name: 'Asia/Urumqi', value: 'Asia/Urumqi' }, 2516 { name: 'Asia/Ust-Nera', value: 'Asia/Ust-Nera' }, 2517 { name: 'Asia/Vientiane', value: 'Asia/Vientiane' }, 2518 { name: 'Asia/Vladivostok', value: 'Asia/Vladivostok' }, 2519 { name: 'Asia/Yakutsk', value: 'Asia/Yakutsk' }, 2520 { name: 'Asia/Yangon', value: 'Asia/Yangon' }, 2521 { name: 'Asia/Yekaterinburg', value: 'Asia/Yekaterinburg' }, 2522 { name: 'Asia/Yerevan', value: 'Asia/Yerevan' }, 2523 { name: 'Atlantic/Azores', value: 'Atlantic/Azores' }, 2524 { name: 'Atlantic/Bermuda', value: 'Atlantic/Bermuda' }, 2525 { name: 'Atlantic/Canary', value: 'Atlantic/Canary' }, 2526 { name: 'Atlantic/Cape Verde', value: 'Atlantic/Cape_Verde' }, 2527 { name: 'Atlantic/Faroe', value: 'Atlantic/Faroe' }, 2528 { name: 'Atlantic/Madeira', value: 'Atlantic/Madeira' }, 2529 { name: 'Atlantic/Reykjavik', value: 'Atlantic/Reykjavik' }, 2530 { name: 'Atlantic/South Georgia', value: 'Atlantic/South_Georgia' }, 2531 { name: 'Atlantic/St Helena', value: 'Atlantic/St_Helena' }, 2532 { name: 'Atlantic/Stanley', value: 'Atlantic/Stanley' }, 2533 { name: 'Australia/Adelaide', value: 'Australia/Adelaide' }, 2534 { name: 'Australia/Brisbane', value: 'Australia/Brisbane' }, 2535 { name: 'Australia/Broken Hill', value: 'Australia/Broken_Hill' }, 2536 { name: 'Australia/Currie', value: 'Australia/Currie' }, 2537 { name: 'Australia/Darwin', value: 'Australia/Darwin' }, 2538 { name: 'Australia/Eucla', value: 'Australia/Eucla' }, 2539 { name: 'Australia/Hobart', value: 'Australia/Hobart' }, 2540 { name: 'Australia/Lindeman', value: 'Australia/Lindeman' }, 2541 { name: 'Australia/Lord Howe', value: 'Australia/Lord_Howe' }, 2542 { name: 'Australia/Melbourne', value: 'Australia/Melbourne' }, 2543 { name: 'Australia/Perth', value: 'Australia/Perth' }, 2544 { name: 'Australia/Sydney', value: 'Australia/Sydney' }, 2545 { name: 'Europe/Amsterdam', value: 'Europe/Amsterdam' }, 2546 { name: 'Europe/Andorra', value: 'Europe/Andorra' }, 2547 { name: 'Europe/Astrakhan', value: 'Europe/Astrakhan' }, 2548 { name: 'Europe/Athens', value: 'Europe/Athens' }, 2549 { name: 'Europe/Belgrade', value: 'Europe/Belgrade' }, 2550 { name: 'Europe/Berlin', value: 'Europe/Berlin' }, 2551 { name: 'Europe/Bratislava', value: 'Europe/Bratislava' }, 2552 { name: 'Europe/Brussels', value: 'Europe/Brussels' }, 2553 { name: 'Europe/Bucharest', value: 'Europe/Bucharest' }, 2554 { name: 'Europe/Budapest', value: 'Europe/Budapest' }, 2555 { name: 'Europe/Busingen', value: 'Europe/Busingen' }, 2556 { name: 'Europe/Chisinau', value: 'Europe/Chisinau' }, 2557 { name: 'Europe/Copenhagen', value: 'Europe/Copenhagen' }, 2558 { name: 'Europe/Dublin', value: 'Europe/Dublin' }, 2559 { name: 'Europe/Gibraltar', value: 'Europe/Gibraltar' }, 2560 { name: 'Europe/Guernsey', value: 'Europe/Guernsey' }, 2561 { name: 'Europe/Helsinki', value: 'Europe/Helsinki' }, 2562 { name: 'Europe/Isle of Man', value: 'Europe/Isle_of_Man' }, 2563 { name: 'Europe/Istanbul', value: 'Europe/Istanbul' }, 2564 { name: 'Europe/Jersey', value: 'Europe/Jersey' }, 2565 { name: 'Europe/Kaliningrad', value: 'Europe/Kaliningrad' }, 2566 { name: 'Europe/Kiev', value: 'Europe/Kiev' }, 2567 { name: 'Europe/Kirov', value: 'Europe/Kirov' }, 2568 { name: 'Europe/Lisbon', value: 'Europe/Lisbon' }, 2569 { name: 'Europe/Ljubljana', value: 'Europe/Ljubljana' }, 2570 { name: 'Europe/London', value: 'Europe/London' }, 2571 { name: 'Europe/Luxembourg', value: 'Europe/Luxembourg' }, 2572 { name: 'Europe/Madrid', value: 'Europe/Madrid' }, 2573 { name: 'Europe/Malta', value: 'Europe/Malta' }, 2574 { name: 'Europe/Mariehamn', value: 'Europe/Mariehamn' }, 2575 { name: 'Europe/Minsk', value: 'Europe/Minsk' }, 2576 { name: 'Europe/Monaco', value: 'Europe/Monaco' }, 2577 { name: 'Europe/Moscow', value: 'Europe/Moscow' }, 2578 { name: 'Europe/Oslo', value: 'Europe/Oslo' }, 2579 { name: 'Europe/Paris', value: 'Europe/Paris' }, 2580 { name: 'Europe/Podgorica', value: 'Europe/Podgorica' }, 2581 { name: 'Europe/Prague', value: 'Europe/Prague' }, 2582 { name: 'Europe/Riga', value: 'Europe/Riga' }, 2583 { name: 'Europe/Rome', value: 'Europe/Rome' }, 2584 { name: 'Europe/Samara', value: 'Europe/Samara' }, 2585 { name: 'Europe/San Marino', value: 'Europe/San_Marino' }, 2586 { name: 'Europe/Sarajevo', value: 'Europe/Sarajevo' }, 2587 { name: 'Europe/Saratov', value: 'Europe/Saratov' }, 2588 { name: 'Europe/Simferopol', value: 'Europe/Simferopol' }, 2589 { name: 'Europe/Skopje', value: 'Europe/Skopje' }, 2590 { name: 'Europe/Sofia', value: 'Europe/Sofia' }, 2591 { name: 'Europe/Stockholm', value: 'Europe/Stockholm' }, 2592 { name: 'Europe/Tallinn', value: 'Europe/Tallinn' }, 2593 { name: 'Europe/Tirane', value: 'Europe/Tirane' }, 2594 { name: 'Europe/Ulyanovsk', value: 'Europe/Ulyanovsk' }, 2595 { name: 'Europe/Uzhgorod', value: 'Europe/Uzhgorod' }, 2596 { name: 'Europe/Vaduz', value: 'Europe/Vaduz' }, 2597 { name: 'Europe/Vatican', value: 'Europe/Vatican' }, 2598 { name: 'Europe/Vienna', value: 'Europe/Vienna' }, 2599 { name: 'Europe/Vilnius', value: 'Europe/Vilnius' }, 2600 { name: 'Europe/Volgograd', value: 'Europe/Volgograd' }, 2601 { name: 'Europe/Warsaw', value: 'Europe/Warsaw' }, 2602 { name: 'Europe/Zagreb', value: 'Europe/Zagreb' }, 2603 { name: 'Europe/Zaporozhye', value: 'Europe/Zaporozhye' }, 2604 { name: 'Europe/Zurich', value: 'Europe/Zurich' }, 2605 { name: 'Indian/Antananarivo', value: 'Indian/Antananarivo' }, 2606 { name: 'Indian/Chagos', value: 'Indian/Chagos' }, 2607 { name: 'Indian/Christmas', value: 'Indian/Christmas' }, 2608 { name: 'Indian/Cocos', value: 'Indian/Cocos' }, 2609 { name: 'Indian/Comoro', value: 'Indian/Comoro' }, 2610 { name: 'Indian/Kerguelen', value: 'Indian/Kerguelen' }, 2611 { name: 'Indian/Mahe', value: 'Indian/Mahe' }, 2612 { name: 'Indian/Maldives', value: 'Indian/Maldives' }, 2613 { name: 'Indian/Mauritius', value: 'Indian/Mauritius' }, 2614 { name: 'Indian/Mayotte', value: 'Indian/Mayotte' }, 2615 { name: 'Indian/Reunion', value: 'Indian/Reunion' }, 2616 { name: 'Pacific/Apia', value: 'Pacific/Apia' }, 2617 { name: 'Pacific/Auckland', value: 'Pacific/Auckland' }, 2618 { name: 'Pacific/Bougainville', value: 'Pacific/Bougainville' }, 2619 { name: 'Pacific/Chatham', value: 'Pacific/Chatham' }, 2620 { name: 'Pacific/Chuuk', value: 'Pacific/Chuuk' }, 2621 { name: 'Pacific/Easter', value: 'Pacific/Easter' }, 2622 { name: 'Pacific/Efate', value: 'Pacific/Efate' }, 2623 { name: 'Pacific/Enderbury', value: 'Pacific/Enderbury' }, 2624 { name: 'Pacific/Fakaofo', value: 'Pacific/Fakaofo' }, 2625 { name: 'Pacific/Fiji', value: 'Pacific/Fiji' }, 2626 { name: 'Pacific/Funafuti', value: 'Pacific/Funafuti' }, 2627 { name: 'Pacific/Galapagos',
2627 value: 'Pacific/Galapagos' }, 2628 { name: 'Pacific/Gambier', value: 'Pacific/Gambier' }, 2629 { name: 'Pacific/Guadalcanal', value: 'Pacific/Guadalcanal' }, 2630 { name: 'Pacific/Guam', value: 'Pacific/Guam' }, 2631 { name: 'Pacific/Honolulu', value: 'Pacific/Honolulu' }, 2632 { name: 'Pacific/Kanton', value: 'Pacific/Kanton' }, 2633 { name: 'Pacific/Kiritimati', value: 'Pacific/Kiritimati' }, 2634 { name: 'Pacific/Kosrae', value: 'Pacific/Kosrae' }, 2635 { name: 'Pacific/Kwajalein', value: 'Pacific/Kwajalein' }, 2636 { name: 'Pacific/Majuro', value: 'Pacific/Majuro' }, 2637 { name: 'Pacific/Marquesas', value: 'Pacific/Marquesas' }, 2638 { name: 'Pacific/Midway', value: 'Pacific/Midway' }, 2639 { name: 'Pacific/Nauru', value: 'Pacific/Nauru' }, 2640 { name: 'Pacific/Niue', value: 'Pacific/Niue' }, 2641 { name: 'Pacific/Norfolk', value: 'Pacific/Norfolk' }, 2642 { name: 'Pacific/Noumea', value: 'Pacific/Noumea' }, 2643 { name: 'Pacific/Pago Pago', value: 'Pacific/Pago_Pago' }, 2644 { name: 'Pacific/Palau', value: 'Pacific/Palau' }, 2645 { name: 'Pacific/Pitcairn', value: 'Pacific/Pitcairn' }, 2646 { name: 'Pacific/Pohnpei', value: 'Pacific/Pohnpei' }, 2647 { name: 'Pacific/Port Moresby', value: 'Pacific/Port_Moresby' }, 2648 { name: 'Pacific/Rarotonga', value: 'Pacific/Rarotonga' }, 2649 { name: 'Pacific/Saipan', value: 'Pacific/Saipan' }, 2650 { name: 'Pacific/Tahiti', value: 'Pacific/Tahiti' }, 2651 { name: 'Pacific/Tarawa', value: 'Pacific/Tarawa' }, 2652 { name: 'Pacific/Tongatapu', value: 'Pacific/Tongatapu' }, 2653 { name: 'Pacific/Wake', value: 'Pacific/Wake' }, 2654 { name: 'Pacific/Wallis', value: 'Pacific/Wallis' } 2655 ], 2656 2657 // Get default timezone based on country code 2658 getDefaultTimezone: function(countryCode) { 2659 var timezoneMap = { 2660 'in': 'Asia/Kolkata', 2661 'ae': 'Asia/Dubai', 2662 'sa': 'Asia/Riyadh', 2663 'qa': 'Asia/Qatar', 2664 'kw': 'Asia/Kuwait', 2665 'bh': 'Asia/Bahrain', 2666 'om': 'Asia/Muscat', 2667 'gb': 'Europe/London', 2668 'us': 'America/New_York', 2669 // Default European timezone 2670 'de': 'Europe/Berlin', 2671 'fr': 'Europe/Paris', 2672 'it': 'Europe/Rome', 2673 'es': 'Europe/Madrid', 2674 'nl': 'Europe/Amsterdam', 2675 'ch': 'Europe/Zurich', 2676 'be': 'Europe/Brussels', 2677 'se': 'Europe/Stockholm', 2678 'no': 'Europe/Oslo', 2679 'dk': 'Europe/Copenhagen', 2680 'fi': 'Europe/Helsinki', 2681 'ie': 'Europe/Dublin', 2682 'at': 'Europe/Vienna', 2683 'pt': 'Europe/Lisbon' 2684 }; 2685 return timezoneMap[countryCode.toLowerCase()] || 'UTC'; 2686 }, 2687 2688 2689 // Create timezone dropdown 2690 createTimezoneSelect: function() { 2691 var self = this; 2692 var container = this.createElement('div', { className: 'booking-form-group' }); 2693 2694 var label = this.createElement('label', {}); 2695 label.textContent = 'Your Time Zone'; 2696 container.appendChild(label); 2697 2698 var selectWrapper = this.createElement('div', { className: 'booking-select-wrapper' }); 2699 2700 var select = this.createElement('select', { 2701 className: 'booking-timezone-select', 2702 attributes: { 2703 required: 'required' 2704 } 2705 }); 2706 2707 // Add default option 2708 var defaultOption = this.createElement('option', { 2709 attributes: { value: '' } 2710 }); 2711 defaultOption.textContent = 'Select timezone...'; 2712 select.appendChild(defaultOption); 2713 2714 // Add timezones to select 2715 this.timezonesList.forEach(function(tz) { 2716 var option = self.createElement('option', { 2717 attributes: { value: tz.value } 2718 }); 2719 option.textContent = tz.name; 2720 select.appendChild(option); 2721 }); 2722 2723 // Set timezone based on IP API response 2724 if (this.userCountry && this.userCountry.timezone) { 2725 var timezone = this.userCountry.timezone; 2726 console.log('Setting timezone from IP API:', timezone); 2727 2728 // Find the matching timezone in our list 2729 var matchingTimezone = this.timezonesList.find(function(tz) { 2730 return tz.value === timezone; 2731 }); 2732 2733 if (matchingTimezone) { 2734 select.value = timezone; 2735 console.log('Found exact timezone match:', timezone); 2736 } else { 2737 // If exact match not found, try to find a similar timezone 2738 var similarTimezone = this.timezonesList.find(function(tz) { 2739 return tz.value.includes(timezone.split('/')[0]); 2740 }); 2741 if (similarTimezone) { 2742 select.value = similarTimezone.value; 2743 console.log('Found similar timezone:', similarTimezone.value); 2744 } else { 2745 // If no match found, use the default timezone for the country 2746 var defaultTimezone = this.getDefaultTimezone(this.userCountry.code); 2747 select.value = defaultTimezone; 2748 console.log('Using default timezone for country:', defaultTimezone); 2749 } 2750 } 2751 } else { 2752 // If no timezone from IP API, use default 2753 var defaultTimezone = this.getDefaultTimezone('in'); // Default to India 2754 select.value = defaultTimezone; 2755 console.log('Using default timezone:', defaultTimezone); 2756 } 2757 2758 selectWrapper.appendChild(select); 2759 container.appendChild(selectWrapper); 2760 2761 // Add help text 2762 var helpText = this.createElement('p', { className: 'booking-help-text' }); 2763 helpText.textContent = 'All class times are shown in your local time zone'; 2764 container.appendChild(helpText); 2765 2766 // Update timezone when country changes 2767 var countrySelect = document.querySelector('.booking-country-code select'); 2768 if (countrySelect) { 2769 countrySelect.addEventListener('change', function() { 2770 // When country changes, update timezone based on IP API data 2771 if (self.userCountry && self.userCountry.timezone) { 2772 select.value = self.userCountry.timezone; 2773 console.log('Timezone updated based on IP API:', self.userCountry.timezone); 2774 self.updateTimeSlotsBasedOnTimezone(self.userCountry.timezone); 2775 } 2776 }); 2777 } 2778 2779 // Add event listener for timezone changes 2780 select.addEventListener('change', function() { 2781 var selectedTimezone = this.value; 2782 console.log('Timezone changed to:', selectedTimezone); 2783 self.updateTimeSlotsBasedOnTimezone(selectedTimezone); 2784 }); 2785 2786 // Style the select 2787 select.style.width = '100%'; 2788 select.style.padding = '12px'; 2789 select.style.border = '1px solid #ddd'; 2790 select.style.borderRadius = '8px'; 2791 select.style.appearance = 'none'; 2792 select.style.backgroundColor = 'white'; 2793 select.style.cursor = 'pointer'; 2794 select.style.fontSize = '14px'; 2795 2796 return container; 2797 }, 2798 2799 // Add new method to update time slots based on timezone 2800 updateTimeSlotsBasedOnTimezone: function(timezone) { 2801 var container = document.querySelector('.booking-frame'); 2802 if (!container) return; 2803 2804 // Get the selected date to determine if it's a weekend 2805 var selectedDate = container.querySelector('.booking-date-btn.active'); 2806 var isWeekend = false;
2807 2808 if (selectedDate) { 2809 var dayText = selectedDate.querySelector('div:first-child').textContent; 2810 // Special case: Tuesday Dec 30, 2025 should have all slots open like a weekend 2811 isWeekend = dayText === 'Saturday' || dayText === 'Sunday' || dayText === 'Tuesday'; 2812 } 2813 2814 // Get the time grid 2815 var timeGrid = container.querySelector('.booking-time-grid'); 2816 if (!timeGrid) return; 2817 2818 // Clear existing time slots 2819 while (timeGrid.firstChild) { 2820 timeGrid.removeChild(timeGrid.firstChild); 2821 } 2822 2823 // Create new time slots based on timezone and weekend status 2824 var timeSlots; 2825 if (timezone === 'Asia/Kolkata') { 2826 // India time slots 2827 if (isWeekend) { 2828 timeSlots = [ 2829 '10:30 AM', '11:30 AM', '12:30 PM', 2830 '1:30 PM', '2:30 PM', '3:30 PM', 2831 '4:30 PM', '5:30 PM', '6:30 PM', 2832 '7:30 PM' 2833 ]; 2834 } else { 2835 timeSlots = [ 2836 '4:30 PM', '5:30 PM', '6:30 PM', '7:30 PM' 2837 ]; 2838 } 2839 } else { 2840 // Rest of the world time slots 2841 if (isWeekend) { 2842 timeSlots = [ 2843 '8:00 AM', '9:00 AM', '10:00 AM', '11:00 AM', 2844 '12:00 PM', '1:00 PM', '2:00 PM', '3:00 PM', 2845 '4:00 PM', '5:00 PM', '6:00 PM', '7:00 PM', 2846 '8:00 PM' 2847 ]; 2848 } else { 2849 timeSlots = [ 2850 '5:00 PM', '6:00 PM', '7:00 PM', '8:00 PM' 2851 ]; 2852 } 2853 } 2854 2855 // Add new time slots to the grid 2856 var self = this; 2857 timeSlots.forEach(function(time) { 2858 var timeBtn = self.createElement('button', { 2859 className: 'booking-time-btn', 2860 attributes: { 'type': 'button' } 2861 }); 2862 timeBtn.textContent = time; 2863 timeGrid.appendChild(timeBtn); 2864 }); 2865 }, 2866 2867 // Helper methods for date formatting 2868 getDayName: function(dayIndex) { 2869 var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 2870 return days[dayIndex]; 2871 }, 2872 2873 getMonthShortName: function(monthIndex) { 2874 var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; 2875 return months[monthIndex]; 2876 }, 2877 2878 // Add form submission and redirect method 2879 submitForm: function(container) { 2880 // Get form data 2881 var formData = this.collectFormData(container); 2882 2883 // Log the form data 2884 console.log('Form submitted with data:', formData); 2885 2886 // Redirect to JetLearn confirmation page 2887 window.location.href = 'https://www.jetlearn.com/thanks-for-registering'; 2888 }, 2889 2890 // Update collectFormData to collect all required fields 2891 collectFormData: function(container) { 2892 var formData = {}; 2893 2894 // Add page URL to form data 2895 formData.page_url = window.location.href; 2896 2897 // Get selected country and phone number 2898 var countrySelect = container.querySelector('select'); 2899 var phoneInput = container.querySelector('input[type="tel"]'); 2900 if (countrySelect && phoneInput) { 2901 var countryCode = countrySelect.value; 2902 var phoneNumber = phoneInput.value; 2903 // Find the country dial code from the countriesList 2904 var country = this.countriesList.find(function(c) { 2905 return c.code.toLowerCase() === countryCode.toLowerCase(); 2906 }); 2907 // Format phone number with proper dial code 2908 formData.phone_number = country ? (country.dial_co
2908de + phoneNumber).replace(/\s+/g, '') : phoneNumber; 2909 } 2910 2911 // Get selected grade 2912 var selectedGrade = container.querySelector('.booking-grade-btn.active'); 2913 if (selectedGrade) { 2914 formData.grade = selectedGrade.getAttribute('data-grade'); 2915 } 2916 2917 // Get selected date and time 2918 var selectedDate = container.querySelector('.booking-date-btn.active'); 2919 if (selectedDate) { 2920 var dayText = selectedDate.querySelector('div:first-child').textContent; 2921 var dateText = selectedDate.querySelector('div:last-child').textContent; 2922 2923 // Parse the date from the selected button 2924 var date = new Date(); 2925 if (dayText === 'Tomorrow') { 2926 date.setDate(date.getDate() + 1); 2927 } else { 2928 // Find the day index (0-6) for the selected day 2929 var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 2930 var dayIndex = days.indexOf(dayText); 2931 if (dayIndex !== -1) { 2932 // Calculate days to add to reach the selected day 2933 var currentDay = date.getDay(); 2934 var daysToAdd = (dayIndex - currentDay + 7) % 7; 2935 date.setDate(date.getDate() + daysToAdd); 2936 } 2937 } 2938 2939 // Format date as YYYY-MM-DD 2940 formData.selectedDate = date.toISOString().split('T')[0]; 2941 console.log('Selected date:', { 2942 dayText: dayText, 2943 dateText: dateText, 2944 calculatedDate: formData.selectedDate 2945 }); 2946 } 2947 2948 var selectedTime = container.querySelector('.booking-time-btn.active'); 2949 if (selectedTime) { 2950 // Format time as HH:MM 2951 var timeText = selectedTime.textContent; 2952 var timeMatch = timeText.match(/(\d+):(\d+)\s*(AM|PM)/i); 2953 if (timeMatch) { 2954 var hours = parseInt(timeMatch[1]); 2955 var minutes = timeMatch[2]; 2956 var period = timeMatch[3].toUpperCase(); 2957 2958 if (period === 'PM' && hours !== 12) { 2959 hours += 12; 2960 } else if (period === 'AM' && hours === 12) { 2961 hours = 0; 2962 } 2963 2964 formData.selectedTime = hours.toString().padStart(2, '0') + ':' + minutes; 2965 } 2966 } 2967 2968 // Get student details 2969 var childName = container.querySelector('#child-name-input'); 2970 var parentName = container.querySelector('#parent-name-input'); 2971 var emailId = container.querySelector('input[type="email"]'); 2972 2973 if (childName) formData.childName = childName.value; 2974 if (parentName) formData.parentName = parentName.value; 2975 if (emailId) formData.emailId = emailId.value; 2976 2977 // Get optional survey responses from step 4 2978 var optionGroups = container.querySelectorAll('#booking-step4 .booking-option-buttons'); 2979 optionGroups.forEach(function(group, index) { 2980 var selectedOption = group.querySelector('.booking-option-btn.active'); 2981 if (selectedOption) { 2982 switch(index) { 2983 case 0: 2984 formData.userRole = selectedOption.textContent; 2985 break; 2986 case 1: 2987 formData.bookingReason = selectedOption.textContent; 2988 break; 2989 case 2: 2990 formData.purchaseTiming = selectedOption.textContent; 2991 break; 2992 } 2993 } 2994 }); 2995 2996 return formData; 2997 }, 2998 2999 // Add method to update time slots based on timezone 3000 updateTimeSlots: function(container, timezone) { 3001 var timeGrid = container.querySelector('.booking-time-grid'); 3002 if (!timeGrid) return; 3003 3004 var dateBtn = container.querySelector('.booking-date-btn.active'); 3005 if (!dateBtn) return; 3006 3007 var dayText = dateBtn.querySelector('div:first-child').textContent; 3008 // Special case: Tuesday Dec 30, 2025 should have all slots open like a weekend 3009 var isWeekend = dayText === 'Saturday' || dayText === 'Sunday' || dayText === 'Tuesday'; 3010 3011 // Clear existing time slots 3012 while (timeGrid.firstChild) { 3013 timeGrid.removeChild(timeGrid.firstChild); 3014 } 3015 3016 // Get time slots based on country and day 3017 var timeSlots; 3018 if (this.userCountry && this.userCountry.isIndia) { 3019 if (isWeekend) { 3020 timeSlots = ['10:30', '11:30', '12:30', '13:30', '14:30', '15:30', '16:30', '17:30', '18:30', '19:30']; 3021 } else { 3022 timeSlots = ['16:30', '17:30', '18:30', '19:30']; 3023 } 3024 } else { 3025 if (isWeekend) { 3026 timeSlots = ['08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00']; 3027 } else { 3028 timeSlots = ['17:00', '18:00', '19:00', '20:00']; 3029 } 3030 } 3031 3032 // Create time buttons with converted times 3033 var self = this;
3034 timeSlots.forEach(function(time) { 3035 var timeBtn = self.createElement('button', { 3036 className: 'booking-time-btn', 3037 attributes: { 'type': 'button' } 3038 }); 3039 3040 // Convert time to selected timezone 3041 var localTime = self.convertTime(time, timezone); 3042 timeBtn.textContent = localTime; 3043 timeGrid.appendChild(timeBtn); 3044 }); 3045 }, 3046 3047 // Add method to convert time to selected timezone 3048 convertTime: function(time, timezone) { 3049 try { 3050 var today = new Date(); 3051 var [hours, minutes] = time.split(':'); 3052 today.setHours(parseInt(hours), parseInt(minutes), 0); 3053 3054 var options = { 3055 hour: 'numeric', 3056 minute: 'numeric', 3057 timeZone: timezone, 3058 hour12: true 3059 }; 3060 3061 return new Intl.DateTimeFormat('en-US', options).format(today); 3062 } catch (error) { 3063 console.error('Error converting time:', error); 3064 return time; 3065 } 3066 }, 3067 3068 // Update createTimeGrid to use fixed slots 3069 createTimeGrid: function(isWeekend) { 3070 var timeGrid = this.createElement('div', { className: 'booking-time-grid' }); 3071 3072 // Define time slots based on country and day 3073 var timeSlots; 3074 if (this.userCountry && this.userCountry.isIndia) { 3075 if (isWeekend) { 3076 // Weekend slots for India: 10:30 AM to 7:30 PM 3077 timeSlots = [ 3078 '10:30 AM', '11:30 AM', '12:30 PM', 3079 '1:30 PM', '2:30 PM', '3:30 PM', 3080 '4:30 PM', '5:30 PM', '6:30 PM', 3081 '7:30 PM' 3082 ]; 3083 } else { 3084 // Weekday slots for India: 4:30 PM to 7:30 PM 3085 timeSlots = [ 3086 '4:30 PM', '5:30 PM', '6:30 PM', '7:30 PM' 3087 ]; 3088 } 3089 } else { 3090 if (isWeekend) { 3091 // Weekend slots for ROW: 8:00 AM to 8:00 PM 3092 timeSlots = [ 3093 '8:00 AM', '9:00 AM', '10:00 AM', '11:00 AM', 3094 '12:00 PM', '1:00 PM', '2:00 PM', '3:00 PM', 3095 '4:00 PM', '5:00 PM', '6:00 PM', '7:00 PM', 3096 '8:00 PM' 3097 ]; 3098 } else { 3099 // Weekday slots for ROW: 5:00 PM to 8:00 PM 3100 timeSlots = [ 3101 '5:00 PM', '6:00 PM', '7:00 PM', '8:00 PM' 3102 ]; 3103 } 3104 } 3105 3106 var self = this; 3107 timeSlots.forEach(function(time) { 3108 var timeBtn = self.createElement('button', { 3109 className: 'booking-time-btn', 3110 attributes: { 'type': 'button' } 3111 }); 3112 timeBtn.textContent = time; 3113 timeGrid.appendChild(timeBtn); 3114 }); 3115 3116 return timeGrid; 3117 }, 3118 3119 // Add a method to prefill and auto-progress with JLID data 3120 prefillAndProgressWithJLID: function(container) { 3121 var data = this.jlidData || {}; 3122 // Step 1: phone and grade 3123 if (data.phone && data.grade) { 3124 // Prefill phone 3125 var phoneInput = container.querySelector('input[type="tel"]'); 3126 if (phoneInput) phoneInput.value = data.phone.replace(/\D/g, ''); 3127 // Prefill grade (age) 3128 var gradeBtn = container.querySelector('.booking-grade-btn[data-grade="' + data.grade + '"]'); 3129 if (gradeBtn) { 3130 gradeBtn.classList.add('active'); 3131 gradeBtn.style.border = '1px solid #ffcf00'; 3132 gradeBtn.style.backgroundColor = 'rgba(255, 207, 0, 0.1)'; 3133 } 3134 // Go to step 2 3135 var step1 = container.querySelector('#booking-step1'); 3136 var step2 = container.querySelector('#booking-step2'); 3137 if (step1 && step2) { 3138 step1.classList.remove('active'); 3139 step2.classList.add('active'); 3140 } 3141 } 3142 // Step 2: date and time 3143 if (data.date && data.time) { 3144 // Prefill date 3145 var dateBtns = container.querySelectorAll('.booking-date-btn');
3146 dateBtns.forEach(function(btn) { 3147 var dateDiv = btn.querySelector('div:last-child'); 3148 if (dateDiv && dateDiv.textContent && data.date) { 3149 // Try to match by date string (e.g., '12 Jun') 3150 if (dateDiv.textContent.trim() === data.date.trim()) { 3151 btn.classList.add('active'); 3152 btn.style.border = '1px solid #ffcf00'; 3153 btn.style.backgroundColor = 'rgba(255, 207, 0, 0.1)'; 3154 } else { 3155 btn.classList.remove('active'); 3156 btn.style.border = '1px solid #ddd'; 3157 btn.style.backgroundColor = 'white'; 3158 } 3159 } 3160 }); 3161 // Prefill time 3162 var timeBtns = container.querySelectorAll('.booking-time-btn'); 3163 timeBtns.forEach(function(btn) { 3164 if (btn.textContent.trim() === data.time.trim()) { 3165 btn.classList.add('active'); 3166 btn.style.border = '1px solid #ffcf00'; 3167 btn.style.backgroundColor = 'rgba(255, 207, 0, 0.1)'; 3168 } else { 3169 btn.classList.remove('active'); 3170 btn.style.border = '1px solid #ddd'; 3171 btn.style.backgroundColor = 'white'; 3172 } 3173 }); 3174 // Go to step 3 3175 var step2 = container.querySelector('#booking-step2'); 3176 var step3 = container.querySelector('#booking-step3'); 3177 if (step2 && step3) { 3178 step2.classList.remove('active'); 3179 step3.classList.add('active'); 3180 } 3181 } 3182 } 3183 }; 3184 3185 // Make the form available globally with a unique name 3186 window.MultiStepBookingForm = { 3187 init: function(containerId) { 3188 BookingFormApp.init(containerId); 3189 } 3190 }; 3191 3192 // Auto-initialize when the DOM is ready 3193 if (document.readyState === 'loading') { 3194 document.addEventListener('DOMContentLoaded', function() { 3195 BookingFormApp.init(); 3196 }); 3197 } else { 3198 BookingFormApp.init(); 3199 } 3200 3201})(window, document);
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.