1(function(f){f.fn.jqm=function(n){var p=f.extend({},f.jqm.params,n);return this.each(function(){var q=f(this),o=f(this).data("jqm");if(!o){o={ID:j++}}q.data("jqm",f.extend(p,o)).addClass("jqm-init");p.trigger&&f(this).jqmAddTrigger(p.trigger)})};f.fn.jqmAddTrigger=function(n){return this.each(function(){if(!a(f(this),"jqmShow",n)){d("jqmAddTrigger must be called on initialized modals")}})};f.fn.jqmAddClose=function(n){return this.each(function(){if(!a(f(this),"jqmHide",n)){d("jqmAddClose must be called on initialized modals")}})};f.fn.jqmShow=function(n){return this.each(function(){!this._jqmShown&&k(f(this),n)})};f.fn.jqmHide=function(n){return this.each(function(){this._jqmShown&&h(f(this),n)})};var d=function(n){if(window.console&&window.console.error){window.console.error(n)}},k=function(u,q){var x=u.data("jqm"),q=q||window.event,w=(parseInt(u.css("z-index"))),w=(w>0)?w:3000,n=f("<div></div>").addClass(x.overlayClass).css({height:"100%",width:"100%",position:"fixed",left:0,top:0,"z-index":w-1,opacity:x.overlay/100}),r={w:u,c:x,o:n,t:q};u.css("z-index",w);if(x.ajax){var s=x.target||u,p=x.ajax;s=(typeof s=="string")?f(s,u):f(s);if(p.substr(0,1)=="@"){p=f(q).attr(p.substring(1))}s.html(x.ajaxText).load(p,function(){x.onLoad&&x.onLoad.call(this,r);g(r)})}else{g(r)}},h=function(q,n){var r=q.data("jqm"),n=n||window.event,p={w:q,c:r,o:q.data("jqmv"),t:n};m(p)},i=function(n){if(n.c.overlay>0){n.o.prependTo("body")}n.w.show();f.jqm.focusFunc(n.w);return true},e=function(n){n.w.hide()&&n.o&&n.o.remove();return true},a=function(p,o,n){if(!p.data("jqm")){return false}return f(n).each(function(){this[o]=this[o]||[];this[o].push(p)}).click(function(){var q=this;f.each(this[o],function(r,s){s[o](q)});return false})},g=function(p){var q=p.w,n=p.o,r=p.c;if(r.onShow(p)!==false){q[0]._jqmShown=true;if(r.modal){!b[0]&&l("bind");b.push(q)}else{q.jqmAddClose(n)}r.closeClass&&q.jqmAddClose(f("."+r.closeClass,q));r.toTop&&n&&q.before('<span id="jqmP'+r.ID+'"></span>').insertAfter(n);q.data("jqmv",n)}},m=function(p){var q=p.w,n=p.o,r=p.c;if(r.onHide(p)!==false){q[0]._jqmShown=false;if(r.modal){b.pop();!b[0]&&l("unbind")}r.toTop&&n&&f("#jqmP"+r.ID).after(q).remove()}},l=function(n){f(document)[n]("keypress keydown mousedown",c)},c=function(p){var o=f(p.target).data("jqm")||f(p.target).parents(".jqm-init:first").data("jqm"),n=b[b.length-1].data("jqm");if(o&&o.ID==n.ID){return true}return f.jqm.focusFunc(n)},j=0,b=[];f.jqm={params:{overlay:50,overlayClass:"jqmOverlay",closeClass:"jqmClose",trigger:".jqModal",ajax:false,target:false,ajaxText:"",modal:false,toTop:false,onShow:i,onHide:e,onLoad:false},focusFunc:function(n){f(":input:visible:first",n).focus();return false}}})(jQuery); 2 3 4$(function () { 5 6 // Check Source 7 checkAffSource(); 8 9 $('.banner_itinerary_section_right .itinerary_text').attr("href", getItineraryPage()); 10 $('li.searchstatus a').attr("href", getItineraryPage()); 11 // Change cart status and add tickets. 12 if (isRerail()) { 13 checkRerailCartStatus(); 14 } else { 15 checkCartStatus(); 16 } 17 18 setTimeout(function(){ 19 $('.ui-dialog.webform-confirmation-modal .ui-dialog-content').html( getThankYouMessageDesktop() ); 20 $('.ui-dialog.webform-confirmation-modal').css('width', '722px'); 21 }, 10); 22 23 if (!docCookies.hasItem('ioc')) { 24 $('.jsIOC').slideDown(); 25 $('.jsIOCFooterPadding').slideDown(); 26 } 27 28 $('.jsCloseIOC').on('click', function () { 29 $('.jsIOC').slideUp(); 30 $('.jsIOCFooterPadding').slideUp(); 31 docCookies.setItem('ioc', 'viewed', Infinity); 32 }); 33}); 34 35$(document).ready(function() { 36 37 var pathname = window.location.pathname; 38 if (pathname != '/') { 39 setTimeout(function() { 40 if (!docCookies.hasItem('italiapass_user')) { 41 // Cookie is not set. Let's open the modal. 42 43 if ($('article.popular-routes-new.full').length > 0) { 44 $('#italia-pass-search-popup .cityFrom').replaceWith('<span class="green cityFrom">' + $('article.popular-routes-new.full').attr('data-departure-station-name') + '</span>'); 45 $('#italia-pass-search-popup .cityTo').replaceWith('<span class="green cityTo">' + $('article.popular-routes-new.full').attr('data-destination-station-name') + '</span>'); 46 $('#italia-pass-search-popup').dialog('open'); 47 } 48 else { 49 $('#italia-pass-search-popup .cities').replaceWith('<span class="cities">Italy</span>'); 50 $('#italia-pass-search-popup').dialog('open'); 51 } 52 } 53 }, 2000); 54 } 55 var firstStationId = drupalSettings.italiarail.articleCity; 56 57 if (firstStationId) { 58 $('#faresearch_route-destination').val(firstStationId); 59 } 60}); 61 62 63function getThankYouMessageDesktop() { 64 65 return '<div class="ir-thank-you-modal">' 66 + '<div class="image"></div>' 67 + '<div class="text">' 68 + '<div class="message-title">Thank You</div>' 69 + '<div class="message">We received your request. A customer service agent will be contacting you shortly.</div>' 70 + '<a href="/" class="btn btn-primary back-home">Return to Homepage</a>' 71 + '</div>' 72 + '</div>'; 73} 74 75function searchLower(){ 76 document.sitesearch.keys.value = document.sitesearch.keys.value.toLowerCase(); 77 return true; 78} 79 80/** 81* Aff_Source cookie create before any call to portal 82*/ 83function checkAffSource(){ 84 if (utm_source = getQueryString('utm_source')) { 85 let source = utm_source; 86 let medium = getQueryString('utm_medium'); 87 docCookies.setItem('aff_source', source+'-'+medium,'','/','.italiarail.com'); 88 } 89} 90 91 92// Check and update cart status on the current app Environment. 93function checkCartStatus() { 94 var app_env = ''; 95 if (drupalSettings.irenv) { 96 if (drupalSettings.irenv == 'stage') { 97 app_env = '-stage'; 98 } 99 if (drupalSettings.irenv == 'dev') { 100 app_env = '-dev'; 101 } 102 } 103 var app_url = 'https://app' + app_env + '.italiarail.com'; 104 105 $.ajax({ 106 url: app_url + '/api/itinerary?aff='+docCookies.getItem('aff'), 107 type: 'GET', 108 xhrFields: { 109 withCredentials: true 110 }, 111 success: function (response) { 112 var itinerary = response.itinerary.length; 113 var products = response.products.length; 114 var tickets = itinerary+products; 115 updateCartIcon(tickets); 116 } 117 }); 118} 119 120function updateCartIcon(count) { 121 if (count) { 122 console.log(count); 123 $('.banner_itinerary_section_right.searchstatus').addClass('withTickets'); 124 $('.banner_itinerary_section_right .itinerary_text').text(Drupal.t('Cart') + ' (' + count + ')'); 125 $('.searchstatus a').text('Cart (' + count + ')'); 126 } else { 127 $('.banner_itinerary_section_right.searchstatus').removeClass('withTickets'); 128 $('.banner_itinerary_section_right .itinerary_text').text(Drupal.t('Cart')); 129 $('li.searchstatus a').text('View Cart'); 130 } 131} 132 133$(function () { 134 $('.breadcrumb').addClass('container'); 135}); 136 137 138function setTrip(from, fromId, to, toId, show_date) { 139 if(show_date === undefined) 140 show_date = true; 141 $("#faresearch_route-origin").val(from); 142 $("#faresearch_route-origin_id").val(fromId); 143 144 $("#faresearch_route-destination").val(to); 145 $("#faresearch_route-destination_id").val(toId); 146 147 // then show the calendar 148 if(show_date){ 149 $('#faresearch_route-departure_date').datepicker('show'); 150 } 151} 152 153function switchOriginDestination() { 154 var from = $("#faresearch_route-origin").val(); 155 var fromId = $("#faresearch_route-origin_id").val(); 156 var to = $("#faresearch_route-destination").val(); 157 var toId = $("#faresearch_route-destination_id").val(); 158 159 $("#faresearch_route-origin").val(to); 160 $("#faresearch_route-origin_id").val(toId); 161 162 $("#faresearch_route-destination").val(from); 163 $("#faresearch_route-destination_id").val(fromId); 164} 165 166var currencySymbol = { 167 'TRE': 'US$', 'ITC': 'C$', 'ITE': 'â¬', 'ITR': '£', 'TRA': 'A$' 168}; 169 170var affCurrency = { 171 TRE: 'USD', 172 ITC: 'CAD', 173 ITR: 'GBP', 174 ITE: 'EUR', 175 TRA: 'AUD' 176}; 177 178var cityToCity = { 179 'rome_to_florence_price': {'TRE': '35', 'ITC': '28', 'ITE': '19', 'ITR': '16', 'TRA': '28'}, 180 'rome_to_venice_price': {'TRE': '18', 'ITC': '43', 'ITE': '29', 'ITR': '25', 'TRA': '43'}, 181 'rome_to_naples_price': {'TRE': '26', 'ITC': '28', 'ITE': '19', 'ITR': '16', 'TRA': '28'}, 182 'rome_to_milan_price': {'TRE': '40', 'ITC': '43', 'ITE': '29', 'ITR': '25', 'TRA': '43'}, 183 'florence_to_rome_price': {'TRE': '26', 'ITC': '28', 'ITE': '19', 'ITR': '16', 'TRA': '28'}, 184 'florence_to_venice_price': {'TRE': '26', 'ITC': '28', 'ITE': '19', 'ITR': '16', 'TRA': '28'}, 185 'florence_to_milan_price': {'TRE': '26', 'ITC': '28', 'ITE': '19', 'ITR': '16', 'TRA': '28'}, 186 'florence_to_pisa_price': {'TRE': '11', 'ITC': '12', 'ITE': '8', 'ITR': '7', 'TRA': '12'}, 187 'venice_to_rome_price': {'TRE': '11', 'ITC': '43', 'ITE': '29', 'ITR': '25', 'TRA': '43'}, 188 'venice_to_florence_price': {'TRE': '26', 'ITC': '28', 'ITE': '19', 'ITR': '16', 'TRA': '28'}, 189 'venice_to_milan_price': {'TRE': '13', 'ITC': '14', 'ITE': '9', 'ITR': '14', 'TRA': '14'}, 190 'venice_to_pisa_price': {'TRE': '37', 'ITC': '40', 'ITE': '27', 'ITR': '40', 'TRA': '40'}, 191 'milan_to_venice_price': {'TRE': '13', 'ITC': '14', 'ITE': '9', 'ITR': '14', 'TRA': '14'}, 192 'milan_to_rome_price': {'TRE': '40', 'ITC': '43', 'ITE': '29', 'ITR': '43', 'TRA': '43'}, 193 'milan_to_florence_price': {'TRE': '26', 'ITC': '28', 'ITE': '19', 'ITR': '28', 'TRA': '28'}, 194 'milan_to_genoa_price': {'TRE': '13', 'ITC': '14', 'ITE': '9', 'ITR': '14', 'TRA': '14'} 195}; 196 197var affiliateMapping = { 198 'S6': {'TRE': 'S61', 'ITC': '', 'ITE': 'S61U', 'ITR': 'S61P', 'TRA': ''}, 199 'IP': {'TRE': 'IPTRE', 'ITC': 'IPITC', 'ITE': 'IPITE', 'ITR': 'IPITR', 'TRA': 'IPTRA'}, 200 'EM': {'TRE': '', 'ITC': '', 'ITE': 'EMI', 'ITR': '', 'TRA': ''}, 201 'FA': {'TRE': 'FAN', 'ITC': '', 'ITE': '', 'ITR': '', 'TRA': ''}, 202 'BN': {'TRE': 'BNUS', 'ITC': '', 'ITE': '', 'ITR': '', 'TRA': ''} 203}; 204 205function getPrices(country, cities) { 206 "use strict"; 207 208 if (currencySymbol.hasOwnProperty(country) && cityToCity.hasOwnProperty(cities)) { 209 return currencySymbol[country] + cityToCity[cities][country]; 210 } else { 211 return '--'; 212 } 213} 214 215function getPassengersTotal() { 216 var tmp_cnt = parseInt($('#faresearch_passengers-child').val()) + 217 parseInt($('#faresearch_passengers-adult').val()); 218 return tmp_cnt; 219} 220 221function getTravelersTitle($passengers) { 222 if ($passengers == 1) return Drupal.t('Traveler'); 223 224 else return Drupal.t('Travelers'); 225} 226 227function openGroupsModal() { 228 if (!$('#groupsmodal').hasClass('popped')) { 229 $('#groupsmodal').addClass('popped'); 230 $('.ir-bookingwidget').addClass('groups-modal-popped'); 231 popup('groupsmodal',!blanket()); 232 } 233} 234 235 236var COMPANY_FOR_AFFILIATE = { 237 'TRE': 'International Rail, LLC', 238 'ITC': 'ItaliaRail.ca Travel Services LTD', 239 'ITR': 'ItaliaRail Limited', 240 'ITE': 'ItaliaRail Limited', 241 'TRA': 'International Rail, LLC' 242}; 243function setCompany(affiliate) { 244 if (affiliate === undefined) { 245 return; 246 } 247 var companyElem = $('#footer').find('div.footer_copy span.company'); 248 var name = COMPANY_FOR_AFFILIATE[affiliate]; 249 if (name === undefined) { 250 return; 251 } 252 companyElem.text(name); 253} 254function setPassholderPref() { 255 if (docCookies.hasItem('include_passholder') && docCookies.getItem('include_passholder')
255=== 'true') { 256 $('#faresearch_passholder').click(); 257 $('#passholder_option').removeClass('hidden'); 258 $('#passholder_toggle').remove(); 259 } 260} 261function saveAffiliateInfo() { 262 var curr = getParameterByName('aff'); 263 var affiliate_id; 264 // TODO: If we get a new affiliate we get the associated currency and the affiliate code. 265 266 // If we are coming from an affiliate we persist it for future link we also set the currency for this 267 // Affiliate. We are from an affiliate when not from a new currency 268 if ((curr) && 269 (curr !== "") && 270 (curr !== "TRE") && 271 (curr !== "ITC") && 272 (curr !== "ITR") && 273 (curr !== "ITE") && 274 (curr !== "TRA") ) { 275 // We set it for one month 276 var mytime = new Date(); 277 mytime.setTime(mytime.getTime()+(30*24*60*60*1000)); 278 docCookies.setItem('affid', curr, mytime.toUTCString(),"/","italiarail.com"); 279 } 280} 281 282function getAffiliate() { 283 // Retrieve currency from our cookies, if they come from elsewhere the cookie was already set b 284 var currency_aff; 285 curr = docCookies.getItem('aff'); 286 affiliate = docCookies.getItem('affid'); 287 288 if (affiliate) { 289 aff = affiliateMapping[affiliate.substring(0, 2)]; 290 if (aff !== undefined) { 291 // We have this listed in our possible affiliates, let's get the corresponding value. 292 currency_aff = aff[curr]; 293 } 294 } 295 296 // No match revert to currency mapping for aff 297 if ((aff === undefined) || (currency_aff === "")) { 298 return curr; 299 } 300 301 // Return affiliate ID mapped to the currency 302 return currency_aff; 303} 304 305function setCurrencySelectionFromPullDown() { 306 var curr = $("#currency_select").val(); 307 if (isValidAffiliate(curr)) { 308 $("#currency_select").val(curr); 309 } 310 var affiliate = $("#currency_select").val(); 311 312 setCompany(affiliate); 313 docCookies.setItem('aff', affiliate); 314} 315 316function isValidAffiliate(affCode) { 317 return contains(['TRE', 'ITC', 'ITE', 'ITR', 'TRA'], affCode); 318} 319 320function isRerailDate(depart) { 321 322 if (!depart) { 323 depart = document.getElementById('faresearch_route-departure_date').value; 324 } 325 // Build ISO using LOCAL date parts (no UTC conversion) 326 const d = new Date(); 327 if (!depart) { 328 depart = (d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0')); 329 document.getElementById('faresearch_route-departure_date').value = depart; 330 } 331 else { 332 console.log(depart); 333 const s = String(depart).trim(); 334 335 const monthMap = { 336 'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', 337 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12' 338 }; 339 340 let year, month, day; 341 let m; 342 343 // Format A: "Mon, Mar 03" 344 m = s.match(/^[A-Za-z]{3},\s*([A-Za-z]{3})\s+(\d{2})$/); 345 if (m) { 346 month = monthMap[m[1]]; 347 console.log(month); 348 day = parseInt(m[2], 10); 349 350 // Choose year intelligently (travel-safe) 351 const today = new Date(); 352 year = today.getFullYear(); 353 console.log(year); 354 } 355 356 // Format B: "9 Feb 2026" or "09 Feb 2026" 357 else if (!m) { 358 m = s.match(/^(\d{1,2})\s+([A-Za-z]{3})\s+(\d{4})$/); 359 if (m) { 360 day = parseInt(m[0], 10); 361 month = monthMap[m[1]]; 362 year = parseInt(m[2], 10); 363 } 364 } 365 if (month == null || !Number.isFinite(day) || !Number.isFinite(year)) { 366 depart = d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); 367 } 368 else { 369 depart = String(year) + '-' + String(month) + '-' + String(day); 370 } 371 372 depart = String(year) + '-' + String(month) + '-' + String(day); 373 console.log(depart); 374 } 375 376 var departure_date = depart; 377 378 return departure_date; 379} 380 381function initializeUI() { 382 383 //The page was accessed by navigating into the history. 384 //We reload to be sure form status is correct 385 if (performance.navigation.type == 2){ 386 if( $("input[value='round_trip']").is(':checked')) { 387 selectRoundTrip() 388 } 389 else { 390 $("input[value='one_way']").prop('checked', true); 391 } 392 } 393 // The page was accessed by clicking the Reload button (1) 394 if ( (performance.navigation.type == 1) ){ 395 //Uncomment when CMS-866 ends 396 if( $("input[value='round_trip']").is(':checked')) { 397 selectRoundTrip(); 398 } 399 } 400 401 402 $('#waiting').dialog({ 403 autoOpen: false, 404 width: 350, 405 draggable: false, 406 resizable: false, 407 dialogClass: "waiting" 408 }); 409 410 /*** Booking widget submit ***/ 411 $('#faresearch #submitButton, #faresearch2 #submitButton, #faresearch2 #submit').click(function () { 412 docCookies.setItem('include_passholder', '' + $('#faresearch_passholder').prop('checked')) 413 var form = $(this).form(); 414 var has_empty = false;
415 var org_value = false; 416 417 418 // GET STATION NAMES 419 var STATION_NAMES = []; 420 $.ajax({ 421 dataType: "json", 422 async: false, 423 url: "/stations.txt", 424 success: function(jsondata){ 425 STATION_NAMES = jsondata; 426 }, 427 error: function(){ 428 /* TBD */ 429 } 430 }); 431 432 // To avoid issue with IOS out of focus issue, at least blank when not resolved. 433 if ($("#faresearch_route-origin").val() !== '') { 434 var origin_check = $("#faresearch_route-origin").val().toLowerCase().trim(); 435 $.each(STATION_NAMES, function(index, value) { 436 if (value.label.toLowerCase() === origin_check) { 437 $("#faresearch_route-origin").val(value.label); 438 $("#faresearch_route-origin_id").val(value.value); 439 org_value = true; 440 } 441 }); 442 } 443 if (org_value === false) { 444 $("#faresearch_route-origin_id").val(''); 445 } 446 if ($("#faresearch_route-origin_id").val() === '') { 447 $("#faresearch_route-origin").val(''); 448 } 449 450 var des_value = false; 451 452 if ($("#faresearch_route-destination").val() !== '') { 453 var dest_check = $("#faresearch_route-destination").val().toLowerCase().trim(); 454 $.each(STATION_NAMES, function(index, value) { 455 if (value.label.toLowerCase() === dest_check) { 456 $("#faresearch_route-destination").val(value.label); 457 $("#faresearch_route-destination_id").val(value.value); 458 des_value = true; 459 } 460 }); 461 } 462 if (des_value === false) { 463 $("#faresearch_route-destination_id").val(''); 464 } 465 if ($("#faresearch_route-destination_id").val() === '') { 466 $("#faresearch_route-destination").val(''); 467 } 468 469 470 var return_date = Date.parse($('#faresearch_route-return_date').val()); 471 472 473 $(form).find(':input').each(function(idx, item) { 474 if ((item.type === 'text') && (item.value === '') && item.style.display != 'none' ) 475 { 476 $(item).css('border', "solid 1px red"); 477 has_empty = true; 478 } 479 else 480 { 481 $(item).css('border', "solid 1px lightgrey"); 482 } 483 484 }); 485 486 var num_passengers = $('#num_passengers').html(); 487 if (parseInt(num_passengers) < 1) { 488 has_empty = true; 489 $('.passengers_toggle_slide a').css('border', "solid 1px red"); 490 } 491 492 if (has_empty) { 493 return false; 494 } 495 496 if ($('#faresearch :input[name="aff"]').val() == 'DISABLED') { 497 $('#faresearch').attr("action", "https://app2.italiarail.com") 498 } 499 500 // Disable submit button to avoid several click. 501 form.find('.submit').attr('disabled', true).addClass('disabled'); 502 if (!docCookies.hasItem('italiapass_user')) { 503 // Cookie is not set. Let's open the modal. 504 // Submitting of closing the modal will submit the search form. 505 if( $('#italia-pass-search-popup').length && searchToTrack() ) { 506 $('#italia-pass-search-popup .cityFrom').replaceWith('<span class="green cityFrom">' + form.find('#faresearch_route-origin').val() + '</span>'); 507 $('#italia-pass-search-popup .cityTo').replaceWith('<span class="green cityTo">' + form.find('#faresearch_route-destination').val() + '</span>'); 508 $('#italia-pass-search-popup').dialog('open'); 509 } 510 else { 511 if (isRerail()) { 512 isRerailSubmit(form); 513 return false; 514 } 515 form.submit(); 516 } 517 } 518 else { 519 //we should track search here. 520 $('#waiting').dialog('open'); 521 if (searchToTrack()) { 522 trackUserSearch(); 523 } 524 else { 525 if (isRerail()) { 526 isRerailSubmit(form); 527 return false;
528 } 529 form.submit(); 530 } 531 } 532 }); 533 /*** Booking widget submit - END ***/ 534 535 $('#rountrip-confirm-modal').on('click', '.btn-primary', function() { 536 var form = $('#faresearch'); 537 if (! form.is('form')) { 538 form = $('#faresearch2'); 539 } 540 form.find('.submit').attr('disabled', true).addClass('disabled'); 541 $('#waiting').dialog('open'); 542 resetRerailCart(function() { 543 disableRerail(); 544 form.submit(); 545 }); 546 }); 547 548 $(".fn-cart-link").click(function (event) { 549 event.preventDefault(); 550 var aff_query; 551 var current_currency = $("#currency_select").val(); 552 // We need to make sure that query parameters are sent in the proper order by environment. 553 if ($(event.currentTarget).attr('href').indexOf('?') >= 0) { 554 aff_query = "&aff=" + current_currency; 555 } 556 else { 557 aff_query = "?aff=" + current_currency; 558 } 559 // Temp for transition 560 if (current_currency == 'DISABLED') { 561 window.location = "https://app2.italiarail.com" + aff_query; 562 } 563 else { 564 window.location = $(event.currentTarget).attr('href') + aff_query; 565 } 566 }); 567 568 569 //### TRAVELERS SELECTOR 570 var passengersAdultOpts = { 571 change: function (event, ui) { 572 $('#num_passengers').text(getPassengersTotal()); 573 $('.passengers_toggle_slide .passenger_label_title').text(getTravelersTitle($('#num_passengers').text())); 574 }, 575 spin: function (event, ui) { 576 $('#num_passengers').text(getPassengersTotal()); 577 $('.passengers_toggle_slide .passenger_label_title').text(getTravelersTitle($('#num_passengers').text())); 578 }, 579 start: function (event, ui) { 580 $('#num_passengers').text(getPassengersTotal()); 581 $('.passengers_toggle_slide .passenger_label_title').text(getTravelersTitle($('#num_passengers').text())); 582 }, 583 stop: function (event, ui) { 584 $('#num_passengers').text(getPassengersTotal()); 585 $('.passengers_toggle_slide .passenger_label_title').text(getTravelersTitle($('#num_passengers').text())); 586 }, 587 min: 1, 588 max: 9 589 }; 590 $('#faresearch_passengers-adult').spinner(passengersAdultOpts); 591 if ($('#productsearch_passengers-adult').length > 0) { 592 $('#productsearch_passengers-adult').spinner(); 593 } 594 595 var childPassengerOps = { 596 change: function (event, ui) { 597 $('#num_passengers').text(getPassengersTotal()); 598 $('.passengers_toggle_slide .passenger_label_title').text(getTravelersTitle($('#num_passengers').text())); 599 600 }, 601 spin: function (event, ui) { 602 $('#num_passengers').text(getPassengersTotal()); 603 $('.passengers_toggle_slide .passenger_label_title').text(getTravelersTitle($('#num_passengers').text())); 604 }, 605 start: function (event, ui) { 606 $('#num_passengers').text(getPassengersTotal()); 607 $('.passengers_toggle_slide .passenger_label_title').text(getTravelersTitle($('#num_passengers').text())); 608 }, 609 stop: function (event, ui) { 610 $('#num_passengers').text(getPassengersTotal()); 611 $('.passengers_toggle_slide .passenger_label_title').text(getTravelersTitle($('#num_passengers').text())); 612 }, 613 min: 0, 614 max: 9 615 }; 616 $('#faresearch_passengers-child').spinner(childPassengerOps); 617 if ($('#productsearch_passengers-child').length > 0) { 618 $('#productsearch_passengers-child').spinner(); 619 } 620 $(".passenger_toggle").click(function () { 621 var curr_arr = $("#date_icon_arrow").attr("src"); 622 $("#date_icon_arrow").toggleClass("active"); 623 624 if ( $("#date_icon_arrow").hasClass('active') ) { 625 $("#date_icon_arrow").attr("src", "/themes/custom/italiarail-v2/img/icon-up-arr.png"); 626 $("#passengers_toggle_panel").show("fast"); 627 } else { 628 $("#date_icon_arrow").attr("src", "/themes/custom/italiarail-v2/img/icon-down-arr.png"); 629 $("#passengers_toggle_panel").hide("fast"); 630 } 631 632 $(this).toggleClass("passengers_toggle_active"); 633 634 if ($(this).hasClass("passengers_toggle_active")) { 635 $("#passengers_toggle_panel").css("display", "block"); 636 } 637 return false;
638 }); 639 640 641 var numberOfMonths = getNumberOfMonths(); 642 //console.log(numberOfMonths); 643 644 645 //### DATE PICKER 646 647 var rtto = $('#faresearch_route-return_date').datepicker({ 648 minDate: 1, 649 dateFormat: 'd M yy', 650 buttonImage: '/themes/custom/italiarail-v2/img/icon-calendar.png', 651 showOn: 'both', 652 hideIfNoPrevNext: true, 653 numberOfMonths: numberOfMonths, 654 onSelect: function() { 655 var datefrom = $(this).datepicker("getDate"); 656 rtfrom.datepicker("option", "maxDate", datefrom || 0); 657 }, 658 }); 659 660 var rtfrom = $('#faresearch_route-departure_date').datepicker({ 661 minDate: 0, 662 dateFormat: 'D, M dd', 663 buttonImage: '/themes/custom/italiarail-v2/img/icon-calendar.png', 664 showOn: 'both', 665 hideIfNoPrevNext: true, 666 numberOfMonths: numberOfMonths, 667 onSelect: function() { 668 if ( $("input[value='round_trip']:checked").val() ){ 669 var date = $(this).datepicker("getDate"); 670 if (date) date.setDate(date.getDate()); // Add one day 671 rtto.datepicker("option","setDate", date); 672 rtto.datepicker("option","minDate", date || 1); 673 //rtto.focus(); 674 }; 675 }, 676 beforeShow: function () { 677 $('#passengers_toggle_panel').hide(); 678 } 679 }); 680 681 rtfrom.datepicker(); 682 rtto.datepicker(); 683 684 685 function getNumberOfMonths() { 686 var n = $(window).width() < 600 ? 1 : 2; 687 return n; 688 } 689 690 //### AUTOCOMPLETE 691 var station_ac_options = (function () { 692 "use strict"; 693 var stations_cache_data, 694 start_stations, 695 mid_stations; 696 697 $.ajax({ 698 url: "/stations.txt", 699 dataType: "json", 700 cache: true, 701 success: function (data) { 702 stations_cache_data = data; 703 } 704 }); 705 706 // The includes() method determines whether an array includes a certain element, returning true or false as appropriate. 707 // https://tc39.github.io/ecma262/#sec-array.prototype.includes 708 if (!Array.prototype.includes) { 709 Object.defineProperty(Array.prototype, 'includes', { 710 value: function(searchElement, fromIndex) { 711 if (this == null) { 712 throw new TypeError('"this" is null or not defined'); 713 } 714 715 var o = Object(this); 716 var len = o.length >>> 0; 717 718 if (len === 0) { 719 return false; 720 } 721 722 var n = fromIndex | 0; 723 var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); 724 725 function sameValueZero(x, y) { 726 return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); 727 } 728 729 while (k < len) { 730 if (sameValueZero(o[k], searchElement)) { 731 return true; 732 } 733 k++; 734 } 735 736 return false; 737 } 738 }); 739 } 740 741 function _filterStations(request, response, stations_cache_data) { 742 var start_matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(request.term), "i"), 743 mid_matcher = new RegExp(".+\\b" + $.ui.autocomplete.escapeRegex(request.term), "i"); 744 745 start_stations = $.grep(stations_cache_data, function (item) { 746 return start_matcher.test(item.label); 747 }); 748 749 mid_stations = $.grep(stations_cache_data, function (item) { 750 return mid_matcher.test(item.label) && !start_stations.includes(item); 751 }); 752 753 start_stations.sort(compareIgnoreCase); 754 mid_stations.sort(compareIgnoreCase); 755 response(start_stations.concat(mid_stations)); 756 } 757 758 return { 759 filterStations: _filterStations, 760 source: function (request, response) {_filterStations(request, response, stations_cache_data);}, 761 focus: function (event, ui) { 762 if (event.which !== undefined) { 763 $(this).val(ui.item.label); 764 } 765 return false;
766 }, 767 select: function (event, ui) { 768 $(this).val(ui.item.label); 769 $("#" + $(this)[0].id + "_id").val(ui.item.value); 770 if (typeof updateSubmit === 'function') { 771 updateSubmit(); 772 } 773 return false; 774 }, 775 autoFocus: true 776 }; 777 }()); 778 779 $('#faresearch_route-origin').autocomplete(station_ac_options); 780 $('#faresearch_route-destination').autocomplete(station_ac_options); 781 782 $('#swap_input').click(function (event, ui) { 783 switchOriginDestination(); 784 }); 785 786 $('#swap_input').mousedown(function () { 787 $(this).addClass('submit_active'); 788 }); 789 790 $('#swap_input').mouseup(function () { 791 $(this).css('background-color', 'white'); 792 $(this).removeClass('submit_active'); 793 }); 794 795 //### GET FROM 796 var fromgood = false; 797 var togood = false; 798 var depgood = false; 799 800 var from = getParameterByName('from'); 801 /*$.grep(STATION_NAMES, function (item, idx) { 802 if (item.value == from || item.label == from) { 803 label = item.label; 804 $("#faresearch_route-origin").val(item.label); 805 $("#faresearch_route-origin_id").val(item.value); 806 fromgood = true; 807 } 808 });*/ 809 810 //### GET TO 811 var to = getParameterByName('to'); 812 /*$.grep(STATION_NAMES, function (item, idx) { 813 814 if (item.value == to || item.label == to) { 815 label = item.label; 816 $("#faresearch_route-destination").val(item.label); 817 $("#faresearch_route-destination_id").val(item.value); 818 togood = true; 819 } 820 });*/ 821 822 if (fromgood && togood) { 823 // then show the calendar 824 //$('#faresearch_route-departure_date').datepicker('show'); 825 } 826 827 //### CURRENCY SELECTOR 828 // Add modal for pass 829 /* $('#railpass-help2').dialog({ 830 autoOpen: false, 831 trigger: '.railpass-help2-open', 832 overlayClass: 'railpass-help2Overlay', 833 closeClass: 'railpass-help2-close' 834 });*/ 835 836 $("#faresearch_passholder").change( 837 function (event) { 838 if ($(event.currentTarget).val() > 0) { 839 //var background = blanket(); 840 popup('railpass-help1',!blanket()); 841 $("input[value='round_trip']").prop("disabled", true); 842 } 843 844 if ($(event.currentTarget).val() == 0) { 845 $("input[value='round_trip']").prop("disabled", false); 846 } 847 } 848 ); 849 850 $(".railpassconfirm").click(function(){ 851 if (get_background_context){ 852 popup('railpass-help1', false); 853 } else { 854 popup('railpass-help1', true); 855 } 856 }); 857 858 $("input[value='one_way']").click(function() { 859 if( $(this).is(':checked')) { 860 $("#faresearch_route-return_date").hide(); 861 $("#faresearch_route-departure_date").removeClass("wide"); 862 $("#faresearch_route-return_date").removeClass("wide"); 863 $("#faresearch_route-return_date").disabled = true; 864 $(".btn.dropdown-toggle").prop("disabled", false); 865 rtfrom.datepicker("option", "maxDate", null); 866 } 867 }); 868 869 $("input[value='round_trip']").click(function() { 870 if( $(this).is(':checked')) { 871 $("#faresearch_route-return_date").show(); 872 $("#faresearch_route-departure_date").addClass("wide"); 873 $("#faresearch_route-return_date").addClass("wide"); 874 $("#faresearch_route-return_date").disabled = false; 875 $(".btn.dropdown-toggle").prop("disabled", true); 876 var date = rtfrom.datepicker("getDate"); 877 if (date){ 878 rtto.datepicker("option","setDate", date); 879 rtto.datepicker("option","minDate", date || 1); 880 //rtto.datepicker(); 881 } 882 var dateto = rtto.datepicker("getDate"); 883 if (dateto){ 884 rtfrom.datepicker("option", "maxDate", dateto); 885 } 886 887 } 888 }); 889 890 $('.railpass-help2-close').on('click', function(){ 891 popup('railpass-help1', !get_background_context()); 892 }); 893 $('.railpass-help2-close').on('click', function(){ 894 //$('#faresearch_passholder').val('0'); 895 $('.btn-group ul.selectpicker li').each(function(){ 896 $(this).removeClass('selected'); 897 $('.btn-group ul.selectpicker li:first-child').addClass('selected'); 898 }); 899 }); 900 901 saveAffiliateInfo(); 902 setPassholderPref(); 903} 904 905function toggle(div_id) { 906 var el = document.getElementById(div_id); 907 if (el.style.display == 'none') { 908 el.style.display = 'block'; 909 } 910 else { 911 el.style.display = 'none'; 912 } 913} 914 915function blanket_size(popUpDivVar) { 916 if (typeof window.innerWidth != 'undefined') { 917 viewportheight = window.innerHeight; 918 } else { 919 viewportheight = document.documentElement.clientHeight; 920 } 921 if ((viewportheight > document.body.parentNode.scrollHeight) && (viewportheight > document.body.parentNode.clientHeight)) { 922 blanket_height = viewportheight; 923 } else { 924 if (document.body.parentNode.clientHeight > document.body.parentNode.scrollHeight) { 925 blanket_height = document.body.parentNode.clientHeight; 926 } else { 927 blanket_height = document.body.parentNode.scrollHeight; 928 } 929 } 930 var blanket = document.getElementById('blanket'); 931 blanket.style.height = blanket_height + 'px'; 932 var popUpDiv = document.getElementById(popUpDivVar); 933 popUpDiv_height = blanket_height / 2 - 400;//150 is half popup's height 934 popUpDiv.style.top = (popUpDiv_height - 200) + 'px'; 935} 936 937function window_pos(popUpDivVar) { 938 if (typeof window.innerWidth != 'undefined') { 939 viewportwidth = window.innerHeight; 940 } else { 941 viewportwidth = document.documentElement.clientHeight; 942 } 943 if ((viewportwidth > document.body.parentNode.scrollWidth) && (viewportwidth > document.body.parentNode.clientWidth)) { 944 window_width = viewportwidth; 945 } else { 946 if (document.body.parentNode.clientWidth > document.body.parentNode.scrollWidth) { 947 window_width = document.body.parentNode.clientWidth; 948 } else { 949 window_width = document.body.parentNode.scrollWidth; 950 } 951 } 952 var popUpDiv = document.getElementById(popUpDivVar); 953 window_width = (window_width / 2 - 150) - 50;//150 is half popup's width 954 popUpDiv.style.left = window_width + 'px'; 955} 956
957/** 958 * Pops up the window indicated as parameter 959 * 960 */ 961function popup(windowname, blanketbackground) { 962 if (undefined === blanketbackground) { 963 blanketbackground = true; 964 } 965 966 var context = get_background_context(); 967 blanket_size(windowname); 968 window_pos(windowname); 969 if (!context) { 970 toggle('blanket'); 971 } else if (blanketbackground){ 972 toggle('blanket'); 973 } 974 toggle(windowname); 975 ga('send', 'event', 'popup', windowname, getAffiliate()); 976} 977 978// Return true if we have the booking widget popup 979function get_background_context(){ 980 return $('#bookingwidgetpopup').length; 981} 982 983// Check if blanket background is active 984function blanket() { 985 if ( $('#blanket').css('display') == 'block' || $('#blanket').css("visibility") == "show"){ 986 // Background is visible 987 return true; 988 } else{ 989 // Background is NOT visible 990 return false; 991 } 992} 993 994/** 995* Wait for element to be visible 996*/ 997var waitForEl = function(selector, callback) { 998 if (jQuery(selector).length) { 999 callback(); 1000 } else { 1001 setTimeout(function() { 1002 waitForEl(selector, callback); 1003 }, 100); 1004 } 1005}; 1006 1007/** 1008* Choose either to track or not the search based on stations. 1009*/ 1010function searchToTrack() { 1011 if (drupalSettings.italiaPassSettings.stations) { 1012 if (((drupalSettings.italiaPassSettings.stations).indexOf($('#faresearch_route-origin_id').val() ) >= 0) || ((drupalSettings.italiaPassSettings.stations).indexOf($('#faresearch_route-destination_id').val() ) >= 0)) { 1013 // We should show the popup 1014 return true; 1015 } 1016 } 1017 return false; 1018}; 1019 1020function selectRoundTrip(){ 1021 $("input[value='round_trip']").prop('checked', true); 1022 $("#faresearch_route-return_date").show(); 1023 $("#faresearch_route-departure_date").addClass("wide"); 1024 $("#faresearch_route-return_date").addClass("wide"); 1025 //$("#faresearch_route-return_date").disabled = false; 1026 waitForEl(".btn.dropdown-toggle", function() { 1027 $(".btn.dropdown-toggle").prop("disabled", true); 1028 }); 1029 1030} 1031 1032/** 1033 * Get parameter from URL query(search) string 1034 * 1035 * Example: 1036 * var id = getQueryString('id') 1037 * 1038 * @param {string} name 1039 * @returns {string} 1040 */ 1041function getQueryString(name) { 1042 var result = location.href.match(new RegExp("[\?\&]" + name + "=([^\&]+)", "i")); 1043 if (result == null || result.length < 1) { 1044 return ""; 1045 } 1046 return decodeURIComponent(result[1]); 1047}; 1048 1049function trackUserSearch(submitForm = true) { 1050 var hash = docCookies.getItem('italiapass_user'); 1051 var email = $('.italy-pass-search-email-form .form-item-email input').val(); 1052 var from = ''; 1053 var to = ''; 1054 var depart = ''; 1055 var roundtrip = 0; 1056 var return_date = ''; 1057 1058 1059 var form = false; 1060 if ( $('#faresearch #submitButton, #faresearch2 #submitButton').length > 0 ) { 1061 var form = $('#faresearch #submitButton, #faresearch2 #submitButton').form(); 1062 var from = form.find('#faresearch_route-origin_id').val(); 1063 var to = form.find('#faresearch_route-destination_id').val(); 1064 var depart = form.find('#faresearch_route-departure_date').val(); 1065 var roundtrip = 0; 1066 var return_date = form.find('#faresearch_route-return_date').val(); 1067 } 1068 1069 var pathname = window.location.pathname; 1070 1071 if (return_date != 'undefined' && return_date != '') { 1072 roundtrip = 1; 1073 return_date = Math.floor(Date.parse(return_date) / 1000); 1074 } 1075 else { 1076 return_date = 0; 1077 } 1078 1079 var depart_date = 0; 1080 1081 if (depart != '') { 1082 depart_date = Math.floor(Date.parse(depart) / 1000); 1083 } 1084 1085 // Change values for popular routes. 1086 if ($('article.popular-routes-new.full').length > 0) { 1087 isPopularRoute = true; 1088 from = $('article.popular-routes-new.full').attr('data-departure-station-id'); 1089 to = $('article.popular-routes-new.full').attr('data-destination-station-id') 1090 } 1091 1092 $.ajax({ 1093 url: '/v1/italiapass/research/add', 1094 type: 'POST', 1095 data: { 1096 hash: hash, 1097 from: from, 1098 to: to, 1099 roundtrip: roundtrip, 1100 depart: depart_date, 1101 return: return_date, 1102 path: pathname, 1103 }, 1104 success: function (response) { 1105 if (submitForm) { 1106 if (isRerail()){ 1107 isRerailSubmit(form); 1108 } else { 1109 form.submit(); 1110 } 1111 } 1112 }, 1113 // We should always submit. 1114 error: function (response) { 1115 if (submitForm) { 1116 if (isRerail()){ 1117 isRerailSubmit(form); 1118 } else { 1119 form.submit(); 1120 } 1121 } 1122 } 1123 }); 1124} 1125 1126 1127function isRerailSubmit(form){ 1128 1129 if (drupalSettings.show_roundtrip != 'undefined' && drupalSettings.show_roundtrip == 0) { 1130 var tripType = 'one_way'; 1131 } 1132 else {
1133 var tripType = $("input[name='faresearch_route-trip_type']:checked").val(); 1134 } 1135 1136 if (tripType.toLowerCase() === 'round_trip') { 1137 getRerailCartCount(function(response) { 1138 if (response.total) { // show confirmation section when cart is not empty 1139 $('#rountrip-confirm-modal').modal(); 1140 return; 1141 } 1142 // Disable submit button to avoid several click. 1143 form.find('.submit').attr('disabled', true).addClass('disabled'); 1144 $('#waiting').dialog('open'); 1145 disableRerail(); 1146 form.submit(); 1147 }); 1148 } else { 1149 var depart = document.getElementById('faresearch_route-departure_date').value; 1150 var departure_date = isRerailDate(depart); 1151 searchRerailTrains( 1152 $('#faresearch_route-origin_id').val(), 1153 $('#faresearch_route-destination_id').val(), 1154 departure_date, 1155 $('#faresearch_route-return_date').val(), 1156 $('#faresearch_passengers-adult').val(), 1157 $('#faresearch_passengers-child').val(), 1158 $('#faresearch_passholder').val(), 1159 $('#aff').val(), 1160 tripType 1161 ); 1162 } 1163 return; 1164 1165} 1166 1167document.addEventListener('DOMContentLoaded', function() { 1168 const searchIcon = document.querySelector('.search-icon'); 1169 const searchContainer = document.querySelector('.search-container'); 1170 1171 searchIcon.addEventListener('click', function() { 1172 searchContainer.classList.toggle('active'); 1173 if (searchContainer.classList.contains('active')) { 1174 document.querySelector('#nav-keys').focus(); 1175 } 1176 }); 1177}); 1178 1179// Function to initialize the countdown on a given wrapper 1180function initializeCountdown(wrapperId) { 1181 const wrapper = document.getElementById(wrapperId); 1182 const countdownEl = wrapper.querySelector(".countdown"); 1183 const timerContainer = wrapper.querySelector(".expiration-timer"); 1184 const chronoIcon = wrapper.querySelector(".cart-trip-chrono"); 1185 const helpBtn = wrapper.querySelector(".help-button"); 1186 const tooltip = wrapper.querySelector(".tooltip"); 1187 1188 // Determine the fetch URL based on the current host 1189 const currentHost = window.location.hostname; 1190 let fetchUrl; 1191 let fetchHost; 1192 1193 // Check if the current host is cms-origin.stage.italiarail.com 1194 if (currentHost === "cms-origin.stage.italiarail.com") { 1195 fetchHost = "https://newshop.stage.italiarail.com"; 1196 } else { 1197 fetchHost = "https://shop.italiarail.com"; 1198 } 1199 1200 // If we have expiration then show the cart icon too with proper env link 1201 let cartLink = document.getElementById("cart"); 1202 1203 if (cartLink) { 1204 // Get the current href 1205 let currentHref = cartLink.getAttribute("href"); 1206 1207 // Update the href attribute 1208 if (!currentHref.startsWith(fetchHost)) { 1209 cartLink.setAttribute("href", fetchHost + currentHref); 1210 } 1211 } 1212 1213 // Construct the endpoint URL based on environment 1214 fetchUrl = fetchHost += '/api/cms/cart/count'; 1215 1216 function startCountdown(expirationTimeStr) { 1217 const expirationTime = new Date(expirationTimeStr); 1218 1219 function updateCountdown() { 1220 const now = new Date(); 1221 const diffMs = expirationTime - now; 1222 1223 if (diffMs <= 0) { 1224 clearInterval(countdownInterval); 1225 wrapper.classList.remove("d-flex"); 1226 return; 1227 } else { 1228 wrapper.classList.add("d-flex"); 1229 } 1230 1231 const totalSeconds = Math.floor(diffMs / 1000); 1232 const minutes = Math.floor(totalSeconds / 60); 1233 const seconds = totalSeconds % 60; 1234 countdownEl.textContent = `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; 1235 1236 if (totalSeconds <= 180) { 1237 timerContainer.classList.add("trip-chrono-warning"); 1238 chronoIcon.classList.add("icon-chrono-warning"); 1239 } 1240 } 1241 1242 updateCountdown(); 1243 var countdownInterval = setInterval(updateCountdown, 1000); 1244 } 1245 1246 // Determine the expiration time before fetching the data 1247 const urlParams = new URLSearchParams(window.location.search); 1248 const useFakeExpiration = urlParams.get('useFakeExpiration') === 'true'; 1249 1250 let expirationTime; 1251 1252 if (useFakeExpiration) { 1253 // Set expiration time to 5 minutes ahead 1254 const now = new Date(); 1255 now.setMinutes(now.getMinutes() + 5); // Add 5 minutes 1256 expirationTime = now.toISOString(); // Convert to ISO 8601 string 1257 } else { 1258 expirationTime = null; // No fake expiration, we will fetch it from the API 1259 } 1260 1261 // Fetch the JSON from API only if no fake expiration is used 1262 if (!useFakeExpiration) { 1263 fetch(fetchUrl, { 1264 credentials: 'include' 1265 }) 1266 .then(response => { 1267 if (!response.ok) throw new Error("Failed to fetch cart data"); 1268 return response.json(); 1269 }) 1270 .then(data => { 1271 if (data.earliestExpirationTime) { 1272 expirationTime = data.earliestExpirationTime; 1273 } else { 1274 console.warn("No expiration time in response"); 1275 timerContainer.style.display = "none"; 1276 if (cartLink) { cartLink.style.display = "none"; } 1277 return; // Exit early if no valid expiration time 1278 } 1279 1280 startCountdown(expirationTime); 1281 }) 1282 .catch(error => { 1283 console.error("Error fetching countdown data:", error); 1284 if (cartLink) { cartLink.style.display = "none"; } 1285 timerContainer.style.display = "none"; 1286 }); 1287 } else { 1288 // Start the countdown with the manually set expiration time (5 minutes ahead) 1289 startCountdown(expirationTime); 1290 } 1291 1292 // Tooltip toggle 1293 helpBtn.addEventListener("click", (e) => { 1294 e.stopPropagation(); 1295 tooltip.classList.toggle("show"); 1296 }); 1297 1298 document.addEventListener("click", () => { 1299 tooltip.classList.remove("show"); 1300 }); 1301 1302} 1303 1304// Initialize countdown for both mobile and desktop wrappers 1305initializeCountdown("mobile-exp-wrapper"); 1306initializeCountdown("desktop-exp-wrapper");
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.