1/* 2 * @file blvd-booking-embed.js 3 * @author Simpatico Design Studio 4 * @copyright Copyright (C) 2023 Simpatico Design Studio 5 * @version 1.0.1 6 * @created 04-06-2023 7 * 8 * @description This script embeds the Simpatico + BLVD Booking App. 9 * 10 * Website: https://simpaticodesignstudio.com 11 * Contact: [email protected] 12 * 13 * Please contact Simpatico Design Studio for usage rights and more information. 14 * 15 * 16 * Querystring Variables: 17 * @url: customer's custom booking app domain 18 * 19 */ 20let showModal; 21let preloadDiv; 22let isBookingAppMobile = isMobileDevice(); 23let div_cart_count = '' 24 25function loadScript(src) { 26 return new Promise(function (resolve, reject) { 27 let script = document.createElement('script'); 28 script.src = src; 29 30 script.onload = () => resolve(script); 31 script.onerror = () => reject(new Error(`Script load error for ${src}`)); 32 33 document.head.append(script); 34 }); 35} 36 37const dynamicLoadBlvdBookingEmbed = document.getElementById('blvd-booking-app-embed')?.getAttribute('data-dynamic') ?? false; 38if (dynamicLoadBlvdBookingEmbed === 'true') { 39 loadBlvdBookingEmbed(); 40} else { 41 document.addEventListener('DOMContentLoaded', function () { 42 loadBlvdBookingEmbed(); 43 }); 44} 45 46function loadBlvdBookingEmbed() { 47 isBlvdEmbedLoaded = true; 48 const scheme = window.location.protocol === 'http:' ? 'http://' : 'https://'; 49 let scriptTag = document.getElementById('blvd-booking-app-embed'); 50 let dataUrl; 51 let dataTheme; 52 let dataTargetId; 53 let dataHOffset = "0px"; 54 let dataHeight; 55 let showCloseButton = true; 56 let div_display = "none"; 57 let div_position = 'fixed'; 58 let div_top = "0"; 59 let div_left = "0"; 60 let div_right = "0"; 61 let div_width = "525px"; 62 let div_maxWidth = "100vw"; 63 let div_height = "1024px"; 64 let div_maxHeight = "100vh"; 65 let div_zIndex = "9999999999"; 66 let div_backgroundColor = '#cccccc'; 67 let div_borderBottomRightRadius = '0px'; 68 let div_overflow = 'hidden'; 69 let div_targetID = ''; 70 let div_backfrop_filter = 'blur(10px)'; 71 let div_animate = '';; 72 let div_login_text = ''; 73 let div_bgloading = ''; 74 let b = getQueryStringValue("b") ?? null; 75 let o = getQueryStringValue("o") ?? null; 76 //console.log('b', b); 77 78 79 if (scriptTag) { 80 dataUrl = scriptTag.getAttribute('data-url'); 81 dataTheme = scriptTag.getAttribute('data-theme') ?? ''; 82 div_targetID = scriptTag.getAttribute('data-targetid') ?? div_targetID; 83 dataTop = scriptTag.getAttribute('data-top') ?? '0px'; 84 dataHOffset = scriptTag.getAttribute('data-hoffset') ?? dataHOffset; 85 showCloseButton = scriptTag.getAttribute('data-close') ?? showCloseButton; 86 div_top = scriptTag.getAttribute('data-top') ?? div_top; 87 div_left = scriptTag.getAttribute('data-left') ?? div_left; 88 div_right = scriptTag.getAttribute('data-right') ?? div_right; 89 div_width = scriptTag.getAttribute('data-width') ?? div_width; 90 div_height = scriptTag.getAttribute('data-height') ?? div_height; 91 div_backgroundColor = scriptTag.getAttribute('data-bgcolor') ?? div_backgroundColor; 92 div_borderBottomRightRadius = scriptTag.getAttribute('data-radius') ?? div_borderBottomRightRadius; 93 div_overflow = scriptTag.getAttribute('data-overflow') ?? div_overflow; 94 div_backfrop_filter = scriptTag.getAttribute('data-blur') ?? div_backfrop_filter; 95 div_animate = scriptTag.getAttribute('data-animate') ?? ''; 96 div_cart_count = scriptTag.getAttribute('data-cartcount') ?? div_cart_count; 97 div_login_text = scriptTag.getAttribute('data-logintext') ?? div_login_text; 98 div_bgloading = scriptTag.getAttribute('data-bgloading') ?? div_bgloading; 99 100 if (!dataUrl) { 101 let scriptSrc = new URL(scriptTag.src); 102 dataUrl = scriptSrc.hostname; 103 } 104 const cookieName = (dataUrl ? dataUrl.replace(/\./g, '_') + '_blvd_session' : '_blvd_session'); 105 106 if (div_cart_count || div_login_text) { 107 updateDivsFromCookies(cookieName); 108 } 109 (function (cookieName) { 110 window.addEventListener('message', function (event) { 111 // Verify the origin of the message to ensure it's from the trusted iframe 112 if (event.origin === scheme + dataUrl) { // Replace with your iframe domain 113 if (event.data) { 114 if (event.data.cartCount !== undefined && div_cart_count) { // Check if div_cart_count is valid 115 document.querySelectorAll('.' + div_cart_count).forEach(function (element) { 116 element.innerText = event.data.cartCount; 117 }); 118 setLoginTextCookie(cookieName + '_count', event.data.cartCount); 119 // console.log('Cart Count', event.data.cartCount); 120 } 121 div_login_text = scriptTag.getAttribute('data-logintext') ?? div_login_text; 122 if (event.data.loginText !== undefined && div_login_text) { // Check if div_login_text is valid 123 loginTextCookie = event.data.loginText; 124 loginTextCookie = loginTextCookie.replace(/\s+/g, ''); 125 console.log('div_login_text', div_login_text); 126 document.getElementById(div_login_text).innerText = loginTextCookie; 127 setLoginTextCookie(cookieName + '_name', loginTextCookie); 128 // console.log('Login Text', loginTextCookie); 129 } 130 } 131 } 132 }, false); 133 })(cookieName); 134 135 } else { 136 console.error("Script tag with id 'blvd-booking-app-embed' not found."); 137 } 138 const blvd_u = getCookie('_blvd_u'); 139 140 let eqs = ""; 141 if (blvd_u) { 142 const blvd_obj = JSON.parse(blvd_u); 143 eqs = `&e=${blvd_obj.e}&f=${blvd_obj.f}&l=${blvd_obj.l}&p=${blvd_obj.p}`; 144 } 145 146 var iframeSrc = appendCampaignParams(scheme + dataUrl + "/" + "?sl=1" + eqs); 147 148 if (!isBookingAppMobile) { 149 // 1. Create a hidden DIV element with an ID of #blvd-booking-app 150 let bookNowDiv; 151 const existingBookNowDiv = document.getElementById('blvd-booking-app'); 152 153 if (dataTheme === 'wide') { 154 //showCloseButton = false;
155 //div_display = "block"; 156 div_top = dataTop //"unset"; 157 //div_left = "unset"; 158 //div_position = 'relative'; 159 //div_width = "100vw"; 160 div_maxWidth = "100%"; 161 if (dataHOffset === "0") { 162 div_height = "100vh"; 163 div_maxHeight = "100%"; 164 } else { 165 div_height = "calc(100vh - " + dataHOffset + ")" //"100vh"; 166 div_maxHeight = "unset"; 167 } 168 div_backgroundColor = '#ffffff'; 169 div_borderBottomRightRadius = '0px'; 170 //div_overflow = 'auto'; 171 } 172 173 if (existingBookNowDiv) { 174 console.log('existingBookNowDiv', existingBookNowDiv); 175 bookNowDiv = existingBookNowDiv; 176 //console.log('existing div'); 177 const theme = bookNowDiv.getAttribute('data-theme'); 178 if (theme === 'wide') { 179 showCloseButton = false; 180 } 181 182 } else { 183 bookNowDiv = document.createElement('div'); 184 bookNowDiv.id = 'blvd-booking-app'; 185 bookNowDiv.style.display = div_display; 186 bookNowDiv.style.position = div_position; 187 bookNowDiv.style.top = "0"; 188 bookNowDiv.style.left = "0"; 189 bookNowDiv.style.width = "100%"; 190 bookNowDiv.style.maxWidth = "100vw"; 191 bookNowDiv.style.height = "100%"; 192 bookNowDiv.style.maxHeight = "100vh"; 193 bookNowDiv.style.zIndex = div_zIndex; 194 bookNowDiv.style.backgroundColor = "rgba(0,0,0,0.5)"; 195 bookNowDiv.style.overflow = div_overflow; 196 bookNowDiv.style.backdropFilter = div_backfrop_filter; 197 198 console.log('bookNowDiv', bookNowDiv); 199 200 if (dataTheme === 'wide') { 201 var targetDiv = document.getElementById(dataTargetId); 202 if (targetDiv) { 203 targetDiv.appendChild(bookNowDiv); 204 } else { 205 //console.error("Element with ID 'dataTargetId' not found."); 206 document.body.appendChild(bookNowDiv); 207 } 208 } else { 209 document.body.appendChild(bookNowDiv); 210 } 211 bookAppDiv = document.createElement('div'); 212 bookAppDiv.id = 'blvd-booking-app-div'; 213 bookAppDiv.style.position = div_position; 214 bookAppDiv.style.top = div_top; 215 bookAppDiv.style.left = div_left; 216 bookAppDiv.style.right = div_right; 217 bookAppDiv.style.width = div_width; 218 bookAppDiv.style.maxWidth = div_maxWidth; 219 bookAppDiv.style.height = div_height; 220 bookAppDiv.style.maxHeight = div_maxHeight; 221 bookAppDiv.style.backgroundColor = div_backgroundColor; 222 bookAppDiv.style.borderBottomRightRadius = div_borderBottomRightRadius; 223 bookAppDiv.style.overflow = div_overflow; 224 bookAppDiv.style.backdropFilter = div_backfrop_filter; 225 bookNowDiv.appendChild(bookAppDiv); 226 227 console.log('bookAppDiv', bookAppDiv); 228 229 230 if (div_bgloading) { 231 // Create the loading div 232 loadingDiv = document.createElement('div'); 233 loadingDiv.style.backgroundImage = `url('${scheme}${dataUrl}/images/${div_bgloading}')`; 234 loadingDiv.style.backgroundRepeat = 'no-repeat'; 235 loadingDiv.style.backgroundPosition = 'center'; 236 loadingDiv.style.backgroundSize = 'contain'; // Ensure the entire image is visible 237 loadingDiv.style.width = '150px'; 238 loadingDiv.style.height = '150px'; 239 loadingDiv.style.margin = '30% auto 0 auto'; 240 loadingDiv.innerHTML = ' '; 241 242 // Create a style element for the keyframes 243 const styleElement = document.createElement('style'); 244 styleElement.textContent = ` 245 @keyframes rotate { 246 from { transform: rotate(0deg); } 247 to { transform: rotate(360deg); } 248 } 249 .rotating-background { 250 animation: rotate 1s linear infinite; 251 } 252 `; 253 254 // Append the style element to the document head 255 document.head.appendChild(styleElement); 256 257 // Apply the rotating-background class to the loading div 258 loadingDiv.classList.add('rotating-background'); 259 260 // Append the loading div to your container (e.g., bookAppDiv) 261 bookAppDiv.appendChild(loadingDiv); 262 } 263 } 264 // 2.a If the URL has a querystring o qs, change the iframe src 265 if (o) { 266 let decoded_o = decodeURIComponent(o); 267 268 if (decoded_o.includes('?')) { 269 const urlParts = decoded_o.split('?'); 270 const queryParams = new URLSearchParams(urlParts[1]); 271 if (!queryParams.has('dl')) { 272 decoded_o += '&dl=1'; 273 } 274 } else { 275 decoded_o += '?dl=1'; 276 } 277 278 iframeSrc = appendCampaignParams(scheme + dataUrl + "/" + decoded_o); 279 } 280 281 // 2.b Loads the Simpatico BLVD Booking App in the DIV 282 283 let iframe; 284 const existingIframe = document.getElementById('blvd-iframe'); 285 286 if (existingIframe) { 287 // If it exists, use the existing iframe 288 iframe = existingIframe; 289 iframe.src = iframeSrc; 290 console.log("existingIframe: ", existingIframe); 291 } else { 292 // If it doesn't exist, create a new iframe 293 //document.requestStorageAccess().then(() => { 294 iframe = document.createElement('iframe'); 295 iframe.src = iframeSrc; 296 iframe.id = 'blvd-iframe'; 297 iframe.allow = 'autofocus'; 298 iframe.style.width = '100%'; 299 iframe.style.height = '100%'; 300 iframe.style.border = 'none'; 301 iframe.style.position = 'absolute'; 302 iframe.style.top = '0'; 303 iframe.style.left = '0'; 304 iframe.style.overflow = 'hidden'; 305 iframe.frameborder = '0'; 306 iframe.seamless = 'seamless'; 307 iframe.sandbox = 'allow-top-navigation allow-scripts allow-same-origin allow-forms allow-modals'; 308 iframe.withCredentials = true; 309 console.log("iframe: ", iframe); 310 bookAppDiv.appendChild(iframe); 311 312 console.log("bookAppDiv: ", bookAppDiv); 313 //}); 314 } 315 // 3.a When a button with the class ".book-now" is clicked, the #blvd-booking-app DIV will become visible as a pop-up modal 316 showModal = () => { 317 bookNowDiv.style.display = 'block'; 318 bookAppDiv.classList.add('animate-modal'); 319 }; 320 321 // 3.b If the URL has a querystring b=1, the app will open 322 if (b == "1") { 323 bookNowDiv.style.display = 'block'; 324 bookAppDiv.classList.add('animate-modal'); 325 } 326 console.log("iframeSrc: ", iframeSrc); 327 328 // Close the modal when clicking outside the iframe 329 bookNowDiv.addEventListener('click', (e) => { 330 if (e.target !== bookAppDiv) { 331 bookNowDiv.style.display = 'none'; 332 } 333 }); 334 335 // Create the close button element 336 if (showCloseButton) { 337 closeTop = scriptTag.getAttribute('data-closetop') ?? "1px"; 338 closeRight = scriptTag.getAttribute('data-closeright') ?? "1px"; 339 closeBG = scriptTag.getAttribute('data-closebg') ?? "#fff"; 340 closeColor = scriptTag.getAttribute('data-closecolor') ?? "#000"; 341 const closeButton = document.createElement('div'); 342 closeButton.classList.add('close-button'); 343 closeButton.innerHTML = '×'; 344 closeButton.style.position = 'absolute'; 345 closeButton.style.top = closeTop; 346 closeButton.style.right = closeRight; 347 closeButton.style.width = '30px'; 348 closeButton.style.height = '30px'; 349 closeButton.style.borderRadius = '50%'; 350 closeButton.style.backgroundColor = closeBG; 351 closeButton.style.color = closeColor; 352 closeButton.style.border = '0.5px solid ' + closeColor; 353 closeButton.style.textAlign = 'center'; 354 closeButton.style.lineHeight = '30px'; 355 closeButton.style.cursor = 'pointer'; 356 closeButton.style.fontSize = '20px'; 357 closeButton.style.fontWeight = 'bold'; 358 359 // Add event listener to close the modal when the close button is clicked 360 closeButton.addEventListener('click', () => { 361 bookNowDiv.style.display = 'none'; 362 bookAppDiv.classList.remove('animate-modal'); 363 document.body.style.overflow = 'auto'; 364 }); 365 366 // Add the close button to the bookAppDiv 367 bookAppDiv.appendChild(closeButton); 368 } 369 370 if (div_animate) { 371 const style = document.createElement('style'); 372 const direction = ['slideUp', 'slideDown', 'slideLeft', 'slideRight'].includes(div_animate) 373 ? div_animate 374 : 'slideUp'; 375 style.innerHTML = ` 376 @keyframes slideUp { 377 from { transform: translateY(100%); } 378 to { transform: translateY(0); } 379 } 380 @keyframes slideDown { 381 from { transform: translateY(-100%); } 382 to { transform: translateY(0); } 383 } 384 @keyframes slideLeft { 385 from { transform: translateX(100%); } 386 to { transform: translateX(0); } 387 } 388 @keyframes slideRight { 389 from { transform: translateX(-100%); } 390 to { transform: translateX(0); } 391 } 392 393 #blvd-booking-app-div.animate-modal { 394 animation: ${direction} 0.3s ease-out; 395 } 396 `; 397 document.head.appendChild(style); 398 } 399 400 } else { 401 console.log('is mobile'); 402 if (b == "1") { 403 console.log('is b') 404 let newWindowURL = scheme + dataUrl; 405 406 if (o) { 407 let decoded_o = decodeURIComponent(o); 408 409 if (decoded_o.includes('?')) { 410 const urlParts = decoded_o.split('?'); 411 const queryParams = new URLSearchParams(urlParts[1]); 412 if (!queryParams.has('dl')) { 413 decoded_o += '&dl=1'; 414 } 415 } else { 416 decoded_o += '?dl=1'; 417 } 418 419 newWindowURL += '/' + decoded_o; 420 } 421 422 newWindowURL = appendCampaignParams(newWindowURL); 423 424 window.location.href = appendReturnUrl(newWindowURL); 425 } 426 } 427 428 //const bookNowButtons = document.querySelectorAll('.book-now'); 429 const bookNowButtons = Array.from(document.querySelectorAll('.book-now')).filter(button => !button.closest('.no-book')); 430 bookNowButtons.forEach((button) => { 431 button.addEventListener('click', function (event) { 432 let link = checkIframeSrc(event.currentTarget.dataset.link) ?? ''; 433 let deepLink = false;
434 if (link !== '') { 435 link = link.includes('?') ? `${link}&dl=1` : `${link}?dl=1`; 436 deepLink = true; 437 } 438 if (!isBookingAppMobile) { 439 let newIframeSrc = iframeSrc; // Default to the original iframeSrc 440 441 if (link) { 442 newIframeSrc = appendCampaignParams(checkIframeSrc(scheme + dataUrl + "/" + link)); 443 } 444 445 const iframeElement = document.getElementById('blvd-iframe'); 446 if (iframeElement) { 447 let shouldShowLoading = newIframeSrc.includes('?sl=1'); 448 // Set the src only when opening the modal 449 if (!iframeElement.src) { 450 iframeElement.src = newIframeSrc; 451 shouldShowLoading = true; 452 } else if (iframeElement.src !== newIframeSrc) { 453 iframeElement.src = newIframeSrc; 454 if (deepLink) { 455 shouldShowLoading = true; 456 } else { 457 shouldShowLoading = false; 458 } 459 } else if (iframeElement.src === newIframeSrc) { 460 shouldShowLoading = false; 461 } 462 463 if (shouldShowLoading === true) { 464 try { 465 iframeElement.contentWindow.postMessage( 466 { showLoading: true }, 467 scheme + dataUrl 468 ); 469 } catch (error) { 470 // Silently fail on privacy-focused browsers like DuckDuckGo that block cross-origin messaging 471 console.debug('postMessage blocked:', error.message); 472 } 473 } 474 475 } else { 476 console.error('Iframe with id blvd-iframe not found'); 477 } 478 479 showModal(); 480 } else { 481 var newWindowURL = iframeSrc; 482 if (link !== '') { 483 newWindowURL = appendCampaignParams(scheme + dataUrl + '/' + link); 484 } 485 window.location.href = appendReturnUrl(newWindowURL); 486 } 487 }); 488 }); 489 490 preloadDiv = document.getElementById('blvd-preload'); 491 492 if (preloadDiv) { createPreloadIframe(preloadDiv, scheme + dataUrl); } 493 494 document.body.addEventListener('change', function (e) { // Note the async keyword here 495 if (e.target && e.target.id === 'preload_location') { 496 //console.log('change location'); 497 const newOptionTags = updateSelectService(e.target.value); // Now you can use await here 498 //console.log('newOptionTags', newOptionTags); 499 500 // Find #preload_service select box 501 const preloadServiceSelect = document.getElementById('preload_service'); 502 if (preloadServiceSelect) { 503 // Empty the #preload_service select box 504 while (preloadServiceSelect.firstChild) { 505 preloadServiceSelect.removeChild(preloadServiceSelect.firstChild); 506 } 507 // Add the "Select" option 508 const selectOption = document.createElement('option'); 509 selectOption.setAttribute('class', ''); 510 selectOption.setAttribute('value', ''); 511 selectOption.setAttribute('selected', ''); 512 selectOption.textContent = 'Select'; 513 preloadServiceSelect.appendChild(selectOption); 514 515 // Convert the returned options string into DOM nodes 516 const tempDiv = document.createElement('div'); 517 tempDiv.innerHTML = newOptionTags; 518 519 // Append the new options into the #preload_service select box 520 Array.from(tempDiv.childNodes).forEach(function (node) { 521 if (node.tagName === 'OPTGROUP') { 522 preloadServiceSelect.appendChild(node.cloneNode(true)); // using cloneNode to ensure we don't move the node 523 } 524 }); 525 } 526 } 527 }); 528}; 529 530function updateDivsFromCookies(cookieName) { 531 var cartCountCookie = getCookie(cookieName + '_count'); 532 var loginTextCookie = getCookie(cookieName + '_name'); 533 534 if (cartCountCookie) { 535 document.querySelectorAll('.' + div_cart_count).forEach(function (element) { 536 element.innerText = cartCountCookie; 537 }); 538 } 539 540 if (loginTextCookie) { 541 scriptTag = document.getElementById('blvd-booking-app-embed'); 542 div_login_text = scriptTag.getAttribute('data-logintext') ?? div_login_text; 543 document.getElementById(div_login_text).innerText = loginTextCookie; 544 } 545} 546 547function createPreloadIframe(preloadDiv, iframeSrc) { 548 return new Promise((resolve, reject) => { 549 // Search for a DIV where element id = "blvd-preload" 550 //console.log('preload div', preloadDiv); 551 // If that exists 552 if (preloadDiv) { 553 preloadDiv.style.width = '100%'; 554 preloadDiv.style.height = '100%'; 555 preloadDiv.style.border = 'none'; 556 const datepicker_css = document.createElement('link'); 557 datepicker_css.rel = 'stylesheet'; 558 datepicker_css.href = 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css'; 559 document.head.appendChild(datepicker_css); 560 561 const app_css = document.createElement('link'); 562 app_css.rel = 'stylesheet'; 563 app_css.href = iframeSrc + '/css/styles-preload-form.css'; 564 document.head.appendChild(app_css); 565 566 loadScript('https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js') 567 .then(() => { 568 fetch(iframeSrc + '/preload') 569 .then(response => response.json()) 570 .then(data => { 571 // Now data.html contains the HTML string generated by your PHP method 572 document.getElementById('blvd-preload').innerHTML = data.html; 573 574 // Initialize the datepicker after the HTML content has been added 575 jQuery(document).ready(function () { 576 var datepickerInitialized = false;
577 578 jQuery('.date').datepicker({ 579 format: 'mm/dd/yyyy' 580 }).on('show', function (e) { 581 // The datepicker is now visible, so you can add your class 582 jQuery('.datepicker').addClass('preload-cal'); 583 datepickerInitialized = true; 584 // Trigger a resize event 585 jQuery(window).trigger('resize'); 586 }); 587 588 // Add a click event handler to open the datepicker 589 jQuery('.date').on('click', function (e) { 590 if (!datepickerInitialized) { 591 jQuery(this).datepicker('show'); 592 } 593 }); 594 }); 595 596 // Add event listener to the button here 597 const preloadButton = document.getElementById('preload_button'); 598 if (preloadButton) { 599 preloadButton.addEventListener('click', function (event) { 600 // Prevent the default action 601 event.preventDefault(); 602 603 // Get the values from the input fields 604 const location_id = document.getElementById('preload_location').value; 605 const service_id = document.getElementById('preload_service').value; 606 const preload_date = document.getElementById('preload_date').value; 607 608 // Get the iframe 609 const iframe = document.getElementById('blvd-iframe'); 610 if (iframe) { 611 // Change the src of the iframe 612 iframe.src = appendCampaignParams(`${iframeSrc}/selectdatetime?loc=${location_id}&srv=${service_id}&dte=${preload_date}`); 613 showModal(); 614 } else { 615 console.error('Error: Could not find iframe with id "blvd-iframe".'); 616 } 617 618 // Scroll to top of page 619 window.scrollTo(0, 0); 620 }); 621 } else { 622 console.error('Error: Could not find element with id "preload_button".'); 623 } 624 }) 625 .catch((error) => { 626 console.error('Error:', error); 627 }); 628 }) // <--- This was missing in your original code 629 } 630 }); 631} 632 633function updateSelectService(location_id) { 634 const params = new URLSearchParams({ 635 'endpoint': 'createCart', 636 'client_id': '', 637 'location_id': location_id 638 }).toString(); 639 640 return fetch(`${iframeSrc}/api/blvd-api.php?${params}`) 641 .then(response => response.json()) 642 .then(data => { 643 let html = ''; 644 645 data.availableCategories.forEach(category => { 646 let service_options = ''; 647 category.availableItems.filter(service => !service.disabled && service.__typename === 'CartAvailableBookableItem') 648 .forEach(service => { 649 service_options += `<option value="${encodeURIComponent(service.id)}">${service.name}</option>`; 650 }); 651 if (service_options !== "") { 652 html += `<optgroup label="${category.name}">`; 653 html += service_options; 654 html += `</optgroup>`; 655 } 656 }); 657 //console.log('html', html); 658 return html; 659 660 }) 661 .catch((error) => { 662 console.error('Error:', error); 663 }); 664} 665 666function getCookie(name) { 667 var value = "; " + document.cookie; 668 var parts = value.split("; " + name + "="); 669 if (parts.length == 2) return parts.pop().split(";").shift(); 670} 671 672function getQueryStringValue(key) { 673 const queryParams = new URLSearchParams(window.location.search); 674 return queryParams.get(key); 675} 676 677// Check if the cookies exist 678function cookiesEmbedExist(cookieName) { 679 return document.cookie.includes('XSRF-TOKEN') && document.cookie.includes(cookieName); 680} 681 682// Function to set cookies 683function setEmbedCookies(dataUrl, cookieName) { 684 // Check if cookies already exist 685 if (!cookiesEmbedExist(cookieName)) { 686 // Set session cookie 687 var now = new Date(); 688 var time = now.getTime(); 689 var expireTime = time + 1000 * 28800; // 8 hours in milliseconds 690 now.setTime(expireTime); 691 692 var cookieS = cookieName + "='';expires=" + now.toUTCString() + ";path=/; domain=" + dataUrl + "; SameSite=None; Secure;"; 693 document.cookie = cookieS; 694 695 // Set XSRF-TOKEN cookie 696 var cookieX = "XSRF-TOKEN='';expires=" + now.toUTCString() + ";path=/; domain=" + dataUrl + "; SameSite=None; Secure"; 697 document.cookie = cookieX; 698 } 699} 700 701function setLoginTextCookie(cookieName, cookieValue) { 702 // Get the domain from the current URL 703 var domainParts = window.location.hostname.split('.'); 704 var domain = domainParts.slice(-2).join('.'); // Get the top-level domain and second-level domain 705 706 // Set session cookie 707 var now = new Date(); 708 var time = now.getTime(); 709 var expireTime = time + 1000 * 28800; // 8 hours in milliseconds 710 now.setTime(expireTime); 711 712 // Create the cookie string 713 var setCookie = cookieName + "=" + encodeURIComponent(cookieValue) + ";expires=" + now.toUTCString() + "; path=/; domain=" + domain + "; SameSite=None; Secure"; 714 //console.log('setCookie', setCookie); 715 // Set the cookie 716 document.cookie = setCookie; 717} 718 719function isMobileDevice() { 720 return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); 721} 722 723function checkIframeSrc(link) { 724 // Check if Iframe Src contains querystrings "?" 725 if (typeof link !== 'undefined' && link !== null) { 726 if (link.indexOf('?') === -1) { 727 // If it does not have "?", add "?sl=1" 728 link += '?sl=1'; 729 } else { 730 // If it has "?", check if it contains "sl=1" 731 const queryParams = new URLSearchParams(link.split('?')[1]); 732 if (!queryParams.has('sl')) { 733 // If "sl" is not present, add "sl=1" as the first query string 734 link = link.replace('?', '?sl=1&'); 735 } else if (queryParams.get('sl') !== '1') { 736 // If "sl" is present but not equal to "1", update it 737 queryParams.set('sl', '1'); 738 const baseUrl = link.split('?')[0]; 739 link = baseUrl + '?' + queryParams.toString(); 740 } 741 } 742 } 743 //console.log('link', link); 744 return link; 745} 746 747function getCampaignParams() { 748 const campaignParamKeys = [ 749 'gad_source', 'gad_campaignid', 'gbraid', 'gclid', 750 'utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_id', 'utm_term', 751 'fbclid' 752 ]; 753
754 const currentParams = new URLSearchParams(window.location.search); 755 const campaignParams = new URLSearchParams(); 756 757 campaignParamKeys.forEach(key => { 758 if (currentParams.has(key)) { 759 campaignParams.set(key, currentParams.get(key)); 760 } 761 }); 762 763 return campaignParams.toString(); 764} 765 766function appendCampaignParams(url) { 767 const campaignParams = getCampaignParams(); 768 if (!campaignParams) { 769 return url; 770 } 771 772 const separator = url.includes('?') ? '&' : '?'; 773 return url + separator + campaignParams; 774} 775 776function appendReturnUrl(url) { 777 const separator = url.includes('?') ? '&' : '?'; 778 return url + separator + 'embed_return_url=' + encodeURIComponent(window.location.href); 779}
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.