commit 66b5cbf6b9cf195da38703f6d4f715e2d253c092
Author: Assos Assos <assos+forge@centrale-marseille.fr>
Date:   Wed Jul 9 22:29:29 2014 +0200

    Weekly update of contrib modules

diff --git a/drupal7/sites/all/modules/captcha/captcha.admin.inc b/drupal7/sites/all/modules/captcha/captcha.admin.inc
index 0d095aed..164fae22 100644
--- a/drupal7/sites/all/modules/captcha/captcha.admin.inc
+++ b/drupal7/sites/all/modules/captcha/captcha.admin.inc
@@ -106,6 +106,14 @@ function captcha_admin_settings() {
     '#options' => $captcha_type_options,
   );
 
+  // Checkbox to add default CAPTCHA to all non listed forms as well.
+  $form['captcha_form_protection']['captcha_default_challenge_on_nonlisted_forms'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Default challenge on non-listed forms.'),
+    '#default_value' => variable_get('captcha_default_challenge_on_nonlisted_forms', FALSE),
+    '#description' => t('Normally, no challenge is added to forms that are not listed above. Enabling this option will add the default challenge instead.'),
+  );
+
   // Field for the CAPTCHA administration mode.
   $form['captcha_form_protection']['captcha_administration_mode'] = array(
     '#type' => 'checkbox',
@@ -272,6 +280,7 @@ function captcha_admin_settings_submit($form, &$form_state) {
   variable_set('captcha_allow_on_admin_pages', $form_state['values']['captcha_allow_on_admin_pages']);
 
   variable_set('captcha_default_challenge', $form_state['values']['captcha_default_challenge']);
+  variable_set('captcha_default_challenge_on_nonlisted_forms', $form_state['values']['captcha_default_challenge_on_nonlisted_forms']);
 
   // Process CAPTCHA points
   if (isset($form_state['values']['captcha_form_id_overview']['captcha_captcha_points'])) {
diff --git a/drupal7/sites/all/modules/captcha/captcha.inc b/drupal7/sites/all/modules/captcha/captcha.inc
index 6af73a66..161bf8c1 100644
--- a/drupal7/sites/all/modules/captcha/captcha.inc
+++ b/drupal7/sites/all/modules/captcha/captcha.inc
@@ -69,9 +69,19 @@ function captcha_set_form_id_setting($form_id, $captcha_type) {
  *   or in the form 'captcha/Math'.
  */
 function captcha_get_form_id_setting($form_id, $symbolic=FALSE) {
+  // Fetch setting from database.
   $result = db_query("SELECT module, captcha_type FROM {captcha_points} WHERE form_id = :form_id",
     array(':form_id' =>  $form_id));
   $captcha_point = $result->fetchObject();
+
+  // If no setting is available in database for the given form,
+  // but 'captcha_default_challenge_on_nonlisted_forms' is enabled, pick the default type anyway
+  if (!$captcha_point && variable_get('captcha_default_challenge_on_nonlisted_forms', FALSE))
+  {
+    $captcha_point = (object) array('captcha_type' => 'default');
+  }
+
+  // Handle (default) settings and symbolic mode.
   if (!$captcha_point) {
     $captcha_point = NULL;
   }
diff --git a/drupal7/sites/all/modules/captcha/captcha.info b/drupal7/sites/all/modules/captcha/captcha.info
index 32c91fca..67abde1d 100644
--- a/drupal7/sites/all/modules/captcha/captcha.info
+++ b/drupal7/sites/all/modules/captcha/captcha.info
@@ -10,9 +10,9 @@ files[] = captcha.admin.inc
 files[] = captcha.install
 files[] = captcha.test
 
-; Information added by drupal.org packaging script on 2013-06-25
-version = "7.x-1.0"
+; Information added by Drupal.org packaging script on 2014-07-06
+version = "7.x-1.1"
 core = "7.x"
 project = "captcha"
-datestamp = "1372203950"
+datestamp = "1404666828"
 
diff --git a/drupal7/sites/all/modules/captcha/captcha.install b/drupal7/sites/all/modules/captcha/captcha.install
index f53f600a..10086290 100644
--- a/drupal7/sites/all/modules/captcha/captcha.install
+++ b/drupal7/sites/all/modules/captcha/captcha.install
@@ -263,7 +263,6 @@ function captcha_update_6201() {
   return $items;
 }
 
-
 /**
  * Implementation of hook_update_N()
  * Add a CAPTCHA token column to captcha_sessions table.
@@ -274,8 +273,6 @@ function captcha_update_6202() {
   return $ret;
 }
 
-
-
 /**
  * Implementation of hook_update_N()
  * Rename the type field to captcha_type in captcha_points.
@@ -286,7 +283,6 @@ function captcha_update_6203() {
   return $ret;
 }
 
-
 /**
  * Migrate form configuration for changed form ids in Drupal 7.
  */
diff --git a/drupal7/sites/all/modules/captcha/captcha.module b/drupal7/sites/all/modules/captcha/captcha.module
index b2772b6a..72e8e9ba 100644
--- a/drupal7/sites/all/modules/captcha/captcha.module
+++ b/drupal7/sites/all/modules/captcha/captcha.module
@@ -6,7 +6,6 @@
  * administrators can add a CAPTCHA to desired forms that users without
  * the 'skip CAPTCHA' permission (typically anonymous visitors) have
  * to solve.
- *
  */
 
 /**
@@ -216,12 +215,20 @@ function captcha_element_process($element, &$form_state, $complete_form) {
     '#value' => $captcha_sid,
   );
 
-  // Additional one time CAPTCHA token: store in database and send with form.
-  $captcha_token = md5(mt_rand());
-  db_update('captcha_sessions')
-    ->fields(array('token' => $captcha_token))
+  $captcha_token = db_select('captcha_sessions', 'c')
+    ->fields('c', array('token'))
     ->condition('csid', $captcha_sid)
-    ->execute();
+    ->execute()
+    ->fetchField();
+  if (!isset($captcha_token) && !$form_state['submitted']) {
+    // Additional one-time CAPTCHA token: store in database and send with form.
+    $captcha_token = md5(mt_rand());
+    db_update('captcha_sessions')
+      ->fields(array('token' => $captcha_token))
+      ->condition('csid', $captcha_sid)
+      ->execute();
+  }
+
   $element['captcha_token'] = array(
     '#type' => 'hidden',
     '#value' => $captcha_token,
@@ -349,10 +356,10 @@ function captcha_form_alter(&$form, &$form_state, $form_id) {
       _captcha_insert_captcha_element($form, $captcha_placement, $captcha_element);
     }
   }
-  else if (
-  variable_get('captcha_administration_mode', FALSE)
-  && user_access('administer CAPTCHA settings')
-  && (arg(0) != 'admin' || variable_get('captcha_allow_on_admin_pages', FALSE))
+  elseif (
+    variable_get('captcha_administration_mode', FALSE)
+    && user_access('administer CAPTCHA settings')
+    && (arg(0) != 'admin' || variable_get('captcha_allow_on_admin_pages', FALSE))
   ) {
     // Add CAPTCHA administration tools.
     module_load_include('inc', 'captcha');
diff --git a/drupal7/sites/all/modules/captcha/captcha_api.txt b/drupal7/sites/all/modules/captcha/captcha_api.txt
index dc343fcc..899beac9 100644
--- a/drupal7/sites/all/modules/captcha/captcha_api.txt
+++ b/drupal7/sites/all/modules/captcha/captcha_api.txt
@@ -1,4 +1,3 @@
-
 This documentation is for developers that want to implement their own
 challenge type and integrate it with the base CAPTCHA module.
 
diff --git a/drupal7/sites/all/modules/captcha/image_captcha/image_captcha.info b/drupal7/sites/all/modules/captcha/image_captcha/image_captcha.info
index 1c0e9b16..7023bf1c 100644
--- a/drupal7/sites/all/modules/captcha/image_captcha/image_captcha.info
+++ b/drupal7/sites/all/modules/captcha/image_captcha/image_captcha.info
@@ -10,9 +10,9 @@ files[] = image_captcha.module
 files[] = image_captcha.admin.inc
 files[] = image_captcha.user.inc
 
-; Information added by drupal.org packaging script on 2013-06-25
-version = "7.x-1.0"
+; Information added by Drupal.org packaging script on 2014-07-06
+version = "7.x-1.1"
 core = "7.x"
 project = "captcha"
-datestamp = "1372203950"
+datestamp = "1404666828"
 
diff --git a/drupal7/sites/all/modules/captcha/image_captcha/image_captcha.user.inc b/drupal7/sites/all/modules/captcha/image_captcha/image_captcha.user.inc
index 07aeb84d..371717a1 100644
--- a/drupal7/sites/all/modules/captcha/image_captcha/image_captcha.user.inc
+++ b/drupal7/sites/all/modules/captcha/image_captcha/image_captcha.user.inc
@@ -29,6 +29,11 @@ function image_captcha_image() {
 
   // Only generate captcha if code exists in the session.
   if ($code !== FALSE) {
+    // Seed the random generators used for image CAPTCHA distortion based on session and code
+    // to counter attacks that re-request the same challenge and pick the simplest image one or combine info.
+    $seed = hexdec(substr(md5($captcha_sid . $code), 0, 8));
+    srand($seed);
+    mt_srand($seed);
     // generate the image
     $image = @_image_captcha_generate_image($code);
     // check of generation was successful
@@ -124,12 +129,12 @@ function _image_captcha_generate_image($code) {
   $distortion_amplitude = .25 * $font_size * variable_get('image_captcha_distortion_amplitude', 0) / 10.0;
   if ($distortion_amplitude > 1) {
     // distortion parameters
-    $wavelength_xr = (2+3*lcg_value())*$font_size;
-    $wavelength_yr = (2+3*lcg_value())*$font_size;
+    $wavelength_xr = (2 + 3 * mt_rand(0, 1000) / 1000) * $font_size;
+    $wavelength_yr = (2 + 3 * mt_rand(0, 1000) / 1000) * $font_size;
     $freq_xr = 2 * 3.141592 / $wavelength_xr;
     $freq_yr = 2 * 3.141592 / $wavelength_yr;
-    $wavelength_xt = (2+3*lcg_value())*$font_size;
-    $wavelength_yt = (2+3*lcg_value())*$font_size;
+    $wavelength_xt = (2 + 3 * mt_rand(0, 1000) / 1000) * $font_size;
+    $wavelength_yt = (2 + 3 * mt_rand(0, 1000) / 1000) * $font_size;
     $freq_xt = 2 * 3.141592 / $wavelength_xt;
     $freq_yt = 2 * 3.141592 / $wavelength_yt;
 
diff --git a/drupal7/sites/all/modules/twitter_block/readme.txt b/drupal7/sites/all/modules/twitter_block/readme.txt
index 63ee7493..6c3a2616 100644
--- a/drupal7/sites/all/modules/twitter_block/readme.txt
+++ b/drupal7/sites/all/modules/twitter_block/readme.txt
@@ -6,6 +6,7 @@ CONTENTS OF THIS FILE
  * Installation
  * Permissions
  * Usage
+ * Theming
 
 INTRODUCTION
 ------------
@@ -64,3 +65,13 @@ You can determine a widget's ID by editing it and inspecting the URL (which
 should be in the form of https://twitter.com/settings/widgets/WIDGET_ID/edit)
 or by looking at the widget's embed code (look for
 data-widget-id="WIDGET_ID").
+
+Theming
+-------
+
+Embedded Timelines offer a number of customization options such as theme, layout
+and border color but, due to the way embedded timelines are implemented, custom
+theming using CSS can be difficult.
+
+To add custom CSS to embedded timelines check out the
+(http://github.com/kevinburke/customize-twitter-1.1) Customize Twitter project.
diff --git a/drupal7/sites/all/modules/twitter_block/tests/twitter_block.test b/drupal7/sites/all/modules/twitter_block/tests/twitter_block.test
index 4e435c5b..eacc1423 100644
--- a/drupal7/sites/all/modules/twitter_block/tests/twitter_block.test
+++ b/drupal7/sites/all/modules/twitter_block/tests/twitter_block.test
@@ -49,6 +49,7 @@ class TwitterBlockTestCase extends DrupalWebTestCase {
     $twitter_block['info'] = $this->randomName(8);
     $twitter_block['title'] = $this->randomName(8);
     $twitter_block['widget_id'] = $this->randomName(8);
+    $twitter_block['username'] = $this->randomName(8);
     $this->drupalPost('admin/structure/block/add-twitter-block', $twitter_block, t('Save block'));
 
     // Confirm that the custom Twitter block has been created, and then query the created bid.
diff --git a/drupal7/sites/all/modules/twitter_block/twitter_block.admin.inc b/drupal7/sites/all/modules/twitter_block/twitter_block.admin.inc
index 71730c6a..272d4590 100644
--- a/drupal7/sites/all/modules/twitter_block/twitter_block.admin.inc
+++ b/drupal7/sites/all/modules/twitter_block/twitter_block.admin.inc
@@ -17,11 +17,11 @@ function twitter_block_add_block_form($form, &$form_state) {
   $form = block_admin_configure($form, $form_state, 'twitter_block', NULL);
 
   // Other modules should be able to use hook_form_block_add_block_form_alter()
-  // to modify this form, so add a base form ID
+  // to modify this form, so add a base form ID.
   $form_state['build_info']['base_form_id'] = 'block_add_block_form';
 
   // Prevent block_add_block_form_validate/submit() from being automatically
-  // added because of the base form ID by providing these handlers manually
+  // added because of the base form ID by providing these handlers manually.
   $form['#validate'] = array('twitter_block_add_block_form_validate');
   $form['#submit'] = array('twitter_block_add_block_form_submit');
 
@@ -71,19 +71,20 @@ function twitter_block_add_block_form_submit($form, &$form_state) {
     'polite' => $form_state['values']['polite'],
   );
 
-  // Save the block configuration
+  // Save the block configuration.
   $delta = db_insert('twitter_block')
     ->fields(array(
       'info' => $form_state['values']['info'],
       'widget_id' => $form_state['values']['widget_id'],
+      'username' => $form_state['values']['username'],
       'data' => serialize($data),
     ))
     ->execute();
 
-  // Store block delta to allow other modules to work with new block
+  // Store block delta to allow other modules to work with new block.
   $form_state['values']['delta'] = $delta;
 
-  // Run the normal new block submission (borrowed from block_add_block_form_submit)
+  // Run the normal new block submission (borrowed from block_add_block_form_submit).
   $query = db_insert('block')->fields(array('visibility', 'pages', 'custom', 'title', 'module', 'theme', 'status', 'weight', 'delta', 'cache'));
   foreach (list_themes() as $key => $theme) {
     if ($theme->status) {
@@ -113,7 +114,7 @@ function twitter_block_add_block_form_submit($form, &$form_state) {
   }
   $query->execute();
 
-  // Store regions per theme for this block
+  // Store regions per theme for this block.
   foreach ($form_state['values']['regions'] as $theme => $region) {
     db_merge('block')
       ->key(array('theme' => $theme, 'delta' => $delta, 'module' => $form_state['values']['module']))
diff --git a/drupal7/sites/all/modules/twitter_block/twitter_block.info b/drupal7/sites/all/modules/twitter_block/twitter_block.info
index 35277f38..bb127f0d 100644
--- a/drupal7/sites/all/modules/twitter_block/twitter_block.info
+++ b/drupal7/sites/all/modules/twitter_block/twitter_block.info
@@ -8,9 +8,9 @@ configure = admin/structure/block
 
 dependencies[] = block
 
-; Information added by drupal.org packaging script on 2013-07-11
-version = "7.x-2.1"
+; Information added by Drupal.org packaging script on 2014-07-09
+version = "7.x-2.2"
 core = "7.x"
 project = "twitter_block"
-datestamp = "1373509571"
+datestamp = "1404893628"
 
diff --git a/drupal7/sites/all/modules/twitter_block/twitter_block.install b/drupal7/sites/all/modules/twitter_block/twitter_block.install
index 279aa980..0b4cbecd 100644
--- a/drupal7/sites/all/modules/twitter_block/twitter_block.install
+++ b/drupal7/sites/all/modules/twitter_block/twitter_block.install
@@ -32,6 +32,13 @@ function twitter_block_schema() {
         'default' => '',
         'description' => 'Widget ID.',
       ),
+      'username' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Account username.',
+      ),
       'data' => array(
         'type' => 'blob',
         'size' => 'big',
@@ -54,7 +61,7 @@ function twitter_block_schema() {
  * Implements hook_uninstall().
  */
 function twitter_block_uninstall() {
-  // Remove blocks
+  // Remove blocks.
   db_delete('block')
     ->condition('module', 'twitter_block')
     ->execute();
@@ -62,7 +69,14 @@ function twitter_block_uninstall() {
     ->condition('module', 'twitter_block')
     ->execute();
 
-  // Clear the site cache
+  // Remove variables.
+  variable_del('twitter_block_cache');
+  variable_del('twitter_block_last_cache');
+
+  // Remove the cache directory.
+  twitter_block_clear_js_cache();
+
+  // Clear the site cache.
   cache_clear_all();
 }
 
@@ -70,10 +84,10 @@ function twitter_block_uninstall() {
  * Remove any old Twitter Block blocks and install the new Twitter Block schema.
  */
 function twitter_block_update_7200() {
-  // Remove old Twitter Block schema
+  // Remove old Twitter Block schema.
   drupal_uninstall_schema('twitter_block');
 
-  // Remove any old Twitter Block blocks
+  // Remove any old Twitter Block blocks.
   db_delete('block')
     ->condition('module', 'twitter_block')
     ->execute();
@@ -81,11 +95,24 @@ function twitter_block_update_7200() {
     ->condition('module', 'twitter_block')
     ->execute();
 
-  // Clear the site cache
+  // Clear the site cache.
   cache_clear_all();
 
-  // Install the new Twitter Block schema
+  // Install the new Twitter Block schema.
   drupal_install_schema('twitter_block');
 
   return t('Removed any old Twitter Block blocks and installed the new Twitter Block schema.');
 }
+
+/**
+ * Add a column to the {twitter_block} table to store the Twitter account username.
+ */
+function twitter_block_update_7201() {
+  db_add_field('twitter_block', 'username', array(
+    'type' => 'varchar',
+    'length' => 128,
+    'not null' => TRUE,
+    'default' => '',
+    'description' => 'Account username.',
+  ));
+}
diff --git a/drupal7/sites/all/modules/twitter_block/twitter_block.module b/drupal7/sites/all/modules/twitter_block/twitter_block.module
index 30c974be..bca3a565 100644
--- a/drupal7/sites/all/modules/twitter_block/twitter_block.module
+++ b/drupal7/sites/all/modules/twitter_block/twitter_block.module
@@ -5,6 +5,20 @@
  * A module to provide simple Twitter blocks using the Twitter Search API.
  */
 
+/**
+ * Implements hook_cron().
+ */
+function twitter_block_cron() {
+  // Regenerate the JavaScript file every day.
+  if (REQUEST_TIME - variable_get('twitter_block_last_cache', 0) >= 86400 && variable_get('twitter_block_cache', TRUE)) {
+    // Synchronize the widget code and update it if remote file have changed.
+    twitter_block_cache(TRUE);
+
+    // Record when the synchronization occurred.
+    variable_set('twitter_block_last_cache', REQUEST_TIME);
+  }
+}
+
 /**
  * Implements hook_help().
  */
@@ -19,7 +33,7 @@ function twitter_block_help($path, $arg) {
  * Implements hook_menu().
  */
 function twitter_block_menu() {
-  // Create an array of block settings
+  // Create an array of block settings.
   $settings = array(
     'title' => 'Add Twitter block',
     'description' => 'Add a new Twitter block.',
@@ -29,15 +43,15 @@ function twitter_block_menu() {
     'file' => 'twitter_block.admin.inc',
   );
 
-  // Add a local action to the block configuration page
+  // Add a local action to the block configuration page.
   $items['admin/structure/block/add-twitter-block'] = array(
     'access arguments' => array('administer blocks'),
   ) + $settings;
 
-  // Get the default site theme
+  // Get the default site theme.
   $default_theme = variable_get('theme_default', 'bartik');
 
-  // Add a local action to the per-theme block configuration pages
+  // Add a local action to the per-theme block configuration pages.
   foreach (list_themes() as $key => $theme) {
     if ($key != $default_theme) {
       $items['admin/structure/block/list/' . $key . '/add-twitter-block'] = array(
@@ -71,7 +85,7 @@ function _twitter_block_themes_access($theme) {
 function twitter_block_form_block_admin_display_form_alter(&$form, &$form_state, $form_id) {
   $result = db_query('SELECT bid FROM {twitter_block}');
 
-  // Add delete links to Twitter Block blocks
+  // Add delete links to Twitter Block blocks.
   foreach ($result as $block) {
     $form['blocks']['twitter_block_' . $block->bid]['delete'] = array(
       '#type' => 'link',
@@ -93,6 +107,7 @@ function twitter_block_form_block_admin_display_form_alter(&$form, &$form_state,
  *   - bid: Block ID.
  *   - info: Block description.
  *   - widget_id: Widget ID.
+ *   - username: Account username.
  *   - theme: Theme.
  *   - link_color: Link color.
  *   - width: Width.
@@ -157,6 +172,7 @@ function twitter_block_custom_block_form($edit = array()) {
   $edit += array(
     'info' => '',
     'widget_id' => '',
+    'username' => '',
     'theme' => '',
     'link_color' => '',
     'width' => '',
@@ -184,6 +200,13 @@ function twitter_block_custom_block_form($edit = array()) {
     '#required' => TRUE,
     '#description' => t('Each Twitter Block block requires a unique widget ID which determines, among other things, the source (user timeline, favourites, list or search) of the tweets to display. You can view a list of your existing embedded timeline widgets (and their widget IDs) or create new embedded timeline widgets by visiting the <a href="@widgets_section">widgets section of your settings page</a> (make sure that you\'re logged in). You can determine a widget\'s ID by editing it and inspecting the URL (which should be in the form of twitter.com/settings/widgets/WIDGET_ID/edit) or by looking at the widget\'s embed code (look for data-widget-id="WIDGET_ID").', array('@widgets_section' => 'https://twitter.com/settings/widgets')),
   );
+  $form['username'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Username'),
+    '#default_value' => $edit['username'],
+    '#required' => TRUE,
+    '#description' => t('A Twitter account username. This is used to generate a fallback link to the Twitter account associated with the widget when JavaScript is not available. You can find your account username by visting the <a href="@account_section">account section of your settings page</a> or on your profile page in the URL or prefixed with @ under your display name.', array('@account_section' => 'https://twitter.com/settings/account')),
+  );
   $form['appearance'] = array(
     '#type' => 'fieldset',
     '#title' => t('Appearance'),
@@ -311,6 +334,7 @@ function twitter_block_block_save($delta = 0, $edit = array()) {
  *   Associative array of fields to save. Array keys:
  *   - info: Block description.
  *   - widget_id: Widget ID.
+ *   - username: Account username.
  *   - theme: Theme.
  *   - link_color: Link color.
  *   - width: Width.
@@ -342,11 +366,12 @@ function twitter_block_custom_block_save($edit, $delta) {
     'polite' => $edit['polite'],
   );
 
-  // Save the block configuration
+  // Save the block configuration.
   $delta = db_update('twitter_block')
     ->fields(array(
       'info' => $edit['info'],
       'widget_id' => $edit['widget_id'],
+      'username' => $edit['username'],
       'data' => serialize($data),
     ))
     ->condition('bid', $delta)
@@ -362,15 +387,15 @@ function twitter_block_block_view($delta) {
   // Load the configuration.
   $config = twitter_block_block_get($delta);
 
-  // Unserialize the timeline 
+  // Unserialize the timeline.
   $data = unserialize($config['data']);
 
   $block = array();
   $block['subject'] = check_plain($config['info']);
   $block['content'] = array(
-    '#theme' => 'link',
-    '#text' => 'Tweets by @twitterapi',
-    '#path' => 'https://twitter.com/twitterapi',
+    '#type' => 'link',
+    '#title' => t('Tweets by @username', array('@username' => $config['username'])),
+    '#href' => 'https://twitter.com/' . $config['username'],
     '#options' => array(
       'attributes' => array(
         'class' => array('twitter-timeline'),
@@ -378,15 +403,26 @@ function twitter_block_block_view($delta) {
       ),
       'html' => FALSE,
     ),
-    '#attached' => array(
-      'js' => array(
-        'https://platform.twitter.com/widgets.js' => array(
-          'type' => 'external',
-        ),
-      ),
-    ),
   );
 
+  // Use a locally cached copy of widgets.js by default.
+  if (variable_get('twitter_block_cache', TRUE) && $url = twitter_block_cache()) {
+    $block['content']['#attached']['js'] = array(
+      array(
+        'data' => $url,
+        'type' => 'file',
+      ),
+    );
+  }
+  else {
+    $block['content']['#attached']['js'] = array(
+      '//platform.twitter.com/widgets.js' => array(
+        'type' => 'external',
+        'scope' => 'footer',
+      ),
+    );
+  }
+
   if (!empty($data['theme'])) {
     $block['content']['#options']['attributes']['data-theme'] = $data['theme'];
   }
@@ -434,8 +470,76 @@ function twitter_block_block_view($delta) {
   }
 
   if (!empty($data['polite'])) {
-    $block['content']['#options']['attributes']['aria-polite'] = $data['polite'];
+    $block['content']['#options']['attributes']['data-aria-polite'] = $data['polite'];
   }
 
   return $block;
 }
+
+/**
+ * Download/Synchronize/Cache widget code file locally.
+ *
+ * @param $sync_cached_file
+ *   Synchronize widget code and update if remote file have changed.
+ * @return mixed
+ *   The path to the local javascript file on success, boolean FALSE on failure.
+ */
+function twitter_block_cache($sync_cached_file = FALSE) {
+  $location = 'http://platform.twitter.com/widgets.js';
+  $path = 'public://twitter_block';
+  $file_destination = $path . '/' . basename($location);
+
+  if (!file_exists($file_destination) || $sync_cached_file) {
+    // Download the latest widget code.
+    $result = drupal_http_request($location);
+
+    if ($result->code == 200) {
+      if (file_exists($file_destination)) {
+        // Synchronize widget code and and replace local file if outdated.
+        $data_hash_local = drupal_hash_base64(file_get_contents($file_destination));
+        $data_hash_remote = drupal_hash_base64($result->data);
+        // Check that the files directory is writable.
+        if ($data_hash_local != $data_hash_remote && file_prepare_directory($path)) {
+          // Save updated widget code file to disk.
+          file_unmanaged_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
+          watchdog('twitter_block', 'Locally cached widget code file has been updated.', array(), WATCHDOG_INFO);
+
+          // Change query-strings on css/js files to enforce reload for all users.
+          _drupal_flush_css_js();
+        }
+      }
+      else {
+        // Check that the files directory is writable.
+        if (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('twitter_block', 'Locally cached widget code file has been saved.', array(), WATCHDOG_INFO);
+
+          // Return the local JS file path.
+          return file_create_url($file_destination);
+        }
+      }
+    }
+  }
+  else {
+    // Return the local JS file path.
+    return file_create_url($file_destination);
+  }
+}
+
+/**
+ * Delete cached files and directory.
+ */
+function twitter_block_clear_js_cache() {
+  $path = 'public://twitter_block';
+  if (file_prepare_directory($path)) {
+    file_scan_directory($path, '/.*/', array('callback' => 'file_unmanaged_delete'));
+    drupal_rmdir($path);
+
+    // Change query-strings on css/js files to enforce reload for all users.
+    _drupal_flush_css_js();
+
+    watchdog('twitter_block', 'Local cache has been purged.', array(), WATCHDOG_INFO);
+  }
+}
