diff --git a/dfp.admin.inc b/dfp.admin.inc
index ce0f01f..27cf0c1 100644
--- a/dfp.admin.inc
+++ b/dfp.admin.inc
@@ -239,6 +239,23 @@ function dfp_admin_settings($form, $form_state) {
     '#rows' => 5,
   );
 
+  // Google Tag Manager.
+  $form['gtm'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Google Tag Manager'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#weight' => 30,
+    '#group' => 'settings',
+  );
+  $form['gtm']['dfp_gtm_container_id'] = array(
+    '#type' => 'textfield',
+    '#title' => t('GTM Container ID'),
+    '#default_value' => variable_get('dfp_gtm_container_id', ''),
+    '#required' => FALSE,
+    '#description' => t('Your Google tag manager container id, without the GTM- prefix'),
+  );
+
   return system_settings_form($form);
 }
 
diff --git a/dfp.module b/dfp.module
index 55acbfa..21a0736 100755
--- a/dfp.module
+++ b/dfp.module
@@ -363,6 +363,31 @@ function dfp_tokens($type, $tokens, array $data = array(), array $options = arra
  * Implements preprocess_html().
  */
 function dfp_preprocess_html($variables) {
+
+  // Allows using google tag manager.
+  $gtm_container_id = variable_get('dfp_gtm_container_id', '');
+  if (!empty($gtm_container_id)) {
+    // Google Tag Manager.
+    $google_tag = array(
+      '#tag' => 'script',
+      '#attributes' => array('type' => 'text/javascript'),
+      '#weight' => -100,
+      '#value' => "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push(
+{'gtm.start': new Date().getTime(),event:'gtm.js'}
+);var f=d.getElementsByTagName(s)[0],
+j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
+'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
+})(window,document,'script','dataLayer','GTM-" . $gtm_container_id . "');",
+    );
+    drupal_add_html_head($google_tag, 'dfp_google_tag_manager');
+
+    // Google Tag Manager (noscript). This should probably be in its own
+    // template.
+    $src = "https://www.googletagmanager.com/ns.html?id=GTM-" . $gtm_container_id;
+    $variables['google_tag_iframe'] = '<noscript><iframe src="' . $src . '"
+height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>';
+  }
+
   // Add the header js here so that enough information has been loaded for
   // tokens to work properly.
   _dfp_js_global_settings();
@@ -551,7 +576,7 @@ function dfp_form_ctools_export_ui_edit_item_form_alter(&$form, &$form_state) {
  */
 function dfp_format_targeting($targeting, $tag = '') {
   foreach ($targeting as $key => &$target) {
-    $target['target'] = '"' . check_plain($target['target']) . '"';
+    $target['target'] = check_plain($target['target']);
     $target['value'] = dfp_token_replace(check_plain($target['value']), $tag, array('sanitize' => TRUE, 'clear' => TRUE));
 
     // The target value could be blank if tokens are used. If so, removed it.
@@ -568,10 +593,10 @@ function dfp_format_targeting($targeting, $tag = '') {
     $values = array_map('trim', $values);
 
     if (count($values) == 1) {
-      $target['value'] = '"' . $values[0] . '"';
+      $target['value'] = $values[0];
     }
     elseif (count($values) > 1) {
-      $target['value'] = '["' . implode('","', $values) . '"]';
+      $target['value'] = $values;
     }
   }
 
@@ -594,10 +619,13 @@ function dfp_format_size($size) {
   $sizes = explode(',', check_plain($size));
   foreach ($sizes as $size) {
     $formatted_size = explode('x', trim($size));
-    $formatted_sizes[] = '[' . implode(', ', $formatted_size) . ']';
+    array_walk($formatted_size, function (&$arg) {
+      $arg = (int) $arg;
+    });
+    $formatted_sizes[] = $formatted_size;
   }
 
-  return count($formatted_sizes) == 1 ? $formatted_sizes[0] : '[' . implode(', ', $formatted_sizes) . ']';
+  return count($formatted_sizes) == 1 ? $formatted_sizes[0] : $formatted_sizes;
 }
 
 /**
@@ -660,79 +688,53 @@ function _dfp_get_ad_category($term, $clean_string = FALSE) {
  * slot definitions.
  */
 function _dfp_js_global_settings() {
-  // Initialize the google varibales and inject user-defined javascript.
-  $js = 'var googletag = googletag || {};' . "\n";
-  $js .= 'googletag.cmd = googletag.cmd || [];';
-  $js .= variable_get('dfp_injected_js', '') . "\n";
-
-  // Add a place to store the slot name variable
-  $js .= 'googletag.slots = googletag.slots || {};';
-
+  // Initialize the google variables and inject user-defined javascript.
   $options = array(
-    'type' => 'inline',
+    'type' => 'file',
     'group' => JS_LIBRARY,
     'every_page' => TRUE,
     'weight' => -10,
     'force header' => TRUE,
   );
-  drupal_add_js($js, $options);
-
-  // Include a script tag for the Google Tag Services.
-  // @todo: One day this should include an async=true attribute. See #1140356.
-  //        However, there is a good chance this might break <= IE8.
-  $options['type'] = 'external';
-  $options['weight']++;
-  drupal_add_js("//" . DFP_GOOGLE_TAG_SERVICES_URL, $options);
-
-  // Add global settings with a heavy weight so that they appear after all the
-  // defineSlot() calls otherwise IE8 and Opera fail to display ads properly.
-  $js = 'googletag.cmd.push(function() {' . "\n";
-  if (variable_get('dfp_async_rendering', 1)) {
-    $js .= '  googletag.pubads().enableAsyncRendering();' . "\n";
-  }
-  else {
-     $js .= '  googletag.pubads().enableSyncRendering();' . "\n";
-  }
-  if (variable_get('dfp_single_request', 1)) {
-    $js .= '  googletag.pubads().enableSingleRequest();' . "\n";
-  }
-  switch (variable_get('dfp_collapse_empty_divs', 1)) {
-    case 1:
-      $js .= '  googletag.pubads().collapseEmptyDivs();' . "\n";
-      break;
-    case 2:
-      $js .= '  googletag.pubads().collapseEmptyDivs(true);' . "\n";
-      break;
-  }
-  if (variable_get('dfp_disable_init_load', 0)) {
-    $js .= '  googletag.pubads().disableInitialLoad();' . "\n";
-  }
-  if (variable_get('dfp_set_centering', 0)) {
-    $js .= '  googletag.pubads().setCentering(true);' . "\n";
-  }
+  // Add google tag services.
+  drupal_add_js(drupal_get_path('module', 'dfp') . '/js/dfp_googletag.js', $options);
 
   // Set global targeting values for this page.
+  $global_targets = array();
   $targeting = variable_get('dfp_targeting', array());
   drupal_alter('dfp_global_targeting', $targeting);
   $targeting = dfp_format_targeting($targeting);
   foreach ($targeting as $key => $target) {
     if (!empty($target['target']) && !empty($target['value'])) {
-      $js .= '  googletag.pubads().setTargeting(' . $target['target'] . ', ' . $target['value'] . ');' . "\n";
+      $global_targets[] = array(
+        'target' => $target['target'],
+        'value' => $target['value'],);
     }
   }
 
-  $js .= '});' . "\n";
-  $js .= variable_get('dfp_injected_js2', '') . "\n";
-  $js .= 'googletag.enableServices();';
+  // Drupal.settings array.
+  $settings = array(
+    'asyncRendering' => variable_get('dfp_async_rendering', 1),
+    'singleRequest' => variable_get('dfp_single_request', 1),
+    'collapseEmptyDivs' => variable_get('dfp_collapse_empty_divs', 1),
+    'disableInitialLoad' => variable_get('dfp_disable_init_load', 0),
+    'setCentering' => variable_get('dfp_set_centering', 0),
+    'globalTargets' => $global_targets,
+    'injected_js_2' => variable_get('dfp_injected_js2', ''),
+  );
 
-  $options = array(
-    'type' => 'inline',
+  // Add our javascript that pushes ad slots into googletag.cmd. This has a
+  // heavy weight so it runs after all the slots are defined.
+  drupal_add_js(drupal_get_path('module', 'dfp') . '/js/dfp_googletag.cmd.js', array(
+    'type' => 'file',
     'group' => JS_DEFAULT,
+    'defer' => TRUE,
     'every_page' => TRUE,
-    'weight' => 10,
+    'weight' => 20,
     'force header' => TRUE,
-  );
-  drupal_add_js($js, $options);
+  ));
+  // Add Drupal.settings.
+  drupal_add_js(array('dfpGoogleTagCmd' => $settings), 'setting');
 }
 
 /**
@@ -746,59 +748,56 @@ function _dfp_js_slot_definition($tag) {
     return;
   }
 
-  // Add the js needed to define this adSlot to <head>.
-  $js = '';
   // Start by defining breakpoints for this ad.
+  $bp_sizes = array();
   if (!empty($tag->breakpoints)) {
     $breakpoints = $tag->breakpoints;
-    $js .= 'var mapping = googletag.sizeMapping()' . "\n";
     foreach ($breakpoints as $breakpoint) {
-      $js .= '  .addSize(' . dfp_format_size($breakpoint['browser_size']) . ', ' . dfp_format_size($breakpoint['ad_sizes']) . ')' . "\n";
+      $bp_sizes[] = array(
+        'browser' => dfp_format_size($breakpoint['browser_size']),
+        'ad' => dfp_format_size($breakpoint['ad_sizes']),
+      );
     }
-    $js .= '  .build();' . "\n";
-  }
-  if (!empty($tag->settings['out_of_page'])) {
-    $js .= 'googletag.slots["' . $tag->machinename . '"] = googletag.defineOutOfPageSlot("' . $tag->adunit . '", "' . $tag->placeholder_id . '")' . "\n";
-  }
-  else {
-    $js .= 'googletag.slots["' . $tag->machinename . '"] = googletag.defineSlot("' . $tag->adunit . '", ' . $tag->size . ', "' . $tag->placeholder_id . '")' . "\n";
   }
+
   $click_url = variable_get('dfp_click_url', '');
   if (!empty($click_url)) {
-    $js .= '  .setClickUrl("' . url($click_url, array('absolute' => TRUE)) . '")' . "\n";
-  }
-  $js .= '  .addService(googletag.pubads())' . "\n";
-
-  if (!empty($tag->adsense_ad_types)) {
-    $js .= '  .set("adsense_ad_types", "' . $tag->adsense_ad_types . '")' . "\n";
-  }
-  if (!empty($tag->adsense_channel_ids)) {
-    $js .= '  .set("adsense_channel_ids", "' . $tag->adsense_channel_ids . '")' . "\n";
+    $click_url = url($click_url, array('absolute' => TRUE));
   }
+  $adsense_colors = array();
   foreach ($tag->adsense_colors as $key => $val) {
     if (!empty($val)) {
       $key = 'adsense_' . $key . '_color';
       $val = '#' . drupal_strtoupper($val);
-      $js .= '  .set("' . $key . '", "' . $val . '")' . "\n";
+      $adsense_colors[] = array(
+        'key' => $key,
+        'value' => $val,
+      );
     }
   }
   $targeting = dfp_format_targeting($tag->targeting, $tag);
-  foreach ($targeting as $target) {
-    $js .= '  .setTargeting(' . $target['target'] . ', ' . $target['value'] . ')' . "\n";
-  }
-  // Apply size mapping when there are breakpoints.
-  if (!empty($tag->breakpoints)) {
-    $js .= '  .defineSizeMapping(mapping)' . "\n";
-  }
-  $js = rtrim($js, "\n") . ';';
 
-  $options = array(
-    'type' => 'inline',
-    'group' => JS_LIBRARY,
-    'weight' => 0,
-    'force header' => TRUE,
+  // Drupal.settings array.
+  $settings = array(
+    $tag->machinename => array(
+      'machineName' => $tag->machinename,
+      'breakpoints' => count($bp_sizes) === 1 ? $bp_sizes[0] : $bp_sizes,
+      'tagSize' => $tag->size,
+      'adUnit' => $tag->adunit,
+      'placeHolderId' => $tag->placeholder_id,
+      'outOfPage' => !empty($tag->settings['out_of_page']) ? TRUE : FALSE,
+      'clickURL' => $click_url,
+      'adSenseAdTypes' => $tag->adsense_ad_types,
+      'adSenseChannelIds' => $tag->adsense_channel_ids,
+      'adSenseColors' => $adsense_colors,
+      'targets' => count($targeting) === 1 ? $targeting[0] : $targeting,
+      'isBanner' => $tag->banner,
+      'enableVast' => !empty($tag->settings['companion']) ? TRUE : FALSE,
+    ),
   );
-  drupal_add_js($js, $options);
+
+  // Add Drupal.settings.
+  drupal_add_js(array('dfpSlotDefinitions' => $settings), 'setting');
 }
 
 /**
diff --git a/js/dfp_googletag.cmd.js b/js/dfp_googletag.cmd.js
index e69de29..6a55d8b 100644
--- a/js/dfp_googletag.cmd.js
+++ b/js/dfp_googletag.cmd.js
@@ -0,0 +1,124 @@
+(function (dfp_entry) {
+
+  Drupal.behaviors.dfpGoogleTagCmd = {
+    attach: function (context, settings) {
+
+      var dfpSlotDefinitions = Drupal.settings.dfpSlotDefinitions || {};
+      var dfpGoogleTagCmd = Drupal.settings.dfpGoogleTagCmd || {};
+      var asyncRendering = dfpGoogleTagCmd.asyncRendering || true;
+      var singleRequest = dfpGoogleTagCmd.singleRequest || true;
+      var collapseEmptyDivs = dfpGoogleTagCmd.collapseEmptyDivs || false;
+      var disableInitialLoad = dfpGoogleTagCmd.disableInitialLoad || false;
+      var setCentering = dfpGoogleTagCmd.setCentering || false;
+      var globalTargets = dfpGoogleTagCmd.globalTargets || [];
+
+      var isVast = false;
+
+      // This ensures that gpt api is ready.
+      googletag.cmd.push(function() {
+
+        for (var slot in dfpSlotDefinitions) {
+          if (dfpSlotDefinitions.hasOwnProperty(slot)) {
+
+            this.localDefinition = dfpSlotDefinitions[slot];
+
+            var name = this.localDefinition.machineName;
+
+            // If we are using breakpoints define our size mappings.
+            if (this.localDefinition.breakpoints.length > 0) {
+
+              // Size mapping just wants an array of arrays.
+              this.mapping = [];
+              for (var b = 0; b < this.localDefinition.breakpoints.length; b++) {
+                this.mapping.push([this.localDefinition.breakpoints[b].browser, this.localDefinition.breakpoints[b].ad]);
+              }
+            }
+
+            // The dfp_entry.
+            if (this.localDefinition.outOfPage) {
+              dfp_entry[name] = googletag.defineOutOfPageSlot(this.localDefinition.adUnit, this.localDefinition.placeHolderId);
+            }
+            else {
+              dfp_entry[name] = googletag.defineSlot(this.localDefinition.adUnit, this.localDefinition.tagSize, this.localDefinition.placeHolderId);
+            }
+
+            // Add size mapping to the slot.
+            dfp_entry[name].defineSizeMapping(this.mapping);
+
+            // Add the click URL if we have one.
+            if (this.localDefinition.clickURL.length !== 0) {
+              dfp_entry[name].setClickUrl(this.localDefinition.clickURL);
+            }
+
+            // Add googletag pubads.
+            dfp_entry[name].addService(googletag.pubads());
+
+            if (this.localDefinition.adSenseAdTypes.length !== 0) {
+              dfp_entry[name].set('adsense_ad_types', this.localDefinition.adSenseAdTypes)
+            }
+            if (this.localDefinition.adSenseChannelIds.length !== 0) {
+              dfp_entry[name].set('adsense_channel_ids', this.localDefinition.adSenseChannelIds)
+            }
+            for (var c = 0; c < this.localDefinition.adSenseColors.length; c++) {
+              dfp_entry[name].set(this.localDefinition.adSenseColors[c]['key'], this.localDefinition.adSenseColors[c]['value']);
+            }
+            for (var t = 0; t < this.localDefinition.targets.length; t++) {
+              dfp_entry[name].setTargeting(this.localDefinition.targets[t]['target'], this.localDefinition.targets[t]['value']);
+            }
+
+            // If this is a VAST ad enable companion ads.
+            if (this.localDefinition.enableVast) {
+              dfp_entry[name].addService(googletag.companionAds());
+              isVast = true;
+            }
+
+            googletag.slots[name] = dfp_entry[name];
+          }
+        }
+
+        if (isVast) {
+          googletag.companionAds().setRefreshUnfilledSlots(true);
+        }
+        // Async / Sync loading of ads
+        if (asyncRendering) {
+          googletag.pubads().enableAsyncRendering();
+        }
+        else {
+          googletag.pubads().enableSyncRendering();
+        }
+
+        // Trigger single request.
+        if (singleRequest) {
+          googletag.pubads().enableSingleRequest();
+        }
+
+        // Toggles visibility of empty ads.
+        if (collapseEmptyDivs) {
+          googletag.pubads().collapseEmptyDivs();
+        }
+        else {
+          googletag.pubads().collapseEmptyDivs(true);
+        }
+
+        // Initial Loads.
+        if (disableInitialLoad) {
+          googletag.pubads().disableInitialLoad();
+        }
+
+        // Ad centering.
+        if (setCentering) {
+          googletag.pubads().setCentering(true);
+        }
+
+        // Set global targeting values for this page.
+        for (var gt = 0; gt < globalTargets.length; gt++) {
+          googletag.pubads().setTargeting(globalTargets[gt]['target'], globalTargets[gt]['value']);
+        }
+
+        googletag.enableServices();
+
+      });
+
+    }
+  };
+})(dfp_entry);
diff --git a/js/dfp_googletag.js b/js/dfp_googletag.js
index e69de29..917dcb5 100644
--- a/js/dfp_googletag.js
+++ b/js/dfp_googletag.js
@@ -0,0 +1,21 @@
+// Create our googletag object.
+var googletag = googletag || {};
+
+// Create our cmd array.
+googletag.cmd = googletag.cmd || [];
+
+// Add a place to store the slot name variable
+googletag.slots = googletag.slots || {};
+
+// Used to store slot entries.
+var dfp_entry = dfp_entry || [];
+
+(function() {
+  var gads = document.createElement("script");
+  gads.async = true;
+  gads.type = "text/javascript";
+  var useSSL = "https:" === document.location.protocol;
+  gads.src = (useSSL ? "https:" : "http:") + "//www.googletagservices.com/tag/js/gpt.js";
+  var node =document.getElementsByTagName("script")[0];
+  node.parentNode.insertBefore(gads, node);
+})();
diff --git a/plugins/content_types/dfp_pane.inc b/plugins/content_types/dfp_pane.inc
index 55cff62..4513342 100644
--- a/plugins/content_types/dfp_pane.inc
+++ b/plugins/content_types/dfp_pane.inc
@@ -23,7 +23,8 @@ $plugin = array(
 function dfp_dfp_pane_render($subtype, $conf, $panel_args, $context) {
   $content = array();
 
-  foreach ($conf['dfp_ads'] as $key => $ad) {
+  $ads = array_filter($conf['dfp_ads']);
+  foreach ($ads as $key => $ad) {
     if (!empty($ad)) {
       $content[$key] = dfp_tag($ad);
     }
diff --git a/plugins/export_ui/dfp_ctools_export_ui.inc b/plugins/export_ui/dfp_ctools_export_ui.inc
index 2b79b62..275a4d4 100644
--- a/plugins/export_ui/dfp_ctools_export_ui.inc
+++ b/plugins/export_ui/dfp_ctools_export_ui.inc
@@ -97,6 +97,30 @@ function dfp_tag_form(&$form, &$form_state) {
     '#parents' => array('settings', 'out_of_page'),
     '#weight' => 0,
   );
+  $form['tag_settings']['companion'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Is this a companion ad?'),
+    '#description' => t('Only if you have a video player that sends out VAST specifications for synchronized ads.'),
+    '#default_value' => isset($tag->settings['companion']) ? $tag->settings['companion'] : 0,
+    '#parents' => array('settings', 'companion'),
+    '#weight' => 0,
+  );
+  $form['tag_settings']['banner'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Is this a banner ad?'),
+    '#description' => t('Banner ads need specific dimensions based on the size of the window.'),
+    '#default_value' => isset($tag->settings['banner']) ? $tag->settings['banner'] : 0,
+    '#parents' => array('settings', 'banner'),
+    '#weight' => 0,
+  );
+  $form['tag_settings']['disable_initial_load'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Disable initial load?'),
+    '#description' => t('Do not load on page load?.'),
+    '#default_value' => isset($tag->settings['disable_initial_load']) ? $tag->settings['companion'] : 0,
+    '#parents' => array('settings', 'disable_initial_load'),
+    '#weight' => 0,
+  );
   $form['tag_settings']['size'] = array(
     '#type' => 'textfield',
     '#title' => t('Size(s)'),
diff --git a/theme/dfp_tag.tpl.php b/theme/dfp_tag.tpl.php
index 0dd89d7..85e7095 100644
--- a/theme/dfp_tag.tpl.php
+++ b/theme/dfp_tag.tpl.php
@@ -10,8 +10,14 @@
     print drupal_render($slug);
   endif; ?>
   <script type="text/javascript">
-    googletag.cmd.push(function() {
-      googletag.display("<?php print $tag->placeholder_id ?>");
+    <?php if (!empty($tag->disable_initial_load)): ?>
+    // MIA
+    <?php else: ?>
+    jQuery(document).ready(function() {
+      googletag.cmd.push(function() {
+        googletag.display("<?php print $tag->placeholder_id ?>");
+      });
     });
+    <?php endif; ?>
   </script>
 </div>
