1 2window.neoScripts = (window.neoScripts === undefined) ? {} : window.neoScripts; 3 4window.IsNeoLoaded = function(scriptId) { return !!GetNeoState(scriptId); }; 5window.IsNeoUnloaded = function(scriptId) { return !GetNeoState(scriptId); }; 6window.GetNeoState = function(scriptId) { return window.neoScripts[scriptId]; }; 7 8window.SetNeoLoaded = function(scriptId) { setNeoState(scriptId, 1); } 9window.SetNeoUnloaded = function(scriptId) { setNeoState(scriptId, 0); } 10window.setNeoState = function(scriptId, state) { window.neoScripts[scriptId] = state; }; 11 12// Track location of full-loaded script (initial page load) 13// to prevent unnecessary reset if double event on same page 14window._neoOriginUrl = window.location.toString(); 15 16(function() { 17 18 var scriptId = "8rjx1wfc"; 19 20 if (!IsNeoLoaded(scriptId)) { 21 SetNeoLoaded(scriptId); 22 23 var $neo; 24if (window.jQuery === undefined) { 25 // jQuery not found 26 var script_tag = document.createElement('script'); 27 script_tag.setAttribute("type","text/javascript"); 28 script_tag.setAttribute("src", "https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"); 29 if (script_tag.readyState) { 30 // For old versions of IE 31 script_tag.onreadystatechange = function () { 32 if (this.readyState == 'complete' || this.readyState == 'loaded') {scriptLoadHandler();} 33 }; 34 } else { 35 // For normal browsers 36 script_tag.onload = scriptLoadHandler; 37 } 38 // Add jQuery to the head 39 (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag); 40} else { 41 // jQuery was found 42 $neo = window.jQuery; 43 main(); 44} 45 46function scriptLoadHandler() { 47 $neo = window.jQuery.noConflict(); 48 main(); 49} 50 51 52 function main() { 53 $neo(function($) { 54 // Helper methods 55// ----------------------------------- 56 57function loadPluginScript(path, resolve, reject) { 58 var script = document.createElement('script'); 59 script.onload = resolve; 60 script.onerror = reject; 61 script.async = true; 62 script.src = path; 63 document.head.appendChild(script); 64} 65 66function loadPluginStyle(path) { 67 if ($("link[href='" + path + "']").length) return; 68 69 var stylesheet = document.createElement('link'); 70 stylesheet.rel = 'stylesheet'; 71 stylesheet.type = 'text/css'; 72 stylesheet.href = path; 73 document.head.appendChild(stylesheet); 74} 75
76// Plugins with jQuery dependency 77// ----------------------------------- 78 79(function(jQuery) { 80 /*! 81 * Javascript Cookie v1.5.1 82 * https://github.com/js-cookie/js-cookie 83 * 84 * Copyright 2006, 2014 Klaus Hartl 85 * Released under the MIT license 86 */ 87(function (factory) { 88 var jQuery; 89 if (typeof exports === 'object') { 90 // Node/CommonJS 91 try { 92 jQuery = require('jquery'); 93 } catch(e) {} 94 module.exports = factory(jQuery); 95 } else { 96 // Browser globals 97 var _OldCookies = window.Cookies; 98 var api = window.Cookies = factory(window.jQuery); 99 api.noConflict = function() { 100 window.Cookies = _OldCookies; 101 return api; 102 }; 103 } 104}(function ($) { 105 106 var pluses = /\+/g; 107 108 function encode(s) { 109 return api.raw ? s : encodeURIComponent(s); 110 } 111 112 function decode(s) { 113 return api.raw ? s : decodeURIComponent(s); 114 } 115 116 function stringifyCookieValue(value) { 117 return encode(api.json ? JSON.stringify(value) : String(value)); 118 } 119 120 function parseCookieValue(s) { 121 if (s.indexOf('"') === 0) { 122 // This is a quoted cookie as according to RFC2068, unescape... 123 s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); 124 } 125 126 try { 127 // Replace server-side written pluses with spaces. 128 // If we can't decode the cookie, ignore it, it's unusable. 129 // If we can't parse the cookie, ignore it, it's unusable. 130 s = decodeURIComponent(s.replace(pluses, ' ')); 131 return api.json ? JSON.parse(s) : s; 132 } catch(e) {} 133 } 134 135 function read(s, converter) { 136 var value = api.raw ? s : parseCookieValue(s); 137 return isFunction(converter) ? converter(value) : value; 138 } 139 140 function extend() { 141 var key, options; 142 var i = 0; 143 var result = {}; 144 for (; i < arguments.length; i++) { 145 options = arguments[ i ]; 146 for (key in options) { 147 result[key] = options[key]; 148 } 149 } 150 return result; 151 } 152 153 function isFunction(obj) { 154 return Object.prototype.toString.call(obj) === '[object Function]'; 155 } 156 157 var api = function (key, value, options) { 158 159 // Write 160 161 if (arguments.length > 1 && !isFunction(value)) { 162 options = extend(api.defaults, options); 163 164 if (typeof options.expires === 'number') { 165 var days = options.expires, t = options.expires = new Date(); 166 t.setMilliseconds(t.getMilliseconds() + days * 864e+5); 167 } 168 169 return (document.cookie = [ 170 encode(key), '=', stringifyCookieValue(value), 171 options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 172 options.path ? '; path=' + options.path : '', 173 options.domain ? '; domain=' + options.domain : '', 174 options.secure ? '; secure' : '' 175 ].join('')); 176 } 177 178 // Read 179 180 var result = key ? undefined : {}, 181 // To prevent the for loop in the first place assign an empty array 182 // in case there are no cookies at all. Also prevents odd result when 183 // calling "get()". 184 cookies = document.cookie ? document.cookie.split('; ') : [], 185 i = 0, 186 l = cookies.length; 187 188 for (; i < l; i++) { 189 var parts = cookies[i].split('='), 190 name = decode(parts.shift()), 191 cookie = parts.join('='); 192 193 if (key === name) { 194 // If second argument (value) is a function it's a converter... 195 result = read(cookie, value); 196 break; 197 } 198 199 // Prevent storing a cookie that we couldn't decode. 200 if (!key && (cookie = read(cookie)) !== undefined) { 201 result[name] = cookie; 202 } 203 } 204 205 return result; 206 }; 207 208 api.get = api.set = api; 209 api.defaults = {}; 210 211 api.remove = function (key, options) { 212 // Must not alter options, thus extending a fresh object... 213 api(key, '', extend(options, { expires: -1 })); 214 return !api(key); 215 }; 216 217 if ( $ ) { 218 $.cookie = api; 219 $.removeCookie = api.remove; 220 } 221 222 return api; 223})); 224 225 /*! flip - v1.1.2 - 2016-10-20 226* https://github.com/nnattawat/flip 227* Copyright (c) 2016 Nattawat Nonsung; Licensed MIT */ 228 229!function(a){var b=function(){var a,b=document.createElement("fakeelement"),c={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(a in c)if(void 0!==b.style[a])return c[a]},c=function(b,c,d){this.setting={axis:"y",reverse:!1,trigger:"click",
229speed:500,forceHeight:!1,forceWidth:!1,autoSize:!0,front:".front",back:".back"},this.setting=a.extend(this.setting,c),"string"!=typeof c.axis||"x"!==c.axis.toLowerCase()&&"y"!==c.axis.toLowerCase()||(this.setting.axis=c.axis.toLowerCase()),"boolean"==typeof c.reverse&&(this.setting.reverse=c.reverse),"string"==typeof c.trigger&&(this.setting.trigger=c.trigger.toLowerCase());var e=parseInt(c.speed);isNaN(e)||(this.setting.speed=e),"boolean"==typeof c.forceHeight&&(this.setting.forceHeight=c.forceHeight),"boolean"==typeof c.forceWidth&&(this.setting.forceWidth=c.forceWidth),"boolean"==typeof c.autoSize&&(this.setting.autoSize=c.autoSize),("string"==typeof c.front||c.front instanceof a)&&(this.setting.front=c.front),("string"==typeof c.back||c.back instanceof a)&&(this.setting.back=c.back),this.element=b,this.frontElement=this.getFrontElement(),this.backElement=this.getBackElement(),this.isFlipped=!1,this.init(d)};a.extend(c.prototype,{flipDone:function(a){var c=this;c.element.one(b(),function(){c.element.trigger("flip:done"),"function"==typeof a&&a.call(c.element)})},flip:function(a){if(!this.isFlipped){this.isFlipped=!0;var b="rotate"+this.setting.axis;this.frontElement.css({transform:b+(this.setting.reverse?"(-180deg)":"(180deg)"),"z-index":"0"}),this.backElement.css({transform:b+"(0deg)","z-index":"1"}),this.flipDone(a)}},unflip:function(a){if(this.isFlipped){this.isFlipped=!1;var b="rotate"+this.setting.axis;this.frontElement.css({transform:b+"(0deg)","z-index":"1"}),this.backElement.css({transform:b+(this.setting.reverse?"(180deg)":"(-180deg)"),"z-index":"0"}),this.flipDone(a)}},getFrontElement:function(){return this.setting.front instanceof a?this.setting.front:this.element.find(this.setting.front)},getBackElement:function(){return this.setting.back instanceof a?this.setting.back:this.element.find(this.setting.back)},init:function(a){var b=this,c=b.frontElement.add(b.backElement),d="rotate"+b.setting.axis,e=2*b.element["outer"+("rotatex"===d?"Height":"Width")](),f={perspective:e,position:"relative"},g={transform:d+"("+(b.setting.reverse?"180deg":"-180deg")+")","z-index":"0",position:"relative"},h={"backface-visibility":"hidden","transform-style":"preserve-3d",position:"absolute","z-index":"1"};b.setting.forceHeight?c.outerHeight(b.element.height()):b.setting.autoSize&&(h.height="100%"),b.setting.forceWidth?c.outerWidth(b.element.width()):b.setting.autoSize&&(h.width="100%"),(window.chrome||window.Intl&&Intl.v8BreakIterator)&&"CSS"in window&&(f["-webkit-transform-style"]="preserve-3d"),c.css(h).find("*").css({"backface-visibility":"hidden"}),b.element.css(f),b.backElement.css(g),setTimeout(function(){var d=b.setting.speed/1e3||.5;c.css({transition:"all "+d+"s ease-out"}),"function"==typeof a&&a.call(b.element)},20),b.attachEvents()},clickHandler:function(b){b||(b=window.event),this.element.find(a(b.target).closest('button, a, input[type="submit"]')).length||(this.isFlipped?this.unflip():this.flip())},hoverHandler:function(){var b=this;b.element.off("mouseleave.flip"),b.flip(),setTimeout(function(){b.element.on("mouseleave.flip",a.proxy(b.unflip,b)),b.element.is(":hover")||b.unflip()},b.setting.speed+150)},attachEvents:function(){var b=this;"click"===b.setting.trigger?b.element.on(a.fn.tap?"tap.flip":"click.flip",a.proxy(b.clickHandler,b)):"hover"===b.setting.trigger&&(b.element.on("mouseenter.flip",a.proxy(b.hoverHandler,b)),b.element.on("mouseleave.flip",a.proxy(b.unflip,b)))},flipChanged:function(a){this.element.trigger("flip:change"),"function"==typeof a&&a.call(this.element)},changeSettings:function(a,b){var c=this,d=!1;if(void 0!==a.axis&&c.setting.axis!==a.axis.toLowerCase()&&(c.setting.axis=a.axis.toLowerCase(),d=!0),void 0!==a.reverse&&c.setting.reverse!==a.reverse&&(c.setting.reverse=a.reverse,d=!0),d){var e=c.frontElement.add(c.backElement),f=e.css(["transition-property","transition-timing-function","transition-duration","transition-delay"]);e.css({transition:"none"});var g="rotate"+c.setting.axis;c.isFlipped?c.frontElement.css({transform:g+(c.setting.reverse?"(-180deg)":"(180deg)"),"z-index":"0"}):c.backElement.css({transform:g+(c.setting.reverse?"(180deg)":"(-180deg)"),"z-index":"0"}
229),setTimeout(function(){e.css(f),c.flipChanged(b)},0)}else c.flipChanged(b)}}),a.fn.flip=function(b,d){return"function"==typeof b&&(d=b),"string"==typeof b||"boolean"==typeof b?this.each(function(){var c=a(this).data("flip-model");"toggle"===b&&(b=!c.isFlipped),b?c.flip(d):c.unflip(d)}):this.each(function(){if(a(this).data("flip-model")){var e=a(this).data("flip-model");!b||void 0===b.axis&&void 0===b.reverse||e.changeSettings(b,d)}else a(this).data("flip-model",new c(a(this),b||{},d))}),this}}(jQuery); 230 231})($); 232 233// Other plugins 234// ----------------------------------- 235 236loadFingerprint(); 237loadIntlTelInput(); 238loadCloudflareTurnstile(); 239loadCompanyAutocomplete(); 240loadDatepicker(); 241 242loadNeoStyle(); 243 244// Fingerprint 245// ----------------------------------- 246 247function loadFingerprint() { 248 if (typeof Promise !== "undefined") { 249 loadFingerprintJS(); 250 } else { 251 loadPluginScript('https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js', loadFingerprintJS); 252 } 253} 254 255function loadFingerprintJS() { 256 document.neoFingerprintPromise = new Promise(function(resolve, reject) { 257 loadPluginScript("https://app.neocamino.com/assets/fp-cca50fb345eb115ababa5976c74b9f17dede76495cd3cf97672363b22d8788e1.js", resolve, reject); 258 }).then(function() { return FingerprintJS.load() }); 259} 260 261// Phone input 262// ----------------------------------- 263 264function loadIntlTelInput() { 265 document.intlTelInputPromise = new Promise(function(resolve, reject) { 266 loadPluginScript('https://cdn.jsdelivr.net/npm/[email protected]/build/js/intlTelInput.js', resolve, reject); 267 }); 268} 269 270// Company Autocomplete 271// ----------------------------------- 272 273function loadCompanyAutocomplete() { 274 document.companyAutocompletePromise = new Promise(function(resolve, reject) { 275 loadPluginScript("https://app.neocamino.com/assets/company-autocomplete-9db3473d1113cbb038ccda8976c4ba10749c619f7a4a479a539494353dfa813d.js", resolve, reject); 276 }); 277} 278 279// Cloudflare Turnstile 280// ----------------------------------- 281 282function loadCloudflareTurnstile() { 283 document.cloudflareTurnstilePromise = new Promise(function(resolve, reject) { 284 loadPluginScript('https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit', resolve, reject); 285 }); 286} 287 288// Date Picker 289// ----------------------------------- 290 291function loadDatepicker() { 292 document.datepickerPromise = new Promise(function(resolve, reject) { 293 loadPluginScript("https://app.neocamino.com/assets/booking-ba9652e5d7fceac4ced51f712da974a63dff2eb941a8ff1e9f8464ad9e24e683.js", resolve, reject); 294 }); 295 296 document.datepickerPromise.then(function() { 297 // some initialization 298 }); 299} 300 301// Neocamino widget style 302// ----------------------------------- 303 304function loadNeoStyle() { 305 loadPluginStyle("https://app.neocamino.com/assets/pub/widget-fc11b13e5929c79856a5bddb2c60b7b4ff15673b6b599883ba8dea029cf8aa71.css"); 306} 307 308 var CookiesNeo = Cookies.noConflict(); 309 310function cookieName(landingPageId) { 311 return "neo_lp_" + landingPageId; 312} 313 314function getRejectedLandingPageIds() { 315 var websiteCookies = CookiesNeo.get(); 316 return $.map(Object.keys(websiteCookies), function(val, i) { 317 if (val.match(/^neo_lp_/) && websiteCookies[val]) { 318 return val.replace('neo_lp_', ''); 319 } 320 }); 321} 322 323function cookieHideWidget(widget) { 324 var cookieExpires = widget.data('cookie-expires'); 325 if (cookieExpires) { logHideWidgetsInCookies(widget.data('lp'), cookieExpires); } 326} 327 328function logHideWidgetsInCookies(landingPageId, cookieExpires) { 329 CookiesNeo.set(cookieName(landingPageId), true, { 330 expires: cookieExpires, 331 path: '/', 332 sameSite: 'None', 333 secure: true 334 }); 335} 336 337function canShowWidget(landingPageId) { 338 return !CookiesNeo.get(cookieName(landingPageId)); 339} 340 341function isMobile() { 342 return ('ontouchstart' in document.documentElement); 343} 344 345function hookRemoveEvents() { 346 $('.neo-wg-close').click(function(e) { 347 var widget = $(this).closest('.neo-widget'); 348 removeWidget(widget, 'logCookie'); 349 }); 350} 351 352var widgetHideHandler = function(e) { 353 var closeBtn = $(e.target); 354 var widget = closeBtn.closest('.neo-widget'); 355 hideWidget(widget); 356} 357 358function hookHideEvents() { 359 $(document).on('click', '.neo-wg-hide', widgetHideHandler); 360} 361 362function unhookHideEvents() { 363 $(document).off('click', '.neo-wg-hide', widgetHideHandler); 364} 365 366function removeWidget(widget, logCookie) { 367 var logCookie = logCookie || false; 368 widget.remove(); 369 enableScrollingWidgets(); 370 if (logCookie) { cookieHideWidget(widget) }; 371} 372 373 374function hideWidget(widget) { 375 enableScrollingWidgets(); 376 widget.hide(); 377} 378 379function hideOrRemoveWidget(widget, logCookie) { 380 var logCookie = logCookie || false; 381 var hide = widget.hasClass('neo-wg-hideable'); 382 hide ? hideWidget(widget) : removeWidget(widget, logCookie); 383} 384 385function appearAfter(scrollingWidget) { 386 var triggerDistance = scrollingWidget.data('scroll-top'); 387 if (triggerDistance == 'middle') { triggerDistance = middleOfPage(); } 388 return triggerDistance; 389} 390 391function middleOfPage() {
392 var viewportHeight = window.innerHeight || document.documentElement.clientHeight; 393 var documentHeight = document.body.scrollHeight; 394 return documentHeight/2 - viewportHeight/2; 395} 396 397function enableScrollingWidgets() { 398 scrollAnchor().on('scroll wheel mousewheel DOMMouseScroll', displayWidgetBasedOnScroll); 399} 400 401function disableScrollingWidgets() { 402 scrollAnchor().off('scroll wheel mousewheel DOMMouseScroll', displayWidgetBasedOnScroll); 403} 404 405function scrollAnchor() { 406 // Odoo-specific anchor 407 var $anchor = $('body > #wrapwrap'); 408 return ($anchor.length ? $anchor : $(window)); 409} 410 411function displayWidgetBasedOnScroll() { 412 // Sites with body height:100% will scroll the body instead of the window 413 var distanceToTop = $(this).scrollTop() || $('body').scrollTop(); 414 415 $('.neo-wg-scroll').each(function() { 416 var scrollingWidget = $(this); 417 var triggerDistance = appearAfter(scrollingWidget); 418 419 if (distanceToTop > triggerDistance) { 420 scrollingWidget.slideDown(); 421 logWidgetApperance(scrollingWidget); 422 } else { 423 scrollingWidget.slideUp(); 424 } 425 }); 426} 427 428function hideScrollingWidgets() { 429 $('.neo-wg-scroll').each(function() { 430 $(this).slideUp(); 431 }); 432} 433 434function removeScrollingWidgets() { 435 $('.neo-wg-scroll').each(function() { 436 $(this).remove(); 437 }); 438} 439 440function startTimers() { 441 var popupsWithTimer = $('.neo-wg-time'); 442 443 popupsWithTimer.each(function() { 444 var popupWithTimer = $(this); 445 var timeToAppear = getTimerValue(popupWithTimer); 446 447 popupWithTimer.timer = setTimeout(function(){ 448 if (noPopupOpened()) { 449 displayPopup(popupWithTimer); 450 logWidgetApperance(popupWithTimer); 451 }; 452 }, timeToAppear); 453 }); 454} 455 456function getTimerValue(widget) { 457 return 1000 * parseInt(widget.data('timer'), 10); 458} 459 460function stopTimers() { 461 var popupsWithTimer = $('.neo-wg-time'); 462 463 popupsWithTimer.each(function() { 464 clearTimeout(this.timer); 465 }); 466} 467 468$(window).resize(function() { 469 centerPopup(); 470}); 471 472$(document).on('click', '.neo-overlay', function() { 473 var widget = $(this).closest('.neo-widget'); 474 hideOrRemoveWidget(widget); 475 widget.trigger('popup:hide'); 476}); 477 478function centerPopup(popup) { 479 if (popup) { 480 var popupContainer = popup.closest('.neo-popup'); 481 } else { 482 var popupContainer = $('.neo-popup'); 483 popup = popupContainer.find('.neo-wg-popup:visible'); 484 } 485 var topPadding = calculateProperPadding(popup); 486 popupContainer.css('padding-top', topPadding); 487} 488 489function calculateProperPadding(popup) { 490 var windowHeight = window.innerHeight || document.body.clientHeight; 491 var top = (windowHeight - popup.outerHeight()) / 2; 492 topPadding = (top > 0) ? top + 'px' : 0 493 return topPadding; 494} 495 496function getClickPopupLpUuids() { 497 var lpUuids = clickPopupLinks().map(function() { 498 return getLandingPageUuid($(this)); 499 }).get().filter(function(n){ return n != undefined }); 500 return lpUuids; 501} 502 503function clickPopupLinks() { // Link that trigger click-popup display (can be 'raw' links or plain-widget links) 504 return $("a[href*='uuid=']"); 505} 506 507function clickPopupLinksFor(uuid) { 508 return $("a[href*='uuid=" + uuid + "']"); 509} 510 511 512function outOfWidgetClickPopupLinks() { // Only raw links 513 var links = clickPopupLinks(); 514 var linksWithinWidget = $(".neo-widget a[href*='uuid=']"); 515 var outOfWidgetlinks = $.grep(links, function(el) { return $.inArray(el, linksWithinWidget) < 0 }); 516 return outOfWidgetlinks; 517} 518 519var widgetClickOnClickPopupLinksHandler = function(e) { 520 var link = $(e.target).closest('a'); 521 var lpUuid = getLandingPageUuid(link); 522 if (!lpUuid || !isValidUuid(lpUuid)) return; 523 524 var popup = $('#' + lpUuid); 525 if (!popup.length) return; 526 527 e.preventDefault(); 528 e.stopPropagation(); 529 logPopupDisplayFromLink(popup, link); 530 displayPopup(popup); 531} 532 533function hookClickOnClickPopupLinks() { 534 $(document).on('click', "a[href*='uuid=']", widgetClickOnClickPopupLinksHandler); 535} 536 537function unhookClickOnClickPopupLinks() { 538 $(document).off('click', "a[href*='uuid=']", widgetClickOnClickPopupLinksHandler); 539} 540 541function displayPopup(popup) { 542 hideScrollingWidgets(); 543 disableScrollingWidgets(); 544 popup.show(); 545 popup.trigger('popup:show'); 546 547 var popupContent = popupVisibleContent(popup); 548 centerPopup(popupContent); 549 focusInPopup(popupContent); 550} 551 552function focusInPopup(popup) {
553 var firstInput = popup.find('input:visible:first'); 554 firstInput.focus(); 555} 556 557function displayThankPopup(form, successNotice) { 558 var popup = findFormPopup(form); 559 if (successNotice) {displaySuccessNotice(popup, successNotice);} 560 removeScrollingWidgets(); 561 flipCard(popup); 562 563 if (popup.is(':visible')) { 564 centerPopup(popupVisibleContent(popup)); 565 } else { 566 displayPopup(popup); 567 } 568} 569 570function findFormPopup(form) { 571 var formWidget = form.closest('.neo-widget'); 572 if (!formWidget.hasClass('neo-popup')) { 573 var lpUuid = form.data('lpUuid'); 574 var popup = $('#' + lpUuid); 575 return popup; 576 } else { 577 return formWidget; 578 } 579} 580 581function removeAlert(form) { 582 var alertContainer = form.find('.neo-alert-container'); 583 alertContainer.html(''); 584} 585 586function noPopupOpened() { 587 var openedPopups = 588 $('.neo-popup, #contact-modal').filter(function () { 589 return $(this).css('display') !== 'none'; 590 }); 591 return openedPopups.length === 0; 592} 593 594function logPopupDisplayFromLink(popup, link) { 595 var neoMedium = getNeoMedium(link) || 'click_popup'; 596 setPopupNeoMedium(popup, neoMedium); 597 logAnalyticsEventForWidget('widget-click', popup) // use popup original label, and category updated in setPopupNeoMedium 598} 599 600function setPopupNeoMedium(popup, neoMedium) { 601 if (neoMedium) { 602 popup.find('#lp_neo_medium').val(neoMedium); 603 popup.attr('data-ga-category', neoMedium); 604 } 605} 606 607function getNeoMedium(link) { 608 return getParamValueFromLink('neo_medium', link); 609} 610 611function getLandingPageUuid(link) { 612 return getParamValueFromLink('uuid', link); 613} 614 615function getParamValueFromLink(param, link) { 616 var url = link.attr('href'); 617 return getParameterByName(param, url); 618} 619 620function getParameterByName(name, url) { 621 if (!url) url = window.location.href; 622 name = name.replace(/[\[\]]/g, "\\$&"); 623 var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 624 results = regex.exec(url); 625 if (!results) return null; 626 if (!results[2]) return ''; 627 return decodeURIComponent(results[2].replace(/\+/g, " ")); 628} 629 630function isValidUuid(uuid) { 631 return /^[a-z0-9-]+$/i.test(uuid); 632} 633 634function hookFlipEvents() { 635 $('.neo-flip-card').flip({trigger: 'manual', front: '.neo-flip-front', back: '.neo-flip-back'}); 636} 637 638function flipCard(popup) { 639 var flipCard = popup.find('.neo-flip-card'); 640 641 flipCard.find('.neo-flip-back').show(0, function() { 642 flipCard.find('.neo-flip-front').hide(); 643 flipCard.flip(true); 644 }); 645} 646 647function popupVisibleContent(popup) { 648 var popupFront = popup.find('.neo-flip-front'); 649 var popupBack = popup.find('.neo-flip-back'); 650 651 var visibleSide = (popupFront.is(':visible')) ? popupFront : popupBack; 652 653 return visibleSide.find('.neo-wg-popup'); 654} 655 656function displaySuccessNotice(popup, successNotice) { 657 popup.find('.neo-success-notice').removeClass('hidden').html(successNotice); 658} 659 660function hookBookingPopups() { 661 document.datepickerPromise.then( 662 function() { 663 // For widget exposed as a page 664 $(document).ready(loadBookingPage); 665 666 $(document).on('popup:show', '.neo-booking-popup', loadBooking); 667 } 668 ); 669} 670 671function unhookBookingPopups() { 672 $(document).off('popup:show', '.neo-booking-popup', loadBooking); 673} 674 675function getFormWidgetLpUuids() { 676 var lpUuids = $(".neo-form-widget").map(function() { 677 return ($(this).data('lp')); 678 }).get().filter(function(n){ return n != undefined }); 679 return lpUuids; 680} 681 682// Function necessary for pushing the event to the datalayer as recommended by Google 683// https://support.google.com/analytics/answer/10271001?hl=fr#zippy=%2Cau-sommaire-de-cet-article 684 685if (!gtagIdLoaded() && (gtagId = getGtagId())) { 686 neoGtag('js', new Date()); 687 neoGtag('config', gtagId); 688} 689 690function gtagIdLoaded() { 691 if (!window.dataLayer) return; 692 693 var configElement = window.dataLayer.find(function(element) { 694 return element[0] == 'config'; 695 }); 696 697 if (configElement) return true; 698} 699 700function getGtagId() { 701 var scriptTag = document.querySelector('script[src*="googletagmanager.com/gtag"]'); 702 703 if (scriptTag == null) return; 704 705 var url = scriptTag.src; 706 var match = url.match(/id=([A-Z0-9-]+)/i); 707 708 if (match && match[1]) { 709 return match[1]; 710 } 711} 712 713function neoGtag() { 714 dataLayer = window.dataLayer || []; 715 dataLayer.push(arguments); 716} 717 718// action: 'display', 'submit_error', 'submit_success' + 'widget-click' for click popup 719// When a btn linked to a click_popup appears on page, 720// we log action: 'display', forceCategory: 'click_popup_link' 721function logAnalyticsEventForWidget(action, widget, forceCategory) { 722 var category = forceCategory || analyticsEventCategory(widget); 723 var label = analyticsEventLabel(widget); 724 var value = eventNeedsValue(action, category) ? analyticsEventValue(widget) : null; 725 var nonInteraction = action == 'display'; 726 logAnalyticsEvent(action, category, label, value, nonInteraction); 727 logDataLayerEvent(action, category, label); 728} 729 730// Method used to send neo events to GTM for custom logic 731// Arguments: 732// - action: display, submit_error, submit_success, widget-click 733// - category: click_popup, banner, scrollbox, show_popup_exit_intent, side_widget 734// - label: name of the landing page 735function logDataLayerEvent(action, category, label) {
736 var neoAction = 'neo.' + action; 737 neoGtag('event', neoAction, { formLocation: category, formName: label }); 738} 739 740function logAnalyticsEvent(action, category, label, value, nonInteraction) { 741 if (!(action && action.length && category && category.length && label && label.length)) { 742 return; 743 } 744 745 neoGtag('event', action, { 746 event_category: category, 747 event_label: label, 748 value: value, 749 non_interaction: nonInteraction, 750 }); 751} 752 753function analyticsEventCategory(widget) { 754 return $(widget).attr('data-ga-category'); 755} 756 757function analyticsEventLabel(widget) { 758 return $(widget).attr('data-ga-label'); 759} 760 761function analyticsEventValue(widget) { 762 var eventValue = 763 $(widget).attr('data-ga-value') || 764 $(widget) 765 .find('form') 766 .attr('data-ga-value'); 767 return eventValue && Number(eventValue); 768} 769 770function eventNeedsValue(action, category) { 771 if (action != 'display') { 772 return true; 773 } 774 return ['popup', 'form_widget'].includes(category); 775} 776 777function phoneValid(form) { 778 const inputs = $(form).find('input[type=tel]'); 779 780 for (var i=0; i < inputs.length; i++) { 781 if (inputs[i].value === "" || inputs[i].value === null) { 782 continue; 783 } else { 784 var iti = intlTelInput.getInstance(inputs[i]); 785 if (!iti.isValidNumber()) return false; 786 } 787 } 788 return true; 789} 790 791function hookPhoneInput() { 792 document.intlTelInputPromise.then( 793 function() { 794 const inputs = $("form.simple_form.lp input[type=tel]"); 795 796 for (var i=0; i < inputs.length; i++) { 797 var existing = intlTelInput.getInstance(inputs[i]); 798 if(existing) { continue; } 799 800 window.intlTelInput(inputs[i], { 801 initialCountry: "FR", 802 separateDialCode: true, 803 loadUtilsOnInit: "https://cdn.jsdelivr.net/npm/[email protected]/build/js/utils.js", 804 }); 805 }; 806 } 807 ) 808} 809
810function invalidPhoneAlert() { 811 return "Le numéro de téléphone n'est pas valide"; 812} 813 814function setIntlPhoneFormat(form) { 815 const inputs = $(form).find('input[type=tel]'); 816 817 for (var i=0; i < inputs.length; i++) { 818 var iti = intlTelInput.getInstance(inputs[i]); 819 inputs[i].value = iti.getNumber(); 820 } 821} 822 823function hookCompanyAutocomplete() { 824 if(!document.querySelectorAll('[data-neo-autocomplete-url]').length){ return; } 825 826 document.companyAutocompletePromise.then(function(){ 827 loadAutocompleteInputs(); 828 }); 829} 830 831function hookCloudflareTurnstile() { 832 if (window.turnstile) { 833 var turnstileContainers = document.querySelectorAll('.cf-turnstile'); 834 turnstileContainers.forEach((container) => { 835 if (container.innerHTML.trim() === '') { 836 turnstile.render(container); 837 } 838 }); 839 } 840} 841 842function getInPageWidgetLpIds() { 843 var lpUuids = $("[class*='neo-wg-lp-side-preview-link']").map(function() { 844 var classesString = $(this).attr('class'); 845 return getWidgetLpId(classesString); 846 }).get().filter(function(n){ return n != undefined }); 847 return lpUuids; 848} 849 850function getWidgetLpId(classesString) { 851 if (!classesString) return; 852 853 classes = classesString || ''; 854 var regexp = /neo-wg-lp-side-preview-link-(\d*)(?:$|\s+)/; 855 var match = classes.match(regexp); 856 if (!match) return; 857 858 return match[1]; 859} 860 861function renderWidgets(widgets) { 862 if (widgets && widgets.length > 0) { 863 $widgets = $.map(widgets, function(widget, index){ 864 $widget = appendWidget(widget.html, widget.container_class ); 865 866 updateClickPopupHref(widget); 867 logInpageWidgetAppearance(widget); 868 869 return $widget; 870 }).filter(function(){ return this != undefined }); 871 872 $widgets = $($widgets); 873 874 logClickPopupLinksAppearance(); 875 } 876} 877 878function updateClickPopupHref(widget) { 879 if ((widget.code !== 'click_popup') && (widget.code !== 'booking_popup')) return; 880 881 clickPopupLinksFor(widget.uuid).each(function() { 882 url = $(this).attr('href'); 883 if (url.startsWith('//')) { 884 url = "http:" + url; 885 } 886 parser = new URL(url); 887 widgetUrl = widget.url + parser.search; 888 $(this).attr('href', widgetUrl); 889 }); 890} 891 892function appendWidget(widget, containerClass) { 893 if (!widget) return; 894 895 container = containerClass ? $('.' + containerClass) : $('body'); 896 897 $widget = $(widget); 898 $widget.appendTo(container); 899 900 return $widget; 901} 902 903// Every hook added here must be unhooked in the cleanWidgets function. 904// This allows removing the widgets properly on a client site. 905function activateWidgets() { 906 startTimers(); 907 enableScrollingWidgets(); 908 hookRemoveEvents(); 909 hookHideEvents(); 910 hookClickOnClickPopupLinks(); 911 hookSubmitWidgetForm(); 912 hookFlipEvents(); 913 hookExitIntentEvent(); 914 915 // The plugins below use JS Promise instead of binded event handlers, no need to unhook 916 hookPhoneInput(); 917 hookCompanyAutocomplete(); 918 hookBookingPopups(); 919 hookCloudflareTurnstile(); 920} 921 922function cleanWidgets() { 923 stopTimers(); 924 disableScrollingWidgets(); 925 unhookHideEvents(); 926 unhookClickOnClickPopupLinks(); 927 unhookSubmitWidgetForm(); 928 unhookExitIntentEvent(); 929 unhookBookingPopups(); 930 931 $widgets.each(function(){ 932 this.remove(); 933 }); 934 935 SetNeoUnloaded(scriptId); 936} 937 938var widgetSubmitTarget = '.neo-widget input[type=submit], .js-landing-page-form input[type=submit]'; 939 940function hookSubmitWidgetForm() { 941 attachHandlerUnlessPresent(document, 'click', widgetSubmitTarget, hookSubmission); 942} 943 944function unhookSubmitWidgetForm() { 945 $(document).on('click', widgetSubmitTarget, function(e) { e.preventDefault(); }); 946 $(document).off('click', widgetSubmitTarget, hookSubmission); 947} 948 949function hookSubmission(event) { 950 var submitBtn = $(event.target); 951 var form = submitBtn.closest('form'); 952 var submitBtnText = submitBtn.val(); 953 var submitBtnDisableWith = submitBtn.data('disableWith'); 954 event.preventDefault(); 955 if (phoneValid(form)) { 956 submitBtn.val(submitBtnDisableWith); 957 setIntlPhoneFormat(form); 958 submitWidgetForm(form, submitBtnText); 959 } else { 960 const alert = "<div class='neo-alert neo-alert-danger js-alert'>" + invalidPhoneAlert() + "</div>"; 961 962 displayAlertInForm(form, alert) 963 } 964} 965 966function submitWidgetForm(form, submitBtnText) { 967 var submitUrl = form.data('submit-url') || form.attr('action'); 968 var params = new FormData(form[0]); 969 970 document.neoFingerprintPromise 971 .then(function(fingerprint) { return fingerprint.get(); }) 972 .then(function(fingerprintResult) { 973 params.set('lp[visitorAnonymousFingerprint]', fingerprintResult.visitorId); 974 975 unhookSubmitWidgetForm(); 976 977 $.ajax({ 978 url: submitUrl, 979 data: params, 980 type: 'POST', 981 headers: { 982 Accept: "application/json" 983 }, 984 dataType: 'json', 985 processData: false, 986 contentType: false, 987 cache: false, 988 success: function(data) { handleSubmitResponse(form, submitBtnText, data); } 989 }); 990 }); 991} 992 993function handleSubmitResponse(form, submitBtnText, submitResponseData) { 994 var widget = $(form).find(':submit').closest('.neo-widget, [data-ga-label]'); 995 var data = submitResponseData; 996 997 if (data.errors) { 998 var alert = data.errors; 999 displayAlertInForm(form, alert); 1000 activateSubmitBtn(form, submitBtnText); 1001 logAnalyticsEventForWidget('submit_error', widget); 1002 } else { 1003 var landingPageId = data.landing_page_id; 1004 var successNotice = data.success_notice; 1005 var redirectUrl = data.redirect_url; 1006 removeAlert(form); 1007 logLandingPageSubscription(landingPageId); 1008 logAnalyticsEventForWidget('submit_success', widget); 1009 1010 var facebookPixelCode = data.facebook_pixel_code; 1011 if (facebookPixelCode) pushFacebookConversionEvent(); 1012 1013 if (redirectUrl) { 1014 location.replace(redirectUrl); 1015 } else { 1016 displayThankPopup(form, successNotice); 1017 } 1018 } 1019 hookSubmitWidgetForm(); 1020} 1021 1022function pushFacebookConversionEvent() { 1023 if (typeof fbq !== 'undefined') { 1024 fbq('track', 'Lead'); 1025 } 1026} 1027 1028function displayAlertInForm(form, alert) { 1029 var alertContainer = form.closest('.neo-widget').find('.neo-alert-container'); 1030 if (alertContainer.length > 0) { 1031 alertContainer.html(alert); 1032 centerPopup(); 1033 } else { 1034 $('.js-alert').remove(); 1035 form.prepend(alert); 1036 } 1037} 1038 1039function activateSubmitBtn(form, submitBtnText) { 1040 var submitBtn = form.find('input[type=submit]'); 1041 submitBtn.val(submitBtnText); 1042} 1043 1044function logLandingPageSubscription(landingPageId) { 1045 logHideWidgetsInCookies(landingPageId, 365); 1046} 1047 1048function logWidgetApperance(widget) { 1049 if (widget.attr('data-appeared')) { return }; 1050 logAnalyticsEventForWidget('display', widget); 1051 widget.attr('data-appeared', true); 1052} 1053 1054function logInpageWidgetAppearance(widgetData) {
1055 var widgetContainerClass = widgetData.container_class 1056 if (!widgetContainerClass) { return }; 1057 var widget = $('.' + widgetContainerClass); 1058 logAnalyticsEventForWidget('display', widget); 1059} 1060 1061function logClickPopupLinksAppearance() { 1062 $.each(outOfWidgetClickPopupLinks(), function(index, link){ 1063 var clickPopupId = getLandingPageUuid($(link)); 1064 var popup = $('#' + clickPopupId); 1065 logAnalyticsEventForWidget('display', popup, 'click_popup_link'); 1066 }); 1067} 1068 1069// Event handler helpers 1070 1071function attachHandlerUnlessPresent(element, event, selector, handler) { 1072 if (handlerAlreadyHooked(element, event, selector, handler)) return false; 1073 1074 $(element).on(event, selector, handler); 1075} 1076 1077function handlerAlreadyHooked(element, event, selector, handler) { 1078 var allHandlers = $._data(element, 'events'); 1079 if (allHandlers === undefined) return false; 1080 1081 var eventHandlers = allHandlers[event]; 1082 if (eventHandlers === undefined) return false; 1083 1084 var selectorHandler = eventHandlers.filter(function(e) { 1085 return e.selector == selector && e.handler == handler; 1086 }); 1087 return selectorHandler.length > 0; 1088} 1089 1090function hookExitIntentEvent() { 1091 var timeBeforeExitIntentDetection = 3000; 1092 var popups = $('#neo-popup'); 1093 1094 popups.each(function() { 1095 var popup = $(this); 1096 1097 popup.intentTimer = setTimeout(function() { 1098 hookExitIntentOnLeave(popup); 1099 }, timeBeforeExitIntentDetection); 1100 }); 1101} 1102 1103function unhookExitIntentEvent() { 1104 var popups = $('#neo-popup'); 1105 1106 popups.each(function() { 1107 clearTimeout(this.intentTimer); 1108 }); 1109} 1110 1111function hookExitIntentOnLeave(popup) { 1112 $(document).on('mouseout', function(event) { 1113 var noPointerTarget = (event.toElement === undefined || event.toElement === null) && event.relatedTarget === null; 1114 1115 if(noPointerTarget && noPopupOpened()) { 1116 logPopupDisplayFromExitIntent(popup); 1117 displayPopup(popup); 1118 1119 $(event.currentTarget).off('mouseout'); 1120 } 1121 }); 1122} 1123 1124function logPopupDisplayFromExitIntent(popup) { 1125 var neoMedium = 'show_popup_exit_intent'; 1126 1127 setPopupNeoMedium(popup, neoMedium); 1128 logAnalyticsEventForWidget('display', popup); 1129} 1130 1131if ($('.js-landing-page-form').length > 0) { 1132 hookSubmitWidgetForm(); 1133} 1134 1135 1136var jsonp_url = "https://app.neocamino.com/api.json?id=8rjx1wfc"; 1137 1138var fetchWidgets = function() { 1139 var inPageWidgetLpIds = getInPageWidgetLpIds(); 1140 var clickPopupLpUuids = getClickPopupLpUuids(); 1141 var formWidgetLpUuids = getFormWidgetLpUuids(); 1142 var rejectedLandingPageIds = getRejectedLandingPageIds(); 1143 1144 document.neoFingerprintPromise 1145 .then(function(fingerprint) { return fingerprint.get(); }) 1146 .then(function(fingerprintResult) { 1147 var hiddenFingerprintInput = document.querySelector("#lp_visitorAnonymousFingerprint"); 1148 if (hiddenFingerprintInput) { 1149 hiddenFingerprintInput.value = fingerprintResult.visitorId; 1150 } 1151 1152 var params = { 1153 url: document.URL, 1154 referrer: document.referrer, 1155 fingerprint: fingerprintResult.visitorId, 1156 mobile: isMobile(), 1157 click_popup_lp_uuids: clickPopupLpUuids, 1158 form_widget_lp_uuids: formWidgetLpUuids, 1159 widget_lp_ids: inPageWidgetLpIds, 1160 rejected_lp_ids: rejectedLandingPageIds 1161 }; 1162 1163 // Send current URL to retrieve the widgets to show on that page 1164 // NB: no error callback in cross-domain JSONP request, so the 1165 // error handling is done in the success callback. 1166 1167 $.ajax({ 1168 url: jsonp_url, 1169 cache: false, 1170 data: params, 1171 dataType: "jsonp", 1172 success: function(data) { 1173 if (data.errors) { 1174 console.log(data.errors); 1175 } else { 1176 renderWidgets(data.widgets); 1177 } 1178 // Activate widgets even if no widgets returned. 1179 // It's used on landing pages where widgets are already on page, not injected. 1180 activateWidgets(); 1181 }, 1182 }); 1183 }); 1184} 1185 1186// Respond to MessageEvent bus 1187var onMessageResetWidgets = function(e) { 1188 var data = e.data; 1189 1190 if (data.ev == 'neocamino:reload' && data.url != window._neoOriginUrl) 1191 { 1192 window._neoOriginUrl = data.url; 1193 cleanWidgets(); 1194 1195 setTimeout(fetchWidgets, 1000); 1196 } 1197} 1198 1199var onPageUnloadCleanWidgets = cleanWidgets; 1200var onPageLoadFetchWidgets = fetchWidgets; 1201
1202window.addEventListener("mercury:unload", onPageUnloadCleanWidgets); 1203window.addEventListener("mercury:load", onPageLoadFetchWidgets); 1204window.addEventListener("message", onMessageResetWidgets); 1205 1206fetchWidgets(); 1207 1208 1209 }); 1210 } 1211 } 1212})();
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.