DuracellTomi's Google Tag Manager for WordPress - Version 1.1

Version Description

  • Added: track embedded YouTube/Vimeo/Soundcloud videos (experimental)
  • Added: new checkbox - use product SKU for AdWords Dynamic Remarketing variables instead of product ID (experimental)
  • Added: place your container code after the opening body tag without modifying your theme files (thx Yaniv Friedensohn)
  • Added: automatic codeless container code injection for Genesis framework users
  • Fixed: Possible PHP error with custom payment gateway (QuickPay) on the checkout page (thx Damiel for findig this)
Download this release

Release Info

Developer duracelltomi
Plugin Icon 128x128 DuracellTomi's Google Tag Manager for WordPress
Version 1.1
Comparing to
See all releases

Code changes from version 1.0 to 1.1

admin/admin.php CHANGED
@@ -129,6 +129,21 @@ $GLOBALS["gtm4wp_eventfieldtexts"] = array(
129
  "description" => __( "Check this option to include a Tag Manager event when a visitor uses a social button to share/like content on a social network.", GTM4WP_TEXTDOMAIN ),
130
  "phase" => GTM4WP_PHASE_STABLE
131
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  GTM4WP_OPTION_EVENTS_OUTBOUND => array(
133
  "label" => __( "Outbound link click events (gtm4wp.outboundClick)", GTM4WP_TEXTDOMAIN ),
134
  "description" => __( "Check this option to include a Tag Manager event when a visitor clicks on a link directing the visitor out of your website.", GTM4WP_TEXTDOMAIN ),
@@ -374,8 +389,13 @@ $GLOBALS["gtm4wp_integratefieldtexts"] = array(
374
  GTM4WP_OPTION_INTEGRATE_WCREMARKETING => array(
375
  "label" => __( "AdWords Remarketing", GTM4WP_TEXTDOMAIN ),
376
  "description" => __( "Enable this to add Google AdWords dynamic remarketing variables to the dataLayer", GTM4WP_TEXTDOMAIN ),
377
- "phase" => GTM4WP_PHASE_STABLE,
378
  "plugintocheck" => "woocommerce/woocommerce.php"
 
 
 
 
 
379
  )
380
  );
381
 
@@ -460,8 +480,9 @@ function gtm4wp_admin_output_field( $args ) {
460
  }
461
 
462
  case GTM4WP_ADMIN_GROUP_PLACEMENT: {
463
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']_0" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']" value="0" ' . ( $gtm4wp_options[GTM4WP_OPTION_GTM_PLACEMENT] == 0 ? 'checked="checked"' : '' ) . '/> ' . __( "Footer of the page (not recommended by Google, no tweak in your template required)", GTM4WP_TEXTDOMAIN ) . '<br />';
464
- echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']_1" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']" value="1" ' . ( $gtm4wp_options[GTM4WP_OPTION_GTM_PLACEMENT] == 1 ? 'checked="checked"' : '' ) . '/> ' . __( "Custom (needs tweak in your template)", GTM4WP_TEXTDOMAIN ) . '<br />' . $args["description"];
 
465
 
466
  break;
467
  }
@@ -542,6 +563,8 @@ function gtm4wp_admin_output_field( $args ) {
542
  }
543
 
544
  function gtm4wp_sanitize_options($options) {
 
 
545
  $output = gtm4wp_reload_options();
546
 
547
  foreach($output as $optionname => $optionvalue) {
@@ -563,6 +586,17 @@ function gtm4wp_sanitize_options($options) {
563
  } else if ( substr($optionname, 0, 6) == "event-" ) {
564
  $output[$optionname] = (boolean) $newoptionvalue;
565
 
 
 
 
 
 
 
 
 
 
 
 
566
  // integrations
567
  } else if ( substr($optionname, 0, 10) == "integrate-" ) {
568
  $output[$optionname] = (boolean) $newoptionvalue;
@@ -584,7 +618,7 @@ function gtm4wp_sanitize_options($options) {
584
  // GTM container code placement
585
  } else if ( $optionname == GTM4WP_OPTION_GTM_PLACEMENT ) {
586
  $output[$optionname] = (int) $newoptionvalue;
587
- if ( ( $output[$optionname] < 0) || ( $output[$optionname] > 1 ) ) {
588
  $output[$optionname] = 0;
589
  }
590
 
@@ -820,6 +854,11 @@ function gtm4wp_admin_init() {
820
  )
821
  );
822
 
 
 
 
 
 
823
  }
824
 
825
  function gtm4wp_show_admin_page() {
@@ -864,7 +903,10 @@ function gtm4wp_add_admin_js($hook) {
864
  "blockmacrostabtitle" => __( "Blacklist macros" , GTM4WP_TEXTDOMAIN ),
865
  "wpcf7tabtitle" => __( "Contact Form 7" , GTM4WP_TEXTDOMAIN ),
866
  "wctabtitle" => __( "WooCommerce" , GTM4WP_TEXTDOMAIN ),
867
- "weathertabtitle" => __( "Weather data" , GTM4WP_TEXTDOMAIN )
 
 
 
868
  );
869
  wp_localize_script( "admin-subtabs", 'gtm4wp', $subtabtexts );
870
  wp_enqueue_script( "admin-subtabs" );
@@ -1019,4 +1061,4 @@ add_action( 'admin_enqueue_scripts', 'gtm4wp_add_admin_js' );
1019
  add_action( 'admin_notices', 'gtm4wp_show_warning' );
1020
  add_action( 'admin_head', 'gtm4wp_admin_head' );
1021
  add_filter( 'plugin_action_links', 'gtm4wp_add_plugin_action_links', 10, 2 );
1022
- add_action( 'wp_ajax_gtm4wp_dismiss_notice', 'gtm4wp_dismiss_notice' );
129
  "description" => __( "Check this option to include a Tag Manager event when a visitor uses a social button to share/like content on a social network.", GTM4WP_TEXTDOMAIN ),
130
  "phase" => GTM4WP_PHASE_STABLE
131
  ),
132
+ GTM4WP_OPTION_EVENTS_YOUTUBE => array(
133
+ "label" => __( "YouTube video events", GTM4WP_TEXTDOMAIN ),
134
+ "description" => __( "Check this option to include a Tag Manager event when a visitor interacts with a YouTube video embeded on your site.", GTM4WP_TEXTDOMAIN ),
135
+ "phase" => GTM4WP_PHASE_EXPERIMENTAL
136
+ ),
137
+ GTM4WP_OPTION_EVENTS_VIMEO => array(
138
+ "label" => __( "Vimeo video events", GTM4WP_TEXTDOMAIN ),
139
+ "description" => __( "Check this option to include a Tag Manager event when a visitor interacts with a Vimeo video embeded on your site.", GTM4WP_TEXTDOMAIN ),
140
+ "phase" => GTM4WP_PHASE_EXPERIMENTAL
141
+ ),
142
+ GTM4WP_OPTION_EVENTS_SOUNDCLOUD => array(
143
+ "label" => __( "Soundcloud events", GTM4WP_TEXTDOMAIN ),
144
+ "description" => __( "Check this option to include a Tag Manager event when a visitor interacts with a Soundcloud media embeded on your site.", GTM4WP_TEXTDOMAIN ),
145
+ "phase" => GTM4WP_PHASE_EXPERIMENTAL
146
+ ),
147
  GTM4WP_OPTION_EVENTS_OUTBOUND => array(
148
  "label" => __( "Outbound link click events (gtm4wp.outboundClick)", GTM4WP_TEXTDOMAIN ),
149
  "description" => __( "Check this option to include a Tag Manager event when a visitor clicks on a link directing the visitor out of your website.", GTM4WP_TEXTDOMAIN ),
389
  GTM4WP_OPTION_INTEGRATE_WCREMARKETING => array(
390
  "label" => __( "AdWords Remarketing", GTM4WP_TEXTDOMAIN ),
391
  "description" => __( "Enable this to add Google AdWords dynamic remarketing variables to the dataLayer", GTM4WP_TEXTDOMAIN ),
392
+ "phase" => GTM4WP_PHASE_STABLE,
393
  "plugintocheck" => "woocommerce/woocommerce.php"
394
+ ),
395
+ GTM4WP_OPTION_INTEGRATE_WCREMARKETINGSKU => array(
396
+ "label" => __( "Use SKU instead of ID", GTM4WP_TEXTDOMAIN ),
397
+ "description" => __( "Check this to use product SKU in the dynamic remarketing variables instead of the ID of the products. Will fallback to ID if no SKU is set.", GTM4WP_TEXTDOMAIN ),
398
+ "phase" => GTM4WP_PHASE_EXPERIMENTAL
399
  )
400
  );
401
 
480
  }
481
 
482
  case GTM4WP_ADMIN_GROUP_PLACEMENT: {
483
+ echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']_' . GTM4WP_PLACEMENT_FOOTER . '" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']" value="' . GTM4WP_PLACEMENT_FOOTER . '" ' . ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] == GTM4WP_PLACEMENT_FOOTER ? 'checked="checked"' : '' ) . '/> ' . __( "Footer of the page (not recommended by Google, no tweak in your template required)", GTM4WP_TEXTDOMAIN ) . '<br />';
484
+ echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']_' . GTM4WP_PLACEMENT_BODYOPEN . '" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']" value="' . GTM4WP_PLACEMENT_BODYOPEN . '" ' . ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] == GTM4WP_PLACEMENT_BODYOPEN ? 'checked="checked"' : '' ) . '/> ' . __( "Custom (needs tweak in your template)", GTM4WP_TEXTDOMAIN ) . '<br />';
485
+ echo '<input type="radio" id="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']_' . GTM4WP_PLACEMENT_BODYOPEN_AUTO . '" name="' . GTM4WP_OPTIONS . '[' . GTM4WP_OPTION_GTM_PLACEMENT . ']" value="' . GTM4WP_PLACEMENT_BODYOPEN_AUTO . '" ' . ( $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] == GTM4WP_PLACEMENT_BODYOPEN_AUTO ? 'checked="checked"' : '' ) . '/> ' . __( "Codeless injection (no tweak, right placement but experimental, could break your frontend)", GTM4WP_TEXTDOMAIN ) . '<br /><br />' . $args["description"];
486
 
487
  break;
488
  }
563
  }
564
 
565
  function gtm4wp_sanitize_options($options) {
566
+ global $wpdb;
567
+
568
  $output = gtm4wp_reload_options();
569
 
570
  foreach($output as $optionname => $optionvalue) {
586
  } else if ( substr($optionname, 0, 6) == "event-" ) {
587
  $output[$optionname] = (boolean) $newoptionvalue;
588
 
589
+ // clear oembed transients when feature is enabled because we need to hook into the oembed process to enable some 3rd party APIs
590
+ if ( $output[$optionname] && !$optionvalue ) {
591
+ if ( GTM4WP_OPTION_EVENTS_YOUTUBE == $optionname ) {
592
+ $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_value LIKE '%youtube.com%' AND meta_key LIKE '_oembed_%'" );
593
+ }
594
+
595
+ if ( GTM4WP_OPTION_EVENTS_VIMEO == $optionname ) {
596
+ $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_value LIKE '%vimeo.com%' AND meta_key LIKE '_oembed_%'" );
597
+ }
598
+ }
599
+
600
  // integrations
601
  } else if ( substr($optionname, 0, 10) == "integrate-" ) {
602
  $output[$optionname] = (boolean) $newoptionvalue;
618
  // GTM container code placement
619
  } else if ( $optionname == GTM4WP_OPTION_GTM_PLACEMENT ) {
620
  $output[$optionname] = (int) $newoptionvalue;
621
+ if ( ( $output[$optionname] < 0) || ( $output[$optionname] > 2 ) ) {
622
  $output[$optionname] = 0;
623
  }
624
 
854
  )
855
  );
856
 
857
+ // apply oembed code changes on the admin as well since the oembed call on the admin is cached by WordPress into a transient
858
+ // that is applied on the frontend later
859
+ require_once( dirname( __FILE__ ) . "/../integration/youtube.php" );
860
+ require_once( dirname( __FILE__ ) . "/../integration/vimeo.php" );
861
+ require_once( dirname( __FILE__ ) . "/../integration/soundcloud.php" );
862
  }
863
 
864
  function gtm4wp_show_admin_page() {
903
  "blockmacrostabtitle" => __( "Blacklist macros" , GTM4WP_TEXTDOMAIN ),
904
  "wpcf7tabtitle" => __( "Contact Form 7" , GTM4WP_TEXTDOMAIN ),
905
  "wctabtitle" => __( "WooCommerce" , GTM4WP_TEXTDOMAIN ),
906
+ "weathertabtitle" => __( "Weather data" , GTM4WP_TEXTDOMAIN ),
907
+ "generaleventstabtitle" => __( "General events" , GTM4WP_TEXTDOMAIN ),
908
+ "mediaeventstabtitle" => __( "Media events" , GTM4WP_TEXTDOMAIN ),
909
+ "depecratedeventstabtitle" => __( "Depecrated" , GTM4WP_TEXTDOMAIN )
910
  );
911
  wp_localize_script( "admin-subtabs", 'gtm4wp', $subtabtexts );
912
  wp_enqueue_script( "admin-subtabs" );
1061
  add_action( 'admin_notices', 'gtm4wp_show_warning' );
1062
  add_action( 'admin_head', 'gtm4wp_admin_head' );
1063
  add_filter( 'plugin_action_links', 'gtm4wp_add_plugin_action_links', 10, 2 );
1064
+ add_action( 'wp_ajax_gtm4wp_dismiss_notice', 'gtm4wp_dismiss_notice' );
common/readoptions.php CHANGED
@@ -28,6 +28,9 @@ define( 'GTM4WP_OPTION_EVENTS_DWLEXT', 'event-download-extensions' );
28
  define( 'GTM4WP_OPTION_EVENTS_EMAILCLICKS', 'event-email-clicks' );
29
  define( 'GTM4WP_OPTION_EVENTS_FORMMOVE', 'event-form-move' );
30
  define( 'GTM4WP_OPTION_EVENTS_SOCIAL', 'event-social' );
 
 
 
31
 
32
  define( 'GTM4WP_OPTION_SCROLLER_ENABLED', 'scroller-enabled' );
33
  define( 'GTM4WP_OPTION_SCROLLER_DEBUGMODE', 'scroller-debug-mode' );
@@ -75,11 +78,13 @@ define( 'GTM4WP_OPTION_INTEGRATE_WOOCOMMERCE', 'integrate-woocommerce' );
75
  define( 'GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC', 'integrate-woocommerce-track-classic-ecommerce' );
76
  define( 'GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC', 'integrate-woocommerce-track-enhanced-ecommerce' );
77
  define( 'GTM4WP_OPTION_INTEGRATE_WCREMARKETING', 'integrate-woocommerce-remarketing' );
 
78
 
79
  define( 'GTM4WP_OPTION_INTEGRATE_WPECOMMERCE', 'integrate-wp-e-commerce' );
80
 
81
- define( 'GTM4WP_PLACEMENT_FOOTER', 0 );
82
- define( 'GTM4WP_PLACEMENT_BODYOPEN', 1 );
 
83
 
84
  $gtm4wp_options = array();
85
 
@@ -112,6 +117,9 @@ $gtm4wp_defaultoptions = array(
112
  GTM4WP_OPTION_EVENTS_EMAILCLICKS => false,
113
  GTM4WP_OPTION_EVENTS_FORMMOVE => true,
114
  GTM4WP_OPTION_EVENTS_SOCIAL => false,
 
 
 
115
 
116
  GTM4WP_OPTION_SCROLLER_ENABLED => false,
117
  GTM4WP_OPTION_SCROLLER_DEBUGMODE => false,
@@ -159,6 +167,7 @@ $gtm4wp_defaultoptions = array(
159
  GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC => false,
160
  GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC => false,
161
  GTM4WP_OPTION_INTEGRATE_WCREMARKETING => false,
 
162
 
163
  GTM4WP_OPTION_INTEGRATE_WPECOMMERCE => false
164
  );
28
  define( 'GTM4WP_OPTION_EVENTS_EMAILCLICKS', 'event-email-clicks' );
29
  define( 'GTM4WP_OPTION_EVENTS_FORMMOVE', 'event-form-move' );
30
  define( 'GTM4WP_OPTION_EVENTS_SOCIAL', 'event-social' );
31
+ define( 'GTM4WP_OPTION_EVENTS_YOUTUBE', 'event-youtube' );
32
+ define( 'GTM4WP_OPTION_EVENTS_VIMEO', 'event-vimeo' );
33
+ define( 'GTM4WP_OPTION_EVENTS_SOUNDCLOUD', 'event-soundcloud' );
34
 
35
  define( 'GTM4WP_OPTION_SCROLLER_ENABLED', 'scroller-enabled' );
36
  define( 'GTM4WP_OPTION_SCROLLER_DEBUGMODE', 'scroller-debug-mode' );
78
  define( 'GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC', 'integrate-woocommerce-track-classic-ecommerce' );
79
  define( 'GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC', 'integrate-woocommerce-track-enhanced-ecommerce' );
80
  define( 'GTM4WP_OPTION_INTEGRATE_WCREMARKETING', 'integrate-woocommerce-remarketing' );
81
+ define( 'GTM4WP_OPTION_INTEGRATE_WCREMARKETINGSKU', 'integrate-woocommerce-remarketing-usesku' );
82
 
83
  define( 'GTM4WP_OPTION_INTEGRATE_WPECOMMERCE', 'integrate-wp-e-commerce' );
84
 
85
+ define( 'GTM4WP_PLACEMENT_FOOTER', 0 );
86
+ define( 'GTM4WP_PLACEMENT_BODYOPEN', 1 );
87
+ define( 'GTM4WP_PLACEMENT_BODYOPEN_AUTO', 2 );
88
 
89
  $gtm4wp_options = array();
90
 
117
  GTM4WP_OPTION_EVENTS_EMAILCLICKS => false,
118
  GTM4WP_OPTION_EVENTS_FORMMOVE => true,
119
  GTM4WP_OPTION_EVENTS_SOCIAL => false,
120
+ GTM4WP_OPTION_EVENTS_YOUTUBE => false,
121
+ GTM4WP_OPTION_EVENTS_VIMEO => false,
122
+ GTM4WP_OPTION_EVENTS_SOUNDCLOUD => false,
123
 
124
  GTM4WP_OPTION_SCROLLER_ENABLED => false,
125
  GTM4WP_OPTION_SCROLLER_DEBUGMODE => false,
167
  GTM4WP_OPTION_INTEGRATE_WCTRACKCLASSICEC => false,
168
  GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC => false,
169
  GTM4WP_OPTION_INTEGRATE_WCREMARKETING => false,
170
+ GTM4WP_OPTION_INTEGRATE_WCREMARKETINGSKU => false,
171
 
172
  GTM4WP_OPTION_INTEGRATE_WPECOMMERCE => false
173
  );
css/admin-gtm4wp.css CHANGED
@@ -68,5 +68,5 @@
68
  }
69
 
70
  .gtm4wp-phase-deprecated:before {
71
- content: "depecrated";
72
  }
68
  }
69
 
70
  .gtm4wp-phase-deprecated:before {
71
+ content: "deprecated";
72
  }
duracelltomi-google-tag-manager-for-wordpress.php CHANGED
@@ -1,14 +1,14 @@
1
  <?php
2
  /*
3
  Plugin Name: Google Tag Manager for Wordpress
4
- Version: 1.0
5
  Plugin URI: https://duracelltomi.com/google-tag-manager-for-wordpress/
6
  Description: The first Google Tag Manager plugin for WordPress with business goals in mind
7
  Author: Thomas Geiger
8
  Author URI: https://duracelltomi.com/
9
  */
10
 
11
- define( 'GTM4WP_VERSION', '1.0' );
12
  define( 'GTM4WP_PATH', plugin_dir_path( __FILE__ ) );
13
  define( 'GTM4WP_TEXTDOMAIN', 'gtm4wp-lang' );
14
 
1
  <?php
2
  /*
3
  Plugin Name: Google Tag Manager for Wordpress
4
+ Version: 1.1
5
  Plugin URI: https://duracelltomi.com/google-tag-manager-for-wordpress/
6
  Description: The first Google Tag Manager plugin for WordPress with business goals in mind
7
  Author: Thomas Geiger
8
  Author URI: https://duracelltomi.com/
9
  */
10
 
11
+ define( 'GTM4WP_VERSION', '1.1' );
12
  define( 'GTM4WP_PATH', plugin_dir_path( __FILE__ ) );
13
  define( 'GTM4WP_TEXTDOMAIN', 'gtm4wp-lang' );
14
 
integration/soundcloud.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function gtm4wp_soundcloud( $return, $url, $data ) {
3
+ if ( false !== strpos( $return, "soundcloud.com" ) ) {
4
+ if ( false === strpos( $return, ' id="' ) ) {
5
+ if ( preg_match('/src="([^\"]+?)"/i', $return, $r) ) {
6
+ $_urlquery = parse_url( $r[1], PHP_URL_QUERY );
7
+ if ( false !== $_urlquery ) {
8
+ parse_str( $_urlquery, $_urlparts );
9
+
10
+ if ( isset( $_urlparts[ "url" ] ) ) {
11
+ $_urlpartsid = explode( "/", $_urlparts[ "url" ] );
12
+ $_playerid = "soundcloudplayer_" . $_urlpartsid[ count( $_urlpartsid )-1 ];
13
+ $return = str_replace( '<iframe ', '<iframe id="' . $_playerid . '" ', $return);
14
+ }
15
+ }
16
+ }
17
+ }
18
+ }
19
+
20
+ return $return;
21
+ }
22
+
23
+ add_filter( "oembed_result", "gtm4wp_soundcloud", 10, 3 );
24
+
25
+ if ( ! is_admin() ) {
26
+ wp_enqueue_script( "gtm4wp-soundcloud-api", "https://w.soundcloud.com/player/api.js", array(), "1.0", false );
27
+ wp_enqueue_script( "gtm4wp-soundcloud", $gtp4wp_plugin_url . "js/gtm4wp-soundcloud.js", array( "jquery" ), "1.0", false );
28
+ }
integration/vimeo.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function gtm4wp_vimeo( $return, $url, $data ) {
3
+ if ( false !== strpos( $return, "vimeo.com" ) ) {
4
+ if ( false === strpos( $return, ' id="' ) ) {
5
+ $_urlparts = explode( "/", $url );
6
+ $_playerid = "vimeoplayer_" . $_urlparts[ count( $_urlparts )-1 ];
7
+
8
+ $return = str_replace( '<iframe ', '<iframe id="' . $_playerid . '" ', $return);
9
+ $return = str_replace( $url, $url . "?api=1&origin=" . site_url() . "&player_id=" . $_playerid, $return);
10
+ } else {
11
+ $return = str_replace( $url, $url . "?api=1&origin=" . site_url(), $return);
12
+ }
13
+ }
14
+
15
+ return $return;
16
+ }
17
+
18
+ add_filter( "oembed_result", "gtm4wp_vimeo", 10, 3 );
19
+ if ( ! is_admin() ) {
20
+ wp_enqueue_script( "gtm4wp-vimeo-froogaloop", $gtp4wp_plugin_url . "js/froogaloop.js", array(), "2.0", false );
21
+ //wp_enqueue_script( "gtm4wp-vimeo-froogaloop", "//f.vimeocdn.com/js/froogaloop2.min.js", array(), "2.0", false );
22
+ wp_enqueue_script( "gtm4wp-vimeo", $gtp4wp_plugin_url . "js/gtm4wp-vimeo.js", array( "jquery" ), "1.0", false );
23
+ }
integration/woocommerce.php CHANGED
@@ -11,13 +11,17 @@ function gtm4wp_woocommerce_addjs( $js ) {
11
  }
12
  }
13
 
 
 
 
 
14
  function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
15
  global $woocommerce, $gtm4wp_options, $wp_query, $gtm4wp_datalayer_name, $gtm4wp_product_counter;
16
 
17
  if ( is_product_category() || is_product_tag() || is_front_page() || is_shop() ) {
18
  if ( ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) || ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) ) {
19
  if ( count( $woocommerce->query->filtered_product_ids ) > 0 ) {
20
- // The following 5 lines are being borrowed from WC source
21
  $paged = max( 1, $wp_query->get( 'paged' ) );
22
  $per_page = $wp_query->get( 'posts_per_page' );
23
  $total = $wp_query->found_posts;
@@ -47,9 +51,23 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
47
  $product_cat = "";
48
  }
49
  $sumprice += $product_price;
50
- $product_ids[] = "'" . $oneproductid . "'";
51
 
52
- $product_impressions[] = "{'name': '" . str_replace( "'", "\\'", $product->get_title() ) . "', 'id': '" . $oneproductid . "', 'price': '" . $product_price . "', 'category': '" . str_replace( "'", "\\'", $product_cat ) . "', 'position': " . ($i+1) . " }";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  $i++;
55
  if ( $i>$last ) {
@@ -58,13 +76,13 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
58
  }
59
 
60
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
61
- $dataLayer["ecomm_prodid"] = '-~-[' . implode( ", ", $product_ids ) . ']-~-';
62
  $dataLayer["ecomm_pagetype"] = ( is_front_page() ? "home" : "category" );
63
- $dataLayer["ecomm_totalvalue"] = $sumprice;
64
  }
65
 
66
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
67
- $dataLayer["ecommerce"] = "-~-{'impressions': [".implode(", ", $product_impressions)."]}-~-";
68
  }
69
  }
70
  }
@@ -73,7 +91,7 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
73
  $prodid = get_the_ID();
74
  $product = get_product( $prodid );
75
  $product_price = $product->get_price();
76
- $_product_cats = get_the_terms($product->id, 'product_cat');
77
  if ( ( is_array($_product_cats) ) && ( count( $_product_cats ) > 0 ) ) {
78
  $product_cat = array_pop( $_product_cats );
79
  $product_cat = $product_cat->name;
@@ -82,13 +100,30 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
82
  }
83
 
84
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
85
- $dataLayer["ecomm_prodid"] = $prodid;
 
 
 
 
 
 
 
 
86
  $dataLayer["ecomm_pagetype"] = "product";
87
- $dataLayer["ecomm_totalvalue"] = $product_price;
88
  }
89
 
90
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
91
- $dataLayer["ecommerce"] = "-~-{'detail': {'products': [{'name': '" . str_replace( "'", "", get_the_title() ) . "', 'id': '" . $prodid . "', 'price': '" . $product_price . "', 'category': '" . str_replace( "'", "", $product_cat ) . "'}]}}-~-";
 
 
 
 
 
 
 
 
 
92
  }
93
  }
94
  } else if ( is_cart() ) {
@@ -121,12 +156,20 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
121
  $products = $woocommerce->cart->get_cart();
122
  $product_ids = array();
123
  foreach( $products as $oneproduct ) {
124
- $product_ids[] = "'" . str_replace( "'", "\\'", $oneproduct['product_id'] ) . "'";
 
 
 
 
 
 
 
 
125
  }
126
 
127
- $dataLayer["ecomm_prodid"] = '-~-[' . implode( ", ", $product_ids ) . ']-~-';
128
  $dataLayer["ecomm_pagetype"] = "cart";
129
- $dataLayer["ecomm_totalvalue"] = $woocommerce->cart->cart_contents_total;
130
  }
131
  } else if ( is_order_received_page() ) {
132
  $order_id = apply_filters( 'woocommerce_thankyou_order_id', empty( $_GET['order'] ) ? ($GLOBALS["wp"]->query_vars["order-received"] ? $GLOBALS["wp"]->query_vars["order-received"] : 0) : absint( $_GET['order'] ) );
@@ -147,7 +190,7 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
147
  $dataLayer["transactionId"] = $order->get_order_number();
148
  $dataLayer["transactionDate"] = date("c");
149
  $dataLayer["transactionType"] = "sale";
150
- $dataLayer["transactionAffiliation"] = get_bloginfo( 'name' );
151
  $dataLayer["transactionTotal"] = $order->get_total();
152
  $dataLayer["transactionShipping"] = $order->get_total_shipping();
153
  $dataLayer["transactionTax"] = $order->get_total_tax();
@@ -158,7 +201,18 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
158
  }
159
 
160
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
161
- $dataLayer["ecommerce"] = "-~-{'purchase': {'actionField': {'id': '" . $order->get_order_number() . "', 'affiliation': '" . get_bloginfo( 'name' ) . "', 'revenue': '" . $order->get_total() . "', 'tax': '" . $order->get_total_tax() . "', 'shipping': '" . $order->get_total_shipping() . "'}, ";
 
 
 
 
 
 
 
 
 
 
 
162
  }
163
 
164
  $_products = array();
@@ -190,11 +244,17 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
190
  $_category = implode( " / ", $out );
191
  }
192
 
 
 
 
 
 
 
193
  $_prodprice = $order->get_item_total( $item );
194
  $_products[] = array(
195
  "id" => $_product->id,
196
  "name" => $item['name'],
197
- "sku" => $_product->get_sku() ? __( 'SKU:', GTM4WP_TEXTDOMAIN ) . ' ' . $_product->get_sku() : $_product->id,
198
  "category" => $_category,
199
  "price" => $_prodprice,
200
  "currency" => get_woocommerce_currency(),
@@ -202,7 +262,7 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
202
  );
203
 
204
  $_sumprice += $_prodprice;
205
- $_product_ids[] = "'" . $_product->id . "'";
206
  }
207
  }
208
 
@@ -212,17 +272,17 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
212
  }
213
 
214
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
215
- $dataLayer["ecommerce"] .= "'products': ".str_replace('"', "'", json_encode($_products))." }}-~-";
216
  $dataLayer["event"] = "gtm4wp.orderCompleted";
217
  }
218
 
219
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
220
- $dataLayer["ecomm_prodid"] = '-~-[' . implode(", ", $_product_ids) . ']-~-';
221
  $dataLayer["ecomm_pagetype"] = "purchase";
222
- $dataLayer["ecomm_totalvalue"] = $_sumprice;
223
  }
224
 
225
- // update_post_meta( $order_id, '_ga_tracked', 1 );
226
  }
227
  } else if ( is_checkout() ) {
228
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
@@ -236,10 +296,23 @@ function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
236
  $product_cat = "";
237
  }
238
 
239
- $gtm4wp_checkout_products[] = "{'name': '" . str_replace("'", "\\'", $product->post->post_title) . "', 'id': '" . $product->id . "', 'price': '" . $product->get_price() . "', 'category': '" . $product_cat . "', 'quantity': '" . $cart_item_data["quantity"] . "'}";
 
 
 
 
 
 
240
  }
241
 
242
- $dataLayer["ecommerce"] = "-~-{'checkout': {'actionField': {'step': 1}, 'products':[" . implode(", ", $gtm4wp_checkout_products) . "]}}-~-";
 
 
 
 
 
 
 
243
  }
244
  } else {
245
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
11
  }
12
  }
13
 
14
+ function gtm4wp_woocommerce_html_entity_decode( $val ) {
15
+ return html_entity_decode( $val, ENT_QUOTES, "utf-8" );
16
+ }
17
+
18
  function gtm4wp_woocommerce_datalayer_filter_items( $dataLayer ) {
19
  global $woocommerce, $gtm4wp_options, $wp_query, $gtm4wp_datalayer_name, $gtm4wp_product_counter;
20
 
21
  if ( is_product_category() || is_product_tag() || is_front_page() || is_shop() ) {
22
  if ( ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) || ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) ) {
23
  if ( count( $woocommerce->query->filtered_product_ids ) > 0 ) {
24
+ // The following 5 lines was being borrowed from WC source
25
  $paged = max( 1, $wp_query->get( 'paged' ) );
26
  $per_page = $wp_query->get( 'posts_per_page' );
27
  $total = $wp_query->found_posts;
51
  $product_cat = "";
52
  }
53
  $sumprice += $product_price;
 
54
 
55
+ $remarketing_id = $oneproductid;
56
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETINGSKU ] ) {
57
+ $product_sku = $product->get_sku();
58
+ if ( "" != $product_sku ) {
59
+ $remarketing_id = $product_sku;
60
+ }
61
+ }
62
+ $product_ids[] = $remarketing_id;
63
+
64
+ $product_impressions[] = array(
65
+ 'name' => $product->get_title(),
66
+ 'id' => $oneproductid,
67
+ 'price' => $product_price,
68
+ 'category' => $product_cat,
69
+ 'position' => ($i+1)
70
+ );
71
 
72
  $i++;
73
  if ( $i>$last ) {
76
  }
77
 
78
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
79
+ $dataLayer["ecomm_prodid"] = $product_ids;
80
  $dataLayer["ecomm_pagetype"] = ( is_front_page() ? "home" : "category" );
81
+ $dataLayer["ecomm_totalvalue"] = (float)$sumprice;
82
  }
83
 
84
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
85
+ $dataLayer["ecommerce"] = array("impressions" => $product_impressions);
86
  }
87
  }
88
  }
91
  $prodid = get_the_ID();
92
  $product = get_product( $prodid );
93
  $product_price = $product->get_price();
94
+ $_product_cats = get_the_terms( $product->id, 'product_cat' );
95
  if ( ( is_array($_product_cats) ) && ( count( $_product_cats ) > 0 ) ) {
96
  $product_cat = array_pop( $_product_cats );
97
  $product_cat = $product_cat->name;
100
  }
101
 
102
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
103
+ $remarketing_id = (string)$prodid;
104
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETINGSKU ] ) {
105
+ $product_sku = $product->get_sku();
106
+ if ( "" != $product_sku ) {
107
+ $remarketing_id = $product_sku;
108
+ }
109
+ }
110
+
111
+ $dataLayer["ecomm_prodid"] = $remarketing_id;
112
  $dataLayer["ecomm_pagetype"] = "product";
113
+ $dataLayer["ecomm_totalvalue"] = (float)$product_price;
114
  }
115
 
116
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
117
+ $dataLayer["ecommerce"] = array(
118
+ "detail" => array(
119
+ "products" => array(array(
120
+ "name" => gtm4wp_woocommerce_html_entity_decode( get_the_title() ),
121
+ "id" => $prodid,
122
+ "price" => $product_price,
123
+ "category" => $product_cat,
124
+ ))
125
+ )
126
+ );
127
  }
128
  }
129
  } else if ( is_cart() ) {
156
  $products = $woocommerce->cart->get_cart();
157
  $product_ids = array();
158
  foreach( $products as $oneproduct ) {
159
+ $remarketing_id = $oneproduct['product_id'];
160
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETINGSKU ] ) {
161
+ $product_sku = $oneproduct['product_sku'];
162
+ if ( "" != $product_sku ) {
163
+ $remarketing_id = $product_sku;
164
+ }
165
+ }
166
+
167
+ $product_ids[] = $remarketing_id;
168
  }
169
 
170
+ $dataLayer["ecomm_prodid"] = $product_ids;
171
  $dataLayer["ecomm_pagetype"] = "cart";
172
+ $dataLayer["ecomm_totalvalue"] = (float)$woocommerce->cart->cart_contents_total;
173
  }
174
  } else if ( is_order_received_page() ) {
175
  $order_id = apply_filters( 'woocommerce_thankyou_order_id', empty( $_GET['order'] ) ? ($GLOBALS["wp"]->query_vars["order-received"] ? $GLOBALS["wp"]->query_vars["order-received"] : 0) : absint( $_GET['order'] ) );
190
  $dataLayer["transactionId"] = $order->get_order_number();
191
  $dataLayer["transactionDate"] = date("c");
192
  $dataLayer["transactionType"] = "sale";
193
+ $dataLayer["transactionAffiliation"] = gtm4wp_woocommerce_html_entity_decode( get_bloginfo( 'name' ) );
194
  $dataLayer["transactionTotal"] = $order->get_total();
195
  $dataLayer["transactionShipping"] = $order->get_total_shipping();
196
  $dataLayer["transactionTax"] = $order->get_total_tax();
201
  }
202
 
203
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
204
+ $dataLayer["ecommerce"] = array(
205
+ "purchase" => array(
206
+ "actionField" => array(
207
+ "id" => $order->get_order_number(),
208
+ "affiliation" => gtm4wp_woocommerce_html_entity_decode( get_bloginfo( 'name' ) ),
209
+ "revenue" => $order->get_total(),
210
+ "tax" => $order->get_total_tax(),
211
+ "shipping" => $order->get_total_shipping(),
212
+ "coupon" => implode( ", ", $order->get_used_coupons() )
213
+ )
214
+ )
215
+ );
216
  }
217
 
218
  $_products = array();
244
  $_category = implode( " / ", $out );
245
  }
246
 
247
+ $remarketing_id = $_product->id;
248
+ $product_sku = $_product->get_sku();
249
+ if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETINGSKU ] && ( "" != $product_sku ) ) {
250
+ $remarketing_id = $product_sku;
251
+ }
252
+
253
  $_prodprice = $order->get_item_total( $item );
254
  $_products[] = array(
255
  "id" => $_product->id,
256
  "name" => $item['name'],
257
+ "sku" => $product_sku ? __( 'SKU:', GTM4WP_TEXTDOMAIN ) . ' ' . $product_sku : $_product->id,
258
  "category" => $_category,
259
  "price" => $_prodprice,
260
  "currency" => get_woocommerce_currency(),
262
  );
263
 
264
  $_sumprice += $_prodprice;
265
+ $_product_ids[] = $remarketing_id;
266
  }
267
  }
268
 
272
  }
273
 
274
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
275
+ $dataLayer["ecommerce"]["purchase"]["products"] = $_products;
276
  $dataLayer["event"] = "gtm4wp.orderCompleted";
277
  }
278
 
279
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
280
+ $dataLayer["ecomm_prodid"] = $_product_ids;
281
  $dataLayer["ecomm_pagetype"] = "purchase";
282
+ $dataLayer["ecomm_totalvalue"] = (float)$_sumprice;
283
  }
284
 
285
+ update_post_meta( $order_id, '_ga_tracked', 1 );
286
  }
287
  } else if ( is_checkout() ) {
288
  if ( true === $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCTRACKENHANCEDEC ] ) {
296
  $product_cat = "";
297
  }
298
 
299
+ $gtm4wp_checkout_products[] = array(
300
+ "id" => $product->id,
301
+ "name" => $product->post->post_title,
302
+ "price" => $product->get_price(),
303
+ "category" => $product_cat,
304
+ "quantity" => $cart_item_data["quantity"]
305
+ );
306
  }
307
 
308
+ $dataLayer["ecommerce"] = array(
309
+ "checkout" => array(
310
+ "actionField" => array(
311
+ "step" => 1
312
+ ),
313
+ "products" => $gtm4wp_checkout_products
314
+ )
315
+ );
316
  }
317
  } else {
318
  if ( $gtm4wp_options[ GTM4WP_OPTION_INTEGRATE_WCREMARKETING ] ) {
integration/youtube.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function gtm4wp_youtube( $return, $url, $data ) {
3
+ if ( false !== strpos( $return, "youtube.com" ) ) {
4
+ return str_replace( "feature=oembed", "feature=oembed&enablejsapi=1&origin=" . site_url(), $return );
5
+ } else {
6
+ return $return;
7
+ }
8
+ }
9
+
10
+ add_filter( "oembed_result", "gtm4wp_youtube", 10, 3 );
11
+
12
+ if ( ! is_admin() ) {
13
+ wp_enqueue_script( "gtm4wp-youtube", $gtp4wp_plugin_url . "js/gtm4wp-youtube.js", array( "jquery" ), "1.0", false );
14
+ }
js/admin-subtabs.js CHANGED
@@ -27,7 +27,20 @@ var adminsubtabs = {
27
  numitems: 2
28
  }
29
  },
30
- 2: {},
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  3: {},
32
  4: {
33
  "blocktags": {
27
  numitems: 2
28
  }
29
  },
30
+ 2: {
31
+ "generalevents": {
32
+ tabtext: gtm4wp.generaleventstabtitle,
33
+ numitems: 2
34
+ },
35
+ "mediaevents": {
36
+ tabtext: gtm4wp.mediaeventstabtitle,
37
+ numitems: 3
38
+ },
39
+ "depecratedevents": {
40
+ tabtext: gtm4wp.depecratedeventstabtitle,
41
+ numitems: 8
42
+ }
43
+ },
44
  3: {},
45
  4: {
46
  "blocktags": {
js/admin-tabcreator.js CHANGED
@@ -64,11 +64,11 @@
64
  jQuery( '#wpbody form .tabinfo:eq(' + tabindex + '),#wpbody form .form-table:eq(' + tabindex + ')' )
65
  .show();
66
 
67
- jQuery( '#wpbody-content #adminsubtabs' + tabindex + ':not(.subtab-activated)' )
68
  .find( 'a:first' )
69
  .trigger( 'click' );
70
 
71
- jQuery( '#wpbody-content #adminsubtabs' + tabindex )
72
  .addClass( 'subtab-activated' )
73
  .show();
74
 
64
  jQuery( '#wpbody form .tabinfo:eq(' + tabindex + '),#wpbody form .form-table:eq(' + tabindex + ')' )
65
  .show();
66
 
67
+ jQuery( '#adminsubtabs' + tabindex + ':not(.subtab-activated)' )
68
  .find( 'a:first' )
69
  .trigger( 'click' );
70
 
71
+ jQuery( '#adminsubtabs' + tabindex )
72
  .addClass( 'subtab-activated' )
73
  .show();
74
 
js/froogaloop.js ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-soundcloud.js ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ;
2
+ var gtm4wp_soundclound_percentage_tracking = 10;
3
+ var gtm4wp_soundclound_percentage_tracking_marks = {};
4
+
5
+ jQuery(function() {
6
+ jQuery( '[id^="soundcloudplayer_"]' ).each(function() {
7
+ var iframe = this,
8
+ widget = SC.Widget( this ),
9
+ jqframe = jQuery( iframe ),
10
+ sound = {};
11
+
12
+ widget.bind( SC.Widget.Events.READY, function() {
13
+ widget.getCurrentSound(function( soundData ) {
14
+
15
+ jqframe.attr( "data-player_id", soundData.id );
16
+ jqframe.attr( "data-player_author", soundData.user.username );
17
+ jqframe.attr( "data-player_title", soundData.title );
18
+ jqframe.attr( "data-player_url", soundData.permalink_url );
19
+ jqframe.attr( "data-player_duration", soundData.duration );
20
+
21
+ sound = soundData;
22
+
23
+ window[ gtm4wp_datalayer_name ].push({
24
+ 'event': 'gtm4wp.mediaPlayerReady',
25
+ 'mediaType': 'soundcloud',
26
+ 'mediaData': {
27
+ 'id': soundData.id,
28
+ 'author': soundData.user.username,
29
+ 'title': soundData.title,
30
+ 'url': soundData.permalink_url,
31
+ 'duration': soundData.duration
32
+ },
33
+ 'mediaCurrentTime': 0
34
+ });
35
+ }); // end of api call getDuration
36
+
37
+ widget.bind( SC.Widget.Events.PLAY_PROGRESS, function( eventData ) {
38
+ gtm4wp_onSoundCloudPercentageChange( eventData );
39
+ });
40
+
41
+ widget.bind( SC.Widget.Events.PLAY, function( eventData ) {
42
+ gtm4wp_onSoundCloudPlayerStateChange( eventData, 'play' );
43
+ });
44
+
45
+ widget.bind( SC.Widget.Events.PAUSE, function( eventData ) {
46
+ gtm4wp_onSoundCloudPlayerStateChange( eventData, 'pause' );
47
+ });
48
+
49
+ widget.bind( SC.Widget.Events.FINISH, function( eventData ) {
50
+ gtm4wp_onSoundCloudPlayerStateChange( eventData, 'finish' );
51
+ });
52
+
53
+ widget.bind( SC.Widget.Events.SEEK, function( eventData ) {
54
+ gtm4wp_onSoundCloudPlayerStateChange( eventData, 'seek' );
55
+ });
56
+
57
+ widget.bind( SC.Widget.Events.CLICK_DOWNLOAD, function() {
58
+ gtm4wp_onSoundCloudPlayerEvent( 'click-download' );
59
+ });
60
+
61
+ widget.bind( SC.Widget.Events.CLICK_BUY, function() {
62
+ gtm4wp_onSoundCloudPlayerEvent( 'click-buy' );
63
+ });
64
+
65
+ widget.bind( SC.Widget.Events.OPEN_SHARE_PANEL, function() {
66
+ gtm4wp_onSoundCloudPlayerEvent( 'open-share-panel' );
67
+ });
68
+
69
+ widget.bind( SC.Widget.Events.ERROR, function() {
70
+ gtm4wp_onSoundCloudPlayerEvent( 'error' );
71
+ });
72
+ });
73
+
74
+ var gtm4wp_onSoundCloudPlayerStateChange = function( eventData, playerState ) {
75
+ window[ gtm4wp_datalayer_name ].push({
76
+ 'event': 'gtm4wp.mediaPlayerStateChange',
77
+ 'mediaType': 'soundcloud',
78
+ 'mediaData': {
79
+ 'id': sound.id,
80
+ 'author': sound.user.username,
81
+ 'title': sound.title,
82
+ 'url': sound.permalink_url,
83
+ 'duration': sound.duration
84
+ },
85
+ 'mediaCurrentTime': eventData.currentPosition,
86
+ 'mediaPlayerState': playerState
87
+ });
88
+ }
89
+
90
+ var gtm4wp_onSoundCloudPercentageChange = function( eventData ) {
91
+ var mediaPercentage = Math.floor( eventData.currentPosition / sound.duration * 100 );
92
+
93
+ if ( typeof gtm4wp_soundclound_percentage_tracking_marks[ sound.id ] == "undefined" ) {
94
+ gtm4wp_soundclound_percentage_tracking_marks[ sound.id ] = [];
95
+ }
96
+
97
+ for( var i=0; i<100; i+=gtm4wp_soundclound_percentage_tracking ) {
98
+ if ( ( mediaPercentage > i ) && ( gtm4wp_soundclound_percentage_tracking_marks[ sound.id ].indexOf( i ) == -1 ) ) {
99
+ gtm4wp_soundclound_percentage_tracking_marks[ sound.id ].push( i );
100
+
101
+ window[ gtm4wp_datalayer_name ].push({
102
+ 'event': 'gtm4wp.mediaPlaybackPercentage',
103
+ 'mediaType': 'soundcloud',
104
+ 'mediaData': {
105
+ 'id': sound.id,
106
+ 'author': sound.user.username,
107
+ 'title': sound.title,
108
+ 'url': sound.permalink_url,
109
+ 'duration': sound.duration
110
+ },
111
+ 'mediaCurrentTime': eventData.currentPosition,
112
+ 'mediaPercentage': i
113
+ });
114
+ }
115
+ }
116
+ }
117
+
118
+ var gtm4wp_onSoundCloudPlayerEvent = function( eventName ) {
119
+ widget.getPosition(function( currentPosition ) {
120
+ window[ gtm4wp_datalayer_name ].push({
121
+ 'event': 'gtm4wp.mediaPlayerEvent',
122
+ 'mediaType': 'soundcloud',
123
+ 'mediaData': {
124
+ 'id': sound.id,
125
+ 'author': sound.user.username,
126
+ 'title': sound.title,
127
+ 'url': sound.permalink_url,
128
+ 'duration': soundData.duration
129
+ },
130
+ 'mediaCurrentTime': currentPosition,
131
+ 'mediaPlayerEvent': eventName
132
+ });
133
+ });
134
+ }
135
+
136
+ });
137
+ });
js/gtm4wp-vimeo.js ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var gtm4wp_vimeo_percentage_tracking = 10;
2
+ var gtm4wp_vimeo_percentage_tracking_marks = {};
3
+
4
+ ;jQuery(function() {
5
+ jQuery( '[id^="vimeoplayer_"]' ).each(function() {
6
+ var vimeoapi = $f( this ),
7
+ jqframe = jQuery( this ),
8
+ videourl = jqframe
9
+ .attr( "src" )
10
+ .split( "?" )
11
+ .shift(),
12
+ videoid = videourl.split( "/" ).pop();
13
+
14
+ jqframe.attr( "data-player_id", videoid );
15
+ jqframe.attr( "data-player_url", videourl );
16
+
17
+ vimeoapi.addEvent( 'ready', function( player_id ) {
18
+ vimeoapi.api( 'getDuration', function( value, player_id ) {
19
+
20
+ jqframe.attr( "data-player_duration", value );
21
+
22
+ window[ gtm4wp_datalayer_name ].push({
23
+ 'event': 'gtm4wp.mediaPlayerReady',
24
+ 'mediaType': 'vimeo',
25
+ 'mediaData': {
26
+ 'id': videoid,
27
+ 'author': '',
28
+ 'title': jqframe.attr( "title" ),
29
+ 'url': videourl,
30
+ 'duration': value
31
+ },
32
+ 'mediaCurrentTime': 0
33
+ });
34
+ }); // end of api call getDuration
35
+
36
+ vimeoapi.addEvent( 'playProgress', function( value, player_id ) {
37
+ gtm4wp_onVimeoPercentageChange( value );
38
+ });
39
+
40
+ vimeoapi.addEvent( 'play', function( player_id ) {
41
+ gtm4wp_onVimeoPlayerStateChange( 'play' );
42
+ });
43
+
44
+ vimeoapi.addEvent( 'pause', function( player_id ) {
45
+ gtm4wp_onVimeoPlayerStateChange( 'pause' );
46
+ });
47
+
48
+ vimeoapi.addEvent( 'finish', function( player_id ) {
49
+ gtm4wp_onVimeoPlayerStateChange( 'finish' );
50
+ });
51
+
52
+ vimeoapi.addEvent( 'seek', function( value, player_id ) {
53
+ gtm4wp_onVimeoPlayerStateChange( 'seek' );
54
+ });
55
+
56
+ var gtm4wp_onVimeoPlayerStateChange = function( player_state ) {
57
+ vimeoapi.api( 'getCurrentTime', function( value, player_id ) {
58
+ window[ gtm4wp_datalayer_name ].push({
59
+ 'event': 'gtm4wp.mediaPlayerStateChange',
60
+ 'mediaType': 'vimeo',
61
+ 'mediaData': {
62
+ 'id': videoid,
63
+ 'author': '',
64
+ 'title': jqframe.attr( "title" ),
65
+ 'url': jqframe.attr( "data-player_url" ),
66
+ 'duration': parseInt( jqframe.attr( "data-player_duration" ) )
67
+ },
68
+ 'mediaPlayerState': player_state,
69
+ 'mediaCurrentTime': value
70
+ });
71
+ });
72
+ }
73
+
74
+ var gtm4wp_onVimeoPercentageChange = function( data ) {
75
+ var videoDuration = parseInt( jqframe.attr( "data-player_duration" ) );
76
+ var videoPercentage = Math.floor( data.seconds / videoDuration * 100 );
77
+
78
+ if ( typeof gtm4wp_vimeo_percentage_tracking_marks[ videoid ] == "undefined" ) {
79
+ gtm4wp_vimeo_percentage_tracking_marks[ videoid ] = [];
80
+ }
81
+
82
+ for( var i=0; i<100; i+=gtm4wp_vimeo_percentage_tracking ) {
83
+ if ( ( videoPercentage > i ) && ( gtm4wp_vimeo_percentage_tracking_marks[ videoid ].indexOf( i ) == -1 ) ) {
84
+ gtm4wp_vimeo_percentage_tracking_marks[ videoid ].push( i );
85
+
86
+ window[ gtm4wp_datalayer_name ].push({
87
+ 'event': 'gtm4wp.mediaPlaybackPercentage',
88
+ 'mediaType': 'vimeo',
89
+ 'mediaData': {
90
+ 'id': videoid,
91
+ 'author': '',
92
+ 'title': jqframe.attr( "title" ),
93
+ 'url': jqframe.attr( "data-player_url" ),
94
+ 'duration': videoDuration
95
+ },
96
+ 'mediaCurrentTime': data.seconds,
97
+ 'mediaPercentage': i
98
+ });
99
+ }
100
+ }
101
+ }
102
+
103
+ });
104
+ });
105
+ });
js/gtm4wp-youtube.js ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var gtm4wp_youtube_percentage_tracking = 10;
2
+ var gtm4wp_youtube_percentage_tracking_timeouts = {};
3
+ var gtm4wp_youtube_percentage_tracking_marks = {};
4
+
5
+ if ( typeof onYouTubeIframeAPIReady === "undefined" ) {
6
+ window.onYouTubeIframeAPIReady = function() {
7
+ window[ gtm4wp_datalayer_name ].push({
8
+ 'event': 'gtm4wp.mediaApiReady',
9
+ 'mediaType': 'youtube'
10
+ });
11
+
12
+ jQuery( "iframe[src^='https://www.youtube.com/embed']" ).each( function() {
13
+ var playerID = jQuery( this ).attr( "id" );
14
+ if ( ( playerID == undefined ) || ( playerID == "" ) ) {
15
+ var _gtm4wp_temp = jQuery( this ).attr( "src" ).split( "?" );
16
+ var _gtm4wp_temp2 = _gtm4wp_temp[ 0 ].split( "/" );
17
+
18
+ var playerID = "youtubeplayer_" + _gtm4wp_temp2[ _gtm4wp_temp2.length-1 ];
19
+ jQuery( this ).attr( "id", playerID );
20
+ }
21
+
22
+ player = new YT.Player( playerID, {
23
+ events: {
24
+ 'onReady': gtm4wp_onYouTubePlayerReady,
25
+ 'onStateChange': gtm4wp_onYouTubePlayerStateChange,
26
+ 'onPlaybackQualityChange': gtm4wp_onYouTubePlaybackQualityChange,
27
+ 'onPlaybackRateChange': gtm4wp_onYouTubePlaybackRateChange,
28
+ 'onError': gtm4wp_onYouTubeError,
29
+ 'onApiChange': gtm4wp_onYouTubeApiChange
30
+ }
31
+ });
32
+ });
33
+ }
34
+
35
+ var tag = document.createElement( 'script' );
36
+ tag.src = "//www.youtube.com/iframe_api";
37
+ var firstScriptTag = document.getElementsByTagName( 'script' )[0];
38
+ firstScriptTag.parentNode.insertBefore( tag, firstScriptTag );
39
+ } else {
40
+ var gtm4wp_err = new Error( "Another code is already utilizing YouTube API, GTM4WP plugin can not load YouTube tracking!" );
41
+ throw gtm4wp_err;
42
+ }
43
+
44
+ function gtm4wp_onYouTubePlayerReady( event ) {
45
+ var videodata = event.target.getVideoData();
46
+
47
+ window[ gtm4wp_datalayer_name ].push({
48
+ 'event': 'gtm4wp.mediaPlayerReady',
49
+ 'mediaType': 'youtube',
50
+ 'mediaData': {
51
+ 'id': videodata.video_id,
52
+ 'author': videodata.author,
53
+ 'title': videodata.title,
54
+ 'url': event.target.getVideoUrl(),
55
+ 'duration': event.target.getDuration()
56
+ },
57
+ 'mediaCurrentTime': event.target.getCurrentTime()
58
+ });
59
+ }
60
+
61
+ function gtm4wp_onYouTubePlayerStateChange( event ) {
62
+ var playerState = "unknown";
63
+ switch( event.data ) {
64
+ case -1: playerState = "unstarted"; break;
65
+ case YT.PlayerState.ENDED: playerState = "ended"; break;
66
+ case YT.PlayerState.PLAYING: playerState = "playing"; break;
67
+ case YT.PlayerState.PAUSED: playerState = "paused"; break;
68
+ case YT.PlayerState.BUFFERING: playerState = "buffering"; break;
69
+ case YT.PlayerState.CUED: playerState = "cued"; break;
70
+ }
71
+
72
+ var videoId = event.target.getVideoData().video_id;
73
+
74
+ if ( ( YT.PlayerState.PLAYING == event.data ) && ( gtm4wp_youtube_percentage_tracking > 0 ) ) {
75
+ gtm4wp_youtube_percentage_tracking_timeouts[ videoId ] = setInterval(function() {
76
+ gtm4wp_onYouTubePercentageChange( event );
77
+ }, 1000);
78
+ } else {
79
+ if ( gtm4wp_youtube_percentage_tracking_timeouts[ videoId ] ) {
80
+ clearInterval( gtm4wp_youtube_percentage_tracking_timeouts[ videoId ] );
81
+ }
82
+ }
83
+
84
+ var videodata = event.target.getVideoData();
85
+
86
+ window[ gtm4wp_datalayer_name ].push({
87
+ 'event': 'gtm4wp.mediaPlayerStateChange',
88
+ 'mediaType': 'youtube',
89
+ 'mediaData': {
90
+ 'id': videodata.video_id,
91
+ 'author': videodata.author,
92
+ 'title': videodata.title,
93
+ 'url': event.target.getVideoUrl(),
94
+ 'duration': event.target.getDuration()
95
+ },
96
+ 'mediaPlayerState': playerState,
97
+ 'mediaCurrentTime': event.target.getCurrentTime()
98
+ });
99
+ }
100
+
101
+ function gtm4wp_onYouTubePlaybackQualityChange( event ) {
102
+ var videodata = event.target.getVideoData();
103
+
104
+ window[ gtm4wp_datalayer_name ].push({
105
+ 'event': 'gtm4wp.mediaPlayerEvent',
106
+ 'mediaType': 'youtube',
107
+ 'mediaData': {
108
+ 'id': videodata.video_id,
109
+ 'author': videodata.author,
110
+ 'title': videodata.title,
111
+ 'url': event.target.getVideoUrl(),
112
+ 'duration': event.target.getDuration()
113
+ },
114
+ 'mediaCurrentTime': event.target.getCurrentTime(),
115
+ 'mediaPlayerEvent': 'quality-change',
116
+ 'mediaPlayerEventParam': event.data
117
+ });
118
+ }
119
+
120
+ function gtm4wp_onYouTubePlaybackRateChange( event ) {
121
+ var videodata = event.target.getVideoData();
122
+
123
+ window[ gtm4wp_datalayer_name ].push({
124
+ 'event': 'gtm4wp.mediaPlayerEvent',
125
+ 'mediaType': 'youtube',
126
+ 'mediaData': {
127
+ 'id': videodata.video_id,
128
+ 'author': videodata.author,
129
+ 'title': videodata.title,
130
+ 'url': event.target.getVideoUrl(),
131
+ 'duration': event.target.getDuration()
132
+ },
133
+ 'mediaCurrentTime': event.target.getCurrentTime(),
134
+ 'mediaPlayerEvent': 'playback-rate-change',
135
+ 'mediaPlayerEventParam': event.data
136
+ });
137
+ }
138
+
139
+ function gtm4wp_onYouTubeError( event ) {
140
+ var videodata = event.target.getVideoData();
141
+
142
+ window[ gtm4wp_datalayer_name ].push({
143
+ 'event': 'gtm4wp.mediaPlayerEvent',
144
+ 'mediaType': 'youtube',
145
+ 'mediaData': {
146
+ 'id': videodata.video_id,
147
+ 'author': videodata.author,
148
+ 'title': videodata.title,
149
+ 'url': event.target.getVideoUrl(),
150
+ 'duration': event.target.getDuration()
151
+ },
152
+ 'mediaCurrentTime': event.target.getCurrentTime(),
153
+ 'mediaPlayerEvent': 'error',
154
+ 'mediaPlayerEventParam': event.data
155
+ });
156
+ }
157
+
158
+ function gtm4wp_onYouTubeApiChange( event ) {
159
+ var videodata = event.target.getVideoData();
160
+
161
+ window[ gtm4wp_datalayer_name ].push({
162
+ 'event': 'gtm4wp.mediaPlayerEvent',
163
+ 'mediaType': 'youtube',
164
+ 'mediaData': {
165
+ 'id': videodata.video_id,
166
+ 'author': videodata.author,
167
+ 'title': videodata.title,
168
+ 'url': event.target.getVideoUrl(),
169
+ 'duration': event.target.getDuration()
170
+ },
171
+ 'mediaCurrentTime': event.target.getCurrentTime(),
172
+ 'mediaPlayerEvent': 'api-change',
173
+ 'mediaPlayerEventParam': event.data
174
+ });
175
+ }
176
+
177
+ function gtm4wp_onYouTubePercentageChange( event ) {
178
+ var videoId = event.target.getVideoData().video_id;
179
+ var videoCurrentTime = event.target.getCurrentTime();
180
+ var videoDuration = event.target.getDuration();
181
+ var videoPercentage = Math.floor( videoCurrentTime / videoDuration * 100 )
182
+
183
+ if ( typeof gtm4wp_youtube_percentage_tracking_marks[ videoId ] == "undefined" ) {
184
+ gtm4wp_youtube_percentage_tracking_marks[ videoId ] = [];
185
+ }
186
+
187
+ var videodata = event.target.getVideoData();
188
+
189
+ for( var i=0; i<100; i+=gtm4wp_youtube_percentage_tracking ) {
190
+ if ( ( videoPercentage > i ) && ( gtm4wp_youtube_percentage_tracking_marks[ videoId ].indexOf( i ) == -1 ) ) {
191
+ gtm4wp_youtube_percentage_tracking_marks[ videoId ].push( i );
192
+
193
+ window[ gtm4wp_datalayer_name ].push({
194
+ 'event': 'gtm4wp.mediaPlaybackPercentage',
195
+ 'mediaType': 'youtube',
196
+ 'mediaData': {
197
+ 'id': videodata.video_id,
198
+ 'author': videodata.author,
199
+ 'title': videodata.title,
200
+ 'url': event.target.getVideoUrl(),
201
+ 'duration': event.target.getDuration()
202
+ },
203
+ 'mediaCurrentTime': event.target.getCurrentTime(),
204
+ 'mediaPercentage': i
205
+ });
206
+ }
207
+ }
208
+ }
public/frontend.php CHANGED
@@ -3,6 +3,8 @@ define( 'GTM4WP_WPFILTER_COMPILE_DATALAYER', 'gtm4wp_compile_datalayer' );
3
  define( 'GTM4WP_WPFILTER_COMPILE_REMARKTING', 'gtm4wp_compile_remarkering' );
4
  define( 'GTM4WP_WPFILTER_GETTHEGTMTAG', 'gtm4wp_get_the_gtm_tag' );
5
 
 
 
6
  if ( $GLOBALS[ "gtm4wp_options" ][ GTM4WP_OPTION_DATALAYER_NAME ] == "" ) {
7
  $GLOBALS[ "gtm4wp_datalayer_name" ] = "dataLayer";
8
  } else {
@@ -382,11 +384,11 @@ function gtm4wp_wp_loaded() {
382
  }
383
 
384
  function gtm4wp_get_the_gtm_tag() {
385
- global $gtm4wp_options, $gtm4wp_datalayer_name;
386
 
387
  $_gtm_tag = '';
388
 
389
- if ( $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] != "" ) {
390
  $_gtm_tag .= '
391
  <noscript><iframe src="//www.googletagmanager.com/ns.html?id=' . $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] . '"
392
  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
@@ -396,9 +398,12 @@ j=d.createElement(s),dl=l!=\'dataLayer\'?\'&l=\'+l:\'\';j.async=true;j.src=
396
  \'//www.googletagmanager.com/gtm.js?id=\'+i+dl;f.parentNode.insertBefore(j,f);
397
  })(window,document,\'script\',\'' . $gtm4wp_datalayer_name . '\',\'' . $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] . '\');</script>
398
  <!-- End Google Tag Manager -->';
 
 
 
399
  }
400
 
401
- return apply_filters( GTM4WP_WPFILTER_GETTHEGTMTAG, $_gtm_tag );
402
  }
403
 
404
  function gtm4wp_the_gtm_tag() {
@@ -436,6 +441,18 @@ function gtm4wp_enqueue_scripts() {
436
  require_once( dirname( __FILE__ ) . "/../integration/woocommerce.php" );
437
  }
438
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  if ( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_ENABLED ] ) {
440
  wp_enqueue_script( "gtm4wp-scroll-tracking", $gtp4wp_plugin_url . "js/analytics-talk-content-tracking.js", array( "jquery" ), "1.0", false );
441
  }
@@ -452,7 +469,7 @@ function gtm4wp_wp_footer() {
452
  function gtm4wp_wp_body_open() {
453
  global $gtm4wp_options;
454
 
455
- if ( GTM4WP_PLACEMENT_BODYOPEN == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
456
  gtm4wp_the_gtm_tag();
457
  }
458
  }
@@ -513,7 +530,7 @@ function gtm4wp_wp_header_end() {
513
  gtm4wp_track_downloads( "' . str_replace( '"', '', $gtm4wp_options[ GTM4WP_OPTION_EVENTS_DWLEXT ] ) . '" );
514
  });';
515
  }
516
-
517
  $_gtm_tag .= '
518
  ' . $gtm4wp_datalayer_name . '.push(' . str_replace(
519
  array( '"-~-', '-~-"' ),
@@ -528,12 +545,26 @@ function gtm4wp_wp_header_end() {
528
  echo $_gtm_tag;
529
  }
530
 
 
 
 
 
 
 
 
 
 
 
531
  add_action( "wp_enqueue_scripts", "gtm4wp_enqueue_scripts" );
532
  add_action( "wp_head", "gtm4wp_wp_header_begin", 1 );
533
  add_action( "wp_head", "gtm4wp_wp_header_end", 100 );
534
  add_action( "wp_footer", "gtm4wp_wp_footer" );
535
  add_action( "wp_loaded", "gtm4wp_wp_loaded" );
 
536
  add_filter( GTM4WP_WPFILTER_COMPILE_DATALAYER, "gtm4wp_add_basic_datalayer_data" );
537
 
538
  // to be able to easily migrate from other Google Tag Manager plugins
539
  add_action( "body_open", "gtm4wp_wp_body_open" );
 
 
 
3
  define( 'GTM4WP_WPFILTER_COMPILE_REMARKTING', 'gtm4wp_compile_remarkering' );
4
  define( 'GTM4WP_WPFILTER_GETTHEGTMTAG', 'gtm4wp_get_the_gtm_tag' );
5
 
6
+ $GLOBALS[ "gtm4wp_container_code_written" ] = false;
7
+
8
  if ( $GLOBALS[ "gtm4wp_options" ][ GTM4WP_OPTION_DATALAYER_NAME ] == "" ) {
9
  $GLOBALS[ "gtm4wp_datalayer_name" ] = "dataLayer";
10
  } else {
384
  }
385
 
386
  function gtm4wp_get_the_gtm_tag() {
387
+ global $gtm4wp_options, $gtm4wp_datalayer_name, $gtm4wp_container_code_written;
388
 
389
  $_gtm_tag = '';
390
 
391
+ if ( ( $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] != "" ) && ( ! $gtm4wp_container_code_written ) ) {
392
  $_gtm_tag .= '
393
  <noscript><iframe src="//www.googletagmanager.com/ns.html?id=' . $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] . '"
394
  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
398
  \'//www.googletagmanager.com/gtm.js?id=\'+i+dl;f.parentNode.insertBefore(j,f);
399
  })(window,document,\'script\',\'' . $gtm4wp_datalayer_name . '\',\'' . $gtm4wp_options[ GTM4WP_OPTION_GTM_CODE ] . '\');</script>
400
  <!-- End Google Tag Manager -->';
401
+
402
+ $_gtm_tag = apply_filters( GTM4WP_WPFILTER_GETTHEGTMTAG, $_gtm_tag );
403
+ $gtm4wp_container_code_written = true;
404
  }
405
 
406
+ return $_gtm_tag;
407
  }
408
 
409
  function gtm4wp_the_gtm_tag() {
441
  require_once( dirname( __FILE__ ) . "/../integration/woocommerce.php" );
442
  }
443
 
444
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_YOUTUBE ] ) {
445
+ require_once( dirname( __FILE__ ) . "/../integration/youtube.php" );
446
+ }
447
+
448
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_VIMEO ] ) {
449
+ require_once( dirname( __FILE__ ) . "/../integration/vimeo.php" );
450
+ }
451
+
452
+ if ( $gtm4wp_options[ GTM4WP_OPTION_EVENTS_SOUNDCLOUD ] ) {
453
+ require_once( dirname( __FILE__ ) . "/../integration/soundcloud.php" );
454
+ }
455
+
456
  if ( $gtm4wp_options[ GTM4WP_OPTION_SCROLLER_ENABLED ] ) {
457
  wp_enqueue_script( "gtm4wp-scroll-tracking", $gtp4wp_plugin_url . "js/analytics-talk-content-tracking.js", array( "jquery" ), "1.0", false );
458
  }
469
  function gtm4wp_wp_body_open() {
470
  global $gtm4wp_options;
471
 
472
+ if ( ( GTM4WP_PLACEMENT_BODYOPEN == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) || ( GTM4WP_PLACEMENT_BODYOPEN_AUTO == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) ) {
473
  gtm4wp_the_gtm_tag();
474
  }
475
  }
530
  gtm4wp_track_downloads( "' . str_replace( '"', '', $gtm4wp_options[ GTM4WP_OPTION_EVENTS_DWLEXT ] ) . '" );
531
  });';
532
  }
533
+ //var_dump($gtm4wp_datalayer_data);
534
  $_gtm_tag .= '
535
  ' . $gtm4wp_datalayer_name . '.push(' . str_replace(
536
  array( '"-~-', '-~-"' ),
545
  echo $_gtm_tag;
546
  }
547
 
548
+ function gtm4wp_body_class( $classes ) {
549
+ // solution is based on the code of Yaniv Friedensohn
550
+ // http://www.affectivia.com/blog/placing-the-google-tag-manager-in-wordpress-after-the-body-tag/
551
+ if ( GTM4WP_PLACEMENT_BODYOPEN_AUTO == $gtm4wp_options[ GTM4WP_OPTION_GTM_PLACEMENT ] ) {
552
+ $classes[] = '">' . gtm4wp_get_the_gtm_tag() . '<br style="display:none;';
553
+ }
554
+
555
+ return $classes;
556
+ }
557
+
558
  add_action( "wp_enqueue_scripts", "gtm4wp_enqueue_scripts" );
559
  add_action( "wp_head", "gtm4wp_wp_header_begin", 1 );
560
  add_action( "wp_head", "gtm4wp_wp_header_end", 100 );
561
  add_action( "wp_footer", "gtm4wp_wp_footer" );
562
  add_action( "wp_loaded", "gtm4wp_wp_loaded" );
563
+ add_filter( "body_class", "gtm4wp_body_class", 10000 );
564
  add_filter( GTM4WP_WPFILTER_COMPILE_DATALAYER, "gtm4wp_add_basic_datalayer_data" );
565
 
566
  // to be able to easily migrate from other Google Tag Manager plugins
567
  add_action( "body_open", "gtm4wp_wp_body_open" );
568
+
569
+ // compatibility with existing themes that natively support code injection after opening body tag
570
+ add_action( "genesis_before", "gtm4wp_wp_body_open" );
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://duracelltomi.com/
4
  Tags: google tag manager, tag manager, gtm, google, adwords, google adwords, adwords remarketing, remarketing, google analytics, analytics
5
  Requires at least: 3.0.1
6
  Tested up to: 4.2.1
7
- Stable tag: 1.0
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl.html
10
 
@@ -40,6 +40,15 @@ This is useful to see what people are searching for that is not available on you
40
  Use post count to generate Analytics events when an empty result is being shown.
41
  This can be useful to catch empty (product) categories.
42
 
 
 
 
 
 
 
 
 
 
43
  = Browser / OS / Device data =
44
 
45
  (beta)
@@ -78,9 +87,25 @@ This plugin can fire several Tag Manager event so that you can include special t
78
 
79
  * the visitor moves between elements of a form (comment, contact, etc.)
80
  * the visitor clicks on a Facebook like/share (limited feature) or Twitter button
81
- * the visitor clicks on an outbound link (depecrated)
82
- * the visitor clicks on a download link (depecrated)
83
- * the visitor clicks on an email link (depecrated)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  = Scroll tracking =
86
 
@@ -142,10 +167,6 @@ Note: list of planned features can change as development goes on!
142
  * 1.2
143
  * MailChimp for WordPress support (request by I-Visio)
144
  * Custom dataLayer elements: place your own items site-wide or per page/post
145
- * 1.1
146
- * YouTube video tracking using GTM events
147
- * Vimeo video tracking using GTM events
148
- * New code insertion option that does not require theme tweaking (request by Phil Pearce)
149
 
150
  == Installation ==
151
 
@@ -235,6 +256,9 @@ In case you found the opening `<body>` tag, open a new line just after it and in
235
  Be careful not to include this line inside any `<div>`, `<p>`, `<header>`, `<article>` and so on.
236
  It can break you theme.
237
 
 
 
 
238
  = Why can not this plugin insert the container snippet after the opening body tag automatically? =
239
 
240
  Currently WordPress has two 'commands' or 'hooks' that a programmer can use: one for the `<head>` section and
@@ -262,6 +286,14 @@ If you or your social plugin inserts the Facebook buttons using IFRAMEs (like So
262
 
263
  == Changelog ==
264
 
 
 
 
 
 
 
 
 
265
  = 1.0 =
266
 
267
  The plugin itself is now declared as stable. This means that it should work with most WordPress instances.
@@ -269,17 +301,17 @@ From now on each version will include features labeled as:
269
 
270
  * Beta: the feature has been proven to work for several users but it can still have some bugs
271
  * Experimental: new feature that needs proper testing with more users
272
- * Depecrated: this feature will be removed in a future version
273
 
274
  If you see any issue with beta or experimental functions just disable the checkbox. Using this error messages should disappear.
275
  Please report all bugs found in my plugin using the [contact form on my website](https://duracelltomi.com/contact).
276
 
277
  * Fixed: wrong GTM container code when renaming default dataLayer variable name (thx Vassilis Papavassiliou)
278
  * Fixed: Enhanced Ecommerce product click data was "undefined" in some cases (thx Sergio Alen)
279
- * Fixed: wrong user role detection while addint visitorType to the dataLayer (thx Philippe Vachon-Rivard)
280
  * Changed: only add visitorId to the dataLayer if there is a logged in user
281
- * Added: feature labels so that you can see beta, experimental and depecrated features
282
- * Depecrated: outbound click, email click and download click events. You should use GTM trigger events instead
283
 
284
  = 0.9.1 =
285
 
4
  Tags: google tag manager, tag manager, gtm, google, adwords, google adwords, adwords remarketing, remarketing, google analytics, analytics
5
  Requires at least: 3.0.1
6
  Tested up to: 4.2.1
7
+ Stable tag: 1.1
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl.html
10
 
40
  Use post count to generate Analytics events when an empty result is being shown.
41
  This can be useful to catch empty (product) categories.
42
 
43
+ = Codeless container code injection =
44
+
45
+ Yaniv Friedensohn showed me a solution that can add the GTM container code after the opening body tag
46
+ for almost every theme without modifying the theme files:
47
+
48
+ http://www.affectivia.com/blog/placing-the-google-tag-manager-in-wordpress-after-the-body-tag/
49
+
50
+ I added this solution to the plugin, currently as an experimental option.
51
+
52
  = Browser / OS / Device data =
53
 
54
  (beta)
87
 
88
  * the visitor moves between elements of a form (comment, contact, etc.)
89
  * the visitor clicks on a Facebook like/share (limited feature) or Twitter button
90
+ * the visitor clicks on an outbound link (deprecated)
91
+ * the visitor clicks on a download link (deprecated)
92
+ * the visitor clicks on an email link (deprecated)
93
+
94
+ = Media player events =
95
+
96
+ (experimental)
97
+
98
+ The plugin can track user interaction with your embeded media:
99
+
100
+ * YouTube
101
+ * Vimeo
102
+ * Soundcloud
103
+
104
+ It fires dataLayer events when a media player was being loaded on the page, when the media is being played, paused or stopped.
105
+ It can fire dataLayer events when the user reaches 10, 20, 30, ..., 90, 100% of the media duration.
106
+
107
+ Note: the plugin can only track media that was being embeded using the internal oEmbed feature of WordPress.
108
+ No 3rd party embedding plugin is currently supported.
109
 
110
  = Scroll tracking =
111
 
167
  * 1.2
168
  * MailChimp for WordPress support (request by I-Visio)
169
  * Custom dataLayer elements: place your own items site-wide or per page/post
 
 
 
 
170
 
171
  == Installation ==
172
 
256
  Be careful not to include this line inside any `<div>`, `<p>`, `<header>`, `<article>` and so on.
257
  It can break you theme.
258
 
259
+ There is also a solution named "Codeless" which tries to add the container code to the right place but
260
+ without additional theme tweaking. This is still experimental, use it wisely.
261
+
262
  = Why can not this plugin insert the container snippet after the opening body tag automatically? =
263
 
264
  Currently WordPress has two 'commands' or 'hooks' that a programmer can use: one for the `<head>` section and
286
 
287
  == Changelog ==
288
 
289
+ = 1.1 =
290
+
291
+ * Added: track embedded YouTube/Vimeo/Soundcloud videos (experimental)
292
+ * Added: new checkbox - use product SKU for AdWords Dynamic Remarketing variables instead of product ID (experimental)
293
+ * Added: place your container code after the opening body tag without modifying your theme files (thx Yaniv Friedensohn)
294
+ * Added: automatic codeless container code injection for Genesis framework users
295
+ * Fixed: Possible PHP error with custom payment gateway (QuickPay) on the checkout page (thx Damiel for findig this)
296
+
297
  = 1.0 =
298
 
299
  The plugin itself is now declared as stable. This means that it should work with most WordPress instances.
301
 
302
  * Beta: the feature has been proven to work for several users but it can still have some bugs
303
  * Experimental: new feature that needs proper testing with more users
304
+ * Deprecated: this feature will be removed in a future version
305
 
306
  If you see any issue with beta or experimental functions just disable the checkbox. Using this error messages should disappear.
307
  Please report all bugs found in my plugin using the [contact form on my website](https://duracelltomi.com/contact).
308
 
309
  * Fixed: wrong GTM container code when renaming default dataLayer variable name (thx Vassilis Papavassiliou)
310
  * Fixed: Enhanced Ecommerce product click data was "undefined" in some cases (thx Sergio Alen)
311
+ * Fixed: wrong user role detection while adding visitorType to the dataLayer (thx Philippe Vachon-Rivard)
312
  * Changed: only add visitorId to the dataLayer if there is a logged in user
313
+ * Added: feature labels so that you can see beta, experimental and deprecated features
314
+ * Deprecated: outbound click, email click and download click events. You should use GTM trigger events instead
315
 
316
  = 0.9.1 =
317