From 0096a04947cb9ca5b9625a194dbfc46e3cc1ef01 Mon Sep 17 00:00:00 2001
From: JohnAlbin <virtually.johnalbin@gmail.com>
Date: Sun, 6 May 2012 00:58:23 +0800
Subject: [PATCH] Issue #1563530: Add option to move JavaScripts to footer

---
 speedy.module |   53 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 51 insertions(+), 2 deletions(-)

diff --git a/speedy.module b/speedy.module
index e1a0053..48d4912 100644
--- a/speedy.module
+++ b/speedy.module
@@ -20,13 +20,52 @@ function speedy_form_system_performance_settings_alter(&$form, &$form_state) {
     '#title' => t('Use minified JS files created by Speedy'),
     '#default_value' => variable_get('speedy_js_production', FALSE),
     '#description' => t('Enabling this will replace non-minified Drupal core
-     Javascript files with a minfied version.'),
+     JavaScript files with minified versions.'),
+  );
+  $form['bandwidth_optimization']['speedy_footer'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Move JS files from &lt;head&gt; to bottom of page'),
+    '#default_value' => variable_get('speedy_footer', FALSE),
+    '#description' => t('Enabling this will improve performance by allowing the
+     content to display before loading the JavaScripts.'),
+  );
+  $header_whitelist = variable_get('speedy_header_whitelist', array(
+    'html5.js',
+    'modernizr_loader.js', // Used by modernizr module.
+  ));
+  $form['bandwidth_optimization']['speedy_header_whitelist'] = array(
+    '#type' => 'textarea',
+    '#rows' => count($header_whitelist) + 3,
+    '#title' => t('List of JavaScripts to remain in header'),
+    '#default_value' => implode("\n", $header_whitelist),
+    '#description' => t('Some JavaScripts do not function properly in the footer. Enter the name of one script per line. Example: modernizr.custom.34637.js'),
+    '#element_validate' => array('speedy_header_whitelist_validate'),
+    '#states' => array(
+      'visible' => array(
+        ':input[name="speedy_footer"]' => array('checked' => TRUE),
+      ),
+    ),
   );
 }
 
+/**
+ * Validator for widget in speedy_form_system_performance_settings_alter().
+ */
+function speedy_header_whitelist_validate($element, &$form_state, $form) {
+  // Convert the setting from a string into an array, since that's what we need
+  // during hook_js_alter().
+  $header_scripts = array();
+  foreach (explode("\n", $element['#value']) as $name) {
+    $name = trim($name);
+    if (!empty($name)) {
+      $header_scripts[] = $name;
+    }
+  }
+  $form_state['values']['speedy_header_whitelist'] = $header_scripts;
+}
 
 /**
- * Implements hook_library_alter().
+ * Implements hook_js_alter().
  *
  * If minified core JS exists for the current version of Drupal swap it out.
  */
@@ -58,9 +97,19 @@ function speedy_js_alter(&$javascript) {
   }
 
   $new_path = drupal_get_path('module', 'speedy') . '/js/' . VERSION . '/';
+  $move_to_footer = variable_get('speedy_footer', FALSE);
+  // Get the list of scripts we want to remain in the header scope.
+  $header_scripts = variable_get('speedy_header_whitelist', array(
+    'html5.js',
+    'modernizr_loader.js',
+  ));
   foreach ($javascript as $name => $data) {
     if ($data['type'] == 'file' && in_array($data['data'], $replacement_map)) {
       $javascript[$name]['data'] = $new_path . $data['data'];
     }
+    // Move the script to the footer if it isn't whitelisted for the header.
+    if ($move_to_footer && $data['scope'] == 'header' && (!is_string($data['data']) || !in_array(basename($data['data']), $header_scripts))) {
+      $javascript[$name]['scope'] = 'footer';
+    }
   }
 }
-- 
1.7.10

