1FP.fns.ga4_set_g_ids = function(nr, type, ID) { 2 3 let ga_ids = FP.readCookie('fp_ga_ids'); 4 5 if ( ga_ids ) { 6 ga_ids = JSON.parse( ga_ids ); 7 } else { 8 ga_ids = {}; 9 } 10 11 ga_ids['ga4' + nr + '_' + type] = ID; 12 13 FP.setCookie( 'fp_ga_ids', JSON.stringify(ga_ids) ); 14} 15 16FP.fns.load_ga4 = function (nr, wait_for_nr2) { 17 18 var _ga = fp[ 'ga4' + nr ], 19 params = {'groups' : 'fupi_ga4'}; 20 21 // CHANGE COOKIE PREFIX 22 // optional for the tracking ID #1 23 if ( _ga.cookie_prefix && nr == '1' ) params.cookie_prefix = '_fp4' + nr; 24 25 // enable GA's debug mode 26 // @ https://support.google.com/analytics/answer/7201382?hl=en 27 params.debug_mode = !! fpdata.cookies.ga4_debug; 28 29 /* <fs_premium_only> */ 30 if ( fp.main.is_pro ) { 31 32 // CHANGE COOKIE PREFIX 33 // obligatory for tracking ID #2 34 if ( nr == '2' ) params.cookie_prefix = '_fp4' + nr; 35 36 // SET "USER ID" (ENCODED) 37 if ( _ga.set_user_id && fpdata.user.id ) { 38 params.user_id = window.btoa(fpdata.user.id).split('=').join(''); 39 } 40 } 41 /* </fs_premium_only> */ 42 43 // SEND CLEAN "PAGE TITLE" 44 if ( _ga.clean_page_title && fpdata.page_title && fpdata.page_title.length > 0 ) { 45 params.page_title = fpdata.page_title; 46 47 // "SEO TITLE" CUSTOM DIMENSION 48 if ( _ga.seo_title ) { 49 params.seo_title = document.title; 50 } 51 } 52 53 // "PAGE LANGUAGE" CUSTOM DIMENSION 54 if ( _ga.page_lang ) { 55 params[_ga.page_lang] = document.documentElement.lang || 'undefined'; 56 } 57 58 // "PAGE TYPE" CONTENT GROUPING 59 if ( _ga.page_type && fpdata.page_type && fpdata.page_type.length > 1 ) { 60 params[_ga.page_type] = fpdata.page_type; 61 } 62 63 // "PAGE ID" CUSTOM DIMENSION 64 if ( _ga.page_id && fpdata.page_id && fpdata.page_id > 0 ) { 65 params[_ga.page_id] = '' + fpdata.page_id; 66 } 67 68 // "PAGE NUMBER" CUSTOM DIMENSION 69 if ( _ga.page_number && fpdata.page_number > 0 ) { 70 params[_ga.page_number] = '' + fpdata.page_number; 71 } 72 73 // "AUTHOR DISPLAY NAME" CUSTOM DIMENSION 74 if ( _ga.post_author && fpdata.author_name && fpdata.author_name.length > 0 ) { 75 params[_ga.post_author] = fpdata.author_name; 76 } 77 78 // "AUTHOR ID" CUSTOM DIMENSION 79 if ( _ga.author_id && fpdata.author_id > 0 ) { 80 params[_ga.author_id] = '' + fpdata.author_id; 81 } 82 83 // "PUBLISHED DATE" CUSTOM DIMENSION 84 if ( _ga.post_date && fpdata.published && fpdata.published.length > 0 ) { 85 params[_ga.post_date] = fpdata.published; 86 } 87 88 /* <fs_premium_only> */ 89 if ( fp.main.is_pro ) { 90 // "CUSTOM META" CUSTOM DIMENSIONS 91 if ( _ga.track_cf ) { 92 for (const i in _ga.track_cf ) { 93 94 let dimension_name = _ga.track_cf[i].dimname, 95 field_data = _ga.track_cf[i].id, 96 field_type = field_data.includes('|') ? field_data.split('|')[0] : 'post', 97 field_id = field_data.includes('|') ? field_data.split('|')[1] : field_data; 98 99 field_type = field_type == 'post' ? 'custom' : field_type + '_custom'; 100 101 if ( fpdata[field_type] && ( fpdata[field_type][field_id] || fpdata[field_type][field_id] === false ) ) { 102 params[dimension_name] = fpdata[field_type][field_id] + ''; 103 } 104 } 105 } 106 } 107 /* </fs_premium_only> */ 108 109 // "SEARCH RESULTS" CUSTOM METRIC 110 if ( _ga.search_results_nr && _ga.search_results_nr.length > 0 && fpdata.search_results && fpdata.search_results > 0 ) { 111 params[_ga.search_results_nr] = parseInt(fpdata.search_results); 112 } 113 114 // "USER ROLE" USER PROPERTY 115 if ( _ga.user_role ) { 116 if ( fpdata.user.role && fpdata.user.role.length > 0 ) { 117 params[_ga.user_role] = fpdata.user.role; 118 } 119 } 120 121 // "TAXONOMY TERMS" CUSTOM DIMENSION 122 if ( _ga.tax_terms && fpdata.terms && fpdata.terms.length > 0 ) { 123 124 var term_arr = fpdata.terms.map( function (term_data) { 125 var term = _ga.send_tax_terms_titles ? term_data.name : term_data.slug; 126 term += _ga.add_tax_term_cat ? ' (' + term_data.taxonomy + ')' : ''; 127 return term; 128 } ); 129 130 params[_ga.tax_terms] = term_arr.join(', '); 131 } 132 133 window.gtag( 'config', _ga.id, params ); 134 135 /* <fs_premium_only> */ 136 if ( fp.main.is_pro ) { 137 138 // Save GA4 client ID in a variable 139 // This function is fired by GA's script! It does not give instant results! 140 if ( typeof gtag !== 'undefined' ) { 141 142 gtag('get', fp.ga41.id, 'client_id', clientID => { 143 FP.fns.ga4_set_g_ids(nr, 'client', clientID); 144 } ); 145 146 gtag('get', fp.ga41.id, 'session_id', sessionID => { 147 FP.fns.ga4_set_g_ids(nr, 'session', sessionID); 148 } ); 149 } 150 151 // Hook up server order tracking 152 153 FP.addAction( ['woo_prepare_for_server_tracking_order'], server_data => { 154 155 // STOP if we don't track the order server-side 156 if ( ! fp.ga41.server_side || ! fp.ga41.adv_orders ) return server_data; 157 158 // STOP if we don't have required data 159 let ga_ids = FP.readCookie('fp_ga_ids');
160 if ( ga_ids ) ga_ids = JSON.parse( ga_ids ); 161 if ( typeof gtag === 'undefined' || ! ga_ids || ! ga_ids.ga41_client || ! ga_ids.ga41_session || ! fpdata.consents || ! fpdata.consents.can_track_stats ) return server_data; 162 163 let ga4_data = { 164 'ga41_client_id' : ga_ids.ga41_client, 165 'ga41_session_id' : ga_ids.ga41_session, 166 'enable_debug_view' : fpdata.cookies.ga4_debug == 'on', 167 'supports' : ['purchase', 'refund'], 168 }; 169 170 // if ( fp.ga42 && fp.ga42.server_side ) { // adv_orders already set in ga41 171 // ga4_data['ga42_client_id'] = ga_ids?.ga41_clientID || false; 172 // ga4_data['ga42_session_id'] = ga_ids?.ga42_sessionID || false; 173 // } 174 175 // prepare data for the server-side purchase tracking 176 server_data.push( ['ga4', ga4_data] ); 177 return server_data; 178 } ); 179 180 // TRACK JS ERRORS 181 // THX: https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror 182 // THX: https://medium.com/@Esakkimuthu/track-javascript-errors-and-how-to-fix-the-errors-60e160851296 183 if ( _ga.js_err_dimens ) { 184 window.onerror = (msg, url, lineNo, columnNo, error)=>{ 185 186 // make sure no unwanted errors slip through 187 if ( !msg || msg.indexOf('debug-evaluate') != -1 || msg.indexOf('ResizeObserver loop limit exceeded') != -1 ) return; 188 189 let data = { 190 'send_to': fp['ga4' + nr].id, 191 }; 192 193 if (msg.indexOf('Script Error') > -1){ 194 data['js_error_details'] = 'External script error: see browser console for details'; 195 } else { 196 197 // we are not sending an error.stack since it is always longer than 100 chars (GA limit) 198 199 if ( url ){ 200 if ( url != document.location.href ) { 201 // get only file name 202 url = url.split('?').shift(); 203 url = ' at ' + url.split('/').pop(); 204 } else { 205 // replace URL with a notice - the url is not important since GA collects it with the event 206 url = ' in an inline script'; 207 } 208 } 209 210 lineNo = lineNo ? ' Line:' + lineNo : ''; 211 columnNo = columnNo ? ' Col:' + columnNo : ''; 212 213 // replaces double quotes with apostrophes (GA replaces quotes with HTML entities which take too many characters) 214 msg = msg.replaceAll('"', '\''); 215 216 let message = msg + url + lineNo + columnNo; 217 message = message.substring(0,100); 218 219 data['js_error_details'] = message; 220 } 221 222 gtag( 'event', 'js_error', data ); 223 if ( fp.main.debug ) console.log('[FP] GA4 #' + nr + ' event: JS error', data ); 224 225 return false; 226 } 227 } 228 229 // Conditional tracking 230 231 function ga4_send_custom_events( evt ){ 232 var data = { 'send_to': _ga.id }; 233 if ( evt.evt_val ) data.fp_event_value = evt.evt_val; 234 gtag( 'event', evt.evt_name, data); 235 if ( fp.main.debug ) console.log( '[FP] GA4 #' + nr + ' custom tracking: ' + evt.evt_name, data ); 236 } 237 238 FP.initCondTracking( _ga.custom_events, ga4_send_custom_events ); 239 } 240 /* </fs_premium_only> */ 241 242 fp.loaded.push('ga4' + nr); 243 if ( fp.main.debug ) console.log('[FP] GA4 #' + nr + ' loaded'); 244 FP.runFn( 'FP.fns.load_ga4_footer', [nr] ); 245};
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.