You are here

function advagg_mod_js_async_defer in Advanced CSS/JS Aggregation 7.2

Same name and namespace in other branches
  1. 8.2 advagg_mod/advagg_mod.module \advagg_mod_js_async_defer()

Add the defer and or the async tag to js.

Parameters

array $js: JS array.

Return value

bool TRUE if jQuery is deferred.

1 call to advagg_mod_js_async_defer()
advagg_mod_js_post_alter in advagg_mod/advagg_mod.module
Alter the js array.

File

advagg_mod/advagg_mod.module, line 2349
Advanced aggregation modifier module.

Code

function advagg_mod_js_async_defer(array &$js) {
  $jquery_deferred = FALSE;
  $jquery_rev = strrev('/jquery.js');
  $jquery_min_rev = strrev('/jquery.min.js');
  $jquery_ui_rev = strrev('/jquery-ui.js');
  $jquery_ui_min_rev = strrev('jquery-ui.min.js');

  // Return early if this is disabled.
  list(, , $no_async_defer_list, $inline_wrapper_list, , , , , $defer_setting, $async_setting) = advagg_mod_get_lists($js);
  if (!$defer_setting && !$async_setting) {

    // Special handling if using loadcss to defer css loading.
    if (!empty($GLOBALS['advagg_mod_loadcss_jquery_holdready'])) {
      foreach ($js as $name => &$values) {

        // Special handling for jQuery.
        if (stripos(strrev($name), $jquery_rev) === 0 || stripos(strrev($name), $jquery_min_rev) === 0) {

          // Do not fire jQuery.ready until Drupal.settings has been defined.
          $values['onload'] = "if(jQuery.isFunction(jQuery.holdReady)){jQuery.holdReady(true);}";
          break;
        }
      }
    }
    return $jquery_deferred;
  }

  // Disable this section of code for now; the on error attribute only works
  // with async safe JS.
  $use_on_error = FALSE;
  if (variable_get('advagg_mod_js_defer_jquery', ADVAGG_MOD_JS_DEFER_JQUERY) !== FALSE && variable_get('advagg_mod_js_defer_inline_alter', ADVAGG_MOD_JS_DEFER_INLINE_ALTER) && variable_get('jquery_update_jquery_cdn', 'none') !== 'none') {
    $use_on_error = TRUE;
  }

  // If everything is async safe then we can use on error.
  // Only needed if the jquery_update javascript is loaded via async/defer.
  if ($use_on_error) {
    $jquery_update_fallback = '';
    $jquery_update_ui_fallback = '';
    $jquery_migrate_fallback = '';
    $inline_array = array();
    if (module_exists('jquery_update') && (variable_get('jquery_update_jquery_cdn', 'none') !== 'none' || variable_get('jquery_update_jquery_migrate_cdn', 'none') !== 'none')) {
      $min = variable_get('jquery_update_compression_type', 'min') == 'none' ? '' : '.min';
      $path = drupal_get_path('module', 'jquery_update');
      foreach ($js as $name => &$values) {

        // Skip if not inline.
        if ($values['type'] !== 'inline') {
          continue;
        }

        // JQuery UI.
        if (stripos($values['data'], 'window.jQuery.ui') !== FALSE && stripos($values['data'], 'document.write("<script') !== FALSE && variable_get('jquery_update_jquery_cdn', 'none') !== 'none') {
          $js_path = $min == '.min' ? '/replace/ui/ui/minified/jquery-ui.min.js' : '/replace/ui/ui/jquery-ui.js';
          $jquery_update_ui_fallback = "{$GLOBALS['base_path']}{$path}{$js_path}";
          if (empty($inline_array)) {
            $inline_array = $values;
          }
          unset($js[$name]);
          continue;
        }

        // JQuery Migrate.
        if (stripos($values['data'], 'window.jQuery.migrateWarnings') !== FALSE && stripos($values['data'], 'document.write("<script') !== FALSE && variable_get('jquery_update_jquery_migrate_cdn', 'none') !== 'none') {
          $version = '1.2.1';
          $jquery_migrate_fallback = "{$GLOBALS['base_path']}{$path}/replace/jquery-migrate/{$version}/jquery-migrate{$min}.js";
          $inline_array = $values;
          unset($js[$name]);
          continue;
        }

        // JQuery.
        // This should always be last.
        if (stripos($values['data'], 'window.jQuery') !== FALSE && stripos($values['data'], 'document.write("<script') !== FALSE && variable_get('jquery_update_jquery_cdn', 'none') !== 'none') {
          $version = variable_get('jquery_update_jquery_version', '1.10');
          $jquery_update_fallback = "{$GLOBALS['base_path']}{$path}/replace/jquery/{$version}/jquery{$min}.js";
          $inline_array = $values;
          unset($js[$name]);
          continue;
        }
      }
      unset($values);
    }
    if (!empty($jquery_update_fallback) || !empty($jquery_update_ui_fallback) || !empty($jquery_migrate_fallback)) {

      // Add in the advagg_fallback() function so it's available inline.
      $inline_array['group'] = '-150';
      $inline_array['weight'] += -10;
      $inline_array['data'] = 'function advagg_fallback(file){var head = document.getElementsByTagName("head")[0];var script = document.createElement("script");script.src = file;script.type = "text/javascript";head.appendChild(script);};';
      $inline_array['scope_lock'] = TRUE;
      $inline_array['movable'] = FALSE;
      $inline_array['no_defer'] = TRUE;
      $inline_array['scope'] = 'header';
      $js['advagg_fallback'] = $inline_array;
    }
  }

  // Make all scripts defer and/or async.
  $hold_ready = FALSE;
  foreach ($js as $name => &$values) {

    // Skip if not a file or external.
    if ($values['type'] !== 'file' && $values['type'] !== 'external') {
      continue;
    }

    // Everything is defer.
    if (variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER) && empty($values['nodefer'])) {
      $values['defer'] = TRUE;
    }

    // Everything is async.
    if (variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC) && empty($values['noasync'])) {
      $values['async'] = TRUE;
    }

    // Special handling for jQuery.
    if (stripos(strrev($name), $jquery_rev) === 0 || stripos(strrev($name), $jquery_min_rev) === 0) {
      $jquery_deferred = TRUE;

      // Do not fire jQuery.ready until Drupal.settings has been defined.
      if (empty($hold_ready)) {
        if (!empty($GLOBALS['advagg_mod_loadcss_jquery_holdready'])) {
          $values['onload'] = "if(jQuery.isFunction(jQuery.holdReady)){jQuery.holdReady(true);jQuery.holdReady(true);}";
        }
        else {
          $values['onload'] = "if(jQuery.isFunction(jQuery.holdReady)){jQuery.holdReady(true);}";
        }
        $hold_ready = TRUE;
      }

      // jquery_update fallback.
      if (module_exists('jquery_update') && variable_get('jquery_update_jquery_cdn', 'none') !== 'none') {
        if ($use_on_error && !empty($jquery_update_fallback)) {
          if (!isset($values['onerror'])) {
            $values['onerror'] = '';
          }
          $values['onerror'] .= "advagg_fallback('{$jquery_update_fallback}');";
        }

        // Do not defer/async the loading of jquery.js.
        if (!variable_get('advagg_mod_js_defer_jquery', ADVAGG_MOD_JS_DEFER_JQUERY) !== FALSE && variable_get('advagg_mod_js_defer_inline_alter', ADVAGG_MOD_JS_DEFER_INLINE_ALTER) && variable_get('jquery_update_jquery_cdn', 'none') !== 'none') {
          if (variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER)) {
            $values['defer'] = FALSE;
            $jquery_deferred = FALSE;
          }
        }
        if (variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC)) {
          $values['async'] = FALSE;
        }

        // Defer/async is off; done with loop.
        continue;
      }
    }

    // Special handling for jQuery migrate.
    if (stripos($name, '/jquery-migrate') !== FALSE) {

      // jquery_update ui fallback.
      if (module_exists('jquery_update') && variable_get('jquery_update_jquery_migrate_cdn', 'none') !== 'none') {
        if ($use_on_error) {
          if (!isset($values['onerror'])) {
            $values['onerror'] = '';
          }
          $values['onerror'] .= "advagg_fallback('{$jquery_migrate_fallback}');";
        }

        // Do not defer/async the loading of jquery-migrate.
        if (!variable_get('advagg_mod_js_defer_jquery', ADVAGG_MOD_JS_DEFER_JQUERY) !== FALSE && variable_get('advagg_mod_js_defer_inline_alter', ADVAGG_MOD_JS_DEFER_INLINE_ALTER) && variable_get('jquery_update_jquery_cdn', 'none') !== 'none') {
          if (variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER)) {
            $values['defer'] = FALSE;
          }
        }
        if (variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC)) {
          $values['async'] = FALSE;
        }

        // Defer/async is off; done with loop.
        continue;
      }
    }

    // Special handling for jQuery UI.
    if (stripos(strrev($name), $jquery_ui_rev) === 0 || stripos(strrev($name), $jquery_ui_min_rev) === 0) {

      // jquery_update ui fallback.
      if (module_exists('jquery_update') && variable_get('jquery_update_jquery_cdn', 'none') !== 'none') {
        if ($use_on_error) {
          if (!isset($values['onerror'])) {
            $values['onerror'] = '';
          }
          $values['onerror'] .= "advagg_fallback('{$jquery_update_ui_fallback}');";
        }

        // Do not defer/async the loading of jquery-ui.js.
        if (!variable_get('advagg_mod_js_defer_jquery', ADVAGG_MOD_JS_DEFER_JQUERY) !== FALSE && variable_get('advagg_mod_js_defer_inline_alter', ADVAGG_MOD_JS_DEFER_INLINE_ALTER) && variable_get('jquery_update_jquery_cdn', 'none') !== 'none') {
          if (variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER)) {
            $values['defer'] = FALSE;
          }
        }
        if (variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC)) {
          $values['async'] = FALSE;
        }

        // Defer/async is off; done with loop.
        continue;
      }
    }

    // Drupal settings; don't run until misc/drupal.js has ran.
    if ($name === 'misc/drupal.js') {

      // Initialize the Drupal.settings JavaScript object after this has
      // loaded.
      if (!isset($values['onload'])) {
        $values['onload'] = '';
      }
      $matches[0] = $matches[2] = 'init_drupal_core_settings();';
      $values['onload'] .= advagg_mod_wrap_inline_js($matches, "window.init_drupal_core_settings && window.jQuery && window.Drupal", 1);
    }

    // No async defer list.
    foreach ($no_async_defer_list as $search_string) {
      if (strpos($name, $search_string) !== FALSE) {

        // Do not defer/async the loading this script.
        if (variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER)) {
          $values['defer'] = FALSE;
        }
        if (variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC)) {
          $values['async'] = FALSE;
        }
      }
    }

    // Do not defer external scripts setting.
    if ($defer_setting == 2 && $values['type'] === 'external') {
      $values['defer'] = FALSE;
    }
  }
  unset($values);

  // Inline script special handling.
  foreach ($js as &$values) {
    if ($values['type'] !== 'inline') {
      continue;
    }
    foreach ($inline_wrapper_list as $search_string => $js_condition) {
      if (strpos($values['data'], $search_string) !== FALSE) {
        $matches[0] = $matches[2] = $values['data'];
        $values['data'] = advagg_mod_wrap_inline_js($matches, $js_condition);
      }
    }
  }
  unset($values);
  return $jquery_deferred;
}