1function fupi_top_helpers(){ 2 3 'use strict'; 4 5 var d = document; 6 7 // VARS 8 9 FP.v = {}; 10 fp.track_queue = []; 11 fpdata.activity = { total : 0, last : 0, current : 0 }; 12 fpdata.refreshed = false; 13 14 if ( document.referrer == document.location.origin + document.location.pathname ) { 15 fpdata.refreshed = true; 16 } else { 17 if ( performance.navigation ? performance.navigation.type === performance.navigation.TYPE_RELOAD : performance.getEntriesByType('navigation')[0].type === 'reload' ) fpdata.refreshed = true; // deprecated method first, the new one later 18 } 19 20 // HELPERS 21 22 function getFormattedDateTime(){ 23 const d = new Date(); 24 25 const year = d.getFullYear(); 26 const month = d.getMonth() + 1; 27 const day = d.getDate(); 28 const hours = d.getHours() * 100; 29 const minutes = d.getMinutes(); 30 31 return { 32 year: year, 33 month: month, 34 day: day, 35 time: hours + minutes 36 }; 37 } 38 39 fpdata.date = getFormattedDateTime(); 40 41 function check_if_can_save_wpfp_cookies( first_check = false ) { 42 43 // save previous state 44 let prev_check = !! fp.vars.can_save_wpfp_cookies; // "!!" changes undefined into false 45 46 // get current state 47 fp.vars.can_save_wpfp_cookies = ! fp.notice.enabled || fp.notice.mode == 'notify' || fp.notice.mode == 'hide' || ( fp.notice.mode == 'optout' && ! fpdata.cookies ) || ( fp.notice.mode == 'optin' && fpdata.cookies && fpdata.cookies.stats ); 48 49 // check if the state has changed 50 fp.vars.consents_changed = first_check ? false : prev_check != fp.vars.can_save_wpfp_cookies; 51 } 52 53 function shouldCustomScriptLoadHere( geo ){ 54 55 let geo_method = geo[0], 56 geo_countries = geo[1]; 57 58 if ( fp.main.geo && geo_method && geo_countries ) { // we check fp.geo to prevent situations when user disabled geo but didn't save new tools settings 59 60 if ( fp.ready ) { 61 return geo_method == 'incl' ? geo_countries.includes( fpdata.country ) : ! geo_countries.includes( fpdata.country ); 62 } 63 return false; 64 } 65 66 return true; 67 } 68 69 function shouldScriptLoadHere( id ){ 70 71 if ( fp.main.geo && fp[id].limit_country ) { // we check fp.main.geo to prevent situations when user disabled geo but didn't save new tools settings 72 73 if ( fp.ready ) { 74 75 if ( fp[id].limit_country.method == 'excl' ) { 76 return ! fp[id].limit_country.countries.includes( fpdata.country ); 77 } else { 78 return fp[id].limit_country.countries.includes( fpdata.country ); 79 } 80 } 81 82 return false; 83 } 84 85 return true; 86 }; 87 88 function get_session_data_from_cookies(){ 89 90 let session_cookie = FP.readCookie('fp_current_session'); 91 if ( session_cookie ) fpdata.current_session = JSON.parse(session_cookie); 92 93 94 } 95 96 // 97 // FP - start 98 // 99 100 FP.setCookie = (name, value, days = 0, mins = 0, path = "/") => { 101 var expires = ""; 102 if ( days > 0 || mins > 0 ) { 103 var date = new Date(); 104 if (days > 0) { 105 date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); 106 } else { 107 date.setTime(date.getTime() + mins * 60 * 1000); 108 } 109 expires = "; expires=" + date.toGMTString(); 110 }; 111 d.cookie = name + "=" + value + expires + "; path=" + path + "; sameSite=strict"; 112 }; 113 114 FP.deleteCookie = name => { FP.setCookie(name, "", -1); }; 115 116 FP.getUrlParamByName = ( name, url = false ) => { 117 if ( ! url ) { 118 url = window.location.search; 119 } else { 120 url = url.split('?')[1]; 121 if ( ! url ) return null; 122 }; 123 124 const queryString = url, 125 urlParams = new URLSearchParams(queryString); 126 return urlParams.get(name); 127 }; 128 129 FP.nl2Arr = nl => nl ? [].slice.call(nl) : false; 130 131 FP.findID = (e, c) => { 132 if ( c === null ) return null; 133 if ( !e ) return false; 134 c = c || document; 135 return c.getElementById(e); 136 }; 137 138 FP.findFirst = (e, c) => { 139 if ( c === null ) return null; 140 if ( !e ) return false; 141 c = c || document; 142 return c.querySelector(e); 143 }; 144 145 FP.findAll = (e, c) => { 146 if ( c === null ) return []; 147 if ( ! e ) return false; 148 c = c || document; 149 return FP.nl2Arr(c.querySelectorAll(e));
150 }; 151 152 FP.getScript = ( url, cb, attrs, tag = 'script', fallback = false ) => { 153 154 attrs = attrs || false; 155 var new_tag = ""; 156 157 if ( tag == "img" ) { 158 new_tag = d.createElement('img'); 159 new_tag.src = url; 160 } else if ( tag == "link" ) { 161 new_tag = d.createElement('link'); 162 new_tag.href = url; 163 } else { 164 new_tag = d.createElement('script') 165 new_tag.src = url; 166 new_tag.type = 'application/javascript'; 167 } 168 169 if ( cb ) new_tag.onload = cb; 170 171 if ( fallback ) new_tag.onerror = ()=>{ FP.getScript( fallback, cb, attrs, tag ); }; 172 173 if ( attrs ) { 174 // check if attrs holds a reference to and HTML tag 175 if ( attrs instanceof Element ) { 176 let old_tag = attrs; 177 // copy from the original tag (with exceptions) 178 Array.from( old_tag.attributes ).forEach( attr => { 179 if ( attr.name !== 'type' && attr.name !== 'data-src' && ! attr.name.includes('fupi') ) { 180 new_tag.setAttribute( attr.name, attr.value ); 181 } 182 }); 183 // or add provided values 184 } else { 185 for (var key in attrs) { 186 if ( key !== "/" ) new_tag.setAttribute(key, attrs[key]); 187 } 188 } 189 } 190 191 d.getElementsByTagName("head")[0].appendChild(new_tag); 192 }; 193 194 FP.createTag = FP.getScript; 195 196 function loadInlineScript(temp_script) { 197 198 let scr_id = temp_script.dataset?.fupi_id, 199 orig_id = scr_id.replace('fp_',''), 200 is_cscr = temp_script.dataset?.module == 'cscr'; 201 202 if ( ! scr_id ) return; 203 204 let new_script = document.createElement('script'); 205 206 new_script.innerHTML = temp_script.innerHTML; 207 temp_script.parentNode.replaceChild(new_script, temp_script); 208 209 if ( fp.main.debug ) { 210 if ( is_cscr && fp.cscr[orig_id] ) { 211 console.log( "[FP] Custom script loaded: " + fp.cscr[orig_id] ); 212 } else { 213 console.log( "[FP] Inline script loaded"); 214 } 215 }; 216 217 fp.loaded.push( orig_id ); 218 }; 219 220 FP.sendEvt = (evt_name, details_a) => { 221 var details = details_a ? { 'detail' : details_a } : {}, 222 fp_event = new CustomEvent( evt_name, details ); 223 document.dispatchEvent(fp_event); 224 }; 225 226 FP.getRandomStr = ()=>{ 227 return ( Math.random() + 1 ).toString(36).substring(2); 228 }; 229 230 FP.updateConsents = () => { 231 232 if ( fp.vars.use_other_cmp ) return; 233 234 if ( ! fp.main.track_current_user ) { 235 236 if ( fp.main.debug ) console.log("[FP] Current user cannot be tracked"); 237 238 // if the user made a choice in the past 239 } else if ( fpdata.cookies ){ 240 241 set_consents_in_fpdata( fpdata?.cookies?.stats, fpdata?.cookies?.personalisation, fpdata?.cookies?.marketing ); 242 243 // if no choice was made in the past 244 } else { 245 246 // deny all if consent banner is in optin mode 247 if ( fp.notice.enabled && fp.notice.mode == "optin" ) { 248 249 set_consents_in_fpdata(); 250 251 // agree to all if consent banner is disabled or we are in optout or notification mode 252 } else { 253 set_consents_in_fpdata(true, true, true); 254 } 255 } 256 257 if ( ! fp.main.is_customizer ) { 258 259 // set MS Ads consent 260 261 if ( fp.main.track_current_user && fpdata.cookies ){ 262 if ( fpdata.cookies.stats && fpdata.cookies.marketing ) { 263 window.uetq.push( "consent", "update", { 264 "ad_storage": "granted" 265 }); 266 } 267 } else { 268 if ( ! ( fp.notice.enabled && fp.notice.mode == "optin" ) ) { 269 window.uetq.push( "consent", "update", { 270 "ad_storage": "granted" 271 }); 272 } 273 } 274 275 // Set GTAG consents 276 277 ["gtag", "fupi_gtm_gtag"].forEach( tag_name => { 278 279 if ( tag_name == "fupi_gtm_gtag" && ! window.fupi_gtm_gtag ) return; 280 281 // update if the user made a choice in the past 282 if ( fp.main.track_current_user && fpdata.cookies ){ 283 284 FP.set_gtag_consents( tag_name, "update", fpdata?.cookies?.stats, fpdata?.cookies?.personalisation, fpdata?.cookies?.marketing ); 285 if ( fp.main.debug ) console.log("[FP] Google consents set to user choices"); 286 287 // if no choice was made in the past 288 } else { 289 290 // agree to all if consent banner is disabled or is in optout or notification mode 291 if ( ! ( fp.notice.enabled && fp.notice.mode == "optin" ) ) { 292 293 FP.set_gtag_consents( tag_name, "update", true, true, true ); 294 if ( fp.main.debug ) console.log("[FP] All Google consents granted"); 295 296 }; 297 } 298 } ); 299 } 300 } 301 302 FP.postToServer = ( event_data_a, cb = false ) => { 303 304 if ( fpdata.is_robot ) return; 305 if ( fp.main.debug ) console.log( "[FP] Posting to server", event_data_a ); 306 307 let fetch_url = fp.main.server_method == "rest" ? "/index.php?rest_route=/fupi/v1/sender" : "/wp-admin/admin-ajax.php?action=fupi_ajax"; 308 309 if ( fp.main.debug || event_data_a[0][0] == 'cdb') { 310 311 fetch( fetch_url, { 312 method: "POST", 313 body: JSON.stringify( event_data_a ), 314 credentials: 'same-origin', 315 headers: { 316 "Content-type": "application/json; charset=UTF-8", 317 // "X-WP-Nonce": fp_nonce 318 } 319 }) 320 .then((response) => response.json()) 321 .then((json) => { 322 if ( cb ) { 323 cb(json); 324 } else { 325 console.log( "[FP] Server response", json); 326 } 327 }); 328 329 } else { 330 331 fetch( fetch_url, { 332 method: "POST", 333 credentials: 'same-origin', 334 body: JSON.stringify( event_data_a ), 335 headers: { 336 "Content-type": "application/json; charset=UTF-8", 337 // "X-WP-Nonce": fp_nonce 338 } 339 }); 340 } 341 }; 342 343 // fires function every X ms with an option to fire them ADDITIONALLY on start ("leading") and after events ("trailing") 344 FP.throttle = function (func, wait, options) { 345 var context, args, result; 346 var timeout = null; 347 var previous = 0; 348 if (!options) options = {}; 349 350 var later = function later() { 351 previous = options.leading === false ? 0 : Date.now(); 352 timeout = null;
353 result = func.apply(context, args); 354 if (!timeout) context = args = null; 355 }; 356 357 return function () { 358 var now = Date.now(); 359 if (!previous && options.leading === false) previous = now; 360 var remaining = wait - (now - previous); 361 context = this; 362 args = arguments; 363 364 if (remaining <= 0 || remaining > wait) { 365 if (timeout) { 366 clearTimeout(timeout); 367 timeout = null; 368 } 369 370 previous = now; 371 result = func.apply(context, args); 372 if (!timeout) context = args = null; 373 } else if (!timeout && options.trailing !== false) { 374 timeout = setTimeout(later, remaining); 375 } 376 377 return result; 378 }; 379 }; 380 381 FP.remove = el => { if (el) el.parentNode.removeChild(el); }; 382 383 FP.addAction = function( action_name, fn ){ 384 385 let arr = Array.isArray(action_name) ? action_name : [action_name]; // make sure that trigger is in an array 386 387 arr.forEach(function ( name , i) { 388 if ( ! fp.actions[name] ) fp.actions[name] = []; 389 fp.actions[name].push( fn ); 390 }); 391 } 392 393 FP.doActions = ( action_name, args_o = null, cb ) => { 394 if ( fp.actions[action_name] ) fp.actions[action_name].forEach( fn => { 395 let return_val = fn( args_o ); 396 if ( typeof return_val !== 'undefined' ) args_o = return_val; 397 } ); 398 if ( cb ) cb( args_o ); 399 } 400 401 FP.hasActions = action_name => { 402 return fp.actions[action_name] && fp.actions[action_name].length > 0; 403 }; 404 405 FP.isScriptAllowedToLoad = function( id, force_load, permissions_a, geo_a ){ 406 407 // STOP if we don't have all the basic data yet 408 if ( ! fp.ready ) return false; 409 410 // STOP if the script has already loaded 411 if ( fp.loaded.includes(id) ) return false; 412 413 // START if the load is forced 414 if ( force_load ) return true; 415 416 // STOP if the tab is not in focus AND we are NOT on WooCommerce checkout page 417 if ( fpdata.page_type !== 'Woo Order Received' ) { 418 if ( document.hidden ) return false; 419 } 420 421 // STOP tracking an excluded visitor (MUST be after checking FORCE LOAD) 422 if ( ! fp.main.track_current_user ) return false; 423 424 // STOP if the script is set NOT to load in the current location 425 if ( geo_a && ! shouldCustomScriptLoadHere( geo_a ) ) return false; 426 427 // STOP if any permissions need to be given in this location... 428 if ( fp.notice.enabled && fp.notice.mode == 'optin' && ! fpdata.cookies && permissions_a.length > 0 ) return false; 429 430 // STOP if required permissions are not given 431 if ( fpdata.cookies && permissions_a.length > 0 ) { 432 if ( ! permissions_a.every( permission => fpdata.cookies[permission] ) ) return false; 433 } 434 435 return true; 436 }; 437 438 FP.isAllowedToLoad = function( module_id, permissions_a, required_a, nr, tool_does_not_use_cookies = false ) { 439 440 var id = nr ? module_id + nr : module_id; 441 442 // STOP if we don't have all the basic data yet 443 if ( ! fp.ready ) return false; 444 445 // STOP if we have no settings for this tool 446 if ( ! fp[id] ) return false; 447 448 // STOP if the script has already loaded 449 if ( fp.loaded.includes(id) ) return false; 450 451 // STOP if the required data is missing 452 if ( required_a.some( req => ! fp[id][req] ) ) return false; 453 454 // START if the load is forced 455 if ( fp[id].force_load ) return true; 456 457 // STOP if the tab is not in focus AND we are NOT on WooCommerce checkout page 458 if ( fpdata.page_type !== 'Woo Order Received' ) { 459 if ( document.hidden ) return false; 460 } 461 462 // STOP tracking an excluded visitor (MUST be after checking FORCE LOAD) 463 if ( ! fp.main.track_current_user ) return false; 464 465 // STOP if the script is set NOT to load in the current location 466 if ( ! shouldScriptLoadHere( id ) ) return false; 467 468 // START 469 // if the tool doesn't use cookies 470 if ( tool_does_not_use_cookies ) return true; 471 472 // STOP if we wait for consent but some permissions need to be given 473 if ( fp.notice.enabled && fp.notice.mode == 'optin' && ! fpdata.cookies && permissions_a.length > 0) return false; 474 475 // STOP if user chose their cookie preferences but some required permissions are missing 476 if ( fp.notice.enabled && fpdata.cookies && permissions_a.length > 0 ) { 477 if ( ! permissions_a.every( permission => fpdata.cookies[permission] ) ) return false; 478 } 479 480 return true; 481 }; 482 483 FP.startActivityTimer = function () { 484 485 fpdata.activity.last = 0; 486 487 if (!FP.v.activityTimerRunning) { 488 FP.v.activityTimerRunning = true; 489 FP.v.activityTimer = setInterval(function () { 490 491 fpdata.activity.total++; 492 fpdata.activity.current++; 493 fpdata.activity.last++; 494 495 FP.doActions( 'active_time_tick' ); 496 497 if ( fpdata.activity.last >= 15 ) FP.stopActivityTimer(); // stop timer after 15 secs from last activity 498 }, 1000); 499 } 500 }; 501 502 FP.stopActivityTimer = function () { 503 if (FP.v.activityTimerRunning) { 504 FP.v.activityTimerRunning = false; 505 clearInterval(FP.v.activityTimer); 506 } 507 }; 508 509 FP.encode = function (string) { 510 return window.btoa(string).split('=').join('') 511 } 512 513 FP.detectAddedElement = callback => { 514 515 const observer = new MutationObserver( mutations => { 516 mutations.forEach( mutation => { 517 if ( mutation.type === 'childList' ) { 518 callback( mutation.addedNodes ); 519 } 520 } ); 521 } ); 522 523 observer.observe( document.body, { 524 childList: true, 525 subtree: true 526 } ); 527 } 528 529 FP.initCondTracking = ( events_settings, cb ) => { 530 531 if ( ! ( events_settings && fp.atrig ) ) return; 532 if ( ! fp.atrig.actions ) fp.atrig.actions = {}; 533
534 events_settings.forEach( e => { 535 536 if ( ! fp.atrig.actions[e.atrig_id] ) fp.atrig.actions[e.atrig_id] = []; 537 538 fp.atrig.actions[e.atrig_id].push( { 539 'func' : cb.bind( this, e ), 540 'repeat' : e.repeat 541 } ); 542 }); 543 } 544 545 FP.updateSessionData = () => { 546 547 check_if_can_save_wpfp_cookies(); 548 549 if ( fp.vars.consents_changed ) { 550 551 // if we can no longer use session cookies 552 if ( ! fp.vars.can_save_wpfp_cookies ) { 553 554 // delete everything 555 FP.deleteCookie( 'fp_current_session' ); 556 delete fpdata.current_session; 557 558 559 // if we can now use session cookies 560 } else { 561 562 // get latest sesion data (in case user agreed to cookies on a different tab) 563 get_session_data_from_cookies(); 564 565 // set new data if it wasn't set before (when a user has just chosen sth in the banner) 566 ! fpdata.current_session ? create_new_single_session_data() : update_single_session_data(); 567 568 }; 569 570 } else { 571 // get latest sesion data (in case user visited a different tab before opening this one) 572 get_session_data_from_cookies(); 573 } 574 } 575 576 FP.isClickTarget = function( selector ){ 577 return ( ! fpdata.clicked.link && ! fpdata.clicked.middle && fpdata.clicked.element.matches( selector ) ) || ( fpdata.clicked.link && fpdata.clicked.link.element.matches( selector ) ); 578 } 579 580 581 582 // 583 // 584 // FP - END 585 // 586 // 587 588 // BROKEN LINK TRACKING + REDIRECT TO A CUSTOM 404 PAGE 589 if ( fp.track.track404 && fpdata.page_type == "404" && ! FP.getUrlParamByName("broken_link_location") ){ 590 const location = fp.track.redirect404_url ? new URL( fp.track.redirect404_url ) : window.location; 591 window.location = location + ( location.search ? "&" : "?" ) + "broken_link_location=" + ( document.referrer || "direct_traffic_or_unknown" ) + "&broken_link=" + window.location; 592 } 593 594 // Random number (sometimes gets handy) 595 fp.random = FP.getRandomStr(7); 596 597 // CHECK FOR BOT TRAFFIC 598 // -- modified version of https://stackoverflow.com/a/65563155/7702522 599 600 fpdata.is_robot = (() => { 601 602 // SMALL list 603 if ( fp.main.bot_list == "basic" ) { 604 605 const robots = new RegExp([/bot/,/spider/,/crawl/,/APIs-Google/,/AdsBot/,/Googlebot/,/mediapartners/,/Google Favicon/,/FeedFetcher/,/Google-Read-Aloud/,/googleweblight/,/bingbot/,/yandex/,/baidu/,/duckduck/,/Yahoo Link Preview/,/ia_archiver/,/facebookexternalhit/,/pinterest\.combot/,/redditbot/,/slackbot/,/Twitterbot/,/WhatsApp/,/S[eE][mM]rushBot/].map((r) => r.source).join("|"),"i"); 606 607 return robots.test(navigator.userAgent); 608 609 // BIG list 610 } else if ( fp.main.bot_list == "big" ) { 611 612 const robots = new RegExp([ 613 /Googlebot/, /AdsBot/, /Feedfetcher-Google/, /Mediapartners-Google/, /Mediapartners/, /APIs-Google/, 614 /Google-InspectionTool/, /Storebot-Google/, /GoogleOther/, /bingbot/, /Slurp/, /wget/, /LinkedInBot/, 615 /Python-urllib/, /python-requests/, /aiohttp/, /httpx/, /libwww-perl/, /httpunit/, /Nutch/, 616 /Go-http-client/, /phpcrawl/, /msnbot/, /jyxobot/, /FAST-WebCrawler/, /FAST Enterprise Crawler/, 617 /BIGLOTRON/, /Teoma/, /convera/, /seekbot/, /Gigabot/, /Gigablast/, /exabot/, /ia_archiver/, 618 /GingerCrawler/, /webmon/, /HTTrack/, /grub\.org/, /UsineNouvelleCrawler/, /antibot/, 619 /netresearchserver/, /speedy/, /fluffy/, /findlink/, /msrbot/, /panscient/, /yacybot/, /AISearchBot/, 620 /ips-agent/, /tagoobot/, /MJ12bot/, /woriobot/, /yanga/, /buzzbot/, /mlbot/, /yandex\.combots/, 621 /purebot/, /Linguee Bot/, /CyberPatrol/, /voilabot/, /Baiduspider/, /citeseerxbot/, /spbot/, 622 /twengabot/, /postrank/, /Turnitin/, /scribdbot/, /page2rss/, /sitebot/, /linkdex/, /Adidxbot/, 623 /ezooms/, /dotbot/, /Mail\.RU_Bot/, /discobot/, /heritrix/, /findthatfile/, /europarchive\.org/, 624 /NerdByNature\.Bot/, /sistrix crawler/, /Ahrefs/, /fuelbot/, /CrunchBot/, /IndeedBot/, 625 /mappydata/, /woobot/, /ZoominfoBot/, /PrivacyAwareBot/, /Multiviewbot/, /SWIMGBot/, /Grobbot/, 626 /eright/, /Apercite/, /semanticbot/, /Aboundex/, /domaincrawler/, /wbsearchbot/, /summify/, /CCBot/, 627 /edisterbot/, /SeznamBot/, /ec2linkfinder/, /gslfbot/, /aiHitBot/, /intelium_bot/, 628 /facebookexternalhit/, /Yeti/, /RetrevoPageAnalyzer/, /lb-spider/, /Sogou/, /lssbot/, /careerbot/, 629 /wotbox/, /wocbot/, /ichiro/, /DuckDuckBot/, /lssrocketcrawler/, /drupact/, /webcompanycrawler/, 630 /acoonbot/, /openindexspider/, /gnam gnam spider/, /web-archive-net\.com\.bot/, /backlinkcrawler/, 631 /coccoc/, /integromedb/, /content crawler spider/, /toplistbot/, /it2media-domain-crawler/, 632 /ip-web-crawler\.com/, /siteexplorer\.info/, /elisabot/, /proximic/, /changedetection/, /arabot/, 633 /WeSEE:Search/, /niki-bot/, /CrystalSemanticsBot/, /rogerbot/, /360Spider/, /psbot/, 634 /InterfaxScanBot/, /CC Metadata Scaper/, /g00g1e\.net/, /GrapeshotCrawler/, /urlappendbot/, 635 /brainobot/, /fr-crawler/, /binlar/, /SimpleCrawler/, /Twitterbot/, /cXensebot/, /smtbot/, 636 /bnf\.fr_bot/, /A6-Indexer/, /ADmantX/, /Facebot/, /OrangeBot/, /memorybot/, /AdvBot/, 637 /MegaIndex/, /SemanticScholarBot/, /ltx71/, /nerdybot/, /xovibot/, /BUbiNG/, /Qwantify/, 638 /archive\.org_bot/, /Applebot/, /TweetmemeBot/, /crawler4j/, /findxbot/, /SemrushBot/, 639 /yoozBot/, /lipperhey/, /Y!J/, /Domain Re-Animator Bot/, /AddThis/, /Screaming Frog SEO Spider/, 640 /MetaURI/, /Scrapy/, /Livelapbot/, /OpenHoseBot/, /CapsuleChecker/, /collection@infegy\.com/,
641 /IstellaBot/, /DeuSu/, /betaBot/, /Cliqzbot/, /MojeekBot/, /netEstate NE Crawler/, 642 /SafeSearch microdata crawler/, /Gluten Free Crawler/, /Sonic/, /Sysomos/, /Trove/, /deadlinkchecker/, 643 /Slack-ImgProxy/, /Embedly/, /RankActiveLinkBot/, /iskanie/, /SafeDNSBot/, /SkypeUriPreview/, 644 /Veoozbot/, /Slackbot/, /redditbot/, /datagnionbot/, /Google-Adwords-Instant/, /adbeat_bot/, 645 /WhatsApp/, /contxbot/, /pinterest\.combot/, /electricmonk/, /GarlikCrawler/, /BingPreview/, 646 /vebidoobot/, /FemtosearchBot/, /Yahoo Link Preview/, /MetaJobBot/, /DomainStatsBot/, /mindUpBot/, 647 /Daum/, /Jugendschutzprogramm-Crawler/, /Xenu Link Sleuth/, /Pcore-HTTP/, /moatbot/, /KosmioBot/, 648 /pingdom/, /AppInsights/, /PhantomJS/, /Gowikibot/, /PiplBot/, /Discordbot/, /TelegramBot/, 649 /Jetslide/, /newsharecounts/, /James BOT/, /Barkrowler/, /TinEye/, /SocialRankIOBot/, 650 /trendictionbot/, /Ocarinabot/, /epicbot/, /Primalbot/, /DuckDuckGo-Favicons-Bot/, /GnowitNewsbot/, 651 /Leikibot/, /LinkArchiver/, /YaK/, /PaperLiBot/, /Digg Deeper/, /dcrawl/, /Snacktory/, /AndersPinkBot/, 652 /Fyrebot/, /EveryoneSocialBot/, /Mediatoolkitbot/, /Luminator-robots/, /ExtLinksBot/, /SurveyBot/, 653 /NING/, /okhttp/, /Nuzzel/, /omgili/, /PocketParser/, /YisouSpider/, /um-LN/, /ToutiaoSpider/, 654 /MuckRack/, /Jamie's Spider/, /AHC/, /NetcraftSurveyAgent/, /Laserlikebot/, /^Apache-HttpClient/, 655 /AppEngine-Google/, /Jetty/, /Upflow/, /Thinklab/, /Traackr\.com/, /Twurly/, /Mastodon/, /http_get/, 656 /DnyzBot/, /botify/, /007ac9 Crawler/, /BehloolBot/, /BrandVerity/, /check_http/, /BDCbot/, 657 /ZumBot/, /EZID/, /ICC-Crawler/, /ArchiveBot/, /^LCC /, /filterdb\.iss\.netcrawler/, /BLP_bbot/, 658 /BomboraBot/, /Buck/, /Companybook-Crawler/, /Genieo/, /magpie-crawler/, /MeltwaterNews/, 659 /Moreover/,/newspaper/,/ScoutJet/,/sentry/,/StorygizeBot/,/UptimeRobot/,/OutclicksBot/, 660 /seoscanners/,/Hatena/,/Google Web Preview/,/MauiBot/,/AlphaBot/,/SBL-BOT/,/IAS crawler/, 661 /adscanner/,/Netvibes/,/acapbot/,/Baidu-YunGuanCe/,/bitlybot/,/blogmuraBot/,/Bot\.AraTurka\.com/, 662 /bot-pge\.chlooe\.com/,/BoxcarBot/,/BTWebClient/,/ContextAd Bot/,/Digincore bot/,/Disqus/,/Feedly/, 663 /Fetch/,/Fever/,/Flamingo_SearchEngine/,/FlipboardProxy/,/g2reader-bot/,/G2 Web Services/,/imrbot/, 664 /K7MLWCBot/,/Kemvibot/,/Landau-Media-Spider/,/linkapediabot/,/vkShare/,/Siteimprove\.com/,/BLEXBot/, 665 /DareBoost/,/ZuperlistBot/,/Miniflux/,/Feedspot/,/Diffbot/,/SEOkicks/,/tracemyfile/,/Nimbostratus-Bot/, 666 /zgrab/,/PR-CY\.RU/,/AdsTxtCrawler/,/Datafeedwatch/,/Zabbix/,/TangibleeBot/,/google-xrawler/,/axios/, 667 /Amazon CloudFront/,/Pulsepoint/,/CloudFlare-AlwaysOnline/,/Google-Structured-Data-Testing-Tool/, 668 /WordupInfoSearch/,/WebDataStats/,/HttpUrlConnection/,/Seekport Crawler/,/ZoomBot/,/VelenPublicWebCrawler/, 669 /MoodleBot/,/jpg-newsbot/,/outbrain/,/W3C_Validator/,/Validator\.nu/,/W3C-checklink/,/W3C-mobileOK/, 670 /W3C_I18n-Checker/,/FeedValidator/,/W3C_CSS_Validator/,/W3C_Unicorn/,/Google-PhysicalWeb/,/Blackboard/, 671 /ICBot/,/BazQux/,/Twingly/,/Rivva/,/Experibot/,/awesomecrawler/,/Dataprovider\.com/,/GroupHigh/, 672 /theoldreader\.com/,/AnyEvent/,/Uptimebot\.org/,/Nmap Scripting Engine/,/2ip\.ru/,/Clickagy/, 673 /Caliperbot/,/MBCrawler/,/online-webceo-bot/,/B2B Bot/,/AddSearchBot/,/Google Favicon/,/HubSpot/, 674 /Chrome-Lighthouse/,/HeadlessChrome/,/CheckMarkNetwork/,/www\.uptime\.com/,/Streamline3Bot/,/serpstatbot/, 675 /MixnodeCache/,/^curl/,/SimpleScraper/,/RSSingBot/,/Jooblebot/,/fedoraplanet/,/Friendica/,/NextCloud/, 676 /Tiny Tiny RSS/,/RegionStuttgartBot/,/Bytespider/,/Datanyze/,/Google-Site-Verification/,/TrendsmapResolver/, 677 /tweetedtimes/,/NTENTbot/,/Gwene/,/SimplePie/,/SearchAtlas/,/Superfeedr/,/feedbot/,/UT-Dorkbot/,/Amazonbot/, 678 /SerendeputyBot/,/Eyeotabot/,/officestorebot/,/Neticle Crawler/,/SurdotlyBot/,/LinkisBot/,/AwarioSmartBot/, 679 /AwarioRssBot/,/RyteBot/,/FreeWebMonitoring SiteChecker/,/AspiegelBot/,/NAVER Blog Rssbot/,/zenback bot/, 680 /SentiBot/,/Domains Project/,/Pandalytics/,/VKRobot/,/bidswitchbot/,/tigerbot/,/NIXStatsbot/,/Atom Feed Robot/, 681 /curebot/,/PagePeeker/,/Vigil/,/rssbot/,/startmebot/,/JobboerseBot/,/seewithkids/,/NINJA bot/,/Cutbot/, 682 /BublupBot/,/BrandONbot/,/RidderBot/,/Taboolabot/,/Dubbotbot/,/FindITAnswersbot/,/infoobot/,/Refindbot/, 683 /BlogTraffic\d\.\d+ Feed-Fetcher/,/SeobilityBot/,/Cincraw/,/Dragonbot/,/VoluumDSP-content-bot/,/FreshRSS/, 684 /BitBot/,/^PHP-Curl-Class/,/Google-Certificates-Bridge/,/centurybot/,/Viber/,/e\.ventures Investment Crawler/, 685 /evc-batch/,/PetalBot/,/virustotal/,/(^| )PTST/,/minicrawler/,/Cookiebot/,/trovitBot/,/seostar\.co/,/IonCrawl/, 686 /Uptime-Kuma/,/SeekportBot/,/FreshpingBot/,/Feedbin/,/CriteoBot/,/Snap URL Preview Service/,/Better Uptime Bot/, 687 /RuxitSynthetic/,/Google-Read-Aloud/,/ValveSteam/,/OdklBot/,/GPTBot/,/ChatGPT-User/,/YandexRenderResourcesBot/, 688 /LightspeedSystemsCrawler/,/ev-crawler/,/BitSightBot/,/woorankreview/,/Google-Safety/,/AwarioB
688ot/,/DataForSeoBot/, 689 /Linespider/,/WellKnownBot/,/A Patent Crawler/,/StractBot/,/search\.marginalia\.nu/,/YouBot/,/Nicecrawler/,/Neevabot/, 690 /BrightEdge Crawler/,/SiteCheckerBotCrawler/,/TombaPublicWebCrawler/,/CrawlyProjectCrawler/,/KomodiaBot/,/KStandBot/, 691 /CISPA Webcrawler/,/MTRobot/,/hyscore\.io/,/AlexandriaOrgBot/,/2ip bot/,/Yellowbrandprotectionbot/,/SEOlizer/, 692 /vuhuvBot/,/INETDEX-BOT/,/Synapse/,/t3versionsBot/,/deepnoc/,/Cocolyzebot/,/hypestat/,/ReverseEngineeringBot/, 693 /sempi\.tech/,/Iframely/,/MetaInspector/,/node-fetch/,/lkxscan/,/python-opengraph/,/OpenGraphCheck/, 694 /developers\.google\.com\+websnippet/,/SenutoBot/,/MaCoCu/,/NewsBlur/,/inoreader/,/NetSystemsResearch/,/PageThing/, 695 /WordPress/,/PhxBot/,/ImagesiftBot/,/Expanse/,/InternetMeasurement/,/^BW/,/GeedoBot/,/Audisto Crawler/, 696 /PerplexityBot/,/claudebot/,/Monsidobot/,/GroupMeBot/].map((r) => r.source).join("|"),"i"); 697 698 return robots.test(navigator.userAgent); 699 700 } else { 701 return false; 702 }; 703 })(); 704 705 // 706 // CONSENTS 707 // 708 709 function set_consents_in_fpdata( stats = false, pers = false, market = false ){ 710 fpdata.consents = { 711 'can_track_stats' : stats, 712 'can_track_pers' : pers, 713 'can_track_market' : market, 714 }; 715 } 716 717 // GET VARS 718 719 let magic_keyw = FP.getUrlParamByName( fp.main.magic_keyword ), 720 ga4_debug = FP.getUrlParamByName("ga4_debug"); 721 722 // UPDATE COOKIE DATA - fupi_cookies and fpdata.cookies 723 724 if ( magic_keyw && magic_keyw == "off" ){ 725 726 fp.main.track_current_user = false; 727 728 fpdata.cookies = { "stats" : false, "personalisation" : false, "marketing" : false, "disabled" : true }; 729 FP.setCookie( "fp_cookie", JSON.stringify(fpdata.cookies), 7300 ); 730 731 } else if ( ga4_debug && typeof fpdata.cookies == "object" ){ 732 733 fpdata.cookies.ga4_debug = ga4_debug == "on" ? "on" : "off"; 734 FP.setCookie( "fp_ga4_debug", JSON.stringify(fpdata.cookies), 7300 ); 735 736 } else if ( FP.getUrlParamByName("reset_cookies") || ( magic_keyw && ( magic_keyw == "reset" || magic_keyw == "on" ) ) ){ 737 738 FP.deleteCookie("fp_cookie"); 739 fpdata.cookies = false; 740 741 // check if should ask for consent again 742 } else if ( fpdata.cookies && ! fpdata.cookies.disabled && fp.notice && ! fp.notice.dont_ask_again) { 743 744 let pp_pub_changed = fp.notice.priv_policy_update && ( ! fpdata.cookies.pp_pub || fpdata.cookies.pp_pub != fp.notice.priv_policy_update ), 745 tools_changed = fp.tools && ( ! fpdata.cookies.tools || ! fp.tools.every( id => fpdata.cookies.tools.includes(id) ) ); 746 747 if ( pp_pub_changed || tools_changed ) { 748 FP.deleteCookie("fp_cookie"); 749 fpdata.cookies = false; 750 } else { 751 if ( fpdata.cookies.disabled ) fp.main.track_current_user = false; 752 } 753 } 754 755 // CONSENT BANNER - apply page blur and lock scroll 756 757 if ( fp.main.track_current_user && ! fp.main.is_customizer && fp.notice.enabled && ! fpdata.cookies && fp.notice.display_notice ) { 758 759 // BLUR BACKGROUND 760 if ( fp.notice.blur_page ) document.getElementsByTagName( "html" )[0].classList.add("fupi_blur"); 761 762 // LOCK PAGESCROLL 763 if ( fp.notice.scroll_lock ) document.getElementsByTagName( "html" )[0].classList.add("fupi_scroll_lock"); 764 } 765 766 let uses_geo = false; 767 768 769 770 if ( ! uses_geo ) { 771 fpdata.country = 'unknown'; 772 FP.updateConsents(); 773 fp.ready = true; 774 } 775 776 // 777 // CONSENTS - END 778 // 779 780 // Check if WPFP can use cookies 781 782 check_if_can_save_wpfp_cookies( true ); 783 784 // Get session data from cookies 785 786 get_session_data_from_cookies(); 787 788 789 790 function create_new_single_session_data( updated_from_free = false ){ 791 792 fpdata.current_session = {}; 793 794 795 796 FP.setCookie( 'fp_current_session', JSON.stringify(fpdata.current_session), 0, 30 ); // 30 mins 797 } 798 799 function update_single_session_data(){ 800 if ( ! fpdata.current_session ) return; 801 802 FP.setCookie( 'fp_current_session', JSON.stringify(fpdata.current_session), 0, 30 ); // 30 mins 803 } 804 805 806 807 // Set or update session data when the page loads 808 function set_session_data(){ 809 810 if ( fp.vars.can_save_wpfp_cookies ) { 811 812 fpdata.new_session = ! fpdata.current_session; 813 814 ! fpdata.current_session ? create_new_single_session_data() : update_single_session_data(); 815 816 817 } else { 818 fpdata.new_session = ! fpdata.refreshed && ! window.sessionStorage['fp_old_session'] && ( window.history.length == 1 || ( window.history.length == 2 && ! document.referrer ) ); 819 } 820 821 fpdata.new_tab = ! fpdata.new_session && ! window.sessionStorage['fp_old_session'];
822 window.sessionStorage['fp_old_session'] = true; // removed on a new tab 823 }; 824 825 set_session_data(); 826 827 // CREATE, UPDATE OR REMOVE SESSION DATA 828 // triggered after users change cookie preferences or when a page gains focus 829 830 // the difference between this Fn and isAnchorLinkToCurrentPage is that this one doesn't check if the link actually works 831 function isLinkToCurrentPage (link) { 832 let target_url_obj = new URL( link.href ); 833 return target_url_obj.origin + target_url_obj.pathname == location.origin + location.pathname; 834 } 835 836 // checks if the link is an anchor to an EXISTING element on the same page 837 function isAnchorLinkToCurrentPage (link){ 838 839 let url = new URL( link.href ), 840 el = false; 841 842 try { 843 el = FP.findFirst( url.hash ); 844 } catch (e) { 845 el = false; 846 } 847 848 return !! el; 849 }; 850 851 function isOutboundLink(el){ 852 return el.href.indexOf(location.host) == -1 && el.href.indexOf('mailto:') != 0 && el.href.indexOf('tel:') != 0 && el.href.indexOf('http') == 0; 853 }; 854 855 function trackClicks(e, is_middle_click){ 856 857 let redirect_stopped = false; 858 859 if ( e.target.classList.contains('fupi_click_stopped') ) return; 860 861 // PREVENT MULTI CLICKS 862 // Do not run this function if a visitor clicks the element multiple times in a short time-span 863 var now = Date.now(); 864 if ( ! ( ! FP.v.lastClick || now - FP.v.lastClick > fp.track.dblclck_time ) ) return; 865 FP.v.lastClick = now; 866 867 // PREVENT REDIRECTS 868 869 // prevent default if a visitor clicked a link that is to be opened on the current tab 870 var link_el = e.target.closest('a[href]'); 871 872 if ( link_el && ! is_middle_click && link_el.target != '_blank' && fp.track.link_click_delay ){ 873 e.preventDefault(); // prevents redirects 874 e.stopPropagation(); // prevents other event listeners from firing 875 e.target.classList.add('fupi_click_stopped'); 876 e.target.style.pointerEvents = 'none'; 877 redirect_stopped = true; 878 if ( fp.main.debug ) console.log('[FP] link-click stopped'); 879 }; 880 881 // GET CLICK DATA 882 883 fpdata.clicked = { 884 'element' : e.target, 885 'middle' : is_middle_click, 886 }; 887 888 if ( link_el && link_el.href.length > 1 ) { 889 890 fpdata.clicked.link = {}; 891 fpdata.clicked.link.element = link_el; 892 fpdata.clicked.link.href = link_el.href; 893 fpdata.clicked.link.is_to_current_page = isLinkToCurrentPage(link_el); 894 fpdata.clicked.link.target_blank = link_el.target == '_blank'; 895 896 fpdata.clicked.link.is_outbound = isOutboundLink(link_el); 897 fpdata.clicked.link.is_anchor = isAnchorLinkToCurrentPage(link_el); 898 899 if ( link_el.href.includes('mailto:') ){ 900 fpdata.clicked.link.is_email = true; 901 fpdata.clicked.link.safe_email = link_el.href.replace('mailto:', '').split('@')[0]; 902 } 903 904 if ( link_el.href.includes('tel:') ){
905 fpdata.clicked.link.is_tel = true; 906 fpdata.clicked.link.safe_tel = link_el.href.replace('tel:','').slice(-5); 907 } 908 } 909 910 // RUN QUEUED FNs 911 912 FP.doActions( 913 'click', 914 false, // no slug 915 () => { // cb after all functions are run 916 if ( fpdata.clicked.element.classList.contains('fupi_click_stopped') && typeof fpdata.clicked.element.click == 'function' ) { 917 fpdata.clicked.element.click(); 918 fpdata.clicked.element.classList.remove('fupi_click_stopped'); 919 e.target.style.pointerEvents = ''; 920 if ( fp.main.debug ) console.log('[FP] Initiated click event after all FP functions'); 921 } 922 } 923 ); 924 }; 925 926 // Add blocked scripts to HTML 927 function addScriptsToDOM() { 928 929 // find scripts which are not loaded 930 let plain_txt_scripts = FP.findAll('script[data-fupi_class*="fupi_blocked_script"]'); 931 932 plain_txt_scripts.forEach( plain_el => { 933 934 let dta = plain_el.dataset, 935 tag_type = dta.fupi_type, 936 scr_id = dta.fupi_id, 937 url = dta.fupi_url ?? false, 938 force_load = dta.fupi_force == '1', 939 permissions_a = dta.fupi_permiss ? dta.fupi_permiss.split(' ') : [], 940 geo_a = dta.fupi_geo ? dta.fupi_geo.split(',') : []; 941 942 if ( FP.isScriptAllowedToLoad( scr_id, force_load, permissions_a, geo_a ) ) { 943 if ( tag_type == 'inline' ){ 944 loadInlineScript( plain_el ); // replaces old tag 945 } else { 946 if ( url ){ 947 FP.createTag( 948 url, 949 ()=>{ FP.loaded( scr_id, scr_id ) }, 950 plain_el, 951 tag_type 952 ); 953 FP.remove(plain_el); 954 } else { 955 if (fp.main.debug) console.error('[FP] Couldn\'t load script with ID ' + scr_id + '. Missing src or href values.') 956 } 957 } 958 } 959 960 }); 961 } 962 963 // FIRE TAGS WHEN THE DOCUMENT GETS AND LOSES FOCUS 964 965 function doPageShowActions(){ 966 967 if ( ! fp.vars.page_show_tracked ){ 968 969 fp.vars.page_show_tracked = true; 970 fp.vars.page_hide_tracked = false; 971 972 fpdata.doc_in_focus = true; 973 974 FP.startActivityTimer(); 975 976 var cookies = FP.readCookie('fp_cookie'); 977 978 if ( cookies ) { 979 if ( fpdata.cookies && cookies == JSON.stringify( fpdata.cookies ) ) return; 980 cookies = JSON.parse(cookies); 981 fpdata.cookies = cookies; 982 FP.updateConsents(); 983 } else { 984 fpdata.cookies = false; 985 } 986 987 FP.updateSessionData(); 988 989 FP.sendEvt( 'fp_load_scripts' ); 990 991 if ( fp.actions.page_in_focus ) FP.doActions('page_in_focus'); 992 } 993 } 994 995 function doPageHideActions(){ 996 if ( ! fp.vars.page_hide_tracked ) { 997 fp.vars.page_hide_tracked = true; 998 fp.vars.page_show_tracked = false; 999 1000 fpdata.doc_in_focus = false; 1001 FP.stopActivityTimer(); 1002 if ( fp.actions.page_in_blur ) FP.doActions('page_in_blur'); 1003 fpdata.activity.current = 0; 1004 } 1005 } 1006 1007 // ON DYNAMIC URL CHANGES 1008 // URL is set every second with setInterval() and not "active_time_tick", to make the check even when the page is not in focus 1009 1010 let currentUrl = location.href; 1011 1012 setInterval( () => { 1013 1014 fpdata.date = getFormattedDateTime(); 1015 1016 if ( location.href !== currentUrl ) { 1017 currentUrl = location.href; 1018 FP.doActions( 'url_change' ); 1019 } 1020 }, 1000); 1021 1022 // pagehide/show fires on some events but is cross-browser 1023 // !! it fires BEFORE visibilitychanges 1024 // !! when the pageshow event fires the document obj doesn't have updated cookies or localStarage yet, thus we don't use it for "show" 1025 window.addEventListener( "pagehide", e => { doPageHideActions() }, false ); 1026 // window.addEventListener( "pageshow", e => { doPageShowActions('pageshow') }, false ); visibilitychange is universal 1027 1028 // beforeunload doesn't work with prev/next page buttons and has problems on mobiles 1029 window.addEventListener( "beforeunload", e => { doPageHideActions() }, false ); 1030 1031 // visibilitychanges fires on all events but has problems on mobiles and in safari 1032 // !! fires AFTER pagehide/show 1033 // !! when the visibilitychange event fires for "show" the document obj already has updated cookies and localStarage 1034 document.addEventListener( 'visibilitychange', function () { 1035 document.hidden ? doPageHideActions() : doPageShowActions('visibilitychange'); 1036 //if ( fp.page_visibility ) FP.doActions( 'page_visibility' ); 1037 }, false); 1038 1039 document.documentElement.addEventListener('mouseleave', (e) => { 1040 // Don't fire if the page is losing focus (not a real mouse leave) 1041 if (document.hidden) return; 1042 1043 // Check if mouse actually left viewport boundaries 1044 if (e.clientY <= 0 || e.clientX <= 0 || 1045 e.clientX >= window.innerWidth || e.clientY >= window.innerHeight) { 1046 FP.doActions('mouse_leave_window'); 1047 } 1048 }, false ); 1049 1050 1051 // load custom/extra scripts to DOM 1052 document.addEventListener( 'fp_load_scripts', addScriptsToDOM ); 1053 1054 function formSubmitEvents( {target} ){
1055 fpdata.submitted_form = { 'element' : target }; 1056 FP.doActions( 'form_submit' ); 1057 } 1058 1059 // ON FIELD BLUR 1060 document.addEventListener('blur', function(e) { 1061 // Check if the blurred element is a form field 1062 if ( e.target.nodeType === Node.ELEMENT_NODE && e.target.matches('input, textarea, select') ) { 1063 fpdata.last_form_field = e.target; 1064 FP.doActions( 'form_field_blur' ); 1065 } 1066 }, true); 1067 1068 // ON DOM LOADED 1069 1070 function do_when_DOM_loaded(){ 1071 1072 addScriptsToDOM(); 1073 1074 // init actions waiting for DOM loaded 1075 setTimeout ( ()=>{ FP.doActions( ['dom_loaded'] ) }, 250 ); // makes sure that footer JS files loaded and hooked to dom_loaded event 1076 1077 // start listening to form submits 1078 setTimeout( ()=>{ document.addEventListener('submit', formSubmitEvents ) }, fp.track.formsubm_trackdelay ? fp.track.formsubm_trackdelay * 1000 : 1000 ); 1079 1080 // start listening to DOM modifications 1081 if ( fp.track.use_mutation_observer ) FP.detectAddedElement( addedNodes => { 1082 let els = FP.nl2Arr( addedNodes ); 1083 if ( els.length > 0 ) FP.doActions( ['dom_modified'], els ); 1084 } ); 1085 } 1086 1087 if ( document.readyState === "complete" ) { 1088 do_when_DOM_loaded(); 1089 } else { 1090 document.addEventListener('DOMContentLoaded', do_when_DOM_loaded ); 1091 } 1092 1093 // ON MOUSE MOVE 1094 document.addEventListener( 'mousemove', FP.throttle( FP.startActivityTimer, 500 ) ); // This starts the user's activity timer 1095 1096 if ( ! document.hidden) { 1097 FP.startActivityTimer(); 1098 fpdata.doc_in_focus = true; 1099 fp.vars.page_show_tracked = true; 1100 fp.vars.page_hide_tracked = false; 1101 }; 1102 1103 // ON LEFT CLICK 1104 window.addEventListener( 'click', function (e) { 1105 if ( fp.actions.click ) trackClicks(e, false); 1106 }, true); 1107 1108 // ON MIDDLE CLICK 1109 window.addEventListener( 'mouseup', function(e) { 1110 if ( fp.actions.click && e.button == 1) trackClicks(e, true); 1111 }); 1112 1113 FP.loaded('head_helpers'); 1114 1115}; 1116 1117FP.load('head_helpers', 'fupi_top_helpers', ['head_js'] );
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.