PageSourceSearch

https://www.seed.ab.ca/wp-content/plugins/full-picture-premium/public/common/fupi-helpers.js?ver=10.1.2.1

js seed.ab.ca collected 2026-10-04 00:18:51 UTC 59,263 bytes, 1,482 lines download raw bytes

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        /* <fs_premium_only> */
94        if ( fp.main.is_pro ) {
95            let recent_sessions_cookie = FP.readCookie('fp_recent_sessions');
96            if ( recent_sessions_cookie ) fpdata.recent_sessions = JSON.parse(recent_sessions_cookie);
97        }
98        /* </fs_premium_only> */
99    }
100
101    //
102    // FP - start
103    //
104
105    FP.setCookie = (name, value, days = 0, mins = 0, path = "/") => {
106		var expires = "";
107		if ( days > 0 || mins > 0 ) {
108			var date = new Date();
109			if (days > 0) {
110				date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
111			} else {
112				date.setTime(date.getTime() + mins * 60 * 1000);
113			}
114			expires = "; expires=" + date.toGMTString();
115		};
116		d.cookie = name + "=" + value + expires + "; path=" + path + "; sameSite=strict";
117	};
118
119    FP.deleteCookie = name => { FP.setCookie(name, "", -1); };
120
121    FP.getUrlParamByName = ( name, url = false ) => {
122		if ( ! url ) {
123            url = window.location.search;
124        } else {
125            url = url.split('?')[1];
126			if ( ! url ) return null;
127        };
128		
129		const queryString = url,
130			urlParams = new URLSearchParams(queryString);
131		return urlParams.get(name);
132	};
133
134    FP.nl2Arr = nl => nl ? [].slice.call(nl) : false;
135
136    FP.findID = (e, c) => {
137        if ( c === null ) return null;
138        if ( !e ) return false;
139        c = c || document;
140        return c.getElementById(e);
141    };
142
143    FP.findFirst = (e, c) => {
144        if ( c === null ) return null;
145        if ( !e ) return false;
146        c = c || document;
147        return c.querySelector(e);
148    };
149
150    FP.findAll = (e, c) => {
151		if ( c === null ) return [];
152		if ( ! e ) return false;
153		c = c || document;
154		return FP.nl2Arr(c.querySelectorAll(e));
155	};
156
157    FP.getScript = ( url, cb, attrs, tag = 'script', fallback = false ) => {
158
159		attrs = attrs || false;
160		var new_tag = "";
161
162		if ( tag == "img" ) {
163			new_tag = d.createElement('img');
164			new_tag.src = url;
165		} else if ( tag == "link" ) {
166			new_tag = d.createElement('link');
167			new_tag.href = url;
168		} else {
169			new_tag = d.createElement('script')
170			new_tag.src = url;
171			new_tag.type = 'application/javascript';
172		}
173
174        if ( cb ) new_tag.onload = cb;
175
176		if ( fallback ) new_tag.onerror = ()=>{ FP.getScript( fallback, cb, attrs, tag ); };
177
178		if ( attrs ) {
179            // check if attrs holds a reference to and HTML tag
180            if ( attrs instanceof Element ) {
181                let old_tag = attrs;
182                // copy from the original tag (with exceptions)
183                Array.from( old_tag.attributes ).forEach( attr => {
184                    if ( attr.name !== 'type' && attr.name !== 'data-src' && ! attr.name.includes('fupi') ) {
185                        new_tag.setAttribute( attr.name, attr.value );
186                    }
187                });
188            // or add provided values
189            } else {
190                for (var key in attrs) {
191                    if ( key !== "/" ) new_tag.setAttribute(key, attrs[key]);
192                }
193            }
194		}
195
196		d.getElementsByTagName("head")[0].appendChild(new_tag);
197	};
198
199    FP.createTag = FP.getScript;
200
201    function loadInlineScript(temp_script) {
202		
203		let scr_id = temp_script.dataset?.fupi_id,
204            orig_id = scr_id.replace('fp_',''),
205            is_cscr = temp_script.dataset?.module == 'cscr';
206        
207        if ( ! scr_id ) return;
208
209		let new_script = document.createElement('script');
210
211		new_script.innerHTML = temp_script.innerHTML;
212		temp_script.parentNode.replaceChild(new_script, temp_script);
213
214		if ( fp.main.debug ) {
215            if ( is_cscr && fp.cscr[orig_id] ) {
216                console.log( "[FP] Custom script loaded: " + fp.cscr[orig_id] );
217            } else {
218                console.log( "[FP] Inline script loaded");
219            }
220        };
221
222		fp.loaded.push( orig_id );
223	};
224
225    FP.sendEvt = (evt_name, details_a) => {
226        var details = details_a ? { 'detail' : details_a } : {},
227            fp_event = new CustomEvent( evt_name, details );
228        document.dispatchEvent(fp_event);
229    };
230
231    FP.getRandomStr = ()=>{
232		return ( Math.random() + 1 ).toString(36).substring(2);
233	};
234
235    FP.updateConsents = () => {
236
237		if ( fp.vars.use_other_cmp ) return;
238		
239		if ( ! fp.main.track_current_user ) {
240
241			if ( fp.main.debug ) console.log("[FP] Current user cannot be tracked");
242
243		// if the user made a choice in the past
244		} else if ( fpdata.cookies ){
245
246			set_consents_in_fpdata( fpdata?.cookies?.stats, fpdata?.cookies?.personalisation, fpdata?.cookies?.marketing );
247
248		// if no choice was made in the past
249		} else {
250			
251			// deny all if consent banner is in optin mode
252			if ( fp.notice.enabled && fp.notice.mode == "optin" ) {
253				
254				set_consents_in_fpdata();
255			
256			// agree to all if consent banner is disabled or we are in optout or notification mode
257			} else {
258				set_consents_in_fpdata(true, true, true);
259			}
260		}
261
262		if ( ! fp.main.is_customizer ) {
263
264			// set MS Ads consent
265			
266			if ( fp.main.track_current_user && fpdata.cookies ){
267				if ( fpdata.cookies.stats && fpdata.cookies.marketing ) {
268					window.uetq.push( "consent", "update", {
269						"ad_storage": "granted"
270					});
271				}
272			} else {
273				if ( ! ( fp.notice.enabled && fp.notice.mode == "optin" ) ) {
274					window.uetq.push( "consent", "update", {
275						"ad_storage": "granted"
276					});
277				}
278			}
279			
280			// Set GTAG consents
281
282			["gtag", "fupi_gtm_gtag"].forEach( tag_name => {
283
284				if ( tag_name == "fupi_gtm_gtag" && ! window.fupi_gtm_gtag ) return;
285
286				// update if the user made a choice in the past
287				if ( fp.main.track_current_user && fpdata.cookies ){
288
289					FP.set_gtag_consents( tag_name, "update", fpdata?.cookies?.stats, fpdata?.cookies?.personalisation, fpdata?.cookies?.marketing );
290					if ( fp.main.debug ) console.log("[FP] Google consents set to user choices");
291				
292				// if no choice was made in the past
293				} else {
294					
295					// agree to all if consent banner is disabled or is in optout or notification mode
296					if ( ! ( fp.notice.enabled && fp.notice.mode == "optin" ) ) {
297
298						FP.set_gtag_consents( tag_name, "update", true, true, true );
299						if ( fp.main.debug ) console.log("[FP] All Google consents granted");
300
301					};
302				}
303			} );
304		}
305	}
306
307	FP.postToServer = ( event_data_a, cb = false ) => {
308
309		if ( fpdata.is_robot ) return;
310		if ( fp.main.debug ) console.log( "[FP] Posting to server", event_data_a );
311
312		let fetch_url = fp.main.server_method == "rest" ? "/index.php?rest_route=/fupi/v1/sender" : "/wp-admin/admin-ajax.php?action=fupi_ajax";
313
314		if ( fp.main.debug || event_data_a[0][0] == 'cdb') {
315		
316			fetch( fetch_url, {
317				method: "POST",
318				body: JSON.stringify( event_data_a ),
319				credentials: 'same-origin',
320				headers: {
321					"Content-type": "application/json; charset=UTF-8",
322					// "X-WP-Nonce": fp_nonce
323				}
324			})
325			.then((response) => response.json())
326			.then((json) => {
327				if ( cb ) { 
328					cb(json);
329				} else {
330					console.log( "[FP] Server response", json);
331				}
332			});
333
334		} else {
335
336			fetch( fetch_url, {
337				method: "POST",
338				credentials: 'same-origin',
339				body: JSON.stringify( event_data_a ),
340				headers: {
341					"Content-type": "application/json; charset=UTF-8",
342					// "X-WP-Nonce": fp_nonce
343				}
344			});
345		}
346	};
347
348    // fires function every X ms with an option to fire them ADDITIONALLY on start ("leading") and after events ("trailing")
349    FP.throttle = function (func, wait, options) {
350        var context, args, result;
351        var timeout = null;
352        var previous = 0;
353        if (!options) options = {};
354
355        var later = function later() {
356            previous = options.leading === false ? 0 : Date.now();
357            timeout = null;
358            result = func.apply(context, args);
359            if (!timeout) context = args = null;
360        };
361
362        return function () {
363            var now = Date.now();
364            if (!previous && options.leading === false) previous = now;
365            var remaining = wait - (now - previous);
366            context = this;
367            args = arguments;
368
369            if (remaining <= 0 || remaining > wait) {
370                if (timeout) {
371                    clearTimeout(timeout);
372                    timeout = null;
373                }
374
375                previous = now;
376                result = func.apply(context, args);
377                if (!timeout) context = args = null;
378            } else if (!timeout && options.trailing !== false) {
379                timeout = setTimeout(later, remaining);
380            }
381
382            return result;
383        };
384    };
385
386    FP.remove = el => { if (el) el.parentNode.removeChild(el); };
387
388    FP.addAction = function( action_name, fn ){
389
390        let arr = Array.isArray(action_name) ? action_name : [action_name]; // make sure that trigger is in an array
391
392        arr.forEach(function ( name , i) { 
393            if ( ! fp.actions[name] ) fp.actions[name] = [];
394            fp.actions[name].push( fn );
395        });
396    }
397
398    FP.doActions = ( action_name, args_o = null, cb ) => {
399        if ( fp.actions[action_name] ) fp.actions[action_name].forEach( fn => {
400            let return_val = fn( args_o );
401            if ( typeof return_val !== 'undefined' ) args_o = return_val;
402        } );
403        if ( cb ) cb( args_o );
404    }
405
406    FP.hasActions = action_name => {
407        return fp.actions[action_name] && fp.actions[action_name].length > 0;
408    };
409
410    FP.isScriptAllowedToLoad = function( id, force_load, permissions_a, geo_a ){
411    
412        // STOP if we don't have all the basic data yet
413        if ( ! fp.ready ) return false;
414
415        // STOP if the script has already loaded
416        if ( fp.loaded.includes(id) ) return false;
417
418        // START if the load is forced
419        if ( force_load ) return true;
420
421        // STOP if the tab is not in focus AND we are NOT on WooCommerce checkout page
422        if ( fpdata.page_type !== 'Woo Order Received' ) {
423            if ( document.hidden ) return false;
424        }
425
426        // STOP tracking an excluded visitor (MUST be after checking FORCE LOAD)
427        if ( ! fp.main.track_current_user ) return false;
428
429        // STOP if the script is set NOT to load in the current location
430        if ( geo_a && ! shouldCustomScriptLoadHere( geo_a ) ) return false;
431
432        // STOP if any permissions need to be given in this location...
433        if ( fp.notice.enabled && fp.notice.mode == 'optin' && ! fpdata.cookies && permissions_a.length > 0 ) return false;
434
435        // STOP if required permissions are not given
436        if ( fpdata.cookies && permissions_a.length > 0 ) {
437            if ( ! permissions_a.every( permission => fpdata.cookies[permission] ) ) return false;
438        }
439
440        return true;
441    };
442
443    FP.isAllowedToLoad = function( module_id, permissions_a, required_a, nr, tool_does_not_use_cookies = false ) {
444
445        var id = nr ? module_id + nr : module_id;
446
447        // STOP if we don't have all the basic data yet
448        if ( ! fp.ready ) return false;
449
450        // STOP if we have no settings for this tool
451        if ( ! fp[id] ) return false;
452
453        // STOP if the script has already loaded
454        if ( fp.loaded.includes(id) ) return false;
455
456        // STOP if the required data is missing
457        if ( required_a.some( req => ! fp[id][req] ) ) return false;
458
459        // START if the load is forced
460        if ( fp[id].force_load ) return true;
461
462        // STOP if the tab is not in focus AND we are NOT on WooCommerce checkout page
463        if ( fpdata.page_type !== 'Woo Order Received' ) {
464            if ( document.hidden ) return false;
465        }
466
467        // STOP tracking an excluded visitor (MUST be after checking FORCE LOAD)
468        if ( ! fp.main.track_current_user ) return false;
469
470        // STOP if the script is set NOT to load in the current location
471        if ( ! shouldScriptLoadHere( id ) ) return false;
472
473        // START 
474        // if the tool doesn't use cookies
475        if ( tool_does_not_use_cookies ) return true;
476
477        // STOP if we wait for consent but some permissions need to be given
478        if ( fp.notice.enabled && fp.notice.mode == 'optin' && ! fpdata.cookies && permissions_a.length > 0) return false;
479
480        // STOP if user chose their cookie preferences but some required permissions are missing
481        if ( fp.notice.enabled && fpdata.cookies && permissions_a.length > 0 ) {
482            if ( ! permissions_a.every( permission => fpdata.cookies[permission] ) ) return false;
483        }
484
485        return true;
486    };
487
488    FP.startActivityTimer = function () {
489
490        fpdata.activity.last = 0;
491
492        if (!FP.v.activityTimerRunning) {
493            FP.v.activityTimerRunning = true;
494            FP.v.activityTimer = setInterval(function () {
495
496                fpdata.activity.total++;
497                fpdata.activity.current++;
498                fpdata.activity.last++;
499
500                FP.doActions( 'active_time_tick' );
501
502                if ( fpdata.activity.last >= 15 ) FP.stopActivityTimer(); // stop timer after 15 secs from last activity
503            }, 1000);
504        }
505    };
506
507    FP.stopActivityTimer = function () {
508        if (FP.v.activityTimerRunning) {
509            FP.v.activityTimerRunning = false;
510            clearInterval(FP.v.activityTimer);
511        }
512    };
513
514    FP.encode = function (string) {
515        return window.btoa(string).split('=').join('')
516    }
517
518    FP.detectAddedElement = callback => {
519
520		const observer = new MutationObserver( mutations => {
521		  	mutations.forEach( mutation => {
522				if ( mutation.type === 'childList' ) {
523			  		callback( mutation.addedNodes );
524				}
525		  	} );
526		} );
527	  
528		observer.observe( document.body, {
529			childList: true,
530			subtree: true
531		} );
532	}
533
534    FP.initCondTracking = ( events_settings, cb ) => {
535		
536		if ( ! ( events_settings && fp.atrig ) ) return;
537		if ( ! fp.atrig.actions ) fp.atrig.actions = {};
538
539		events_settings.forEach( e => {
540			
541			if ( ! fp.atrig.actions[e.atrig_id] ) fp.atrig.actions[e.atrig_id] = [];
542			
543			fp.atrig.actions[e.atrig_id].push( {
544				'func' : cb.bind( this, e ),
545				'repeat' : e.repeat
546			} );
547		});
548	}
549
550    FP.updateSessionData = () => {
551        
552        check_if_can_save_wpfp_cookies();
553
554        if ( fp.vars.consents_changed ) {
555            
556            // if we can no longer use session cookies
557            if ( ! fp.vars.can_save_wpfp_cookies ) {
558                
559                // delete everything
560                FP.deleteCookie( 'fp_current_session' );
561                delete fpdata.current_session;
562                /* <fs_premium_only> */
563                if ( fp.main.is_pro ) {
564                    FP.deleteCookie( 'fp_recent_sessions' );
565                    delete fpdata.recent_sessions;
566                }
567                /* </fs_premium_only> */
568            
569            // if we can now use session cookies
570            } else {
571
572                // get latest sesion data (in case user agreed to cookies on a different tab)
573                get_session_data_from_cookies();
574
575                // set new data if it wasn't set before (when a user has just chosen sth in the banner)
576                ! fpdata.current_session ? create_new_single_session_data() : update_single_session_data();
577                /* <fs_premium_only> */
578                if ( fp.main.is_pro ) {
579                    ! fpdata.recent_sessions ? create_new_recent_sessions_data() : update_recent_sessions_data();
580                }
581                /* </fs_premium_only> */
582            };
583
584        } else {
585            // get latest sesion data (in case user visited a different tab before opening this one)
586            get_session_data_from_cookies();
587        }
588    }
589    
590    FP.isClickTarget = function( selector ){
591		return ( ! fpdata.clicked.link && ! fpdata.clicked.middle && fpdata.clicked.element.matches( selector ) ) || ( fpdata.clicked.link && fpdata.clicked.link.element.matches( selector ) );
592	}
593
594    /* <fs_premium_only> */
595
596    FP.changeReferrer = ref => {
597	
598		if ( ref == 'non_http' ) {
599			
600			let rules = JSON.parse( fp.track.track_non_http );
601
602			for ( const nr in rules ) {
603				
604				let match_found = false;
605
606				if ( rules[nr].compare == "incl" ) {
607					match_found = document.referrer.includes(rules[nr].search); 
608				} else if ( rules[nr].compare == "start" ){
609					match_found = document.referrer.indexOf(rules[nr].search) == 0;
610				} else {
611					match_found = rules[nr].search == document.referrer;
612				};
613				
614				if ( match_found ) {
615					ref = rules[nr].replace.indexOf('http') == 0 ? rules[nr].replace : "https://" + rules[nr].replace;
616					ref = ref.slice(-1) == "/" ? ref : ref + "/";
617					delete window.document.referrer;
618					window.document.__defineGetter__('referrer', ()=>{return ref;});
619					break;
620				}
621			}
622
623		} else if ( ref == 'switch_ref' ) {
624			
625			let rules = JSON.parse( fp.track.switch_ref );
626
627			for ( const nr in rules ) {
628				if ( document.location.href.includes( rules[nr].search ) ) {
629					delete window.document.referrer;
630					window.document.__defineGetter__('referrer', ()=>{return rules[nr].replace;});
631					break;
632				};
633			}
634
635		} else {
636			delete window.document.referrer;
637			window.document.__defineGetter__('referrer', ()=>{return ref;});
638		}
639	};
640
641    /* </fs_premium_only> */
642
643    //
644    //
645    // FP - END
646    //
647    //
648
649    // BROKEN LINK TRACKING + REDIRECT TO A CUSTOM 404 PAGE
650	if ( fp.track.track404 && fpdata.page_type == "404" && ! FP.getUrlParamByName("broken_link_location") ){
651		const location = fp.track.redirect404_url ? new URL( fp.track.redirect404_url ) : window.location;
652		window.location = location + ( location.search ? "&" : "?" ) + "broken_link_location=" + ( document.referrer || "direct_traffic_or_unknown" ) + "&broken_link=" + window.location;
653	}
654	
655    // Random number (sometimes gets handy)
656	fp.random = FP.getRandomStr(7);
657
658    // CHECK FOR BOT TRAFFIC
659	// -- modified version of https://stackoverflow.com/a/65563155/7702522
660
661	fpdata.is_robot = (() => {
662		
663		// SMALL list
664		if ( fp.main.bot_list == "basic" ) {
665			
666			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");
667
668			return robots.test(navigator.userAgent);
669
670		// BIG list
671		} else if ( fp.main.bot_list == "big" ) {
672
673			const robots = new RegExp([
674				/Googlebot/, /AdsBot/, /Feedfetcher-Google/, /Mediapartners-Google/, /Mediapartners/, /APIs-Google/, 
675				/Google-InspectionTool/, /Storebot-Google/, /GoogleOther/, /bingbot/, /Slurp/, /wget/, /LinkedInBot/, 
676				/Python-urllib/, /python-requests/, /aiohttp/, /httpx/, /libwww-perl/, /httpunit/, /Nutch/, 
677				/Go-http-client/, /phpcrawl/, /msnbot/, /jyxobot/, /FAST-WebCrawler/, /FAST Enterprise Crawler/, 
678				/BIGLOTRON/, /Teoma/, /convera/, /seekbot/, /Gigabot/, /Gigablast/, /exabot/, /ia_archiver/, 
679				/GingerCrawler/, /webmon/, /HTTrack/, /grub\.org/, /UsineNouvelleCrawler/, /antibot/, 
680				/netresearchserver/, /speedy/, /fluffy/, /findlink/, /msrbot/, /panscient/, /yacybot/, /AISearchBot/, 
681				/ips-agent/, /tagoobot/, /MJ12bot/, /woriobot/, /yanga/, /buzzbot/, /mlbot/, /yandex\.combots/, 
682				/purebot/, /Linguee Bot/, /CyberPatrol/, /voilabot/, /Baiduspider/, /citeseerxbot/, /spbot/, 
683				/twengabot/, /postrank/, /Turnitin/, /scribdbot/, /page2rss/, /sitebot/, /linkdex/, /Adidxbot/, 
684				/ezooms/, /dotbot/, /Mail\.RU_Bot/, /discobot/, /heritrix/, /findthatfile/, /europarchive\.org/, 
685				/NerdByNature\.Bot/, /sistrix crawler/, /Ahrefs/, /fuelbot/, /CrunchBot/, /IndeedBot/, 
686				/mappydata/, /woobot/, /ZoominfoBot/, /PrivacyAwareBot/, /Multiviewbot/, /SWIMGBot/, /Grobbot/, 
687				/eright/, /Apercite/, /semanticbot/, /Aboundex/, /domaincrawler/, /wbsearchbot/, /summify/, /CCBot/, 
688				/edisterbot/, /SeznamBot/, /ec2linkfinder/, /gslfbot/, /aiHitBot/, /intelium_bot/, 
689				/facebookexternalhit/, /Yeti/, /RetrevoPageAnalyzer/, /lb-spider/, /Sogou/, /lssbot/, /careerbot/, 
690				/wotbox/, /wocbot/, /ichiro/, /DuckDuckBot/, /lssrocketcrawler/, /drupact/, /webcompanycrawler/, 
691				/acoonbot/, /openindexspider/, /gnam gnam spider/, /web-archive-net\.com\.bot/, /backlinkcrawler/, 
692				/coccoc/, /integromedb/, /content crawler spider/, /toplistbot/, /it2media-domain-crawler/, 
693				/ip-web-crawler\.com/, /siteexplorer\.info/, /elisabot/, /proximic/, /changedetection/, /arabot/, 
694				/WeSEE:Search/, /niki-bot/, /CrystalSemanticsBot/, /rogerbot/, /360Spider/, /psbot/, 
695				/InterfaxScanBot/, /CC Metadata Scaper/, /g00g1e\.net/, /GrapeshotCrawler/, /urlappendbot/, 
696				/brainobot/, /fr-crawler/, /binlar/, /SimpleCrawler/, /Twitterbot/, /cXensebot/, /smtbot/, 
697				/bnf\.fr_bot/, /A6-Indexer/, /ADmantX/, /Facebot/, /OrangeBot/, /memorybot/, /AdvBot/, 
698				/MegaIndex/, /SemanticScholarBot/, /ltx71/, /nerdybot/, /xovibot/, /BUbiNG/, /Qwantify/, 
699				/archive\.org_bot/, /Applebot/, /TweetmemeBot/, /crawler4j/, /findxbot/, /SemrushBot/, 
700				/yoozBot/, /lipperhey/, /Y!J/, /Domain Re-Animator Bot/, /AddThis/, /Screaming Frog SEO Spider/, 
701				/MetaURI/, /Scrapy/, /Livelapbot/, /OpenHoseBot/, /CapsuleChecker/, /collection@infegy\.com/, 
702				/IstellaBot/, /DeuSu/, /betaBot/, /Cliqzbot/, /MojeekBot/, /netEstate NE Crawler/, 
703				/SafeSearch microdata crawler/, /Gluten Free Crawler/, /Sonic/, /Sysomos/, /Trove/, /deadlinkchecker/, 
704				/Slack-ImgProxy/, /Embedly/, /RankActiveLinkBot/, /iskanie/, /SafeDNSBot/, /SkypeUriPreview/, 
705				/Veoozbot/, /Slackbot/, /redditbot/, /datagnionbot/, /Google-Adwords-Instant/, /adbeat_bot/, 
706				/WhatsApp/, /contxbot/, /pinterest\.combot/, /electricmonk/, /GarlikCrawler/, /BingPreview/, 
707				/vebidoobot/, /FemtosearchBot/, /Yahoo Link Preview/, /MetaJobBot/, /DomainStatsBot/, /mindUpBot/, 
708				/Daum/, /Jugendschutzprogramm-Crawler/, /Xenu Link Sleuth/, /Pcore-HTTP/, /moatbot/, /KosmioBot/, 
709				/pingdom/, /AppInsights/, /PhantomJS/, /Gowikibot/, /PiplBot/, /Discordbot/, /TelegramBot/, 
710				/Jetslide/, /newsharecounts/, /James BOT/, /Barkrowler/, /TinEye/, /SocialRankIOBot/, 
711				/trendictionbot/, /Ocarinabot/, /epicbot/, /Primalbot/, /DuckDuckGo-Favicons-Bot/, /GnowitNewsbot/, 
712				/Leikibot/, /LinkArchiver/, /YaK/, /PaperLiBot/, /Digg Deeper/, /dcrawl/, /Snacktory/, /AndersPinkBot/, 
713				/Fyrebot/, /EveryoneSocialBot/, /Mediatoolkitbot/, /Luminator-robots/, /ExtLinksBot/, /SurveyBot/, 
714				/NING/, /okhttp/, /Nuzzel/, /omgili/, /PocketParser/, /YisouSpider/, /um-LN/, /ToutiaoSpider/, 
715				/MuckRack/, /Jamie's Spider/, /AHC/, /NetcraftSurveyAgent/, /Laserlikebot/, /^Apache-HttpClient/, 
716				/AppEngine-Google/, /Jetty/, /Upflow/, /Thinklab/, /Traackr\.com/, /Twurly/, /Mastodon/, /http_get/, 
717				/DnyzBot/, /botify/, /007ac9 Crawler/, /BehloolBot/, /BrandVerity/, /check_http/, /BDCbot/, 
718				/ZumBot/, /EZID/, /ICC-Crawler/, /ArchiveBot/, /^LCC /, /filterdb\.iss\.netcrawler/, /BLP_bbot/, 
719				/BomboraBot/, /Buck/, /Companybook-Crawler/, /Genieo/, /magpie-crawler/, /MeltwaterNews/,
720				/Moreover/,/newspaper/,/ScoutJet/,/sentry/,/StorygizeBot/,/UptimeRobot/,/OutclicksBot/,
721				/seoscanners/,/Hatena/,/Google Web Preview/,/MauiBot/,/AlphaBot/,/SBL-BOT/,/IAS crawler/,
722				/adscanner/,/Netvibes/,/acapbot/,/Baidu-YunGuanCe/,/bitlybot/,/blogmuraBot/,/Bot\.AraTurka\.com/,
723				/bot-pge\.chlooe\.com/,/BoxcarBot/,/BTWebClient/,/ContextAd Bot/,/Digincore bot/,/Disqus/,/Feedly/,
724				/Fetch/,/Fever/,/Flamingo_SearchEngine/,/FlipboardProxy/,/g2reader-bot/,/G2 Web Services/,/imrbot/,
725				/K7MLWCBot/,/Kemvibot/,/Landau-Media-Spider/,/linkapediabot/,/vkShare/,/Siteimprove\.com/,/BLEXBot/,
726				/DareBoost/,/ZuperlistBot/,/Miniflux/,/Feedspot/,/Diffbot/,/SEOkicks/,/tracemyfile/,/Nimbostratus-Bot/,
727				/zgrab/,/PR-CY\.RU/,/AdsTxtCrawler/,/Datafeedwatch/,/Zabbix/,/TangibleeBot/,/google-xrawler/,/axios/,
728				/Amazon CloudFront/,/Pulsepoint/,/CloudFlare-AlwaysOnline/,/Google-Structured-Data-Testing-Tool/,
729				/WordupInfoSearch/,/WebDataStats/,/HttpUrlConnection/,/Seekport Crawler/,/ZoomBot/,/VelenPublicWebCrawler/,
730				/MoodleBot/,/jpg-newsbot/,/outbrain/,/W3C_Validator/,/Validator\.nu/,/W3C-checklink/,/W3C-mobileOK/,
731				/W3C_I18n-Checker/,/FeedValidator/,/W3C_CSS_Validator/,/W3C_Unicorn/,/Google-PhysicalWeb/,/Blackboard/,
732				/ICBot/,/BazQux/,/Twingly/,/Rivva/,/Experibot/,/awesomecrawler/,/Dataprovider\.com/,/GroupHigh/,
733				/theoldreader\.com/,/AnyEvent/,/Uptimebot\.org/,/Nmap Scripting Engine/,/2ip\.ru/,/Clickagy/,
734				/Caliperbot/,/MBCrawler/,/online-webceo-bot/,/B2B Bot/,/AddSearchBot/,/Google Favicon/,/HubSpot/,
735				/Chrome-Lighthouse/,/HeadlessChrome/,/CheckMarkNetwork/,/www\.uptime\.com/,/Streamline3Bot/,/serpstatbot/,
736				/MixnodeCache/,/^curl/,/SimpleScraper/,/RSSingBot/,/Jooblebot/,/fedoraplanet/,/Friendica/,/NextCloud/,
737				/Tiny Tiny RSS/,/RegionStuttgartBot/,/Bytespider/,/Datanyze/,/Google-Site-Verification/,/TrendsmapResolver/,
738				/tweetedtimes/,/NTENTbot/,/Gwene/,/SimplePie/,/SearchAtlas/,/Superfeedr/,/feedbot/,/UT-Dorkbot/,/Amazonbot/,
739				/SerendeputyBot/,/Eyeotabot/,/officestorebot/,/Neticle Crawler/,/SurdotlyBot/,/LinkisBot/,/AwarioSmartBot/,
740				/AwarioRssBot/,/RyteBot/,/FreeWebMonitoring SiteChecker/,/AspiegelBot/,/NAVER Blog Rssbot/,/zenback bot/,
741				/SentiBot/,/Domains Project/,/Pandalytics/,/VKRobot/,/bidswitchbot/,/tigerbot/,/NIXStatsbot/,/Atom Feed Robot/,
742				/curebot/,/PagePeeker/,/Vigil/,/rssbot/,/startmebot/,/JobboerseBot/,/seewithkids/,/NINJA bot/,/Cutbot/,
743				/BublupBot/,/BrandONbot/,/RidderBot/,/Taboolabot/,/Dubbotbot/,/FindITAnswersbot/,/infoobot/,/Refindbot/,
744				/BlogTraffic\d\.\d+ Feed-Fetcher/,/SeobilityBot/,/Cincraw/,/Dragonbot/,/VoluumDSP-content-bot/,/FreshRSS/,
745				/BitBot/,/^PHP-Curl-Class/,/Google-Certificates-Bridge/,/centurybot/,/Viber/,/e\.ventures Investment Crawler/,
746				/evc-batch/,/PetalBot/,/virustotal/,/(^| )PTST/,/minicrawler/,/Cookiebot/,/trovitBot/,/seostar\.co/,/IonCrawl/,
747				/Uptime-Kuma/,/SeekportBot/,/FreshpingBot/,/Feedbin/,/CriteoBot/,/Snap URL Preview Service/,/Better Uptime Bot/,
748				/RuxitSynthetic/,/Google-Read-Aloud/,/ValveSteam/,/OdklBot/,/GPTBot/,/ChatGPT-User/,/YandexRenderResourcesBot/,
749				/LightspeedSystemsCrawler/,/ev-crawler/,/BitSightBot/,/woorankreview/,/Google-Safety/,/AwarioB
749ot/,/DataForSeoBot/,
750				/Linespider/,/WellKnownBot/,/A Patent Crawler/,/StractBot/,/search\.marginalia\.nu/,/YouBot/,/Nicecrawler/,/Neevabot/,
751				/BrightEdge Crawler/,/SiteCheckerBotCrawler/,/TombaPublicWebCrawler/,/CrawlyProjectCrawler/,/KomodiaBot/,/KStandBot/,
752				/CISPA Webcrawler/,/MTRobot/,/hyscore\.io/,/AlexandriaOrgBot/,/2ip bot/,/Yellowbrandprotectionbot/,/SEOlizer/,
753				/vuhuvBot/,/INETDEX-BOT/,/Synapse/,/t3versionsBot/,/deepnoc/,/Cocolyzebot/,/hypestat/,/ReverseEngineeringBot/,
754				/sempi\.tech/,/Iframely/,/MetaInspector/,/node-fetch/,/lkxscan/,/python-opengraph/,/OpenGraphCheck/,
755				/developers\.google\.com\+websnippet/,/SenutoBot/,/MaCoCu/,/NewsBlur/,/inoreader/,/NetSystemsResearch/,/PageThing/,
756				/WordPress/,/PhxBot/,/ImagesiftBot/,/Expanse/,/InternetMeasurement/,/^BW/,/GeedoBot/,/Audisto Crawler/,
757				/PerplexityBot/,/claudebot/,/Monsidobot/,/GroupMeBot/].map((r) => r.source).join("|"),"i");
758
759				return robots.test(navigator.userAgent);
760
761		} else {
762			return false;
763		};
764	})();
765
766    //
767    // CONSENTS
768    //
769
770	function set_consents_in_fpdata( stats = false, pers = false, market = false ){
771		fpdata.consents = {
772			'can_track_stats' : stats,
773			'can_track_pers' : pers,
774			'can_track_market' : market,
775		};
776	}
777
778    // GET VARS
779
780	let magic_keyw = FP.getUrlParamByName( fp.main.magic_keyword ),
781		ga4_debug = FP.getUrlParamByName("ga4_debug");
782
783	// UPDATE COOKIE DATA - fupi_cookies and fpdata.cookies
784
785	if ( magic_keyw && magic_keyw == "off" ){
786
787		fp.main.track_current_user = false;
788		
789		fpdata.cookies = { "stats" : false, "personalisation" : false, "marketing" : false, "disabled" : true };
790		FP.setCookie( "fp_cookie", JSON.stringify(fpdata.cookies), 7300 );
791
792	} else if ( ga4_debug && typeof fpdata.cookies == "object" ){
793			
794		fpdata.cookies.ga4_debug = ga4_debug == "on" ? "on" : "off";
795		FP.setCookie( "fp_ga4_debug", JSON.stringify(fpdata.cookies), 7300 );
796
797	} else if ( FP.getUrlParamByName("reset_cookies") || ( magic_keyw && ( magic_keyw == "reset" || magic_keyw == "on" ) ) ){
798
799		FP.deleteCookie("fp_cookie");
800		fpdata.cookies = false;
801
802	// check if should ask for consent again
803	} else if ( fpdata.cookies && ! fpdata.cookies.disabled && fp.notice && ! fp.notice.dont_ask_again) {
804
805        let pp_pub_changed = fp.notice.priv_policy_update && ( ! fpdata.cookies.pp_pub || fpdata.cookies.pp_pub != fp.notice.priv_policy_update ),
806            tools_changed = fp.tools && ( ! fpdata.cookies.tools || ! fp.tools.every( id => fpdata.cookies.tools.includes(id) ) );
807
808        if ( pp_pub_changed || tools_changed ) {
809            FP.deleteCookie("fp_cookie");
810            fpdata.cookies = false;
811        } else {
812            if ( fpdata.cookies.disabled ) fp.main.track_current_user = false;
813        }
814	}
815
816    // CONSENT BANNER - apply page blur and lock scroll
817
818	if ( fp.main.track_current_user && ! fp.main.is_customizer && fp.notice.enabled && ! fpdata.cookies && fp.notice.display_notice ) {
819		
820		// BLUR BACKGROUND
821		if ( fp.notice.blur_page ) document.getElementsByTagName( "html" )[0].classList.add("fupi_blur");
822		
823		// LOCK PAGESCROLL
824		if ( fp.notice.scroll_lock ) document.getElementsByTagName( "html" )[0].classList.add("fupi_scroll_lock");
825	}
826
827    let uses_geo = false;
828
829    /* <fs_premium_only> */
830	
831    if ( fp.main.is_pro ) {
832
833        // MODIFY REFERRER
834
835        if ( fp.track.join_ref && document.referrer.indexOf('http') == 0 ){
836
837            if ( fp.track.join_ref.includes('fb') ) {
838                if ( !! document.referrer.match(/https*:\/\/.+\.facebook\.com\/*/) || document.referrer == "https://www.facebook.com/" || document.referrer == "http://m.facebook.com" ) {
839                    FP.changeReferrer('https://facebook.com/');
840                }
841            }
842
843            if ( fp.track.join_ref.includes('insta') ) {
844                if ( !! document.referrer.match(/https*:\/\/.+\.instagram\.com\/*/) || document.referrer == "http://instagram.com/" ) {
845                    FP.changeReferrer('https://instagram.com/');
846                }
847            }
848
849            if ( fp.track.join_ref.includes('pin') ) {
850                if ( document.referrer.includes(".pinterest.") && ! document.referrer.includes("/pin/") && ! document.referrer.includes("utm_") ) {
851                    FP.changeReferrer('https://pinterest.com/');
852                }
853            }
854        }
855
856        if ( fp.track.track_non_http ){
857            if ( document.referrer.length > 0 && document.referrer.indexOf('http') !== 0 ){
858                if ( ! document.location.search.includes('utm_') && ! document.location.search.includes('gad_s
858ource') ) {
859                    FP.changeReferrer('non_http');
860                }
861            }
862        }
863
864        if ( fp.track.switch_ref ){
865            FP.changeReferrer('switch_ref');
866        }
867    }
868
869    function setNoticeMode(){
870
871        if ( fp.notice.enabled && ( fp.notice.main_mode == "auto_strict" || fp.notice.main_mode == "auto_lax" || fp.notice.main_mode == "manual" ) ) {
872                
873            // country unknown
874            if ( fpdata.country == "unknown" ) {
875                
876                fp.notice.mode = fp.notice.enable_scripts_after ? fp.notice.enable_scripts_after : "optin";
877            
878            // country known, region unknown
879            } else if ( fpdata.region == "unknown" ){
880
881                let country_regex = new RegExp( "[\\s,]*" + fpdata.country + "\\s*," );
882                if ( fp.notice.optin ){
883                    let optin_countries = "," + fp.notice.optin.replace( /\([A-Z,\s]*\)/g, '') + ",";
884                    if ( fp.notice.optin == "all" || fp.notice.optin != "none" && !! optin_countries.match( country_regex ) ) fp.notice.mode = "optin";
885                };
886
887                if ( ! fp.notice.mode && fp.notice.optout ){
888                    let optout_countries = "," + fp.notice.optout.replace( /\([A-Z,\s]*\)/g, '') + ",";
889                    if ( fp.notice.optout == "all" || fp.notice.optout != "none" && !! optout_countries.match( country_regex ) ) fp.notice.mode = "optout";
890                };
891
892                if ( ! fp.notice.mode && fp.notice.inform ){
893                    let inform_countries = "," + fp.notice.inform.replace( /\([A-Z,\s]*\)/g, '') + ",";
894                    if ( fp.notice.inform == "all" || fp.notice.inform != "none" && !! inform_countries.match( country_regex ) ) fp.notice.mode = "notify";
895                };
896
897                if ( ! fp.notice.mode ) fp.notice.mode = "hide";
898
899            // country & region known
900            } else {
901                
902                let country_regex = new RegExp( "[\\s,]*" + fpdata.country + "\\s*," ),
903                    region_regex = new RegExp( "[\\s,]*" + fpdata.country + "\\s*\\(.*" + fpdata.region + ".*\\)" );
904
905                if ( fp.notice.optin ){
906                    let optin_countries = "," + fp.notice.optin + ",";
907                    if ( fp.notice.optin == "all" || fp.notice.optin != "none" && ( !! optin_countries.match( region_regex ) || !! optin_countries.match( country_regex ) ) ) fp.notice.mode = "optin";
908                };
909
910                if ( ! fp.notice.mode && fp.notice.optout ){
911                    let optout_countries = "," + fp.notice.optout + ",";
912                    if ( fp.notice.optout == "all" || fp.notice.optout != "none" && ( !! optout_countries.match( region_regex ) || !! optout_countries.match( country_regex ) ) ) fp.notice.mode = "optout";
913                };
914
915                if ( ! fp.notice.mode && fp.notice.inform ){
916                    let inform_countries = "," + fp.notice.inform + ",";
917                    if ( fp.notice.inform == "all" || fp.notice.inform != "none" && ( !! inform_countries.match( region_regex ) || !! inform_countries.match( country_regex ) ) ) fp.notice.mode = "notify";
918                };
919
920                if ( ! fp.notice.mode ) fp.notice.mode = "hide";
921            }
922        } else {
923            if ( fp.notice.enabled ) fp.notice.mode = fp.notice.main_mode;
924        }
925    }
926
927    if ( fp.main.is_pro && fp.main.geo ) { // disables geo for users who moved from pro to free when this JS is saved in file
928
929        uses_geo = true;
930        fp.ready = false;
931    
932        FP.init = function( send_evt = true ){
933            setNoticeMode();
934            FP.updateConsents();
935            fp.ready = true;
936            if ( send_evt ) FP.sendEvt( 'fp_load_scripts', ['all'] );
937        }
938    
939        // GET LOCATION AND TRIGGER EVERYTHING
940        
941        // Get country from URL param...
942
943        let url_country = FP.getUrlParamByName("fp_set_country");
944        
945        if ( url_country ) {
946            if ( fp.main.debug ) console.log( "[FP] Geolocation - country set manually" );
947            let expire = fp.main.geo && fp.main.remember_geo ? fp.main.remember_geo : 30;
948            FP.setCookie( "fp_country", url_country, expire );
949            fpdata.country = url_country;
950            FP.init();
951        
952        // If we are not setting country with a URL parameter...
953
954        } else {
955
956            // Check if it was saved in a cookie
957
958            let remembered_country = FP.readCookie("fp_country");
959            
960            // if we have no country in a cookie then we check geolocation
961            if ( ( ! remembered_country || ( remembered_country == "unknown" && fpdata.new_session ) ) && window.fetch ) {
962                
963                fpdata.country = "checking";
964
965                let has_req_geo_settings = true;
966
967                switch ( fp.main.geo ) {
968                    case 'ipdata':
969                        if ( ! fp.main.ipdata_api_key ) has_req_geo_settings = false;
970                    break;
971                    case 'cf_worker_url':
972                        if ( ! fp.main.cf_worker_url ) has_req_geo_settings = false;
973                    break;
974                }
975
976                if ( ! has_req_geo_settings ) {
977                    console.error('[FP] Missing configuration data for geolocation method ' + fp.main.geo );
978                }
979
980                if ( ! fpdata.is_robot && has_req_geo_settings ){
981                    
982                    if ( fp.main.debug ) console.log( "[FP] Checking location with " + fp.main.geo );
983                    
984                    // Set timeout that will be cleared on success
985                    let geoTimeout = setTimeout(() => {
986                        if (fpdata.country === "checking") {
987                            fpdata.country = "unknown";
988                            FP.setCookie('fp_country', 'unknown');
989                            if (!fp.ready) {
990                                if (fp.main.debug) console.log("[FP] Geolocation timeout");
991                                FP.init();
992                            }
993                        }
994                    }, 1000);
995                    
996                    // Use the new FP.fetchGeo function
997                    FP.fetchGeo( fp.main.geo, {
998                        ajaxUrl: fp.main.ajax_url
999                    })
1000                    .then(result => {
1001                        clearTimeout(geoTimeout); // Prevent duplicate init
1002                        
1003                        fpdata.country = result.country;
1004                        if (fp.main.debug) console.log("[FP] Geolocated country: " + result.country );
1005
1006                        fpdata.geo = result.geo;
1007                        if (result.region !== 'unknown') fpdata.region = result.region;
1008                        
1009                        let expire = fp.main?.remember_geo || 30;
1010                        let cookieValue = result.country;
1011                        if (Array.isArray(result.region)) {
1012                            cookieValue += '|' + (result.region[0] || "") + "|" + (result.region[1] || "");
1013                        }
1014                        
1015                        FP.setCookie('fp_country', cookieValue, expire);
1016                        if (!fp.ready) FP.init();
1017                    })
1018                    .catch(() => {
1019                        clearTimeout(geoTimeout); // Prevent duplicate init
1020                        fpdata.country = 'unknown';
1021                        FP.setCookie('fp_country', 'unknown');
1022                        if (!fp.ready) FP.init();
1023                    });
1024                    
1025                } else {
1026                    fpdata.country = 'unknown';
1027                    FP.init(false);
1028                }
1029
1030            // If we have a country in a cookie, then we use it
1031            } else {
1032
1033                let location = remembered_country.split("|");
1034                
1035                fpdata.country = location[0] || "unknown";
1036                fpdata.region = location[1] || location[2] || "unknown";
1037                
1038                if ( fp.notice.enabled && ! fp.notice.mode ) {
1039                    FP.init(false);
1039 // we need to run FP.init() to learn the mode and style of the notice when it is opened by a privacy policy link. We do not send the evt
1040                } else {
1041                    FP.updateConsents();
1042                    fp.ready = true;
1043                }
1044            }
1045        }
1046    }
1047
1048    /* </fs_premium_only> */
1049
1050    if ( ! uses_geo ) {
1051		fpdata.country = 'unknown';
1052		FP.updateConsents();
1053		fp.ready = true;
1054	}
1055
1056    //
1057    // CONSENTS - END
1058    //
1059
1060    // Check if WPFP can use cookies
1061
1062    check_if_can_save_wpfp_cookies( true );
1063
1064    // Get session data from cookies
1065
1066    get_session_data_from_cookies();
1067
1068    /* <fs_premium_only> */
1069    // Functions for creatining and updating session cookies
1070    function get_url_params(){
1071
1072        if ( ! fp.main.is_pro ) return {};
1073        
1074        if ( ! document.location.search ) return {};
1075
1076        // turn "search" string into an object with param keys and values
1077        let search_str = document.location.search.length > 3 ? document.location.search.substring(1) : false,
1078            params = {};
1079
1080        if ( search_str ) {
1081
1082            let search_arr = search_str.split('&');
1083
1084            search_arr.forEach(str => {
1085                let arr = str.split('=');
1086                if ( arr.length == 2 ){
1087                    let param_name = arr[0].replace('?',''),
1088                        param_val = arr[1].replace('%20', ' '); // add spaces where they should appear
1089
1090                    params[param_name] = param_val;
1091                }
1092            })
1093        }
1094
1095        return params;
1096    }
1097    /* </fs_premium_only> */
1098
1099    function create_new_single_session_data( updated_from_free = false ){
1100        
1101        fpdata.current_session = {};
1102
1103        /* <fs_premium_only> */
1104        if ( fp.main.is_pro ) {
1105            
1106            let url_params = get_url_params();
1107    
1108            fpdata.current_session = {
1109                'url_params' : url_params,
1110                'landing_page' : {
1111                    'url' : document.location.href,
1112                    'url_params' : url_params,
1113                    'referrer' : document.referrer
1114                },
1115                'page_views' : updated_from_free ? 0 : 1,
1116                'page_types' : {},
1117                'lead_score' : 0,
1118                'lead_level' : 0,
1119            };
1120
1121            fpdata.current_session.page_types[fpdata.page_type] = 1; // count views of this pagetype
1122        }
1123        /* </fs_premium_only> */
1124
1125        FP.setCookie( 'fp_current_session', JSON.stringify(fpdata.current_session), 0, 30 ); // 30 mins
1126    }
1127
1128    function update_single_session_data(){
1129        if ( ! fpdata.current_session ) return;
1130        /* <fs_premium_only> */
1131        if ( fp.main.is_pro ) {
1132
1133            // If a WP FP Free saved empty current_session object, then update it to Pro object
1134            if ( ! fpdata.current_session.page_types ) create_new_single_session_data( true );
1135
1136            fpdata.current_session.page_views++;
1137            fpdata.current_session.page_types[fpdata.page_type] = fpdata.current_session.page_types[fpdata.page_type] ? fpdata.current_session.page_types[fpdata.page_type] + 1 : 1;
1138        }
1139        /* </fs_premium_only> */
1140        FP.setCookie( 'fp_current_session', JSON.stringify(fpdata.current_session), 0, 30 ); // 30 mins
1141    }
1142
1143    /* <fs_premium_only> */
1144    function create_new_recent_sessions_data(){
1145
1146        fpdata.recent_sessions = {
1147            'sessions' : 1,
1148            'page_views' : 1,
1149            'page_types' : {},
1150            'reactions' : [] // Initialize empty reactions array
1151        };
1152
1153        fpdata.recent_sessions.page_types[fpdata.page_type] = 1; // count views of this pagetype
1154
1155        FP.setCookie('fp_recent_sessions', JSON.stringify(fpdata.recent_sessions), 30 ); // 30 days
1156    }
1157
1158    function update_recent_sessions_data(){
1159        if ( ! fpdata.recent_sessions ) return;
1160        if ( fpdata.new_session ) fpdata.recent_sessions.sessions++; // add 1 session only if we start a new session
1161        fpdata.recent_sessions.page_views++; // add 1 pageview
1162        fpdata.recent_sessions.page_types[fpdata.page_type] = fpdata.recent_sessions.page_types[fpdata.page_type] ? fpdata.recent_sessions.page_types[fpdata.page_type] + 1 : 1; // count views of this pagetype
1163        FP.setCookie('fp_recent_sessions', JSON.stringify(fpdata.recent_sessions), 30 );
1163 // 30 days
1164    }
1165    
1166    /* </fs_premium_only> */
1167
1168    // Set or update session data when the page loads
1169    function set_session_data(){
1170        
1171        if ( fp.vars.can_save_wpfp_cookies ) {
1172            
1173            fpdata.new_session = ! fpdata.current_session;
1174            
1175            ! fpdata.current_session ? create_new_single_session_data() : update_single_session_data();
1176            /* <fs_premium_only> */
1177            if ( fp.main.is_pro ) { 
1178                ! fpdata.recent_sessions ? create_new_recent_sessions_data() : update_recent_sessions_data();
1179            }
1180            /* </fs_premium_only> */
1181
1182        } else {
1183            fpdata.new_session = ! fpdata.refreshed && ! window.sessionStorage['fp_old_session'] && ( window.history.length == 1 || ( window.history.length == 2 && ! document.referrer ) );
1184        }
1185
1186        fpdata.new_tab = ! fpdata.new_session && ! window.sessionStorage['fp_old_session'];
1187        window.sessionStorage['fp_old_session'] = true; // removed on a new tab
1188    };
1189
1190    set_session_data();
1191
1192    // CREATE, UPDATE OR REMOVE SESSION DATA
1193    // triggered after users change cookie preferences or when a page gains focus
1194    
1195    // the difference between this Fn and isAnchorLinkToCurrentPage is that this one doesn't check if the link actually works
1196    function isLinkToCurrentPage (link) {
1197        let target_url_obj = new URL( link.href );
1198        return target_url_obj.origin + target_url_obj.pathname == location.origin + location.pathname;
1199    }
1200	
1201	// checks if the link is an anchor to an EXISTING element on the same page
1202	function isAnchorLinkToCurrentPage (link){
1203        
1204        let url = new URL( link.href ),
1205            el = false;
1206        
1207        try {
1208            el = FP.findFirst( url.hash );
1209        } catch (e) {
1210            el = false;
1211        }
1212        
1213        return !! el;
1214    };
1215
1216	function isOutboundLink(el){
1217        return el.href.indexOf(location.host) == -1 && el.href.indexOf('mailto:') != 0 && el.href.indexOf('tel:') != 0 && el.href.indexOf('http') == 0;
1218    };
1219
1220	function trackClicks(e, is_middle_click){
1221
1222		let redirect_stopped = false;
1223
1224		if ( e.target.classList.contains('fupi_click_stopped') ) return;
1225		
1226		// PREVENT MULTI CLICKS
1227	    // Do not run this function if a visitor clicks the element multiple times in a short time-span
1228	    var now = Date.now();
1229	    if ( ! ( ! FP.v.lastClick || now - FP.v.lastClick > fp.track.dblclck_time ) ) return;
1230	    FP.v.lastClick = now;
1231		
1232		// PREVENT REDIRECTS
1233
1234		// prevent default if a visitor clicked a link that is to be opened on the current tab
1235		var link_el = e.target.closest('a[href]');
1236
1237		if ( link_el && ! is_middle_click && link_el.target != '_blank' && fp.track.link_click_delay  ){
1238			e.preventDefault(); // prevents redirects
1239			e.stopPropagation(); // prevents other event listeners from firing
1240			e.target.classList.add('fupi_click_stopped');
1241			e.target.style.pointerEvents = 'none';
1242			redirect_stopped = true;
1243			if ( fp.main.debug ) console.log('[FP] link-click stopped');
1244		};
1245
1246		// GET CLICK DATA
1247
1248		fpdata.clicked = {
1249			'element' : e.target,
1250			'middle' : is_middle_click,
1251		};
1252
1253		if ( link_el && link_el.href.length > 1 ) {
1254
1255			fpdata.clicked.link 				= {};
1256			fpdata.clicked.link.element			= link_el;
1257			fpdata.clicked.link.href			= link_el.href;
1258			fpdata.clicked.link.is_to_current_page = isLinkToCurrentPage(link_el);
1259			fpdata.clicked.link.target_blank 	= link_el.target == '_blank';
1260
1261			fpdata.clicked.link.is_outbound 	= isOutboundLink(link_el);
1262			fpdata.clicked.link.is_anchor 		= isAnchorLinkToCurrentPage(link_el);
1263
1264            if ( link_el.href.includes('mailto:') ){
1265                fpdata.clicked.link.is_email = true;
1266                fpdata.clicked.link.safe_email = link_el.href.replace('mailto:', '').split('@')[0];
1267            }
1268
1269            if ( link_el.href.includes('tel:') ){
1270                fpdata.clicked.link.is_tel = true;
1271                fpdata.clicked.link.safe_tel = link_el.href.replace('tel:','').slice(-5);
1272            }
1273		}
1274
1275		// RUN QUEUED FNs
1276
1277		FP.doActions( 
1278			'click', 
1279			false, // no slug
1280			() => { // cb after all functions are run
1281				if ( fpdata.clicked.element.classList.contains('fupi_click_stopped') && typeof fpdata.clicked.element.click == 'function' ) {
1282					fpdata.clicked.element.click();
1283					fpdata.clicked.element.classList.remove('fupi_click_stopped');
1284					e.target.style.pointerEvents = '';
1285					if ( fp.main.debug ) console.log('[FP] Initiated click event after all FP functions');
1286				}
1287			}
1288		);
1289    };
1290
1291    // Add blocked scripts to HTML
1292    function addScriptsToDOM() {
1293
1294        // find scripts which are not loaded
1295        let plain_txt_scripts = FP.findAll('script[data-fupi_class*="fupi_blocked_script"]');
1296
1297        plain_txt_scripts.forEach( plain_el => {
1298            
1299            let dta = plain_el.dataset,
1300                tag_type = dta.fupi_type,
1301                scr_id = dta.fupi_id,
1302                url = dta.fupi_url ?? false,
1303                force_load = dta.fupi_force == '1',
1304                permissions_a = dta.fupi_permiss ? dta.fupi_permiss.split(' ') : [],
1305                geo_a = dta.fupi_geo ? dta.fupi_geo.split(',') : [];
1306            
1307            if ( FP.isScriptAllowedToLoad( scr_id, force_load, permissions_a, geo_a ) ) {
1308                if ( tag_type == 'inline' ){
1309                    loadInlineScript( plain_el ); // replaces old tag
1310                } else {
1311                    if ( url ){
1312                        FP.createTag(
1313                            url, 
1314                            ()=>{ FP.loaded( scr_id, scr_id ) }, 
1315                            plain_el,
1316                            tag_type
1317                        );
1318                        FP.remove(plain_el);
1319                    } else {
1320                        if (fp.main.debug) console.error('[FP] Couldn\'t load script with ID ' + scr_id +  '. Missing src or href values.')
1321                    }
1322                }
1323            }
1324                
1325        });
1326	}
1327
1328    // FIRE TAGS WHEN THE DOCUMENT GETS AND LOSES FOCUS
1329
1330    function doPageShowActions(){
1331
1332        if ( ! fp.vars.page_show_tracked ){
1333
1334            fp.vars.page_show_tracked = true;
1335            fp.vars.page_hide_tracked = false;
1336
1337            fpdata.doc_in_focus = true;
1338            
1339            FP.startActivityTimer();
1340            
1341            var cookies = FP.readCookie('fp_cookie');
1342
1343            if ( cookies ) {
1344                if ( fpdata.cookies && cookies == JSON.stringify( fpdata.cookies ) ) return;
1345                cookies = JSON.parse(cookies);
1346                fpdata.cookies = cookies;
1347                FP.updateConsents();
1348            } else {
1349                fpdata.cookies = false;
1350            }
1351            
1352            FP.updateSessionData();
1353
1354            FP.sendEvt( 'fp_load_scripts' );
1355
1356            if ( fp.actions.page_in_focus ) FP.doActions('page_in_focus');
1357        }
1358    }
1359
1360    function doPageHideActions(){
1361        if ( ! fp.vars.page_hide_tracked ) {
1362            fp.vars.page_hide_tracked = true;
1363            fp.vars.page_show_tracked = false;
1364
1365            fpdata.doc_in_focus = false;
1366            FP.stopActivityTimer();
1367            if ( fp.actions.page_in_blur ) FP.doActions('page_in_blur');
1368            fpdata.activity.current = 0;
1369        }
1370    }
1371
1372    // ON DYNAMIC URL CHANGES
1373    // URL is set every second with setInterval() and not "active_time_tick", to make the check even when the page is not in focus
1374
1375	let currentUrl = location.href;
1376	  
1377	setInterval( () => { 
1378
1379        fpdata.date = getFormattedDateTime();
1380
1381		if ( location.href !== currentUrl ) {
1382			currentUrl = location.href;
1383			FP.doActions( 'url_change' );
1384		}
1385	}, 1000);
1386
1387    // pagehide/show fires on some events but is cross-browser
1388    // !! it fires BEFORE visibilitychanges
1389    // !! when the pageshow event fires the document obj doesn't have updated cookies or localStarage yet, thus we don't use it for "show"
1390    window.addEventListener( "pagehide", e => { doPageHideActions() }, false );
1391    // window.addEventListener( "pageshow", e => { doPageShowActions('pageshow') }, false ); visibilitychange is universal
1392
1393    // beforeunload doesn't work with prev/next page buttons and has problems on mobiles
1394    window.addEventListener( "beforeunload", e => { doPageHideActions() }, false );
1395
1396    // visibilitychanges fires on all events but has problems on mobiles and in safari
1397    // !! fires AFTER pagehide/show
1398    // !! when the visibilitychange event fires for "show" the document obj already has updated cookies and localStarage
1399    document.addEventListener( 'visibilitychange', function () {
1400        document.hidden ? doPageHideActions() :  doPageShowActions('visibilitychange');
1401        //if ( fp.page_visibility ) FP.doActions( 'page_visibility' );
1402    }, false);
1403
1404    document.documentElement.addEventListener('mouseleave', (e) => {
1405        // Don't fire if the page is losing focus (not a real mouse leave)
1406        if (document.hidden) return;
1407        
1408        // Check if mouse actually left viewport boundaries
1409        if (e.clientY <= 0 || e.clientX <= 0 || 
1410            e.clientX >= window.innerWidth || e.clientY >= window.innerHeight) {
1411            FP.doActions('mouse_leave_window');
1412        }
1413    }, false );
1414
1415
1416    // load custom/extra scripts to DOM
1417    document.addEventListener( 'fp_load_scripts', addScriptsToDOM );
1418
1419    function formSubmitEvents( {target} ){
1420        fpdata.submitted_form = { 'element' : target };
1421		FP.doActions( 'form_submit' );
1422    }
1423
1424    // ON FIELD BLUR
1425    document.addEventListener('blur', function(e) {
1426        // Check if the blurred element is a form field
1427        if ( e.target.nodeType === Node.ELEMENT_NODE && e.target.matches('input, textarea, select') ) {
1428            fpdata.last_form_field = e.target;
1429            FP.doActions( 'form_field_blur' );
1430        }
1431    }, true);
1432
1433    // ON DOM LOADED
1434
1435    function do_when_DOM_loaded(){
1436        
1437        addScriptsToDOM();
1438
1439        // init actions waiting for DOM loaded
1440        setTimeout ( ()=>{ FP.doActions( ['dom_loaded'] ) }, 250 ); // makes sure that footer JS files loaded and hooked to dom_loaded event
1441
1442        // start listening to form submits
1443        setTimeout( ()=>{ document.addEventListener('submit', formSubmitEvents ) }, fp.track.formsubm_trackdelay ? fp.track.formsubm_trackdelay * 1000 : 1000 );
1444        
1445        // start listening to DOM modifications
1446		if ( fp.track.use_mutation_observer ) FP.detectAddedElement( addedNodes => {
1447            let els = FP.nl2Arr( addedNodes );
1448            if ( els.length > 0 ) FP.doActions( ['dom_modified'], els );
1449        } );
1450    }
1451
1452    if ( document.readyState === "complete" ) {
1453        do_when_DOM_loaded();
1454    } else {
1455        document.addEventListener('DOMContentLoaded', do_when_DOM_loaded );
1456    }
1457
1458    // ON MOUSE MOVE
1459    document.addEventListener( 'mousemove', FP.throttle( FP.startActivityTimer, 500 ) ); // This starts the user's activity timer
1460
1461    if ( ! document.hidden) {
1462        FP.startActivityTimer();
1463        fpdata.doc_in_focus = true;
1464        fp.vars.page_show_tracked = true;
1465        fp.vars.page_hide_tracked = false;
1466    };
1467
1468    // ON LEFT CLICK
1469	window.addEventListener( 'click', function (e) {
1470		if ( fp.actions.click ) trackClicks(e, false);
1471	}, true);
1472
1473	// ON MIDDLE CLICK
1474	window.addEventListener( 'mouseup', function(e) {
1475		if ( fp.actions.click && e.button == 1) trackClicks(e, true);
1476	});
1477
1478    FP.loaded('head_helpers');
1479
1480};
1481
1482FP.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.