PageSourceSearch

https://app.neocamino.com/api?id=8rjx1wfc

js neocamino.com collected 2026-10-03 19:32:03 UTC 38,399 bytes, 1,212 lines download raw bytes

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.