Version Description
- Fixed: undefined google_business_vertical
- Fixed: missing product price in product impression data
- Fixed: better compatibility with cache plugins and lazy load functionalities
- Fixed: Added optional chaining operator to form move tracker code
Download this release
Release Info
Developer | duracelltomi |
Plugin | DuracellTomi's Google Tag Manager for WordPress |
Version | 1.14.2 |
Comparing to | |
See all releases |
Code changes from version 1.14.1 to 1.14.2
- duracelltomi-google-tag-manager-for-wordpress.php +2 -2
- js/admin-tabcreator.js +82 -80
- js/froogaloop.js +0 -259
- js/gtm4wp-form-move-tracker.js +33 -33
- js/gtm4wp-woocommerce-enhanced.js +20 -5
- readme.txt +12 -1
duracelltomi-google-tag-manager-for-wordpress.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<?php
|
2 |
/*
|
3 |
Plugin Name: Google Tag Manager for Wordpress
|
4 |
-
Version: 1.14.
|
5 |
Plugin URI: https://gtm4wp.com/
|
6 |
Description: The first Google Tag Manager plugin for WordPress with business goals in mind
|
7 |
Author: Thomas Geiger
|
@@ -13,7 +13,7 @@ WC requires at least: 3.2
|
|
13 |
WC tested up to: 5.9.0
|
14 |
*/
|
15 |
|
16 |
-
define( 'GTM4WP_VERSION', '1.14.
|
17 |
define( 'GTM4WP_PATH', plugin_dir_path( __FILE__ ) );
|
18 |
|
19 |
global $gtp4wp_plugin_url, $gtp4wp_plugin_basename;
|
1 |
<?php
|
2 |
/*
|
3 |
Plugin Name: Google Tag Manager for Wordpress
|
4 |
+
Version: 1.14.2
|
5 |
Plugin URI: https://gtm4wp.com/
|
6 |
Description: The first Google Tag Manager plugin for WordPress with business goals in mind
|
7 |
Author: Thomas Geiger
|
13 |
WC tested up to: 5.9.0
|
14 |
*/
|
15 |
|
16 |
+
define( 'GTM4WP_VERSION', '1.14.2' );
|
17 |
define( 'GTM4WP_PATH', plugin_dir_path( __FILE__ ) );
|
18 |
|
19 |
global $gtp4wp_plugin_url, $gtp4wp_plugin_basename;
|
js/admin-tabcreator.js
CHANGED
@@ -1,80 +1,82 @@
|
|
1 |
-
jQuery( function() {
|
2 |
-
var admintabs = [];
|
3 |
-
var adminsubtabsdata = window.adminsubtabs || {};
|
4 |
-
var adminsubtabs = [];
|
5 |
-
|
6 |
-
jQuery( '#wpbody form h2' ).each(function( i ) {
|
7 |
-
admintabs.push( '<a class="nav-tab" href="#">' + jQuery(this).text() + '</a>' );
|
8 |
-
|
9 |
-
jQuery(this)
|
10 |
-
.remove();
|
11 |
-
|
12 |
-
if ( adminsubtabsdata[ i ] ) {
|
13 |
-
var _subtabs = [];
|
14 |
-
var _startrow = 0;
|
15 |
-
|
16 |
-
for (var j in adminsubtabsdata[ i ] ) {
|
17 |
-
_subtabs.push( '<a href="#" data-formtableid="' + i + '" data-startrow="' + _startrow + '" data-endrow="' + (_startrow + adminsubtabsdata[ i ][ j ].numitems) + '">' + adminsubtabsdata[ i ][ j ].tabtext + '</a>' );
|
18 |
-
_startrow += adminsubtabsdata[ i ][ j ].numitems;
|
19 |
-
}
|
20 |
-
|
21 |
-
if ( _subtabs.length > 0 ) {
|
22 |
-
adminsubtabs.push( '<ul class="adminsubtabs" id="adminsubtabs' + i + '"><li>' + _subtabs.join('</li><li>') + '</li></ul>' );
|
23 |
-
}
|
24 |
-
}
|
25 |
-
});
|
26 |
-
|
27 |
-
jQuery("#wpbody form[action='options.php']")
|
28 |
-
.prepend( adminsubtabs.join('') )
|
29 |
-
.prepend( '<h2 class="nav-tab-wrapper">' + admintabs.join('') + '</h2>' );
|
30 |
-
|
31 |
-
jQuery( '.adminsubtabs li a' )
|
32 |
-
.on( 'click', function() {
|
33 |
-
var jqthis = jQuery(this);
|
34 |
-
|
35 |
-
jqthis
|
36 |
-
.parent()
|
37 |
-
.parent()
|
38 |
-
.find( 'a' )
|
39 |
-
.removeClass( 'subtab-active' );
|
40 |
-
|
41 |
-
jqthis
|
42 |
-
.addClass( 'subtab-active' );
|
43 |
-
|
44 |
-
jQuery( '.form-table:eq(' + jqthis.data( 'formtableid' ) + ') tr' )
|
45 |
-
.hide()
|
46 |
-
.slice( jqthis.data('startrow'), jqthis.data('endrow') )
|
47 |
-
.show();
|
48 |
-
|
49 |
-
return false;
|
50 |
-
});
|
51 |
-
|
52 |
-
jQuery( '.nav-tab-wrapper a' )
|
53 |
-
.on( "click", function() {
|
54 |
-
jQuery( '.nav-tab-wrapper a.nav-tab-active' )
|
55 |
-
.removeClass( "nav-tab-active" );
|
56 |
-
|
57 |
-
jQuery( '#wpbody form .tabinfo,#wpbody form .form-table,.adminsubtabs' )
|
58 |
-
.hide();
|
59 |
-
|
60 |
-
var tabindex = jQuery(this)
|
61 |
-
.addClass( 'nav-tab-active' )
|
62 |
-
.index();
|
63 |
-
|
64 |
-
jQuery( '#wpbody form .tabinfo:eq(' + tabindex + '),#wpbody form .form-table:eq(' + tabindex + ')' )
|
65 |
-
.show();
|
66 |
-
|
67 |
-
jQuery( '#wpbody #adminsubtabs' + tabindex + ':not(.subtab-activated)' )
|
68 |
-
.find( 'a
|
69 |
-
.
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
.
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
|
|
|
1 |
+
jQuery( function() {
|
2 |
+
var admintabs = [];
|
3 |
+
var adminsubtabsdata = window.adminsubtabs || {};
|
4 |
+
var adminsubtabs = [];
|
5 |
+
|
6 |
+
jQuery( '#wpbody form h2' ).each(function( i ) {
|
7 |
+
admintabs.push( '<a class="nav-tab" href="#">' + jQuery(this).text() + '</a>' );
|
8 |
+
|
9 |
+
jQuery(this)
|
10 |
+
.remove();
|
11 |
+
|
12 |
+
if ( adminsubtabsdata[ i ] ) {
|
13 |
+
var _subtabs = [];
|
14 |
+
var _startrow = 0;
|
15 |
+
|
16 |
+
for (var j in adminsubtabsdata[ i ] ) {
|
17 |
+
_subtabs.push( '<a href="#" data-formtableid="' + i + '" data-startrow="' + _startrow + '" data-endrow="' + (_startrow + adminsubtabsdata[ i ][ j ].numitems) + '">' + adminsubtabsdata[ i ][ j ].tabtext + '</a>' );
|
18 |
+
_startrow += adminsubtabsdata[ i ][ j ].numitems;
|
19 |
+
}
|
20 |
+
|
21 |
+
if ( _subtabs.length > 0 ) {
|
22 |
+
adminsubtabs.push( '<ul class="adminsubtabs" id="adminsubtabs' + i + '"><li>' + _subtabs.join('</li><li>') + '</li></ul>' );
|
23 |
+
}
|
24 |
+
}
|
25 |
+
});
|
26 |
+
|
27 |
+
jQuery("#wpbody form[action='options.php']")
|
28 |
+
.prepend( adminsubtabs.join('') )
|
29 |
+
.prepend( '<h2 class="nav-tab-wrapper">' + admintabs.join('') + '</h2>' );
|
30 |
+
|
31 |
+
jQuery( '.adminsubtabs li a' )
|
32 |
+
.on( 'click', function() {
|
33 |
+
var jqthis = jQuery(this);
|
34 |
+
|
35 |
+
jqthis
|
36 |
+
.parent()
|
37 |
+
.parent()
|
38 |
+
.find( 'a' )
|
39 |
+
.removeClass( 'subtab-active' );
|
40 |
+
|
41 |
+
jqthis
|
42 |
+
.addClass( 'subtab-active' );
|
43 |
+
|
44 |
+
jQuery( '.form-table:eq(' + jqthis.data( 'formtableid' ) + ') tr' )
|
45 |
+
.hide()
|
46 |
+
.slice( jqthis.data('startrow'), jqthis.data('endrow') )
|
47 |
+
.show();
|
48 |
+
|
49 |
+
return false;
|
50 |
+
});
|
51 |
+
|
52 |
+
jQuery( '.nav-tab-wrapper a' )
|
53 |
+
.on( "click", function() {
|
54 |
+
jQuery( '.nav-tab-wrapper a.nav-tab-active' )
|
55 |
+
.removeClass( "nav-tab-active" );
|
56 |
+
|
57 |
+
jQuery( '#wpbody form .tabinfo,#wpbody form .form-table,.adminsubtabs' )
|
58 |
+
.hide();
|
59 |
+
|
60 |
+
var tabindex = jQuery(this)
|
61 |
+
.addClass( 'nav-tab-active' )
|
62 |
+
.index();
|
63 |
+
|
64 |
+
jQuery( '#wpbody form .tabinfo:eq(' + tabindex + '),#wpbody form .form-table:eq(' + tabindex + ')' )
|
65 |
+
.show();
|
66 |
+
|
67 |
+
jQuery( '#wpbody #adminsubtabs' + tabindex + ':not(.subtab-activated)' )
|
68 |
+
.find( 'a' )
|
69 |
+
.first()
|
70 |
+
.trigger( 'click' );
|
71 |
+
|
72 |
+
jQuery( '#wpbody #adminsubtabs' + tabindex )
|
73 |
+
.addClass( 'subtab-activated' )
|
74 |
+
.show();
|
75 |
+
|
76 |
+
return false;
|
77 |
+
});
|
78 |
+
|
79 |
+
jQuery( '.nav-tab-wrapper a' )
|
80 |
+
.first()
|
81 |
+
.trigger( 'click' );
|
82 |
+
});
|
js/froogaloop.js
DELETED
@@ -1,259 +0,0 @@
|
|
1 |
-
// Init style shamelessly stolen from jQuery http://jquery.com
|
2 |
-
var Froogaloop = (function() {
|
3 |
-
// Define a local copy of Froogaloop
|
4 |
-
function Froogaloop(iframe) {
|
5 |
-
// The Froogaloop object is actually just the init constructor
|
6 |
-
return new Froogaloop.fn.init(iframe);
|
7 |
-
}
|
8 |
-
|
9 |
-
var eventCallbacks = {},
|
10 |
-
hasWindowEvent = false,
|
11 |
-
isReady = false,
|
12 |
-
slice = Array.prototype.slice,
|
13 |
-
playerOrigin = '*';
|
14 |
-
|
15 |
-
Froogaloop.fn = Froogaloop.prototype = {
|
16 |
-
element: null,
|
17 |
-
|
18 |
-
init: function(iframe) {
|
19 |
-
if (typeof iframe === "string") {
|
20 |
-
iframe = document.getElementById(iframe);
|
21 |
-
}
|
22 |
-
|
23 |
-
this.element = iframe;
|
24 |
-
|
25 |
-
return this;
|
26 |
-
},
|
27 |
-
|
28 |
-
/*
|
29 |
-
* Calls a function to act upon the player.
|
30 |
-
*
|
31 |
-
* @param {string} method The name of the Javascript API method to call. Eg: 'play'.
|
32 |
-
* @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method
|
33 |
-
* or callback function when the method returns a value.
|
34 |
-
*/
|
35 |
-
api: function(method, valueOrCallback) {
|
36 |
-
if (!this.element || !method) {
|
37 |
-
return false;
|
38 |
-
}
|
39 |
-
|
40 |
-
var self = this,
|
41 |
-
element = self.element,
|
42 |
-
target_id = element.id !== '' ? element.id : null,
|
43 |
-
params = !isFunction(valueOrCallback) ? valueOrCallback : null,
|
44 |
-
callback = isFunction(valueOrCallback) ? valueOrCallback : null;
|
45 |
-
|
46 |
-
// Store the callback for get functions
|
47 |
-
if (callback) {
|
48 |
-
storeCallback(method, callback, target_id);
|
49 |
-
}
|
50 |
-
|
51 |
-
postMessage(method, params, element);
|
52 |
-
return self;
|
53 |
-
},
|
54 |
-
|
55 |
-
/*
|
56 |
-
* Registers an event listener and a callback function that gets called when the event fires.
|
57 |
-
*
|
58 |
-
* @param eventName (String): Name of the event to listen for.
|
59 |
-
* @param callback (Function): Function that should be called when the event fires.
|
60 |
-
*/
|
61 |
-
addEvent: function(eventName, callback) {
|
62 |
-
if (!this.element) {
|
63 |
-
return false;
|
64 |
-
}
|
65 |
-
|
66 |
-
var self = this,
|
67 |
-
element = self.element,
|
68 |
-
target_id = element.id !== '' ? element.id : null;
|
69 |
-
|
70 |
-
|
71 |
-
storeCallback(eventName, callback, target_id);
|
72 |
-
|
73 |
-
// The ready event is not registered via postMessage. It fires regardless.
|
74 |
-
if (eventName != 'ready') {
|
75 |
-
postMessage('addEventListener', eventName, element);
|
76 |
-
} else if (eventName == 'ready' && isReady) {
|
77 |
-
callback.call(null, target_id);
|
78 |
-
}
|
79 |
-
|
80 |
-
return self;
|
81 |
-
},
|
82 |
-
|
83 |
-
/*
|
84 |
-
* Unregisters an event listener that gets called when the event fires.
|
85 |
-
*
|
86 |
-
* @param eventName (String): Name of the event to stop listening for.
|
87 |
-
*/
|
88 |
-
removeEvent: function(eventName) {
|
89 |
-
if (!this.element) {
|
90 |
-
return false;
|
91 |
-
}
|
92 |
-
|
93 |
-
var self = this,
|
94 |
-
element = self.element,
|
95 |
-
target_id = element.id !== '' ? element.id : null,
|
96 |
-
removed = removeCallback(eventName, target_id);
|
97 |
-
|
98 |
-
// The ready event is not registered
|
99 |
-
if (eventName != 'ready' && removed) {
|
100 |
-
postMessage('removeEventListener', eventName, element);
|
101 |
-
}
|
102 |
-
}
|
103 |
-
};
|
104 |
-
|
105 |
-
/**
|
106 |
-
* Handles posting a message to the parent window.
|
107 |
-
*
|
108 |
-
* @param method (String): name of the method to call inside the player. For api calls
|
109 |
-
* this is the name of the api method (api_play or api_pause) while for events this method
|
110 |
-
* is api_addEventListener.
|
111 |
-
* @param params (Object or Array): List of parameters to submit to the method. Can be either
|
112 |
-
* a single param or an array list of parameters.
|
113 |
-
* @param target (HTMLElement): Target iframe to post the message to.
|
114 |
-
*/
|
115 |
-
|
116 |
-
function postMessage(method, params, target) {
|
117 |
-
if (!target.contentWindow.postMessage) {
|
118 |
-
return false;
|
119 |
-
}
|
120 |
-
|
121 |
-
var data = JSON.stringify({
|
122 |
-
method: method,
|
123 |
-
value: params
|
124 |
-
});
|
125 |
-
|
126 |
-
target.contentWindow.postMessage(data, playerOrigin);
|
127 |
-
}
|
128 |
-
|
129 |
-
/**
|
130 |
-
* Event that fires whenever the window receives a message from its parent
|
131 |
-
* via window.postMessage.
|
132 |
-
*/
|
133 |
-
|
134 |
-
function onMessageReceived(event) {
|
135 |
-
var data, method;
|
136 |
-
|
137 |
-
try {
|
138 |
-
data = JSON.parse(event.data);
|
139 |
-
method = data.event || data.method;
|
140 |
-
} catch (e) {
|
141 |
-
//fail silently... like a ninja!
|
142 |
-
}
|
143 |
-
|
144 |
-
if (method == 'ready' && !isReady) {
|
145 |
-
isReady = true;
|
146 |
-
}
|
147 |
-
|
148 |
-
// Handles messages from the vimeo player only
|
149 |
-
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
|
150 |
-
return false;
|
151 |
-
}
|
152 |
-
|
153 |
-
if (playerOrigin === '*') {
|
154 |
-
playerOrigin = event.origin;
|
155 |
-
}
|
156 |
-
|
157 |
-
var value = data.value,
|
158 |
-
eventData = data.data,
|
159 |
-
target_id = target_id === '' ? null : data.player_id,
|
160 |
-
|
161 |
-
callback = getCallback(method, target_id),
|
162 |
-
params = [];
|
163 |
-
|
164 |
-
if (!callback) {
|
165 |
-
return false;
|
166 |
-
}
|
167 |
-
|
168 |
-
if (value !== undefined) {
|
169 |
-
params.push(value);
|
170 |
-
}
|
171 |
-
|
172 |
-
if (eventData) {
|
173 |
-
params.push(eventData);
|
174 |
-
}
|
175 |
-
|
176 |
-
if (target_id) {
|
177 |
-
params.push(target_id);
|
178 |
-
}
|
179 |
-
|
180 |
-
return params.length > 0 ? callback.apply(null, params) : callback.call();
|
181 |
-
}
|
182 |
-
|
183 |
-
|
184 |
-
/**
|
185 |
-
* Stores submitted callbacks for each iframe being tracked and each
|
186 |
-
* event for that iframe.
|
187 |
-
*
|
188 |
-
* @param eventName (String): Name of the event. Eg. api_onPlay
|
189 |
-
* @param callback (Function): Function that should get executed when the
|
190 |
-
* event is fired.
|
191 |
-
* @param target_id (String) [Optional]: If handling more than one iframe then
|
192 |
-
* it stores the different callbacks for different iframes based on the iframe's
|
193 |
-
* id.
|
194 |
-
*/
|
195 |
-
|
196 |
-
function storeCallback(eventName, callback, target_id) {
|
197 |
-
if (target_id) {
|
198 |
-
if (!eventCallbacks[target_id]) {
|
199 |
-
eventCallbacks[target_id] = {};
|
200 |
-
}
|
201 |
-
eventCallbacks[target_id][eventName] = callback;
|
202 |
-
} else {
|
203 |
-
eventCallbacks[eventName] = callback;
|
204 |
-
}
|
205 |
-
}
|
206 |
-
|
207 |
-
/**
|
208 |
-
* Retrieves stored callbacks.
|
209 |
-
*/
|
210 |
-
|
211 |
-
function getCallback(eventName, target_id) {
|
212 |
-
if (target_id) {
|
213 |
-
return eventCallbacks[target_id][eventName];
|
214 |
-
} else {
|
215 |
-
return eventCallbacks[eventName];
|
216 |
-
}
|
217 |
-
}
|
218 |
-
|
219 |
-
function removeCallback(eventName, target_id) {
|
220 |
-
if (target_id && eventCallbacks[target_id]) {
|
221 |
-
if (!eventCallbacks[target_id][eventName]) {
|
222 |
-
return false;
|
223 |
-
}
|
224 |
-
eventCallbacks[target_id][eventName] = null;
|
225 |
-
} else {
|
226 |
-
if (!eventCallbacks[eventName]) {
|
227 |
-
return false;
|
228 |
-
}
|
229 |
-
eventCallbacks[eventName] = null;
|
230 |
-
}
|
231 |
-
|
232 |
-
return true;
|
233 |
-
}
|
234 |
-
|
235 |
-
function isFunction(obj) {
|
236 |
-
return !!(obj && obj.constructor && obj.call && obj.apply);
|
237 |
-
}
|
238 |
-
|
239 |
-
function isArray(obj) {
|
240 |
-
return toString.call(obj) === '[object Array]';
|
241 |
-
}
|
242 |
-
|
243 |
-
// Give the init function the Froogaloop prototype for later instantiation
|
244 |
-
Froogaloop.fn.init.prototype = Froogaloop.fn;
|
245 |
-
|
246 |
-
// Listens for the message event.
|
247 |
-
// W3C
|
248 |
-
if (window.addEventListener) {
|
249 |
-
window.addEventListener('message', onMessageReceived, false);
|
250 |
-
}
|
251 |
-
// IE
|
252 |
-
else {
|
253 |
-
window.attachEvent('onmessage', onMessageReceived);
|
254 |
-
}
|
255 |
-
|
256 |
-
// Expose froogaloop to the global object
|
257 |
-
return (window.Froogaloop = window.$f = Froogaloop);
|
258 |
-
|
259 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/gtm4wp-form-move-tracker.js
CHANGED
@@ -1,33 +1,33 @@
|
|
1 |
-
document.addEventListener("focusin", function(event) {
|
2 |
-
const elem = event.target
|
3 |
-
if ( elem ) {
|
4 |
-
window[ gtm4wp_datalayer_name ].push({
|
5 |
-
'event' : 'gtm4wp.formElementEnter',
|
6 |
-
|
7 |
-
'inputID' : elem.getAttribute("id") || "(no input ID)",
|
8 |
-
'inputName' : elem.getAttribute("name") || "(no input name)",
|
9 |
-
'inputClass': elem.getAttribute("class") || "(no input class)",
|
10 |
-
|
11 |
-
'formID' : elem.form
|
12 |
-
'formName' : elem.form
|
13 |
-
'formClass': elem.form
|
14 |
-
});
|
15 |
-
}
|
16 |
-
}, false);
|
17 |
-
|
18 |
-
document.addEventListener("focusout", function(event) {
|
19 |
-
const elem = event.target.closest("input,select,textarea,button,meter,progress");
|
20 |
-
if ( elem ) {
|
21 |
-
window[ gtm4wp_datalayer_name ].push({
|
22 |
-
'event' : 'gtm4wp.formElementLeave',
|
23 |
-
|
24 |
-
'inputID' : elem.getAttribute("id") || "(no input ID)",
|
25 |
-
'inputName' : elem.getAttribute("name") || "(no input name)",
|
26 |
-
'inputClass': elem.getAttribute("class") || "(no input class)",
|
27 |
-
|
28 |
-
'formID' : elem.form
|
29 |
-
'formName' : elem.form
|
30 |
-
'formClass': elem.form
|
31 |
-
});
|
32 |
-
};
|
33 |
-
}, false);
|
1 |
+
document.addEventListener("focusin", function(event) {
|
2 |
+
const elem = event.target?.closest("input,select,textarea,button,meter,progress");
|
3 |
+
if ( elem ) {
|
4 |
+
window[ gtm4wp_datalayer_name ].push({
|
5 |
+
'event' : 'gtm4wp.formElementEnter',
|
6 |
+
|
7 |
+
'inputID' : elem.getAttribute("id") || "(no input ID)",
|
8 |
+
'inputName' : elem.getAttribute("name") || "(no input name)",
|
9 |
+
'inputClass': elem.getAttribute("class") || "(no input class)",
|
10 |
+
|
11 |
+
'formID' : elem.form?.getAttribute("id") || "(no form ID)",
|
12 |
+
'formName' : elem.form?.getAttribute("name") || "(no form name)",
|
13 |
+
'formClass': elem.form?.getAttribute("class") || "(no form class)"
|
14 |
+
});
|
15 |
+
}
|
16 |
+
}, false);
|
17 |
+
|
18 |
+
document.addEventListener("focusout", function(event) {
|
19 |
+
const elem = event.target.closest("input,select,textarea,button,meter,progress");
|
20 |
+
if ( elem ) {
|
21 |
+
window[ gtm4wp_datalayer_name ].push({
|
22 |
+
'event' : 'gtm4wp.formElementLeave',
|
23 |
+
|
24 |
+
'inputID' : elem.getAttribute("id") || "(no input ID)",
|
25 |
+
'inputName' : elem.getAttribute("name") || "(no input name)",
|
26 |
+
'inputClass': elem.getAttribute("class") || "(no input class)",
|
27 |
+
|
28 |
+
'formID' : elem.form?.getAttribute("id") || "(no form ID)",
|
29 |
+
'formName' : elem.form?.getAttribute("name") || "(no form name)",
|
30 |
+
'formClass': elem.form?.getAttribute("class") || "(no form class)"
|
31 |
+
});
|
32 |
+
};
|
33 |
+
}, false);
|
js/gtm4wp-woocommerce-enhanced.js
CHANGED
@@ -51,8 +51,8 @@ function gtm4wp_map_eec_to_ga4( productdata ) {
|
|
51 |
ga4_product.coupon = productdata.coupon;
|
52 |
}
|
53 |
|
54 |
-
ga4_product.google_business_vertical =
|
55 |
-
ga4_product[
|
56 |
|
57 |
return ga4_product;
|
58 |
}
|
@@ -252,7 +252,7 @@ function gtm4wp_handle_shipping_method_change() {
|
|
252 |
gtm4wp_checkout_step_fired.push( 'shipping_method' );
|
253 |
}
|
254 |
|
255 |
-
|
256 |
window.gtm4wp_is_cart = document.querySelector( 'body' )?.classList?.contains( 'woocommerce-cart' );
|
257 |
window.gtm4wp_is_checkout = document.querySelector( 'body' )?.classList?.contains( 'woocommerce-checkout' );
|
258 |
|
@@ -322,7 +322,7 @@ document.addEventListener( 'DOMContentLoaded', function() {
|
|
322 |
let product_data;
|
323 |
|
324 |
document.querySelectorAll( '.gtm4wp_productdata,.widget-product-item' ).forEach( function( dom_productdata ) {
|
325 |
-
productprice = dom_productdata.getAttribute( 'gtm4wp_product_price' );
|
326 |
|
327 |
if ( typeof productprice == "string" ) {
|
328 |
productprice = parseFloat( productprice );
|
@@ -1188,4 +1188,19 @@ document.addEventListener( 'DOMContentLoaded', function() {
|
|
1188 |
}
|
1189 |
});
|
1190 |
}
|
1191 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
}
|
252 |
gtm4wp_checkout_step_fired.push( 'shipping_method' );
|
253 |
}
|
254 |
|
255 |
+
function gtm4wp_process_woocommerce_pages() {
|
256 |
window.gtm4wp_is_cart = document.querySelector( 'body' )?.classList?.contains( 'woocommerce-cart' );
|
257 |
window.gtm4wp_is_checkout = document.querySelector( 'body' )?.classList?.contains( 'woocommerce-checkout' );
|
258 |
|
322 |
let product_data;
|
323 |
|
324 |
document.querySelectorAll( '.gtm4wp_productdata,.widget-product-item' ).forEach( function( dom_productdata ) {
|
325 |
+
productprice = dom_productdata.getAttribute( 'data-gtm4wp_product_price' );
|
326 |
|
327 |
if ( typeof productprice == "string" ) {
|
328 |
productprice = parseFloat( productprice );
|
1188 |
}
|
1189 |
});
|
1190 |
}
|
1191 |
+
};
|
1192 |
+
|
1193 |
+
function gtm4wp_page_loading_completed() {
|
1194 |
+
document.removeEventListener( "DOMContentLoaded", gtm4wp_page_loading_completed );
|
1195 |
+
window.removeEventListener( "load", gtm4wp_page_loading_completed );
|
1196 |
+
gtm4wp_process_woocommerce_pages();
|
1197 |
+
}
|
1198 |
+
|
1199 |
+
// code and idea borrowed from jQuery:
|
1200 |
+
// https://github.com/jquery/jquery/blob/main/src/core/ready.js
|
1201 |
+
if ( document.readyState !== "loading" ) {
|
1202 |
+
window.setTimeout( gtm4wp_process_woocommerce_pages );
|
1203 |
+
} else {
|
1204 |
+
document.addEventListener( "DOMContentLoaded", gtm4wp_page_loading_completed );
|
1205 |
+
window.addEventListener( "load", gtm4wp_page_loading_completed );
|
1206 |
+
}
|
readme.txt
CHANGED
@@ -5,7 +5,7 @@ Tags: google tag manager, tag manager, gtm, google, adwords, google adwords, goo
|
|
5 |
Requires at least: 3.4.0
|
6 |
Requires PHP: 5.6
|
7 |
Tested up to: 5.8.2
|
8 |
-
Stable tag: 1.14.
|
9 |
License: GPLv3
|
10 |
License URI: http://www.gnu.org/licenses/gpl.html
|
11 |
|
@@ -233,6 +233,13 @@ https://gtm4wp.com/how-to-articles/how-to-exclude-admin-users-from-being-tracked
|
|
233 |
|
234 |
== Changelog ==
|
235 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
= 1.14.1 =
|
237 |
|
238 |
* Bugfixes
|
@@ -742,6 +749,10 @@ Please report all bugs found in my plugin using the [contact form on my website]
|
|
742 |
|
743 |
== Upgrade Notice ==
|
744 |
|
|
|
|
|
|
|
|
|
745 |
= 1.14.1 =
|
746 |
|
747 |
Bugfix release
|
5 |
Requires at least: 3.4.0
|
6 |
Requires PHP: 5.6
|
7 |
Tested up to: 5.8.2
|
8 |
+
Stable tag: 1.14.2
|
9 |
License: GPLv3
|
10 |
License URI: http://www.gnu.org/licenses/gpl.html
|
11 |
|
233 |
|
234 |
== Changelog ==
|
235 |
|
236 |
+
= 1.14.2 =
|
237 |
+
|
238 |
+
* Fixed: undefined google_business_vertical
|
239 |
+
* Fixed: missing product price in product impression data
|
240 |
+
* Fixed: better compatibility with cache plugins and lazy load functionalities
|
241 |
+
* Fixed: Added optional chaining operator to form move tracker code
|
242 |
+
|
243 |
= 1.14.1 =
|
244 |
|
245 |
* Bugfixes
|
749 |
|
750 |
== Upgrade Notice ==
|
751 |
|
752 |
+
= 1.14.2 =
|
753 |
+
|
754 |
+
Bugfix release
|
755 |
+
|
756 |
= 1.14.1 =
|
757 |
|
758 |
Bugfix release
|