diff --git a/javascript_libraries.admin.inc b/javascript_libraries.admin.inc
index ae9359f..585abd0 100644
--- a/javascript_libraries.admin.inc
+++ b/javascript_libraries.admin.inc
@@ -402,6 +402,7 @@ function javascript_libraries_edit_form($form, &$form_state, $library = array())
     '#disabled' => isset($library['type']),
   );
 
+  $external_access = empty($library['type']) || $library['type'] == 'external';
   $form['external_url'] = array(
     '#type' => 'textfield',
     '#title' => t('URL'),
@@ -412,7 +413,20 @@ function javascript_libraries_edit_form($form, &$form_state, $library = array())
       ),
     ),
     '#default_value' => isset($library['uri']) ? $library['uri'] : '',
-    '#access' => empty($library['type']) || $library['type'] == 'external',
+    '#access' => $external_access,
+  );
+
+
+  $form['cache_external'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Cache script locally'),
+    '#default_value' => isset($library['cache']) ? $library['cache'] : FALSE,
+    '#access' => $external_access,
+    '#states' => array(
+      'visible' => array(
+        ':input[name="library_type"]' => array('value' => 'external'),
+      ),
+    ),
   );
 
   $file_access = empty($library['type']) || $library['type'] == 'file';
@@ -513,6 +527,7 @@ function javascript_libraries_edit_form_submit($form, &$form_state) {
         'name' => $form_state['values']['name'],
         'weight' => $form['#library']['weight'],
         'uri' => $form_state['values']['external_url'],
+        'cache' => $form_state['values']['cache_external'],
       );
       variable_set('javascript_libraries_custom_libraries', $custom);
       break;
diff --git a/javascript_libraries.module b/javascript_libraries.module
index 6857204..3fb1010 100644
--- a/javascript_libraries.module
+++ b/javascript_libraries.module
@@ -148,6 +148,24 @@ function javascript_libraries_valid_external_url($url) {
 }
 
 /**
+ * Implements hook_cron().
+ */
+function javascript_libraries_cron() {
+  // Force an update once per day.
+  $last = variable_get('javascript_libraries_last_sync', 0);
+  if (REQUEST_TIME > $last + 86400) {
+    $custom = variable_get('javascript_libraries_custom_libraries', array());
+    foreach ($custom as $library) {
+      // Get/build local cached versions of external scripts.
+      if ($library['type'] == 'external' && !empty($library['cache'])) {
+        javascript_libraries_cache($library['uri'], TRUE);
+      }
+    }
+    variable_set('javascript_libraries_last_sync', REQUEST_TIME);
+  }
+}
+
+/**
  * Implements hook_theme().
  */
 function javascript_libraries_theme() {
@@ -181,20 +199,40 @@ function javascript_libraries_init() {
   }
 
   $custom = variable_get('javascript_libraries_custom_libraries', array());
+  $options = array();
+  $options['every_page'] = TRUE;
+  foreach ($custom as $library) {
+    if ($library['scope'] != 'disabled') {
+      javascript_libraries_add_js($library, $options);
+    }
+  }
+}
+
+/**
+ * Helper function that calls drupal_add_js().
+ */
+function javascript_libraries_add_js($library, $options = array()) {
   // Add a variable offset so use-added scripts come after theme scripts.
   $offset = variable_get('javascript_libraries_custom_weight_offset', 100);
-  foreach ($custom as $library) {
-    if ($library['scope'] == 'disabled') {
-      continue;
+  // Test taken from drupal_get_js() for whether we are preprocessing.
+  $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
+  // Get/build local cached versions of external scripts.
+  if ($preprocess_js && $library['type'] == 'external' && !empty($library['cache'])) {
+    $local_path = javascript_libraries_cache($library['uri']);
+    if ($local_path) {
+      $options['type'] = 'file';
+      $library['uri'] = $local_path;
     }
-    $options = array();
+  }
+  else {
     $options['type'] = $library['type'];
+  }
+  if (!isset($options['scope'])) {
     $options['scope'] = $library['scope'];
-    $options['weight'] = $library['weight'] + $offset;
-    $options['group'] = JS_THEME;
-    $options['every_page'] = TRUE;
-    drupal_add_js($library['uri'], $options);
   }
+  $options['weight'] = $library['weight'] + $offset;
+  $options['group'] = JS_THEME;
+  drupal_add_js($library['uri'], $options);
 }
 
 /**
@@ -232,27 +270,79 @@ function javascript_libraries_library() {
 }
 
 /**
+ * Implements hook_flush_caches().
+ */
+function javascript_libraries_flush_caches() {
+  variable_del('javascript_libraries_last_sync');
+  javascript_libraries_cron();
+}
+
+/**
+ * Download/Synchronize/Cache tracking code file locally.
+ *
+ * Stolen from google analytics module.
+ *
+ * @param $uri
+ *   The full URL to the external javascript file.
+ * @param $sync_cached_file
+ *   Synchronize tracking code and update if remote file have changed.
+ * @return mixed
+ *   The path to the local javascript file on success, boolean FALSE on failure.
+ */
+function javascript_libraries_cache($uri, $sync_cached_file = FALSE) {
+  $path = 'public://javascript_libraries';
+
+  $file_destination = $path . '/' . basename(parse_url($uri, PHP_URL_PATH));
+  $file_exists = file_exists($file_destination);
+
+  if (!$file_exists || $sync_cached_file) {
+    // Download the library.
+    $result = drupal_http_request($uri);
+
+    if ($result->code != 200) {
+      watchdog('javascript_libraries', t('Error fetching remote file @uri HTTP code: @code.'), array('@uri' => $uri, '@code' => $result->code), WATCHDOG_ERROR);
+      return FALSE;
+    }
+    if ($file_exists) {
+      // Save updated tracking code file to disk.
+      file_unmanaged_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
+      watchdog('javascript_libraries', t('Locally cached javascript library has been updated.'), array(), WATCHDOG_INFO);
+      drupal_clear_js_cache();
+    }
+    elseif (file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
+      // There is no need to flush JS here as core refreshes JS caches
+      // automatically if new files are added.
+      file_unmanaged_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
+      watchdog('javascript_libraries', 'Locally cached javascript library has been saved.', array(), WATCHDOG_INFO);
+    }
+    else {
+      return FALSE;
+    }
+  }
+  // Return the local JS file path.
+  return file_create_url($file_destination);
+}
+
+/**
  * Implements hook_block_view_alter().
  */
 function javascript_libraries_block_view_alter(&$data, $block) {
+  if (path_is_admin(current_path())) {
+    // Don't load any custom JS on admin pages.
+    return;
+  }
   $module = $block->module;
   $delta = $block->delta;
+  $options = array();
+  $options['scope'] = 'footer';
   $settings = variable_get('javascript_libraries_block_settings', array());
   if (!empty($settings[$module][$delta])) {
-    // Add a variable offset so use-added scripts come after theme scripts.
-    $offset = variable_get('javascript_libraries_custom_weight_offset', 100);
     foreach ($settings[$module][$delta] as $id) {
       $library = javascript_libraries_custom_load($id);
-      if (empty($library) || $library['scope'] != 'disabled') {
-        continue;
+      // Only libraries that are not otherwise loaded will be loaded for this block.
+      if (!empty($library) && $library['scope'] == 'disabled') {
+        javascript_libraries_add_js($library, $options);
       }
-      $options = array();
-      $options['type'] = $library['type'];
-      $options['scope'] = 'footer';
-      $options['weight'] = $library['weight'] + $offset;
-      $options['group'] = JS_THEME;
-      $options['every_page'] = FALSE;
-      drupal_add_js($library['uri'], $options);
     }
   }
 }
