1window.gtm4wp_last_selected_product_variation; 2window.gtm4wp_changedetail_fired_during_pageload=false; 3 4window.gtm4wp_is_cart = false; 5window.gtm4wp_is_checkout = false; 6window.gtm4wp_checkout_step_fired = []; // step 1 will be the billing section which is reported during pageload, no need to handle here 7window.gtm4wp_shipping_payment_method_step_offset = gtm4wp_needs_shipping_address ? 0 : -1; 8 9window.gtm4wp_first_container_id = ""; 10 11function gtm4wp_map_eec_to_ga4( productdata ) { 12 if ( !productdata ) { 13 return; 14 } 15 16 const category_path = productdata.category ? productdata.category : ''; 17 const category_parts = category_path.toString().split('/'); 18 19 // default, required parameters 20 let ga4_product = { 21 'item_id': productdata.id ? productdata.id : '', 22 'item_name': productdata.name ? productdata.name : '', 23 'item_brand': productdata.brand ? productdata.brand : '', 24 'price': productdata.price ? productdata.price : "" 25 }; 26 27 // category, also handle category path 28 if ( 1 == category_parts.length ) { 29 ga4_product.item_category = category_parts[0]; 30 } else if ( category_parts.length > 1 ) { 31 ga4_product.item_category = category_parts[0]; 32 for( let i=1; i < Math.min( 5, category_parts.length ); i++ ) { 33 ga4_product[ 'item_category' + (i+1) ] = category_parts[i]; 34 } 35 } 36 37 // optional parameters which should not be included in the array if not set 38 if ( productdata.variant ) { 39 ga4_product.item_variant = productdata.variant; 40 } 41 if ( productdata.list ) { 42 ga4_product.item_list_name = productdata.list; 43 } 44 if ( productdata.position ) { 45 ga4_product.index = productdata.position; 46 } 47 if ( productdata.quantity ) { 48 ga4_product.quantity = productdata.quantity; 49 } 50 if ( productdata.coupon ) { 51 ga4_product.coupon = productdata.coupon; 52 } 53 54 ga4_product.google_business_vertical = gtm4wp_business_vertical; 55 ga4_product[ gtm4wp_business_vertical_id ] = gtm4wp_id_prefix + ga4_product[ "item_id" ]; 56 57 return ga4_product; 58} 59 60function gtm4wp_handle_cart_qty_change() { 61 document.querySelectorAll( '.product-quantity input.qty' ).forEach(function( qty_el ) { 62 const original_value = qty_el.defaultValue; 63 64 let current_value = parseInt( qty_el.value ); 65 if ( isNaN( current_value ) ) { 66 current_value = original_value; 67 } 68 69 // is quantity changed changed? 70 if ( original_value != current_value ) { 71 const cart_item_temp = qty_el.closest( '.cart_item' ); 72 const productdata = cart_item_temp && cart_item_temp.querySelector( '.remove' ); 73 if ( !productdata ) { 74 return; 75 } 76 77 let productprice = productdata.getAttribute( 'data-gtm4wp_product_price' ); 78 79 if ( typeof productprice == "string" ) { 80 productprice = parseFloat( productprice ); 81 if ( isNaN( productprice ) ) { 82 productprice = 0; 83 } 84 } else if ( typeof productprice != "number" ) { 85 productprice = 0; 86 } 87 88 // does the quantity increase? 89 if ( original_value < current_value ) { 90 // yes => handle add to cart event 91 const product_data = { 92 'name': productdata.getAttribute( 'data-gtm4wp_product_name' ), 93 'id': productdata.getAttribute( 'data-gtm4wp_product_id' ), 94 'price': productprice.toFixed(2), 95 'category': productdata.getAttribute( 'data-gtm4wp_product_cat' ), 96 'variant': productdata.getAttribute( 'data-gtm4wp_product_variant' ), 97 'stocklevel': productdata.getAttribute( 'data-gtm4wp_product_stocklevel' ), 98 'brand': productdata.getAttribute( 'data-gtm4wp_product_brand' ), 99 'quantity': current_value - original_value 100 }; 101 102 // fire ga3 version 103 window[ gtm4wp_datalayer_name ].push({ 104 'event': 'gtm4wp.addProductToCartEEC', 105 'ecommerce': { 106 'currencyCode': gtm4wp_currency, 107 'add': { 108 'products': [ product_data ] 109 } 110 } 111 }); 112 113 // fire ga4 version 114 window[ gtm4wp_datalayer_name ].push({ 115 'event': 'add_to_cart', 116 'ecommerce': { 117 'currency': gtm4wp_currency, // ga4 version 118 'value': productprice.toFixed(2) * (current_value - original_value), 119 'items': [ gtm4wp_map_eec_to_ga4( product_data ) ] 120 } 121 }); 122 } else { 123 // no => handle remove from cart event 124 const product_data = { 125 'name': productdata.getAttribute( 'data-gtm4wp_product_name' ), 126 'id': productdata.getAttribute( 'data-gtm4wp_product_id' ), 127 'price': productprice.toFixed(2), 128 'category': productdata.getAttribute( 'data-gtm4wp_product_cat' ), 129 'variant': productdata.getAttribute( 'data-gtm4wp_product_variant' ), 130 'stocklevel': productdata.getAttribute( 'data-gtm4wp_product_stocklevel' ), 131 'brand': productdata.getAttribute( 'data-gtm4wp_product_brand' ), 132 'quantity': original_value - current_value 133 }; 134 135 // fire ga3 version 136 window[ gtm4wp_datalayer_name ].push({ 137 'event': 'gtm4wp.removeFromCartEEC', 138 'ecommerce': { 139 'currencyCode': gtm4wp_currency, 140 'remove': { 141 'products': [ product_data ] 142 } 143 } 144 }); 145 146 // fire ga4 version 147 window[ gtm4wp_datalayer_name ].push({ 148 'event': 'remove_from_cart', 149 'ecommerce': { 150 'currency': gtm4wp_currency, 151 'value': productprice.toFixed(2) * (original_value - current_value), 152 'items': [ gtm4wp_map_eec_to_ga4( product_data ) ] 153 } 154 }); 155 } 156 } // end if qty changed 157 }); // end each qty field 158} // end gtm4wp_handle_cart_qty_change() 159 160function gtm4wp_handle_payment_method_change() { 161 // do not report checkout step if already reported 162 if ( gtm4wp_checkout_step_fired.indexOf( 'payment_method' ) > -1 ) { 163 return; 164 } 165 166 // do not fire event during page load 167 if ( 'complete' != document.readyState ) { 168 return; 169 } 170 171 let payment_type = '(payment type not found)'; 172 let payment_el = document.querySelector( '.payment_methods input:checked' ); 173 if ( !payment_el ) { 174 payment_el = document.querySelector( 'input[name^=payment_method]' ); // select the first input element 175 } 176 if ( payment_el ) { 177 payment_type = payment_el.value; 178 } 179 180 // fire ga3 version 181 window[ gtm4wp_datalayer_name ].push({ 182 'event': 'gtm4wp.checkoutStepEEC', 183 'ecommerce': { 184 'currencyCode': gtm4wp_currency, 185 'checkout': { 186 'actionField': { 187 'step': 4 + window.gtm4wp_checkout_step_offset + gtm4wp_shipping_payment_method_step_offset 188 }, 189 'products': window.gtm4wp_checkout_products 190 } 191 } 192 }); 193 194 // fire ga4 version 195 window[ gtm4wp_datalayer_name ].push({ 196 'event': 'add_payment_info', 197 'ecommerce': { 198 'currency': gtm4wp_currency, 199 'payment_type': payment_type, 200 'value': window.gtm4wp_checkout_value, 201 'items': window.gtm4wp_checkout_products_ga4 202 } 203 }); 204 205 gtm4wp_checkout_step_fired.push( 'payment_method' ); 206} // end gtm4wp_handle_payment_method_change() 207 208function gtm4wp_handle_shipping_method_change() { 209 // do not report checkout step if already reported 210 if ( gtm4wp_checkout_step_fired.indexOf( 'shipping_method' ) > -1 ) { 211 return; 212 } 213 214 // do not fire event during page load 215 if ( 'complete' != document.readyState ) { 216 return; 217 } 218 219 let shipping_tier = '(shipping tier not found)'; 220 let shipping_el = document.querySelector( 'input[name^=shipping_method]:checked' ); 221 if ( !shipping_el ) { 222 shipping_el = document.querySelector( 'input[name^=shipping_method]' ); // select the first input element 223 } 224 if ( shipping_el ) { 225 shipping_tier = shipping_el.value; 226 } 227 228 // fire ga3 version 229 window[ gtm4wp_datalayer_name ].push({ 230 'event': 'gtm4wp.checkoutStepEEC', 231 'ecommerce': { 232 'currencyCode': gtm4wp_currency, 233 'checkout': { 234 'actionField': { 235 'step': 3 + window.gtm4wp_checkout_step_offset + gtm4wp_shipping_payment_method_step_offset 236 }, 237 'products': window.gtm4wp_checkout_products 238 } 239 } 240 }); 241 242 // fire ga4 version 243 window[ gtm4wp_datalayer_name ].push({ 244 'event': 'add_shipping_info', 245 'ecommerce': { 246 'currency': gtm4wp_currency, 247 'shipping_tier': shipping_tier, 248 'value': window.gtm4wp_checkout_value, 249 'items': window.gtm4wp_checkout_products_ga4 250 } 251 }); 252 253 gtm4wp_checkout_step_fired.push( 'shipping_method' ); 254} 255 256function gtm4wp_process_woocommerce_pages() { 257 window.gtm4wp_is_cart = false;
258 window.gtm4wp_is_checkout = false; 259 260 const doc_body = document.querySelector( 'body' ); 261 if ( doc_body ) { 262 window.gtm4wp_is_cart = doc_body.classList && doc_body.classList.contains( 'woocommerce-cart' ); 263 window.gtm4wp_is_checkout = doc_body.classList && doc_body.classList.contains( 'woocommerce-checkout' ); 264 } 265 266 // loop through WC blocks to set proper listname and position parameters 267 const gtm4wp_product_block_names = { 268 'wp-block-handpicked-products': { 269 'displayname': 'Handpicked Products', 270 'counter': 1 271 }, 272 'wp-block-product-best-sellers': { 273 'displayname': 'Best Selling Products', 274 'counter': 1 275 }, 276 'wp-block-product-category': { 277 'displayname': 'Product Category List', 278 'counter': 1 279 }, 280 'wp-block-product-new': { 281 'displayname': 'New Products', 282 'counter': 1 283 }, 284 'wp-block-product-on-sale': { 285 'displayname': 'Sale Products', 286 'counter': 1 287 }, 288 'wp-block-products-by-attribute': { 289 'displayname': 'Products By Attribute', 290 'counter': 1 291 }, 292 'wp-block-product-tag': { 293 'displayname': 'Products By Tag', 294 'counter': 1 295 }, 296 'wp-block-product-top-rated': { 297 'displayname': 'Top Rated Products', 298 'counter': 1 299 }, 300 } 301 document.querySelectorAll( '.wc-block-grid .wc-block-grid__product' ).forEach( function( product_grid_item ) { 302 303 const product_grid_container = product_grid_item.closest( '.wc-block-grid' ); 304 const product_data = product_grid_item.querySelector( '.gtm4wp_productdata' ); 305 306 if ( product_grid_container && product_data ) { 307 308 const product_grid_container_classes = product_grid_container.classList; 309 310 if ( product_grid_container_classes ) { 311 312 for(let i in gtm4wp_product_block_names) { 313 if ( product_grid_container_classes.contains( i ) ) { 314 product_data.setAttribute("data-gtm4wp_productlist_name", gtm4wp_product_block_names[i].displayname); 315 product_data.setAttribute("data-gtm4wp_product_listposition", gtm4wp_product_block_names[i].counter); 316 317 gtm4wp_product_block_names[i].counter++; 318 } 319 } 320 } 321 } 322 }); 323 324 // track impressions of products in product lists 325 if ( document.querySelectorAll( '.gtm4wp_productdata,.widget-product-item' ).length > 0 ) { 326 let products = []; 327 let ga4_products = []; 328 let productprice = 0; 329 let product_data; 330 331 document.querySelectorAll( '.gtm4wp_productdata,.widget-product-item' ).forEach( function( dom_productdata ) { 332 productprice = dom_productdata.getAttribute( 'data-gtm4wp_product_price' ); 333 334 if ( typeof productprice == "string" ) { 335 productprice = parseFloat( productprice ); 336 if ( isNaN( productprice ) ) { 337 productprice = 0; 338 } 339 } else if ( typeof productprice != "number" ) { 340 productprice = 0; 341 } 342 343 product_data = { 344 'name': dom_productdata.getAttribute( 'data-gtm4wp_product_name' ), 345 'id': dom_productdata.getAttribute( 'data-gtm4wp_product_id' ), 346 'price': productprice.toFixed(2), 347 'category': dom_productdata.getAttribute( 'data-gtm4wp_product_cat' ), 348 'position': dom_productdata.getAttribute( 'data-gtm4wp_product_listposition' ), 349 'list': dom_productdata.getAttribute( 'data-gtm4wp_productlist_name' ), 350 'stocklevel': dom_productdata.getAttribute( 'data-gtm4wp_product_stocklevel' ), 351 'brand': dom_productdata.getAttribute( 'data-gtm4wp_product_brand' ) 352 }; 353 354 products.push(product_data); 355 ga4_products.push( gtm4wp_map_eec_to_ga4( product_data ) ); 356 }); 357 358 if ( gtm4wp_product_per_impression > 0 ) { 359 // Need to split the product submissions up into chunks in order to avoid the GA 8kb submission limit 360 let chunk 361 let ga4_chunk; 362 363 while ( products.length ) { 364 chunk = products.splice( 0, gtm4wp_product_per_impression ); 365 ga4_chunk = ga4_products.splice( 0, gtm4wp_product_per_impression ); 366 367 // fire ga3 version 368 window[ gtm4wp_datalayer_name ].push({ 369 'event': 'gtm4wp.productImpressionEEC', 370 'ecommerce': { 371 'currencyCode': gtm4wp_currency, 372 'impressions': chunk 373 } 374 }); 375 376 // fire ga4 version 377 window[ gtm4wp_datalayer_name ].push({ 378 'event': 'view_item_list', 379 'ecommerce': { 380 'currency': gtm4wp_currency, 381 'items': ga4_chunk 382 } 383 }); 384 } 385 } else { 386 for( var i=0; i<window[ gtm4wp_datalayer_name ].length; i++ ) { 387 if ( window[ gtm4wp_datalayer_name ][ i ][ 'ecommerce' ] ) { 388 389 if ( ! window[ gtm4wp_datalayer_name ][ i ][ 'ecommerce' ][ 'impressions' ] ) { 390 window[ gtm4wp_datalayer_name ][ i ][ 'ecommerce' ][ 'impressions' ] = products; 391 } else { 392 window[ gtm4wp_datalayer_name ][ i ][ 'ecommerce' ][ 'impressions' ] = window[ gtm4wp_datalayer_name ][ i ][ 'ecommerce' ][ 'impressions' ].concat( products ); 393 } 394 395 break; 396 } 397 } 398 399 if ( i == window[ gtm4wp_datalayer_name ].length ) { 400 // no existing ecommerce data found in the datalayer 401 i = 0; 402 window[ gtm4wp_datalayer_name ][ i ][ 'ecommerce' ] = {}; 403 window[ gtm4wp_datalayer_name ][ i ][ 'ecommerce' ][ 'impressions' ] = products; 404 } 405 406 window[ gtm4wp_datalayer_name ][ i ][ 'ecommerce' ][ 'currencyCode' ] = gtm4wp_currency; 407 } 408 } 409 410 // track add to cart events for simple products in product lists 411 document.addEventListener( 'click', function( e ) { 412 let event_target_element = e.target; 413 414 if ( !event_target_element ) { 415 // for some reason event target is not specificed 416 return true; 417 } 418 419 try { 420 if ( !event_target_element.closest( '.add_to_cart_button:not(.product_type_variable, .product_type_grouped, .single_add_to_cart_button)' ) ) { 421 return true; 422 } 423 } catch (e) { 424 // during beta testing, closest() sometimes threw SyntaxError which is thrown if selector is invali
424d. But the selector above should be valid in all cases 425 // assumption was that perhaps event_target_element was not set or not a proper DOM node for some reasons 426 return true; 427 } 428 429 const product_el = event_target_element.closest( '.product,.wc-block-grid__product' ); 430 const productdata = product_el && product_el.querySelector( '.gtm4wp_productdata' ); 431 if ( !productdata ) { 432 return true; 433 } 434 435 let productprice = productdata.getAttribute( 'data-gtm4wp_product_price' ); 436 437 if ( typeof productprice == "string" ) { 438 productprice = parseFloat( productprice ); 439 if ( isNaN( productprice ) ) { 440 productprice = 0; 441 } 442 } else if ( typeof productprice != "number" ) { 443 productprice = 0; 444 } 445 446 const product_data = { 447 'name': productdata.getAttribute( 'data-gtm4wp_product_name' ), 448 'id': productdata.getAttribute( 'data-gtm4wp_product_id' ), 449 'price': productprice.toFixed(2), 450 'category': productdata.getAttribute( 'data-gtm4wp_product_cat' ), 451 'stocklevel': productdata.getAttribute( 'data-gtm4wp_product_stocklevel' ), 452 'brand': productdata.getAttribute( 'data-gtm4wp_product_brand' ), 453 'quantity': 1 454 }; 455 456 // fire ga3 version 457 window[ gtm4wp_datalayer_name ].push({ 458 'event': 'gtm4wp.addProductToCartEEC', 459 'ecommerce': { 460 'currencyCode': gtm4wp_currency, 461 'add': { 462 'products': [ product_data ] 463 } 464 } 465 }); 466 467 // fire ga4 version 468 window[ gtm4wp_datalayer_name ].push({ 469 'event': 'add_to_cart', 470 'ecommerce': { 471 'currency': gtm4wp_currency, 472 'value': productprice.toFixed(2), 473 'items': [ gtm4wp_map_eec_to_ga4( product_data ) ] 474 } 475 }); 476 }); 477 478 // track add to cart events for products on product detail pages 479 document.addEventListener( 'click', function( e ) { 480 let event_target_element = e.target; 481 482 if ( !event_target_element ) { 483 // for some reason event target is not specificed 484 return true; 485 } 486 487 try { 488 if ( !event_target_element.closest( '.single_add_to_cart_button:not(.disabled)' ) ) { 489 return true; 490 } 491 } catch (e) { 492 // during beta testing, closest() sometimes threw SyntaxError which is thrown if selector is invalid. But the selector above should be valid in all cases 493 // assumption was that perhaps event_target_element was not set or not a proper DOM node for some reasons 494 return true; 495 } 496 497 const product_form = event_target_element.closest( 'form.cart' ); 498 if ( !product_form ) { 499 return true; 500 } 501 502 let product_variant_id = product_form.querySelectorAll( '[name=variation_id]' );
503 let product_is_grouped = product_form.classList && product_form.classList.contains( 'grouped_form' ); 504 505 if ( product_variant_id.length > 0 ) { 506 if ( gtm4wp_last_selected_product_variation ) { 507 const qty_el = product_form.querySelector( '[name=quantity]' ); 508 gtm4wp_last_selected_product_variation.quantity = (qty_el && qty_el.value) || 1; 509 510 // fire ga3 version 511 window[ gtm4wp_datalayer_name ].push({ 512 'event': 'gtm4wp.addProductToCartEEC', 513 'ecommerce': { 514 'currencyCode': gtm4wp_currency, 515 'add': { 516 'products': [gtm4wp_last_selected_product_variation] 517 } 518 } 519 }); 520 521 // fire ga4 version 522 window[ gtm4wp_datalayer_name ].push({ 523 'event': 'add_to_cart', 524 'ecommerce': { 525 'currency': gtm4wp_currency, 526 'value': gtm4wp_last_selected_product_variation.price * gtm4wp_last_selected_product_variation.quantity, 527 'items': [ gtm4wp_map_eec_to_ga4( gtm4wp_last_selected_product_variation ) ] 528 } 529 }); 530 } 531 } else if ( product_is_grouped ) { 532 const products_in_group = document.querySelectorAll( '.grouped_form .gtm4wp_productdata' ); 533 let products = []; 534 let ga4_products = []; 535 let sum_value = 0; 536 537 products_in_group.forEach( function( dom_productdata ) { 538 const product_qty_input = document.querySelectorAll( 'input[name=quantity\\[' + dom_productdata.getAttribute( 'data-gtm4wp_product_id' ) + '\\]]' ); 539 if ( product_qty_input.length > 0 ) { 540 product_qty = (product_qty_input[0] && product_qty_input[0].value) || 1; 541 } else { 542 return true; 543 } 544 545 if ( 0 == product_qty ) { 546 return true; 547 } 548 549 const product_data = { 550 'id': gtm4wp_use_sku_instead ? dom_productdata.getAttribute( 'data-gtm4wp_product_sku' ) : dom_productdata.getAttribute( 'data-gtm4wp_product_id' ), 551 'name': dom_productdata.getAttribute( 'data-gtm4wp_product_name' ), 552 'price': dom_productdata.getAttribute( 'data-gtm4wp_product_price' ), 553 'category': dom_productdata.getAttribute( 'data-gtm4wp_product_cat' ), 554 'quantity': product_qty, 555 'stocklevel': dom_productdata.getAttribute( 'data-gtm4wp_product_stocklevel' ), 556 'brand': dom_productdata.getAttribute( 'data-gtm4wp_product_brand' ) 557 }; 558 559 products.push( product_data ); 560 ga4_products.push( gtm4wp_map_eec_to_ga4( product_data ) ); 561 sum_value += product_data.price * product_data.quantity; 562 }); 563 564 if ( 0 == products.length ) { 565 return true; 566 } 567 568 // fire ga3 version 569 window[ gtm4wp_datalayer_name ].push({ 570 'event': 'gtm4wp.addProductToCartEEC', 571 'ecommerce': { 572 'currencyCode': gtm4wp_currency, 573 'add': { 574 'products': products 575 } 576 } 577 }); 578 579 // fire ga4 version 580 window[ gtm4wp_datalayer_name ].push({ 581 'event': 'add_to_cart', 582 'ecommerce': { 583 'currency': gtm4wp_currency, 584 'value': sum_value, 585 'items': ga4_products 586 } 587 }); 588 } else { 589 const product_id_el = gtm4wp_use_sku_instead ? product_form.querySelector( '[name=gtm4wp_sku]' ) : product_form.querySelector( '[name=gtm4wp_id]' ); 590 const product_data = { 591 'id': product_id_el && product_id_el.value, 592 'name': product_form.querySelector( '[name=gtm4wp_name]' ) && product_form.querySelector( '[name=gtm4wp_name]' ).value, 593 'price': product_form.querySelector( '[name=gtm4wp_price]' ) && product_form.querySelector( '[name=gtm4wp_price]' ).value, 594 'category': product_form.querySelector( '[name=gtm4wp_category]' ) && product_form.querySelector( '[name=gtm4wp_category]' ).value, 595 'quantity': product_form.querySelector( '[name=quantity]' ) && product_form.querySelector( '[name=quantity]' ).value, 596 'stocklevel': product_form.querySelector( '[name=gtm4wp_stocklevel]' ) && product_form.querySelector( '[name=gtm4wp_stocklevel]' ).value, 597 'brand': product_form.querySelector( '[name=gtm4wp_brand]' ) && product_form.querySelector( '[name=gtm4wp_brand]' ).value 598 }; 599 600 // fire ga3 version 601 window[ gtm4wp_datalayer_name ].push({ 602 'event': 'gtm4wp.addProductToCartEEC', 603 'ecommerce': { 604 'currencyCode': gtm4wp_currency, 605 'add': { 606 'products': [ product_data ] 607 } 608 } 609 }); 610 611 // fire ga4 version 612 window[ gtm4wp_datalayer_name ].push({ 613 'event': 'add_to_cart', 614 'ecommerce': { 615 'currency': gtm4wp_currency, 616 'value': product_data.price * product_data.quantity, 617 'items': [ gtm4wp_map_eec_to_ga4( product_data ) ] 618 } 619 }); 620 } 621 }); 622 623 // track remove links in mini cart widget and on cart page 624 document.addEventListener( 'click', function( e ) { 625 const dom_productdata = e.target; 626 627 if ( !dom_productdata || !dom_productdata.closest( '.mini_cart_item a.remove,.product-remove a.remove' ) ) { 628 return true; 629 } 630 631 let qty = 0; 632 const cart_item_el = dom_productdata.closest( '.cart_item' ); 633 let qty_element = cart_item_el && cart_item_el.querySelectorAll( '.product-quantity input.qty' ); 634 if ( !qty_element || ( qty_element.length === 0 ) ) { 635 const mini_cart_item_el = dom_productdata.closest( '.mini_cart_item' ); 636 qty_element = mini_cart_item_el && mini_cart_item_el.querySelectorAll( '.quantity' ); 637 if ( qty_element && ( qty_element.length > 0 ) ) { 638 qty = parseInt( qty_element[0].textContent ); 639 640 if ( Number.isNaN( qty ) ) { 641 qty = 0; 642 } 643 } 644 } else { 645 qty = qty_element[0].value; 646 } 647 648 if ( qty === 0 ) { 649 return true; 650 } 651 652 const product_data = { 653 'name': dom_productdata.getAttribute( 'data-gtm4wp_product_name' ), 654 'id': dom_productdata.getAttribute( 'data-gtm4wp_product_id' ), 655 'price': dom_productdata.getAttribute( 'data-gtm4wp_product_price' ), 656 'category': dom_productdata.getAttribute( 'data-gtm4wp_product_cat' ), 657 'variant': dom_productdata.getAttribute( 'data-gtm4wp_product_variant' ), 658 'stocklevel': dom_productdata.getAttribute( 'data-gtm4wp_product_stocklevel' ), 659 'brand': dom_productdata.getAttribute( 'data-gtm4wp_product_brand' ), 660 'quantity': qty 661 }; 662 663 // fire ga3 version 664 window[ gtm4wp_datalayer_name ].push({ 665 'event': 'gtm4wp.removeFromCartEEC', 666 'ecommerce': { 667 'currencyCode': gtm4wp_currency, 668 'remove': { 669 'products': [ product_data ] 670 } 671 } 672 }); 673 674 // fire ga4 version 675 window[ gtm4wp_datalayer_name ].push({ 676 'event': 'remove_from_cart', 677 'ecommerce': { 678 'currency': gtm4wp_currency, 679 'value': product_data.price * product_data.quantity, 680 'items': [ gtm4wp_map_eec_to_ga4( product_data ) ] 681 } 682 }); 683 }); 684 685 // track clicks in product lists 686 let productlist_item_selector = '.products li:not(.product-category) a:not(.add_to_cart_button):not(.quick-view-button),' 687 +'.wc-block-grid__products li:not(.product-category) a:not(.add_to_cart_button):not(.quick-view-button),' 688 +'.products>div:not(.product-category) a:not(.add_to_cart_button):not(.quick-view-button),' 689 +'.widget-product-item,' 690 +'.woocommerce-grouped-product-list-item__label a' 691 document.addEventListener( 'click', function( e ) {
692 // do nothing if GTM is blocked for some reason 693 if ( 'undefined' == typeof google_tag_manager ) { 694 return true; 695 } 696 697 const event_target_element = e.target; 698 const matching_link_element = event_target_element.closest( productlist_item_selector ); 699 700 if ( !matching_link_element ) { 701 return true; 702 } 703 704 let temp_selector = event_target_element.closest( '.product,.wc-block-grid__product' ); 705 let dom_productdata; 706 707 if ( temp_selector ) { 708 dom_productdata = temp_selector.querySelector( '.gtm4wp_productdata' ); 709 710 } else { 711 temp_selector = event_target_element.closest( '.products li' ); 712 713 if ( temp_selector ) { 714 dom_productdata = temp_selector.querySelector( '.gtm4wp_productdata' ); 715 716 } else { 717 temp_selector = event_target_element.closest( '.products>div' ); 718 719 if ( temp_selector ) { 720 dom_productdata = temp_selector.querySelector( '.gtm4wp_productdata' ); 721 722 } else { 723 temp_selector = event_target_element.closest( '.woocommerce-grouped-product-list-item__label' ); 724 725 if ( temp_selector ) { 726 dom_productdata = temp_selector.querySelector( '.gtm4wp_productdata' ); 727 } else { 728 dom_productdata = event_target_element; 729 } 730 } 731 } 732 } 733 734 if ( ( 'undefined' == typeof dom_productdata.getAttribute( 'data-gtm4wp_product_id' ) ) || ( '' == dom_productdata.getAttribute( 'data-gtm4wp_product_id' ) ) ) { 735 return true; 736 } 737 738 // only act on links pointing to the product detail page 739 if ( dom_productdata.getAttribute( 'data-gtm4wp_product_url' ) != matching_link_element.getAttribute( 'href' ) ) { 740 return true; 741 } 742 743 const product_data = { 744 'id': dom_productdata.getAttribute( 'data-gtm4wp_product_id' ), 745 'name': dom_productdata.getAttribute( 'data-gtm4wp_product_name' ), 746 'price': dom_productdata.getAttribute( 'data-gtm4wp_product_price' ), 747 'category': dom_productdata.getAttribute( 'data-gtm4wp_product_cat' ), 748 'stocklevel': dom_productdata.getAttribute( 'data-gtm4wp_product_stocklevel' ), 749 'brand': dom_productdata.getAttribute( 'data-gtm4wp_product_brand' ), 750 'position': dom_productdata.getAttribute( 'data-gtm4wp_product_listposition' ) 751 }; 752 753 for (let i in window.google_tag_manager) { 754 if (i.substring(0,4).toLowerCase() == "gtm-") { 755 window.gtm4wp_first_container_id = i; 756 break; 757 } 758 } 759 760 // do not do anything if GTM was not loaded for any reason 761 if ( "" === window.gtm4wp_first_container_id ) { 762 return true; 763 } 764 765 const ctrl_key_pressed = e.ctrlKey || e.metaKey; 766 767 e.preventDefault(); 768 if ( ctrl_key_pressed ) { 769 // we need to open the new tab/page here so that popup blocker of the browser doesn't block our code 770 window.productpage_window = window.open( 'about:blank', '_blank' ); 771 } 772 773 // fire ga3 version 774 window[ gtm4wp_datalayer_name ].push({ 775 'event': 'gtm4wp.productClickEEC', 776 'ecommerce': { 777 'currencyCode': gtm4wp_currency, 778 'click': { 779 'actionField': {'list': dom_productdata.getAttribute( 'data-gtm4wp_productlist_name' )}, 780 'products': [ product_data ] 781 } 782 }, 783 'eventCallback': function( container_id ) { 784 if ( "undefined" !== typeof container_id && window.gtm4wp_first_container_id != container_id) { 785 // only call this for the first loaded container 786 return true; 787 } 788 789 // fire ga4 version 790 window[ gtm4wp_datalayer_name ].push({ 791 'event': 'select_item', 792 'ecommerce': { 793 'currency': gtm4wp_currency, 794 'items': [ gtm4wp_map_eec_to_ga4( product_data ) ] 795 }, 796 'eventCallback': function() { 797 if ( ctrl_key_pressed && productpage_window ) { 798 productpage_window.location.href = dom_productdata.getAttribute( 'data-gtm4wp_product_url' ); 799 } else { 800 document.location.href = dom_productdata.getAttribute( 'data-gtm4wp_product_url' ); 801 } 802 803 }, 804 'eventTimeout': 2000 805 }); 806 }, 807 'eventTimeout': 2000 808 }); 809 }); 810 811 // track variable products on their detail pages 812 // currently, we need to use jQuery here since WooCommerce is firing this event using jQuery 813 // that can not be catched using vanilla JS 814 jQuery( document ).on( 'found_variation', function( event, product_variation ) { 815 if ( "undefined" == typeof product_variation ) { 816 // some ither plugins trigger this event without variation data 817 return; 818 } 819 820 if ( (document.readyState === "interactive") && gtm4wp_changedetail_fired_during_pageload ) { 821 // some custom attribute rendering plugins fire this event multiple times during page load 822 return; 823 } 824 825 const product_form = event.target; 826 const product_variant_id = product_form.querySelector( '[name=variation_id]' ) && product_form.querySelector( '[name=variation_id]' ).value; 827 const product_id = product_form.querySelector( '[name=gtm4wp_id]' ) && product_form.querySelector( '[name=gtm4wp_id]' ).value; 828 const product_name = product_form.querySelector( '[name=gtm4wp_name]' ) && product_form.querySelector( '[name=gtm4wp_name]' ).value; 829 const product_sku = product_form.querySelector( '[name=gtm4wp_sku]' ) && product_form.querySelector( '[name=gtm4wp_sku]' ).value; 830 const product_category = product_form.querySelector( '[name=gtm4wp_category]' ) && product_form.querySelector( '[name=gtm4wp_category]' ).value; 831 const product_price = product_form.querySelector( '[name=gtm4wp_price]' ) && product_form.querySelector( '[name=gtm4wp_price]' ).value; 832 const product_stocklevel = product_form.querySelector( '[name=gtm4wp_stocklevel]' ) && product_form.querySelector( '[name=gtm4wp_stocklevel]' ).value; 833 const product_brand = product_form.querySelector( '[name=gtm4wp_brand]' ) && product_form.querySelector( '[name=gtm4wp_brand]' ).value; 834 835 let current_product_detail_data = { 836 name: product_name, 837 id: 0, 838 price: 0, 839 category: product_category, 840 stocklevel: product_stocklevel, 841 brand: product_brand, 842 variant: '' 843 }; 844 845 current_product_detail_data.id = product_variation.variation_id; 846 if ( gtm4wp_use_sku_instead && product_variation.sku && ('' !== product_variation.sku) ) { 847 current_product_detail_data.id = product_variation.sku; 848 } 849 current_product_detail_data.price = product_variation.display_price; 850 851 let product_variation_attribute_values = []; 852 for( let attrib_key in product_variation.attributes ) { 853 product_variation_attribute_values.push( product_variation.attributes[ attrib_key ] ); 854 } 855 current_product_detail_data.variant = product_variation_attribute_values.join(','); 856 gtm4wp_last_selected_product_variation = current_product_detail_data; 857 858 // fire ga3 version 859 window[ gtm4wp_datalayer_name ].push({
860 'event': 'gtm4wp.changeDetailViewEEC', 861 'ecommerce': { 862 'currencyCode': gtm4wp_currency, 863 'detail': { 864 'products': [ current_product_detail_data ] 865 } 866 }, 867 'ecomm_prodid': gtm4wp_id_prefix + current_product_detail_data.id, 868 'ecomm_pagetype': 'product', 869 'ecomm_totalvalue': current_product_detail_data.price, 870 }); 871 872 // fire ga4 version 873 window[ gtm4wp_datalayer_name ].push({ 874 'event': 'view_item', 875 'ecommerce': { 876 'currency': gtm4wp_currency, 877 'value': current_product_detail_data.price, 878 'items': [ gtm4wp_map_eec_to_ga4( current_product_detail_data ) ] 879 } 880 }); 881 882 if ( document.readyState === "interactive" ) { 883 gtm4wp_changedetail_fired_during_pageload = true; 884 } 885 }); 886 jQuery( '.variations select' ).trigger( 'change' ); 887 888 // initiate codes in WooCommere Quick View 889 // currently, we need to use jQuery here since WooCommerce Quick View is showing the popup using 890 // jQuery AJAX calls that can not be catched using vanilla JS 891 jQuery( document ).ajaxSuccess( function( event, xhr, settings ) { 892 if(typeof settings !== 'undefined') { 893 if (settings.url.indexOf( 'wc-api=WC_Quick_View' ) > -1 ) { 894 setTimeout( function() { 895 896 const dl_data = document.querySelector('#gtm4wp_quickview_data'); 897 if ( dl_data && dl_data.dataset && dl_data.dataset.gtm4wp_datalayer ) { 898 try { 899 const dl_data_obj = JSON.parse( dl_data.dataset.gtm4wp_datalayer ); 900 if ( dl_data_obj && window.dataLayer ) { 901 window.dataLayer.push(dl_data_obj); 902 } 903 } catch(e) { 904 console && console.error && console.error( e.message ); 905 } 906 } 907 908 }, 500); 909 } 910 } 911 }); 912 913 // codes for enhanced ecommerce events on cart page 914 if ( gtm4wp_is_cart ) { 915 document.addEventListener( 'click', function( e ) { 916 let event_target_element = e.target; 917 918 if ( !event_target_element ) { 919 // for some reason event target is not specificed 920 return true; 921 } 922 923 if ( !event_target_element.closest( '[name=update_cart]' ) ) { 924 return true; 925 } 926 927 gtm4wp_handle_cart_qty_change(); 928 }); 929 930 document.addEventListener( 'keypress', function( e ) { 931 let event_target_element = e.target; 932 933 if ( !event_target_element ) { 934 // for some reason event target is not specificed 935 return true; 936 } 937 938 if ( !event_target_element.closest( '.woocommerce-cart-form input[type=number]' ) ) { 939 return true; 940 } 941 942 gtm4wp_handle_cart_qty_change(); 943 }); 944 } 945 946 // codes for enhanced ecommerce events on checkout page 947 if ( gtm4wp_is_checkout ) { 948 window.gtm4wp_checkout_step_offset = window.gtm4wp_checkout_step_offset || 0; 949 window.gtm4wp_checkout_value = window.gtm4wp_checkout_value || 0; 950 window.gtm4wp_checkout_products = window.gtm4wp_checkout_products || []; 951 window.gtm4wp_checkout_products_ga4 = window.gtm4wp_checkout_products_ga4 || []; 952 953 // this checkout step is not reported to GA4 as currently there is no option to report in-between custom steps 954 document.addEventListener( 'focusout', function( e ) { 955 let event_target_element = e.target; 956 957 if ( !event_target_element ) { 958 // for some reason event target is not specificed 959 return true; 960 } 961 962 if ( !event_target_element.closest || !event_target_element.closest( 'input[name^=shipping_]:not(input[name^=shipping_method])' ) ) { 963 return true; 964 } 965 966 // do not report checkout step if already reported 967 if ( gtm4wp_checkout_step_fired.indexOf( 'shipping' ) > -1 ) { 968 return; 969 } 970 971 // do not report checkout step if user is traversing through the section without filling in any data 972 if ( event_target_element.value.trim() == '' ) { 973 return; 974 } 975 976 window[ gtm4wp_datalayer_name ].push({ 977 'event': 'gtm4wp.checkoutStepEEC', 978 'ecommerce': { 979 'currencyCode': gtm4wp_currency, // ga3 version 980 'checkout': { 981 'actionField': { 982 'step': 2 + window.gtm4wp_checkout_step_offset 983 }, 984 'products': window.gtm4wp_checkout_products 985 } 986 } 987 }); 988 989 gtm4wp_checkout_step_fired.push( 'shipping' ); 990 }); 991 992 document.addEventListener( 'change', function( e ) { 993 let event_target_element = e.target; 994 995 if ( !event_target_element ) { 996 // for some reason event target is not specificed 997 return true; 998 } 999 1000 if ( !event_target_element.closest( 'input[name^=shipping_method]' ) ) { 1001 return true; 1002 } 1003 1004 gtm4wp_handle_shipping_method_change(); 1005 }); 1006 1007 document.addEventListener( 'change', function( e ) { 1008 let event_target_element = e.target; 1009 1010 if ( !event_target_element ) { 1011 // for some reason event target is not specificed 1012 return true; 1013 } 1014 1015 if ( !event_target_element.closest( 'input[name=payment_method]' ) ) { 1016 return true; 1017 } 1018 1019 gtm4wp_handle_payment_method_change(); 1020 }); 1021 1022 document.addEventListener( 'submit', function( e ) { 1023 let event_target_element = e.target; 1024 1025 if ( !event_target_element ) { 1026 // for some reason event target is not specificed 1027 return true; 1028 } 1029 1030 if ( !event_target_element.closest( 'form[name=checkout]' ) ) { 1031 return true; 1032 } 1033 1034 if ( gtm4wp_checkout_step_fired.indexOf( 'shipping_method' ) == -1 ) { 1035 // shipping methods are not visible if only one is available 1036 // and if the user has already a pre-selected method, no click event will fire to report the checkout step 1037 gtm4wp_handle_shipping_method_change(); 1038 } 1039 1040 if ( gtm4wp_checkout_step_fired.indexOf( 'payment_method' ) == -1 ) { 1041 // if the user has already a pre-selected method, no click event will fire to report the checkout step 1042 gtm4wp_handle_payment_method_change(); 1043 } 1044 1045 let shipping_el = document.querySelector( 'input[name^=shipping_method]:checked' ); 1046 if ( !shipping_el ) { 1047 shipping_el = document.querySelector( 'input[name^=shipping_method]' ); // select the first input element 1048 } 1049 if ( shipping_el ) { 1050 window[ gtm4wp_datalayer_name ].push({ 1051 'event': 'gtm4wp.checkoutOptionEEC', 1052 'ecommerce': { 1053 'checkout_option': { 1054 'actionField': { 1055 'step': 3 + window.gtm4wp_checkout_step_offset + gtm4wp_shipping_payment_method_step_offset, 1056 'option': 'Shipping: ' + shipping_el.value 1057 } 1058 } 1059 } 1060 }); 1061 } 1062 1063 let payment_el = document.querySelector( '.payment_methods input:checked' ); 1064 if ( !payment_el ) { 1065 payment_el = document.querySelector( 'input[name^=payment_method]' ); // select the first input element 1066 } 1067 if ( payment_el ) { 1068 window[ gtm4wp_datalayer_name ].push({ 1069 'event': 'gtm4wp.checkoutOptionEEC', 1070 'ecommerce': { 1071 'checkout_option': { 1072 'actionField': { 1073 'step': 4 + window.gtm4wp_checkout_step_offset + gtm4wp_shipping_payment_method_step_offset, 1074 'option': 'Payment: ' + payment_el.value 1075 } 1076 } 1077 } 1078 }); 1079 } 1080 }); 1081 } 1082 1083 // codes for Google Ads dynamic remarketing 1084 // this part of the code is deprecated and will be removed in a later version 1085 // therefore jQuery usage will be not rewritten 1086 // turn of the deprecated Google Ads remarketing feature and this code will not execute 1087 if ( window.gtm4wp_remarketing&& !gtm4wp_is_cart && !gtm4wp_is_checkout ) { 1088 if ( jQuery( '.gtm4wp_productdata' ).length > 0 ) { 1089 for( var i=0; i<window[ gtm4wp_datalayer_name ].length; i++ ) { 1090 if ( window[ gtm4wp_datalayer_name ][ i ][ 'ecomm_prodid' ] ) { 1091 break; 1092 } 1093 } 1094 1095 if ( i == window[ gtm4wp_datalayer_name ].length ) { 1096 // no existing dyn remarketing data found in the datalayer 1097 i = 0; 1098 window[ gtm4wp_datalayer_name ][ i ][ 'ecomm_prodid' ] = []; 1099 } 1100 1101 if ( 'undefined' !== typeof window[ gtm4wp_datalayer_name ][ i ][ 'ecomm_prodid' ].push ) { 1102 var productdata; 1103 jQuery( '.gtm4wp_productdata' ).each( function() { 1104 productdata = jQuery( this ); 1105 1106 window[ gtm4wp_datalayer_name ][ i ][ 'ecomm_prodid' ].push( gtm4wp_id_prefix + productdata.data( 'gtm4wp_product_id' ) ); 1107 }); 1108 } 1109 } 1110 } 1111 1112 // loop through datalayer and fire GA4 version of EEC events 1113 if ( window[ gtm4wp_datalayer_name ] && window[ gtm4wp_datalayer_name ].forEach ) { 1114 window[ gtm4wp_datalayer_name ].forEach(function( item ) { 1115 1116 if ( item && item.ecommerce && item.ecommerce.detail ) { 1117 window[ gtm4wp_datalayer_name ].push({ 1118 'event': 'view_item', 1119 'ecommerce': { 1120 'currency': gtm4wp_currency, 1121 'value': item.ecommerce.detail.products[0].price, 1122 'items': [ gtm4wp_map_eec_to_ga4( item.ecommerce.detail.products[0] ) ] 1123 } 1124 }); 1125 } 1126 1127 if ( item && item.ecommerce && ( item.ecommerce.cart || (item.ecommerce.checkout && gtm4wp_is_cart) ) ) { 1128 let source_products = item.ecommerce.cart || item.ecommerce.checkout.products; 1129 let ga4_products = []; 1130 let sum_value = 0; 1131
1132 source_products.forEach(function( product ) { 1133 ga4_products.push( gtm4wp_map_eec_to_ga4( product ) ); 1134 sum_value += product.price * product.quantity; 1135 }); 1136 1137 window[ gtm4wp_datalayer_name ].push({ 1138 'event': 'view_cart', 1139 'ecommerce': { 1140 'currency': gtm4wp_currency, 1141 'value': sum_value.toFixed(2), 1142 'items': ga4_products 1143 } 1144 }); 1145 } 1146 1147 if ( item && item.ecommerce && item.ecommerce.checkout && !gtm4wp_is_cart ) { 1148 let ga4_products = []; 1149 let sum_value = 0; 1150 1151 item.ecommerce.checkout.products.forEach(function( product ) { 1152 ga4_products.push( gtm4wp_map_eec_to_ga4( product ) ); 1153 sum_value += product.price * product.quantity; 1154 }); 1155 1156 window[ gtm4wp_datalayer_name ].push({ 1157 'event': 'begin_checkout', 1158 'ecommerce': { 1159 'currency': gtm4wp_currency, 1160 'value': sum_value, 1161 'items': ga4_products 1162 } 1163 }); 1164 } 1165 1166 // present if product is readded into cart just after removel 1167 if ( item && item.ecommerce && item.ecommerce.add ) { 1168 let ga4_products = []; 1169 let sum_value = 0; 1170 1171 item.ecommerce.add.products.forEach(function( product ) { 1172 ga4_products.push( gtm4wp_map_eec_to_ga4( product ) ); 1173 sum_value += product.price * product.quantity; 1174 }); 1175 1176 window[ gtm4wp_datalayer_name ].push({ 1177 'event': 'add_to_cart', 1178 'ecommerce': { 1179 'currency': gtm4wp_currency, 1180 'value': sum_value, 1181 'items': ga4_products 1182 } 1183 }); 1184 } 1185 1186 if ( item && item.ecommerce && item.ecommerce.purchase ) { 1187 let ga4_products = []; 1188 item.ecommerce.purchase.products.forEach(function( product ) { 1189 ga4_products.push( gtm4wp_map_eec_to_ga4( product ) ); 1190 }); 1191 1192 window[ gtm4wp_datalayer_name ].push({ 1193 'event': 'purchase', 1194 'ecommerce': { 1195 'currency': gtm4wp_currency, 1196 'items': ga4_products, 1197 'transaction_id': item.ecommerce.purchase.actionField.id, 1198 'affiliation': item.ecommerce.purchase.actionField.affiliation, 1199 'value': item.ecommerce.purchase.actionField.revenue, 1200 'tax': item.ecommerce.purchase.actionField.tax, 1201 'shipping': item.ecommerce.purchase.actionField.shipping, 1202 'coupon': item.ecommerce.purchase.actionField.coupon 1203 } 1204 }); 1205 } 1206 }); 1207 } 1208}; 1209 1210function gtm4wp_page_loading_completed() { 1211 document.removeEventListener( "DOMContentLoaded", gtm4wp_page_loading_completed ); 1212 window.removeEventListener( "load", gtm4wp_page_loading_completed ); 1213 gtm4wp_process_woocommerce_pages(); 1214} 1215 1216// code and idea borrowed from jQuery: 1217// https://github.com/jquery/jquery/blob/main/src/core/ready.js 1218if ( document.readyState !== "loading" ) { 1219 window.setTimeout( gtm4wp_process_woocommerce_pages ); 1220} else { 1221 document.addEventListener( "DOMContentLoaded", gtm4wp_page_loading_completed ); 1222 window.addEventListener( "load", gtm4wp_page_loading_completed ); 1223}
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.