You are here

async_js.module in Asynchronous JavaScript 8

Same filename and directory in other branches
  1. 7 async_js.module

Queue JavaScript files to be loaded asynchronously.

File

async_js.module
View source
<?php

/**
 * @file
 * Queue JavaScript files to be loaded asynchronously.
 */

/**
 * Queue scripts to be loaded asynchronously.
 *
 * @param string $src
 *  The path to the file relative to base_path().
 * @param array $options
 *  An associative array of options for each script. The following elements
 *  affect asynchronous loading functionality:
 *  -  'type': The type of JavaScript that is to be added to the page.
 *      Allowed values are 'file' or 'external'. Defaults to 'file'.
 *  -  'async_dependencies': An indexed array of scripts which must be loaded
 *      before the current script. The value of each item in the array must
 *      correspond exactly to the $src parameter originally used to load the
 *      dependency.
 *  -  'async_callback': The name of a javascript function, or an array of them,
 *      in the global scope which will be run once this particular script is
 *      finished loading.
 *  -  'async_fade': An array of jQuery selectors which correspond to elements
 *      on the page to fadeIn after a specified timeout and in unison. The
 *      fadeIn effect will be applied universally to all elements defined in
 *      this way on a single page.
 *  -  'async_container': A jQuery selector which corresponds to the element on
 *      the page in which the script should be inserted. If left blank, the
 *      script will be inserted before the first script element in the document.
 *  -  'cache': If set to FALSE, the JavaScript file is loaded anew on every
 *      asynchronous request. Defaults to TRUE.
 *
 * @see async_js_js_alter()
 */
function async_js_add_js($src = NULL, array $options = array()) {
  $javascript =& drupal_static(__FUNCTION__, array());
  if (!empty($src)) {

    // Set default options.
    $options += array(
      'type' => 'file',
      'async_dependencies' => array(),
      'async_callback' => array(),
      'async_fade' => array(),
      'async_container' => NULL,
      'cache' => TRUE,
    );

    // Add script to queue.
    $javascript[$src] = array(
      'data' => $src,
    ) + $options;
  }
  return $javascript;
}

/**
 * Implements hook_js_alter().
 *
 * @see async_js_add_js()
 */
function async_js_js_alter(&$javascript) {

  // Add any scripts to be loaded asynchronously.
  foreach ($javascript as $key => $script) {
    if (!empty($script['async_js'])) {
      async_js_add_js($script['data'], $script);
      unset($javascript[$key]);
    }
  }

  // Retrieve all scripts to be loaded asynchronously.
  $async_js = async_js_add_js();
  if (!empty($async_js)) {

    // Process dependencies.
    $async_js = _async_js_process_dependencies($async_js);

    // Build file URLs.
    $async_js = _async_js_build_urls($async_js);

    // Initialize settings array.
    $settings = array();
    $javascript['settings']['data'][]['async_js'] =& $settings;

    // Add settings for each script.
    foreach ($async_js as $script) {

      // Prep $script for JavaScript.
      $script = array(
        'data' => $script['data'],
        // Use an array of callbacks, even if only given a string.
        'callbacks' => (array) $script['async_callback'],
        'fade' => $script['async_fade'],
        'container' => $script['async_container'],
        'dependencies' => $script['async_dependencies'],
        'dependents' => $script['async_dependents'],
        'queued' => FALSE,
        'loaded' => FALSE,
      );

      // Add file to script list.
      $settings['javascript'][$script['data']] = $script;
    }

    // Add additional settings.
    $settings['timeout'] = variable_get('async_js_timeout', 1000);
    $settings['finalCallback'] = variable_get('async_js_final_callback', '');

    // Add our own script.
    $data = drupal_get_path('module', 'async_js') . '/async_js.js';
    $javascript[$data] = drupal_js_defaults($data);
  }
}

/**
 * Prepares script data for actual requests.
 *
 * @param array $scripts
 *  An array of scripts as returned by async_js_add_js().
 *
 * @see async_js_js_alter()
 * @see async_js_add_js()
 */
function _async_js_build_urls(array $scripts = array()) {

  // Initialize variables necessary for building the file url.
  $default_query_string = variable_get('css_js_query_string', '0');
  $js_version_string = variable_get('drupal_js_version_query_string', 'v=');

  // Keep track of any changes we make so we can reference them later when
  // dealing with dependencies and dependents.
  $changes = array();

  // Build the urls.
  foreach ($scripts as &$script) {
    if ($script['type'] != 'external') {
      $old_data = $script['data'];
      $query_string = empty($script['version']) ? $default_query_string : $js_version_string . $script['version'];
      $query_string_separator = strpos($script['data'], '?') !== FALSE ? '&' : '?';
      $script['data'] = file_create_url($script['data']) . $query_string_separator . ($script['cache'] ? $query_string : REQUEST_TIME);
      $changes[$old_data] = $script['data'];
    }
  }

  // Handle potential dependencies and dependents.
  foreach ($scripts as &$script) {

    // Handle potential dependencies.
    if (!empty($script['async_dependencies'])) {
      foreach ($script['async_dependencies'] as $key => $dependency) {
        if (!empty($changes[$dependency])) {
          $script['async_dependencies'][$key] = $changes[$dependency];
        }
      }
    }

    // Handle potential dependents.
    if (!empty($script['async_dependents'])) {
      foreach ($script['async_dependents'] as $key => $dependent) {
        if (!empty($changes[$dependent])) {
          $script['async_dependents'][$key] = $changes[$dependent];
        }
      }
    }
  }
  return $scripts;
}

/**
 * Processes script dependencies, creating corresponding dependent references.
 *
 * @param array $scripts
 *  An array of scripts as returned by async_js_add_js().
 *
 * @see async_js_js_alter()
 * @see async_js_add_js()
 */
function _async_js_process_dependencies(array $scripts = array()) {
  foreach ($scripts as $key => $script) {

    // Make sure every script has an empty async_dependents array by default.
    $scripts[$key]['async_dependents'] = array();
  }
  $scripts_copy = $scripts;

  // Create corresponding dependent references.
  foreach ($scripts_copy as $key => $script) {
    if (!empty($script['async_dependencies'])) {
      foreach ($script['async_dependencies'] as $dependency) {
        if (!empty($scripts[$dependency])) {
          $scripts[$dependency]['async_dependents'][] = $script['data'];
        }
        else {
          unset($scripts[$key]);
          drupal_set_message(t('The script, @script is dependent on @dependency, which does not exist on this page.', array(
            '@script' => $script['data'],
            '@dependency' => $dependency,
          )), 'error');
        }
      }
    }
  }
  return $scripts;
}

Functions

Namesort descending Description
async_js_add_js Queue scripts to be loaded asynchronously.
async_js_js_alter Implements hook_js_alter().
_async_js_build_urls Prepares script data for actual requests.
_async_js_process_dependencies Processes script dependencies, creating corresponding dependent references.