$implementations['advagg_mod']); unset($implementations['advagg_mod']); $implementations = array_merge($item, $implementations); } // Remove advagg_mod. Function gets called directly. if ($hook === 'html_head_alter' && array_key_exists('advagg_mod', $implementations)) { unset($implementations['advagg_mod']); } // Move advagg_mod_css_post_alter to the bottom. if ($hook === 'css_post_alter' && array_key_exists('advagg_mod', $implementations)) { $item = $implementations['advagg_mod']; unset($implementations['advagg_mod']); $implementations['advagg_mod'] = $item; } // Move advagg_mod_js_post_alter to the bottom. if ($hook === 'js_post_alter' && array_key_exists('advagg_mod', $implementations)) { $item = $implementations['advagg_mod']; unset($implementations['advagg_mod']); $implementations['advagg_mod'] = $item; } } /** * Implements hook_library_alter(). */ function advagg_mod_library_alter(&$javascript, $module) { if (!advagg_enabled()) { return; } if (!module_exists('jquery_update')) { return; } if (!advagg_mod_inline_page_js()) { return; } // Set the CDN to none for this page as everything is going to inlined. $GLOBALS['conf']['jquery_update_jquery_cdn'] = 'none'; $GLOBALS['conf']['jquery_update_jquery_migrate_cdn'] = 'none'; } /** * Implements hook_init(). */ function advagg_mod_init() { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Adjust devel_shutdown callback. if (variable_get('advagg_enabled', ADVAGG_ENABLED) && (variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER) || variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC) )) { $callbacks = &drupal_register_shutdown_function(); foreach ($callbacks as $key => $values) { if ($values['callback'] === 'devel_shutdown') { $callbacks[$key]['callback'] = 'advagg_mod_devel_shutdown'; break; } } reset($callbacks); } // Return if unified_multisite_dir is not set. $dir = rtrim(variable_get('advagg_mod_unified_multisite_dir', ''), '/'); if (!empty($dir) && file_exists($dir) && is_dir($dir)) { $counter_filename = $dir . '/' . ADVAGG_SPACE . 'advagg_global_counter'; $local_counter = advagg_get_global_counter(); if (!file_exists($counter_filename)) { module_load_include('inc', 'advagg', 'advagg.missing'); advagg_save_data($counter_filename, $local_counter); } else { $shared_counter = (int) advagg_file_get_contents($counter_filename); if ($shared_counter == $local_counter) { // Counters are the same, return. } elseif ($shared_counter < $local_counter) { // Local counter is higher, update saved file and return. module_load_include('inc', 'advagg', 'advagg.missing'); advagg_save_data($counter_filename, $local_counter, TRUE); } elseif ($shared_counter > $local_counter) { // Shared counter is higher, update local copy and return. variable_set('advagg_global_counter', $shared_counter); } } } // Disable js in footer on imce page. // Disable js defer on imce page. // https://www.drupal.org/node/2817523 if (module_exists('imce')) { $args = arg(); if ($args[0] === 'imce' && empty($args[1])) { if (variable_get('advagg_mod_js_footer', ADVAGG_MOD_JS_FOOTER)) { $GLOBALS['conf']['advagg_mod_js_footer'] = 0; } if (variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER)) { $GLOBALS['conf']['advagg_mod_js_defer'] = 0; } } } } /** * Implements hook_menu(). */ function advagg_mod_menu() { $file_path = drupal_get_path('module', 'advagg_mod'); $config_path = advagg_admin_config_root_path(); $items[$config_path . '/advagg/mod'] = array( 'title' => 'Modifications', 'description' => 'Turn on or off various mods for CSS/JS.', 'page callback' => 'drupal_get_form', 'page arguments' => array('advagg_mod_admin_settings_form'), 'type' => MENU_LOCAL_TASK, 'access arguments' => array('administer site configuration'), 'file path' => $file_path, 'file' => 'advagg_mod.admin.inc', 'weight' => 10, ); return $items; } /** * Implements hook_element_info_alter(). */ function advagg_mod_element_info_alter(&$type) { if (!isset($type['styles']['#pre_render'])) { $type['styles']['#pre_render'] = array(); } $key_drupal = array_search('drupal_pre_render_styles', $type['styles']['#pre_render']); $key_advagg = array_search('advagg_pre_render_styles', $type['styles']['#pre_render']); if ($key_drupal !== FALSE) { $type['styles']['#pre_render'] = advagg_insert_into_array_at_location($type['styles']['#pre_render'], array('_advagg_mod_pre_render_styles'), $key_drupal); } elseif ($key_advagg !== FALSE) { $type['styles']['#pre_render'] = advagg_insert_into_array_at_location($type['styles']['#pre_render'], array('_advagg_mod_pre_render_styles'), $key_advagg); } else { $type['styles']['#pre_render'][] = '_advagg_mod_pre_render_styles'; } if (!isset($type['scripts']['#pre_render'])) { $type['scripts']['#pre_render'] = array(); } $key_drupal = array_search('drupal_pre_render_scripts', $type['scripts']['#pre_render']); $key_advagg = array_search('advagg_pre_render_scripts', $type['scripts']['#pre_render']); $key_omega = array_search('omega_pre_render_scripts', $type['scripts']['#pre_render']); $key_aurora = array_search('aurora_pre_render_scripts', $type['scripts']['#pre_render']); if ($key_drupal !== FALSE) { $type['scripts']['#pre_render'] = advagg_insert_into_array_at_location($type['scripts']['#pre_render'], array('_advagg_mod_pre_render_scripts'), $key_drupal); } elseif ($key_advagg !== FALSE) { $type['scripts']['#pre_render'] = advagg_insert_into_array_at_location($type['scripts']['#pre_render'], array('_advagg_mod_pre_render_scripts'), $key_advagg); } elseif ($key_omega !== FALSE) { $type['scripts']['#pre_render'] = advagg_insert_into_array_at_location($type['scripts']['#pre_render'], array('_advagg_mod_pre_render_scripts'), $key_omega); } elseif ($key_aurora !== FALSE) { $type['scripts']['#pre_render'] = advagg_insert_into_array_at_location($type['scripts']['#pre_render'], array('_advagg_mod_pre_render_scripts'), $key_aurora); } else { $type['scripts']['#pre_render'][] = '_advagg_mod_pre_render_scripts'; } } /** * Implements hook_css_alter(). */ function advagg_mod_css_alter(&$css) { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Force all CSS to be preprocessed. if (variable_get('advagg_mod_css_preprocess', ADVAGG_MOD_CSS_PREPROCESS)) { foreach ($css as &$values) { if (!empty($values['preprocess_lock'])) { continue; } $values['preprocess'] = TRUE; } unset($values); } } /** * Implements hook_css_post_alter(). */ function advagg_mod_css_post_alter(&$css) { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Change sort order so aggregates do not get split up. if (variable_get('advagg_mod_css_adjust_sort_external', ADVAGG_MOD_CSS_ADJUST_SORT_EXTERNAL) || variable_get('advagg_mod_css_adjust_sort_inline', ADVAGG_MOD_CSS_ADJUST_SORT_INLINE) || variable_get('advagg_mod_css_adjust_sort_browsers', ADVAGG_MOD_CSS_ADJUST_SORT_BROWSERS) ) { advagg_mod_sort_css_js($css, 'css'); } } /** * Implements hook_html_head_alter(). */ function advagg_mod_html_head_alter(&$head_elements) { if (!module_exists('advagg') || !advagg_enabled()) { return; } foreach ($head_elements as $key => $element) { // CSS. if (variable_get('advagg_mod_css_head_extract', ADVAGG_MOD_CSS_HEAD_EXTRACT) && !empty($element['#tag']) && $element['#tag'] === 'link' && !empty($element['#attributes']['type']) && $element['#attributes']['type'] === 'text/css' && !empty($element['#attributes']['href']) ) { $type = 'file'; if (strpos($element['#attributes']['href'], 'http://') === 0 || strpos($element['#attributes']['href'], 'https://') === 0 || strpos($element['#attributes']['href'], '//') === 0 ) { $type = 'external'; } drupal_add_css($element['#attributes']['href'], array( 'type' => $type, 'group' => CSS_SYSTEM, 'every_page' => TRUE, 'weight' => -50000, )); unset($head_elements[$key]); } // JS. if (variable_get('advagg_mod_js_head_extract', ADVAGG_MOD_JS_HEAD_EXTRACT) && !empty($element['#tag']) && $element['#tag'] === 'script' && !empty($element['#attributes']['type']) && $element['#attributes']['type'] === 'text/javascript' && !empty($element['#attributes']['src']) ) { $type = 'file'; if (strpos($element['#attributes']['src'], 'http://') === 0 || strpos($element['#attributes']['src'], 'https://') === 0 || strpos($element['#attributes']['src'], '//') === 0 ) { $type = 'external'; } drupal_add_js($element['#attributes']['src'], array( 'type' => $type, 'scope' => 'header', 'group' => JS_LIBRARY, 'every_page' => TRUE, 'weight' => -50000, )); unset($head_elements[$key]); } } } /** * Implements hook_theme_registry_alter(). * * Insert advagg_mod_process_move_js before _advagg_process_html. */ function advagg_mod_theme_registry_alter(&$theme_registry) { if (!module_exists('advagg') || !advagg_enabled()) { return; } if (!isset($theme_registry['html'])) { return; } // Find template_process_html/_advagg_process_html. $index = array_search('_advagg_process_html', $theme_registry['html']['process functions']); if ($index === FALSE) { $index = array_search('template_process_html', $theme_registry['html']['process functions']); if ($index === FALSE) { return; } } // Insert advagg_mod_process_move_js before _advagg_process_html. array_splice($theme_registry['html']['process functions'], $index, 0, 'advagg_mod_process_move_js'); } /** * Implements hook_process(). * * Used to wrap inline JS in a function in order to prevent js errors when JS is * moved to the footer. */ function advagg_mod_process_move_js(array &$variables) { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Return if settings are disabled. if (!variable_get('advagg_mod_js_footer_inline_alter', ADVAGG_MOD_JS_FOOTER_INLINE_ALTER) && !variable_get('advagg_mod_js_inline_resource_hints', ADVAGG_MOD_JS_INLINE_RESOURCE_HINTS) ) { return; } // Search all the children for script tags. foreach (element_children($variables) as $child) { // Skip if empty. if (empty($variables[$child])) { continue; } // Handle strings. if (is_string($variables[$child]) && stripos($variables[$child], '') === 0) { $inline_css = trim(substr($inline_css, 7)); } $len = strlen($inline_css); if (stripos($inline_css, '') === $len - 8) { $inline_css = trim(substr($inline_css, 0, $len - 8)); } // Extract url() references to add to the preloaded links. $matches = array(); // Match url ( "' ... '" ). $pattern = '/url\s*\(\s*[\'"]?(.+?)[\'"]?\s*\)/i'; preg_match_all($pattern, $inline_css, $matches); if (!empty($matches[1])) { foreach ($matches[1] as $key => $url) { $parsed = parse_url($url); // Remove data URIs. if (!empty($parsed['scheme']) && $parsed['scheme'] === 'data') { unset($matches[1][$key]); continue; } // Remote paths without a period. if (empty($parsed['path']) || strpos($parsed['path'], '.') === FALSE) { unset($matches[1][$key]); continue; } if (isset($parsed['host'])) { $domains_from_inline_css[] = $url; } } $preload_from_inline_css = $matches[1]; } // Add critical css. drupal_add_css($inline_css, array( 'type' => 'inline', 'group' => CSS_SYSTEM - 1, 'weight' => -50000, 'movable' => FALSE, 'critical-css' => TRUE, )); // Add critical css js loader. advagg_mod_add_loadcss_js_lib(); } // Add in domain prefetch. $domains = array(); if (!empty($inline_strings[1])) { $domains = preg_split("/\\r\\n|\\r|\\n/", $inline_strings[1]); } if (empty($domains) && !empty($filenames[1]) && is_readable($filenames[1]) ) { $domains = file($filenames[1], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); } $domains = array_merge($domains, $domains_from_inline_css); // Remove duplicates and empty sets. $domains = array_filter(array_unique($domains)); if (!empty($domains)) { foreach ($domains as $domain) { advagg_add_dns_prefetch(trim($domain)); } } // Add in files to preload. $preload = array(); if (!empty($inline_strings[2])) { $preload = preg_split("/\\r\\n|\\r|\\n/", $inline_strings[2]); } if (empty($preload) && !empty($filenames[2]) && is_readable($filenames[2]) ) { $preload = file($filenames[2], FILE_IGNORE_NEW_LINES); } $preload = array_merge($preload, $preload_from_inline_css); // Remove duplicates and empty sets. $preload = array_filter(array_unique($preload)); if (!empty($preload)) { $preload_array = array(); $counter = 0; foreach ($preload as $value) { if (empty($value)) { $counter++; continue; } if (stripos($value, 'as: ') === 0) { $preload_array[$counter]['as'] = trim(substr($value, 4)); } elseif (stripos($value, 'type: ') === 0) { $preload_array[$counter]['type'] = trim(substr($value, 6)); } elseif (stripos($value, 'media: ') === 0) { $preload_array[$counter]['media'] = trim(substr($value, 7)); } elseif (stripos($value, 'crossorigin: ') === 0) { $preload_array[$counter]['crossorigin'] = trim(substr($value, 13)); } elseif (stripos($value, 'url: ') === 0) { if (!empty($preload_array[$counter]['url'])) { $counter++; } $preload_array[$counter]['url'] = trim(substr($value, 4)); } else { if (!empty($preload_array[$counter]['url'])) { $counter++; } $preload_array[$counter]['url'] = trim($value); } } foreach ($preload_array as $values) { // Skip if url is not set. if (empty($values['url'])) { continue; } $url = $values['url']; $media = ''; if (!empty($values['media'])) { $media = $values['media']; } $as = ''; if (!empty($values['as'])) { $as = $values['as']; } $type = ''; if (!empty($values['type'])) { $type = $values['type']; } $crossorigin = NULL; if (!empty($values['crossorigin'])) { $crossorigin = $values['crossorigin']; } advagg_add_preload_link($url, $media, $as, $type, $crossorigin); } } } /** * @} End of "addtogroup hooks". */ /** * @addtogroup advagg_hooks * @{ */ /** * Implements hook_advagg_modify_js_pre_render_alter(). */ function advagg_mod_advagg_modify_js_pre_render_alter(&$children, &$elements) { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Do not use defer/async shim if JS is inlined. if (advagg_mod_inline_page() || advagg_mod_inline_page_js()) { return; } if (variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER) || variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC) ) { // Capture all onload code. $onload_code = array(); foreach ($children as $values) { if (isset($values['#attributes']['onload'])) { $onload_code[$values['#attributes']['src']] = $values['#attributes']['onload']; } } $jquery_rev = strrev('/jquery.js'); $jquery_min_rev = strrev('/jquery.min.js'); $ie_fixes = array(); foreach ($elements['#groups'] as $group) { if ($group['type'] !== 'file' || empty($group['defer']) || empty($group['items']['files']) ) { continue; } $found = FALSE; foreach ($group['items']['files'] as $name => &$values) { // Special handling for jQuery. if (stripos(strrev($name), $jquery_rev) === 0 || stripos(strrev($name), $jquery_min_rev) === 0 ) { $found = TRUE; } } if ($found) { $ie_fixes[] = basename($group['data']); } } foreach ($children as $key => &$values) { if (!empty($values['#attributes']['src']) && isset($values['#attributes']['defer']) && empty($values['#browsers']) ) { $ie_key = array_search(basename($values['#attributes']['src']), $ie_fixes); if ($ie_key !== FALSE) { unset($ie_fixes[$ie_key]); // Not IE supports defer. $values['#browsers'] = array( 'IE' => FALSE, '!IE' => TRUE, ); // IE10+ supports defer. $copy = $values; $copy['#browsers'] = array( 'IE' => 'gt IE 9', '!IE' => FALSE, ); $copy['#attributes']['src'] .= '#ie10+'; array_splice($children, $key, 0, array($copy)); // IE9- does not support defer. $copy = $values; $copy['#browsers'] = array( 'IE' => 'lte IE 9', '!IE' => FALSE, ); unset($copy['defer']); unset($copy['#attributes']['defer']); $copy['#attributes']['src'] .= '#ie9-'; array_splice($children, $key, 0, array($copy)); } } } // Count the number of holdReady's there are. $holdready_count = array(); foreach ($children as $key => &$values) { if (!empty($values['#attributes']['onload']) && (stripos($values['#attributes']['onload'], 'jQuery.holdReady(true)') !== FALSE || stripos($values['#attributes']['onload'], 'jQuery.holdReady(!0)') !== FALSE ) ) { // Normalize the src attribute. $src = $values['#attributes']['src']; $pos = strpos($values['#attributes']['src'], '#'); if ($pos !== FALSE) { $src = substr($values['#attributes']['src'], 0, $pos); } $holdready_count[$src] = TRUE; break; } } foreach ($children as $key => &$values) { // Core's Drupal.settings. Put inside wrapper if there is an onload call // for init_drupal_core_settings. Have to do this here because the // settings needed to be rendered. if (!empty($values['#value']) && strpos($values['#value'], 'jQuery.extend(Drupal.settings') !== FALSE) { $found = FALSE; foreach ($onload_code as $src => $code) { if (strpos($code, 'init_drupal_core_settings(') !== FALSE) { $found = TRUE; unset($onload_code[$src]); break; } } if ($found) { $holdready_string = ''; if (!empty($holdready_count)) { $holdready_string = "\nif(jQuery.isFunction(jQuery.holdReady)){jQuery.holdReady(false);}"; } $values['#value'] = "function init_drupal_core_settings() {{$values['#value']} {$holdready_string}} if(window.jQuery && window.Drupal){init_drupal_core_settings();}"; } } } unset($values); } if (variable_get('advagg_mod_js_async_shim', ADVAGG_MOD_JS_ASYNC_SHIM)) { foreach ($children as &$values) { if (isset($values['#attributes']) && isset($values['#attributes']['async']) && $values['#attributes']['async'] === 'async' && !empty($values['#attributes']['src'])) { $source = $values['#attributes']['src']; if (strpos($source, 'http://') !== 0 && strpos($source, 'https://') !== 0 && strpos($source, '//') !== 0 ) { $source = url($values['#attributes']['src']); } $values['#value'] = "(function() { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = '$source'; var d = document.getElementsByTagName('script')[0]; d.parentNode.insertBefore(s, d); })();"; unset($values['#attributes']['async']); unset($values['#attributes']['src']); } } unset($values); } } /** * Implements hook_advagg_modify_css_pre_render_alter(). */ function advagg_mod_advagg_modify_css_pre_render_alter(&$children, &$elements) { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Return early if this setting is disabled. list(, , , , , , , , , , $css_defer) = advagg_mod_get_lists(array(), $elements['#items']); if (empty($css_defer)) { return; } $critical_css = FALSE; // Only check for the critical-css key if configured to do so. if (variable_get('advagg_mod_css_defer_visibility', ADVAGG_MOD_VISIBILITY_LISTED) == ADVAGG_MOD_VISIBILITY_FILE_CONTROLLED) { foreach ($elements['#items'] as $item) { if ($item['type'] === 'inline' && !empty($item['critical-css'])) { $critical_css = TRUE; break; } } // Return early if there's no critical css for the path and deferring is // file controlled. if (!$critical_css) { return; } } if (!$critical_css) { // Return early if we're in a page that is not specified in the settings for // specific pages. if (!advagg_mod_css_defer_page()) { return; } // Return early if we're in the admin theme and this setting is disabled. $css_defer_admin = variable_get('advagg_mod_css_defer_admin', ADVAGG_MOD_CSS_DEFER_ADMIN); if (empty($css_defer_admin) && path_is_admin(current_path())) { return; } } // Modify css. static $added; advagg_mod_add_loadcss_js_lib(array(), $elements['#items']); // Wrap CSS in noscript tags. $defer_skip_first_file = variable_get('advagg_mod_css_defer_skip_first_file', ADVAGG_MOD_CSS_DEFER_SKIP_FIRST_FILE); $options = array( 'type' => 'inline', 'scope' => $css_defer >= 5 ? 'footer' : 'header', 'scope_lock' => TRUE, 'group' => $css_defer == 1 ? JS_LIBRARY - 1 : JS_DEFAULT, 'weight' => $css_defer == 1 ? -50000 : 0, 'movable' => $css_defer == 1 ? FALSE : TRUE, ); // Get the key of the last css file that will use loadcss. $last_key = NULL; foreach ($children as $children_key => $values) { // Do not count inline styles. if ($values['#tag'] === 'style' || empty($values['#attributes']['href']) ) { continue; } // Only use if no browser conditionals. if (isset($values['#browsers']['!IE']) && $values['#browsers']['!IE'] === TRUE && isset($values['#browsers']['IE']) && $values['#browsers']['IE'] === TRUE ) { $last_key = $children_key; } } // If all css uses a browser conditional, then use the last one to release. if ($last_key === NULL) { $last_key = $children_key; } $preload_array = array(); foreach ($children as $children_key => &$values) { // Do not defer inline styles. if ($values['#tag'] === 'style' || empty($values['#attributes']['href'])) { continue; } if (empty($preload_array) && $defer_skip_first_file == 2) { $preload_array[] = array(); continue; } // If this is the last CSS file release the hold on jquery.ready. $onload_extra = ''; if ($last_key === $children_key) { // Run holdready once it is defined. $holdready_script = 'window.advagg_mod_loadcss = function() {if (window.jQuery) {if (jQuery.isFunction(jQuery.holdReady)){jQuery.holdReady(false);}} else {setTimeout(advagg_mod_loadcss, 100);}};'; $onload_extra = "{$holdready_script}setTimeout(advagg_mod_loadcss, 200);"; $GLOBALS['advagg_mod_loadcss_jquery_holdready'] = TRUE; } $id = "advagg_loadcss_$children_key"; if ($css_defer == 4) { $copy = $values; $copy['#attributes']['rel'] = 'preload'; $copy['#attributes']['as'] = 'style'; $copy['#attributes']['onload'] = "{$onload_extra}this.onload=null;this.rel='stylesheet'"; $preload_array[$children_key] = $copy; } else { // Add browsers to the js options. if (isset($values['#browsers'])) { $options['browsers'] = $values['#browsers']; } // Create loadCSS wrapper code. $inline = "loadCSS(\"{$values['#attributes']['href']}\", document.getElementById(\"$id\")"; if ($values['#attributes']['media'] !== 'all') { $inline .= ", \"{$values['#attributes']['media']}\""; } if (!empty($values['#attributes']['crossorigin'])) { $inline .= ", \"{$values['#attributes']['crossorigin']}\""; } $inline .= ')'; // Create onloadCSS wrapper code. if (!empty($values['#attributes']['onloadCSS'])) { $inline = "onloadCSS({$inline}, function() {{$onload_extra}{$values['#attributes']['onloadCSS']}});"; } elseif (!empty($onload_extra)) { $inline = "onloadCSS({$inline}, function() {{$onload_extra}});"; } // Make code work if loader code is below loadcss calls. $matches[2] = $matches[0] = $inline; $inline = advagg_mod_wrap_inline_js($matches, "window.loadCSS", 40); // Add in script tags to load css via js. if (!isset($added[$values['#attributes']['href']])) { drupal_add_js($inline, $options); $added[$values['#attributes']['href']] = TRUE; } } // Wrap current css in noscript tags. $values['#prefix'] = "'; // Reset for next item in loop. if (isset($options['browsers'])) { unset($options['browsers']); } } unset($values); // Add in the element after the noscript element keeping the css order. $new_elements = array(); foreach ($elements as $elements_key => $elements_value) { // Build old array. if (is_numeric($elements_key)) { $new_elements[] = $elements_value; } else { $new_elements[$elements_key] = $elements_value; } // Splice in new data. if (isset($preload_array[$elements_key])) { $new_elements[] = $preload_array[$elements_key]; } } $elements = $new_elements; unset($new_elements); } /** * Implements hook_advagg_hooks_implemented_alter(). */ function advagg_mod_advagg_hooks_implemented_alter(&$hooks, $all) { if ($all) { $hooks += array( 'advagg_mod_get_lists_alter' => array(), ); } } /** * Implements hook_advagg_get_root_files_dir_alter(). */ function advagg_mod_advagg_get_root_files_dir_alter(&$css_paths, &$js_paths) { $dir = rtrim(variable_get('advagg_mod_unified_multisite_dir', ''), '/'); if (empty($dir) || !file_exists($dir) || !is_dir($dir)) { return; } // Change directory. $css_paths[0] = $dir . '/advagg_css'; $js_paths[0] = $dir . '/advagg_js'; file_prepare_directory($css_paths[0], FILE_CREATE_DIRECTORY); file_prepare_directory($js_paths[0], FILE_CREATE_DIRECTORY); // Set the URI of the directory. $css_paths[1] = advagg_get_relative_path($css_paths[0]); $js_paths[1] = advagg_get_relative_path($js_paths[0]); } /** * Implements hook_advagg_current_hooks_hash_array_alter(). */ function advagg_mod_advagg_current_hooks_hash_array_alter(&$aggregate_settings) { // JS Settings. $aggregate_settings['variables']['advagg_mod_js_async_shim'] = variable_get('advagg_mod_js_async_shim', ADVAGG_MOD_JS_ASYNC_SHIM); // Make safe if using the aggressive cache. if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 5) { $aggregate_settings['variables']['advagg_mod_js_preprocess'] = variable_get('advagg_mod_js_preprocess', ADVAGG_MOD_JS_PREPROCESS); $aggregate_settings['variables']['advagg_mod_js_remove_unused'] = variable_get('advagg_mod_js_remove_unused', ADVAGG_MOD_JS_REMOVE_UNUSED); $aggregate_settings['variables']['advagg_mod_js_head_extract'] = variable_get('advagg_mod_js_head_extract', ADVAGG_MOD_JS_HEAD_EXTRACT); $aggregate_settings['variables']['advagg_mod_js_adjust_sort_external'] = variable_get('advagg_mod_js_adjust_sort_external', ADVAGG_MOD_JS_ADJUST_SORT_EXTERNAL); $aggregate_settings['variables']['advagg_mod_js_adjust_sort_inline'] = variable_get('advagg_mod_js_adjust_sort_inline', ADVAGG_MOD_JS_ADJUST_SORT_INLINE); $aggregate_settings['variables']['advagg_mod_js_adjust_sort_browsers'] = variable_get('advagg_mod_js_adjust_sort_browsers', ADVAGG_MOD_JS_ADJUST_SORT_BROWSERS); $aggregate_settings['variables']['advagg_mod_ga_inline_to_file'] = variable_get('advagg_mod_ga_inline_to_file', ADVAGG_MOD_GA_INLINE_TO_FILE); $aggregate_settings['variables']['advagg_mod_js_footer'] = variable_get('advagg_mod_js_footer', ADVAGG_MOD_JS_FOOTER); $aggregate_settings['variables']['advagg_mod_js_defer'] = variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER); $aggregate_settings['variables']['advagg_mod_js_footer_inline_alter'] = variable_get('advagg_mod_js_footer_inline_alter', ADVAGG_MOD_JS_FOOTER_INLINE_ALTER); $aggregate_settings['variables']['advagg_mod_js_async'] = variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC); } // CSS Settings. $aggregate_settings['variables']['advagg_mod_css_translate'] = variable_get('advagg_mod_css_translate', ADVAGG_MOD_CSS_TRANSLATE); if (variable_get('advagg_mod_css_translate', ADVAGG_MOD_CSS_TRANSLATE)) { $aggregate_settings['variables']['advagg_mod_css_translate_lang'] = isset($GLOBALS['language']->language) ? $GLOBALS['language']->language : 'en'; } $aggregate_settings['variables']['advagg_mod_css_adjust_sort_external'] = variable_get('advagg_mod_css_adjust_sort_external', ADVAGG_MOD_CSS_ADJUST_SORT_EXTERNAL); $aggregate_settings['variables']['advagg_mod_css_adjust_sort_inline'] = variable_get('advagg_mod_css_adjust_sort_inline', ADVAGG_MOD_CSS_ADJUST_SORT_INLINE); // Make safe if using the aggressive cache. if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 5) { $aggregate_settings['variables']['advagg_mod_css_preprocess'] = variable_get('advagg_mod_css_preprocess', ADVAGG_MOD_CSS_PREPROCESS); $aggregate_settings['variables']['advagg_mod_css_head_extract'] = variable_get('advagg_mod_css_head_extract', ADVAGG_MOD_CSS_HEAD_EXTRACT); $aggregate_settings['variables']['advagg_mod_css_adjust_sort_browsers'] = variable_get('advagg_mod_css_adjust_sort_browsers', ADVAGG_MOD_CSS_ADJUST_SORT_BROWSERS); $aggregate_settings['variables']['advagg_mod_css_defer'] = variable_get('advagg_mod_css_defer', ADVAGG_MOD_CSS_DEFER); $aggregate_settings['variables']['advagg_mod_css_defer_js_code'] = variable_get('advagg_mod_css_defer_js_code', ADVAGG_MOD_CSS_DEFER_JS_CODE); $aggregate_settings['variables']['advagg_mod_inline_visibility'] = variable_get('advagg_mod_inline_visibility', ADVAGG_MOD_VISIBILITY_LISTED); $aggregate_settings['variables']['advagg_mod_inline_pages'] = variable_get('advagg_mod_inline_pages', ''); $aggregate_settings['variables']['advagg_mod_css_defer_visibility'] = variable_get('advagg_mod_css_defer_visibility', ADVAGG_MOD_VISIBILITY_LISTED); $aggregate_settings['variables']['advagg_mod_css_defer_pages'] = variable_get('advagg_mod_css_defer_pages', ''); // Run functions for page visibility. $aggregate_settings['variables']['advagg_mod_inline_page'] = advagg_mod_inline_page(); $aggregate_settings['variables']['advagg_mod_inline_page_js'] = advagg_mod_inline_page_js(); $aggregate_settings['variables']['advagg_mod_inline_page_css'] = advagg_mod_inline_page_css(); $aggregate_settings['variables']['advagg_mod_css_defer_page'] = advagg_mod_css_defer_page(); } } /** * @} End of "addtogroup advagg_hooks". */ /** * @addtogroup 3rd_party_hooks * @{ */ /** * Implements hook_libraries_info(). */ function advagg_mod_libraries_info() { $libraries['loadCSS'] = array( 'name' => 'loadCSS', 'vendor url' => 'https://github.com/filamentgroup/loadCSS', 'download url' => 'https://github.com/filamentgroup/loadCSS/archive/master.zip', 'version arguments' => array( 'file' => 'package.json', 'pattern' => '/"version":\\s+"([0-9\.]+)"/', 'lines' => 10, ), // Called before the library is loaded. 'callbacks' => array( 'pre-load' => array( 'advagg_mod_libraries_preload_callback', ), ), 'local version' => '2.0.1', 'remote' => array( 'callback' => 'advagg_get_github_version_json', 'url' => 'https://rawgit.com/filamentgroup/loadCSS/master/package.json', ), 'files' => array( 'js' => array( 'src/loadCSS.js' => array( 'type' => 'file', 'async' => TRUE, ), ), ), ); // Get the latest tagged version for external file loading. $version = advagg_get_remote_libraries_version('loadCSS', $libraries['loadCSS']); // Get the advagg_mod path for local loading. $advagg_mod_path = drupal_get_path('module', 'advagg_mod'); $libraries['loadCSS'] += array( 'variants' => array( 'normal-preload' => array( 'files' => array( 'js' => array( 'src/cssrelpreload.js' => array( 'type' => 'file', 'async' => TRUE, ), ), ), ), 'normal-onload' => array( 'files' => array( 'js' => array( 'src/loadCSS.js' => array( 'type' => 'file', 'async' => TRUE, ), 'src/onloadCSS.js' => array( 'type' => 'file', 'async' => TRUE, ), ), ), ), 'minified' => array( 'files' => array( 'js' => array( 'src/loadCSS.min.js' => array( 'type' => 'file', 'async' => TRUE, ), ), ), ), 'minified-preload' => array( 'files' => array( 'js' => array( 'src/cssrelpreload.min.js' => array( 'type' => 'file', 'async' => TRUE, ), ), ), ), 'minified-onload' => array( 'files' => array( 'js' => array( 'src/loadCSS.min.js' => array( 'type' => 'file', 'async' => TRUE, ), 'src/onloadCSS.min.js' => array( 'type' => 'file', 'async' => TRUE, ), ), ), ), 'external' => array( 'files' => array( 'js' => array( "//cdn.rawgit.com/filamentgroup/loadCSS/v{$version}/src/loadCSS.js" => array( 'type' => 'external', 'data' => "//cdn.rawgit.com/filamentgroup/loadCSS/v{$version}/src/loadCSS.js", 'async' => TRUE, ), ), ), ), 'external-preload' => array( 'files' => array( 'js' => array( "//cdn.rawgit.com/filamentgroup/loadCSS/v{$version}/src/cssrelpreload.js" => array( 'type' => 'external', 'data' => "//cdn.rawgit.com/filamentgroup/loadCSS/v{$version}/src/cssrelpreload.js", 'async' => TRUE, ), ), ), ), 'external-onload' => array( 'files' => array( 'js' => array( "//cdn.rawgit.com/filamentgroup/loadCSS/v{$version}/src/loadCSS.js" => array( 'type' => 'external', 'data' => "//cdn.rawgit.com/filamentgroup/loadCSS/v{$version}/src/loadCSS.js", 'async' => TRUE, ), "//cdn.rawgit.com/filamentgroup/loadCSS/v{$version}/src/onloadCSS.js" => array( 'type' => 'external', 'data' => "//cdn.rawgit.com/filamentgroup/loadCSS/v{$version}/src/onloadCSS.js", 'async' => TRUE, ), ), ), ), 'local' => array( 'version' => '1.3.1', 'files' => array( 'js' => array( "{$advagg_mod_path}/loadCSS.js" => array( 'type' => 'file', 'data' => "{$advagg_mod_path}/loadCSS.js", 'async' => TRUE, ), ), ), ), 'local-preload' => array( 'version' => '1.3.1', 'files' => array( 'js' => array( "{$advagg_mod_path}/cssrelpreload.js" => array( 'type' => 'file', 'data' => "{$advagg_mod_path}/cssrelpreload.js", 'async' => TRUE, ), ), ), ), 'local-onload' => array( 'version' => '1.3.1', 'files' => array( 'js' => array( "{$advagg_mod_path}/loadCSS.js" => array( 'type' => 'file', 'data' => "{$advagg_mod_path}/loadCSS.js", 'async' => TRUE, ), "{$advagg_mod_path}/onloadCSS.js" => array( 'type' => 'file', 'data' => "{$advagg_mod_path}/onloadCSS.js", 'async' => TRUE, ), ), ), ), 'local-minified' => array( 'version' => '1.3.1', 'files' => array( 'js' => array( "{$advagg_mod_path}/loadCSS.min.js" => array( 'type' => 'file', 'data' => "{$advagg_mod_path}/loadCSS.min.js", 'async' => TRUE, ), ), ), ), 'local-minified-preload' => array( 'version' => '1.3.1', 'files' => array( 'js' => array( "{$advagg_mod_path}/cssrelpreload.min.js" => array( 'type' => 'file', 'data' => "{$advagg_mod_path}/cssrelpreload.min.js", 'async' => TRUE, ), ), ), ), 'local-minified-onload' => array( 'version' => '1.3.1', 'files' => array( 'js' => array( "{$advagg_mod_path}/loadCSS.min.js" => array( 'type' => 'file', 'data' => "{$advagg_mod_path}/loadCSS.min.js", 'async' => TRUE, ), "{$advagg_mod_path}/onloadCSS.min.js" => array( 'type' => 'file', 'data' => "{$advagg_mod_path}/onloadCSS.min.js", 'async' => TRUE, ), ), ), ), ), ); // Add inline data. $loadcss_loc = "{$advagg_mod_path}/loadCSS.min.js"; $cssrelpreload_loc = "{$advagg_mod_path}/cssrelpreload.min.js"; $onloadcss_loc = "{$advagg_mod_path}/onloadCSS.min.js"; // Use given library if there. $libraries_paths = array(); if (is_callable('libraries_get_libraries')) { $libraries_paths = libraries_get_libraries(); } if (isset($libraries_paths['loadCSS'])) { // Get location of loadCSS. if (is_readable($libraries_paths['loadCSS'] . '/src/loadCSS.min.js')) { $loadcss_loc = $libraries_paths['loadCSS'] . '/src/loadCSS.min.js'; $libraries['loadCSS']['variants']['minified']['#files_exists'] = TRUE; } elseif (is_readable($libraries_paths['loadCSS'] . '/src/loadCSS.js')) { $loadcss_loc = $libraries_paths['loadCSS'] . '/src/loadCSS.js'; } // Get location of cssrelpreload. if (is_readable($libraries_paths['loadCSS'] . '/src/cssrelpreload.min.js')) { $cssrelpreload_loc = $libraries_paths['loadCSS'] . '/src/cssrelpreload.min.js'; if ($libraries['loadCSS']['variants']['minified']['#files_exists']) { $libraries['loadCSS']['variants']['minified-preload']['#files_exists'] = TRUE; } } elseif (is_readable($libraries_paths['loadCSS'] . '/src/cssrelpreload.js')) { $cssrelpreload_loc = $libraries_paths['loadCSS'] . '/src/cssrelpreload.js'; } // Get location of onloadCSS. if (is_readable($libraries_paths['loadCSS'] . '/src/onloadCSS.min.js')) { $onloadcss_loc = $libraries_paths['loadCSS'] . '/src/onloadCSS.min.js'; if ($libraries['loadCSS']['variants']['minified']['#files_exists']) { $libraries['loadCSS']['variants']['minified-preload']['#files_exists'] = TRUE; } } elseif (is_readable($libraries_paths['loadCSS'] . '/src/onloadCSS.js')) { $onloadcss_loc = $libraries_paths['loadCSS'] . '/src/onloadCSS.js'; } } // Add inline scripts. $libraries['loadCSS']['variants'] += array( 'inline' => array( 'files' => array( 'js' => array( 'loadCSS_inline' => array( 'type' => 'inline', 'data' => (string) @advagg_file_get_contents($loadcss_loc), 'no_defer' => TRUE, ), ), ), ), 'inline-preload' => array( 'files' => array( 'js' => array( 'cssrelpreload_inline' => array( 'type' => 'inline', 'data' => (string) @advagg_file_get_contents($cssrelpreload_loc), 'no_defer' => TRUE, ), ), ), ), 'inline-onload' => array( 'files' => array( 'js' => array( 'loadCSS_inline' => array( 'type' => 'inline', 'data' => (string) @advagg_file_get_contents($loadcss_loc), 'no_defer' => TRUE, ), 'onloadCSS_inline' => array( 'type' => 'inline', 'data' => (string) @advagg_file_get_contents($onloadcss_loc), 'no_defer' => TRUE, ), ), ), ), ); if (!is_callable('libraries_detect')) { // Set defaults. $default_options = advagg_mod_loadcss_js_defaults(); foreach ($libraries['loadCSS']['files']['js'] as &$value) { $value += $default_options; } foreach ($libraries['loadCSS']['variants'] as &$values) { foreach ($values['files']['js'] as &$value) { $value += $default_options; } } } return $libraries; } /** * Implements hook_magic(). */ function advagg_mod_magic(array $magic_settings, $theme) { // $magic_settings is READ ONLY. $settings = array(); // If possible disable access and set default to false. if (!isset($magic_settings['css']['magic_embedded_mqs']['#access'])) { $settings['css']['magic_embedded_mqs']['#access'] = FALSE; } if (!isset($magic_settings['css']['magic_embedded_mqs']['#default_value'])) { $settings['css']['magic_embedded_mqs']['#default_value'] = FALSE; } if (!isset($magic_settings['js']['magic_footer_js']['#access'])) { $settings['js']['magic_footer_js']['#access'] = FALSE; } if (!isset($magic_settings['js']['magic_footer_js']['#default_value'])) { $settings['js']['magic_footer_js']['#default_value'] = FALSE; } if (!isset($magic_settings['js']['magic_library_head']['#access'])) { $settings['js']['magic_library_head']['#access'] = FALSE; } if (!isset($magic_settings['js']['magic_library_head']['#default_value'])) { $settings['js']['magic_library_head']['#default_value'] = FALSE; } if (!isset($magic_settings['js']['magic_experimental_js']['#access'])) { $settings['js']['magic_experimental_js']['#access'] = FALSE; } if (!isset($magic_settings['js']['magic_experimental_js']['#default_value'])) { $settings['js']['magic_experimental_js']['#default_value'] = FALSE; } // Add in our own validate function so we can preprocess variables before // they are saved. $settings['#validate'] = array('advagg_mod_magic_form_validate'); // Must not contain anything from the $magic_settings array. return $settings; } /** * @} End of "addtogroup 3rd_party_hooks". */ /** * Get the default loadcss options for the js used. * * @return array * Key => value options array for drupal_add_js(). */ function advagg_mod_loadcss_js_defaults() { list(, , , , , , , , , , $css_defer) = advagg_mod_get_lists(); $default_options = array( 'scope' => $css_defer >= 7 ? 'footer' : 'header', 'scope_lock' => TRUE, 'every_page' => TRUE, 'group' => $css_defer == 1 ? JS_LIBRARY - 1 : JS_LIBRARY, 'weight' => $css_defer == 1 ? -50000 : 0, 'movable' => $css_defer == 1 ? FALSE : TRUE, ); return $default_options; } /** * Callback right before loadcss lib is loaded; set defaults. * * @param array $version_variant * Array of the library that is about to be loaded. */ function advagg_mod_libraries_preload_callback(array &$version_variant) { // Get default options. $default_options = advagg_mod_loadcss_js_defaults(); // Set defaults for the given configuration. foreach ($version_variant['files']['js'] as &$value) { $value += $default_options; } } /** * Adds the loadcss js library if needed. * * @param array $js * The JS array. * @param array $css * The CSS array. */ function advagg_mod_add_loadcss_js_lib(array $js = array(), array $css = array()) { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Return early if this setting is disabled. list(, , , , , , , , , , $css_defer) = advagg_mod_get_lists($js, $css); if (empty($css_defer)) { return; } static $added; $library = advagg_get_library('loadCSS', 'advagg_mod'); $options_defaults = advagg_mod_loadcss_js_defaults(); $preload = '-onload'; if ($css_defer == 4) { $preload = '-preload'; } $css_defer_js_code = variable_get('advagg_mod_css_defer_js_code', ADVAGG_MOD_CSS_DEFER_JS_CODE); // Inline load. if ($css_defer_js_code == 0) { if (!empty($library['installed'])) { libraries_load('loadCSS', "inline{$preload}"); } else { foreach ($library['variants']["inline{$preload}"]['files']['js'] as $data => $options) { if (!isset($added[$data])) { if (!empty($options['data'])) { drupal_add_js($options['data'], $options + $options_defaults); $added[$data] = TRUE; } else { // Fallback to load as a file if no inline js. $css_defer_js_code = 2; } } } } } // Load as a file. if ($css_defer_js_code == 2) { if ($library['installed']) { if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 0 && $library['variants']['minified']['#files_exists']) { libraries_load('loadCSS', "minified{$preload}"); } else { if ($preload) { libraries_load('loadCSS'); } else { libraries_load('loadCSS', "normal{$preload}"); } } } else { foreach ($library['variants']["local{$preload}"]['files']['js'] as $data => $options) { if (!isset($added[$data])) { if (!empty($options['data'])) { drupal_add_js($options['data'], $options + $options_defaults); $added[$data] = TRUE; } else { // Fallback to external load. $css_defer_js_code = 4; } } } } } // Load external library. if ($css_defer_js_code == 4) { foreach ($library['variants']["external{$preload}"]['files']['js'] as $data => $options) { if (!isset($added[$data])) { drupal_add_js($options['data'], $options + $options_defaults); $added[$data] = TRUE; } } } } /** * Try to find the critical css file. * * @return array * The css and dns files to use. */ function advagg_mod_find_critical_css_file() { $filename = FALSE; // Normalize request uri. $base_path = base_path(); $request_uri = request_uri(); $pos = strpos($request_uri, $base_path); if ($pos === 0) { $request_uri = substr($request_uri, strlen($base_path)); } $dirs = array( 0 => drupal_get_path('theme', $GLOBALS['theme']) . '/', 1 => 'critical-css/', // Use authenticated|anonymous or all. 2 => user_is_logged_in() ? 'authenticated/' : 'anonymous/', 3 => 'all/', // Use urls or object_type. 4 => 'urls/', 5 => 'type/', // Different variations of the current URL. 6 => current_path(), 7 => advagg_url_to_filename($request_uri, FALSE), 8 => advagg_url_to_filename(request_path(), FALSE), 9 => $request_uri, 10 => request_path(), ); $front_page = drupal_is_front_page(); if (!$front_page) { $front_page = drupal_get_path_alias() == variable_get('site_frontpage', 'node'); } $object = menu_get_object(); $params = array($dirs, $front_page, $object); $inline_strings = array('', '', ''); // Allow for altering the starting point. // Call hook_advagg_mod_critical_css_file_pre_alter(). drupal_alter('advagg_mod_critical_css_file_pre', $filename, $params, $inline_strings); list($dirs, $front_page, $object) = $params; // Look in themename/critical-css/authenticated|anonymous/urls|object_type. if (!empty($dirs[0]) || !empty($dirs[1])) { if (!$filename && $front_page && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}front.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}front"; } if (!$filename && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}{$dirs[6]}.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}{$dirs[6]}"; } if (!$filename && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}{$dirs[7]}.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}{$dirs[7]}"; } if (!$filename && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}{$dirs[8]}.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[4]}{$dirs[8]}"; } if (!$filename && isset($object->type) && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[5]}{$object->type}.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[2]}{$dirs[5]}{$object->type}"; } // Look in themename/critical-css/all/urls|object_type. if (!$filename && $front_page && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}front.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}front"; } if (!$filename && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}{$dirs[6]}.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}{$dirs[6]}"; } if (!$filename && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}{$dirs[7]}.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}{$dirs[7]}"; } if (!$filename && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}{$dirs[8]}.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[4]}{$dirs[8]}"; } if (!$filename && isset($object->type) && is_readable("{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[5]}{$object->type}.css") ) { $filename = "{$dirs[0]}{$dirs[1]}{$dirs[3]}{$dirs[5]}{$object->type}"; } } // Build filenames array. $filenames = array('', '', ''); if ($filename) { $filenames = array( "$filename.css", "$filename.dns", "$filename.pre", ); } // Allow for altering the ending point. // Call hook_advagg_mod_critical_css_file_post_alter(). drupal_alter('advagg_mod_critical_css_file_post', $filenames, $params, $inline_strings); return array($filenames, $params, $inline_strings); } /** * Form validation handler. Disable certain magic settings before being saved. */ function advagg_mod_magic_form_validate($form, &$form_state) { // Disable magic functionality if it is a duplicate of AdvAgg. $form_state['values']['magic_embedded_mqs'] = 0; $form_state['values']['magic_footer_js'] = 0; $form_state['values']['magic_library_head'] = 0; $form_state['values']['magic_experimental_js'] = 0; } /** * Alter the js array. * * @param array $js * JS array. */ function advagg_mod_js_pre_alter(array &$js) { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Change google analytics inline loader to be inside of an aggregrated file. if (variable_get('advagg_mod_ga_inline_to_file', ADVAGG_MOD_GA_INLINE_TO_FILE)) { advagg_mod_ga_inline_to_file($js); } if (variable_get('advagg_mod_js_inline_resource_hints', ADVAGG_MOD_JS_INLINE_RESOURCE_HINTS)) { advagg_mod_find_inline_domains($js); } } /** * Add dns_prefetch for inline js domains. * * @param array $js * JS array. */ function advagg_mod_find_inline_domains(array &$js) { $parsed = @parse_url($GLOBALS['base_root']); $host = $parsed['host']; foreach ($js as &$values) { if ($values['type'] !== 'inline') { continue; } // Find quoted strings in JS. $matches = array(); $pattern = "/[\"'](.*?)[\"']/"; $matched = preg_match_all($pattern, $values['data'], $matches); if (!$matched) { continue; } // Find domains in the quoted strings and dns_prefetch it. foreach ($matches[1] as $value) { if (strpos($value, '//') !== FALSE) { $parsed = @parse_url($value); if (!empty($parsed['host']) && $host !== $parsed['host']) { $values['dns_prefetch'][] = $value; } } } } } /** * Alter the js array. * * @param array $js * JS array. */ function advagg_mod_js_post_alter(array &$js) { if (!module_exists('advagg') || !advagg_enabled()) { return; } // Only add JS if it's actually needed. if (variable_get('advagg_mod_js_remove_unused', ADVAGG_MOD_JS_REMOVE_UNUSED)) { advagg_mod_remove_js_if_not_used($js); } // Change sort order so aggregates do not get split up. if (variable_get('advagg_mod_js_adjust_sort_external', ADVAGG_MOD_JS_ADJUST_SORT_EXTERNAL) || variable_get('advagg_mod_js_adjust_sort_inline', ADVAGG_MOD_JS_ADJUST_SORT_INLINE) || variable_get('advagg_mod_js_adjust_sort_browsers', ADVAGG_MOD_JS_ADJUST_SORT_BROWSERS) ) { advagg_mod_sort_css_js($js, 'js'); } // Move JS to the footer. advagg_mod_js_move_to_footer($js); // Force all JS to be preprocessed. if (variable_get('advagg_mod_js_preprocess', ADVAGG_MOD_JS_PREPROCESS)) { foreach ($js as &$values) { if (!empty($values['preprocess_lock'])) { continue; } $values['preprocess'] = TRUE; $values['cache'] = TRUE; } unset($values); } // Add the defer or the async tag to JS. $jquery_deferred = advagg_mod_js_async_defer($js); // Inline JS defer. advagg_mod_inline_defer($js, $jquery_deferred); // Move all async JS to the header. if (variable_get('advagg_mod_js_async_in_header', ADVAGG_MOD_JS_ASYNC_IN_HEADER)) { foreach ($js as &$values) { // Skip if not file or external. if ($values['type'] !== 'file' && $values['type'] !== 'external') { continue; } // Skip if not async. if (empty($values['async']) && empty($values['attributes']['async'])) { continue; } // Skip if scope locked. if (!empty($values['scope_lock'])) { continue; } // Move to the header with a group of 1000. $values['scope'] = 'header'; $values['group'] = 1000; } unset($values); } advagg_mod_prefetch_link($js); } /** * Have the browser prefech this domain to open the connection. * * @param array $js * JS array. */ function advagg_mod_prefetch_link(array &$js) { if (!variable_get('advagg_mod_prefetch', ADVAGG_MOD_PREFETCH)) { return; } foreach ($js as &$values) { if (!isset($values['dns_prefetch'])) { continue; } foreach ($values['dns_prefetch'] as &$url) { // Prefetch stats.g.doubleclick.net domain. if (strpos($url, '//stats.g.doubleclick.net') === FALSE) { continue; } if (variable_get('advagg_resource_hints_preconnect', ADVAGG_RESOURCE_HINTS_PRECONNECT)) { $parse = @parse_url($url); $inline_script = 'var preconnect_support = false; try {if (document.createElement("link").relList.supports("preconnect")) {preconnect_support = true;}} catch (e) {} if (!preconnect_support) { var prefetch = document.createElement("link"); prefetch.href = "https://' . $parse['host'] . '/robots.txt"; prefetch.rel="prefetch"; document.getElementsByTagName("head")[0].appendChild(prefetch);}'; $js['advagg_preconnect_support'] = array( 'type' => 'inline', 'group' => JS_LIBRARY - 1, 'weight' => -50000, 'scope_lock' => TRUE, 'movable' => FALSE, 'no_defer' => TRUE, 'data' => $inline_script, ) + drupal_js_defaults($inline_script); } else { $url .= '#prefetch'; } } } } /** * Remove ajaxPageState CSS/JS if misc/ajax.js is not used. * * @param array $scripts * Render array. */ function advagg_mod_js_no_ajaxpagestate(array &$scripts) { if (!module_exists('advagg') || !advagg_enabled()) { return; } if (!variable_get('advagg_mod_js_no_ajaxpagestate', ADVAGG_MOD_JS_NO_AJAXPAGESTATE) || variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) { return; } // Search for the ajax file in the #items array. $ajax_found = FALSE; if (isset($scripts['#items']) && is_array($scripts['#items'])) { foreach ($scripts['#items'] as $key => $values) { if (strpos($key, 'misc/ajax.js') !== FALSE || strpos($key, 'misc/ajax.min.js')) { $ajax_found = TRUE; break; } } } // The ajax.js file was not found and there is a settings array. if (!$ajax_found && isset($scripts['#items']['settings']['data'])) { foreach ($scripts['#items']['settings']['data'] as $delta => $setting) { if (array_key_exists('ajaxPageState', $setting)) { // Remove js files. if (isset($scripts['#items']['settings']['data'][$delta]['ajaxPageState']['js'])) { unset($scripts['#items']['settings']['data'][$delta]['ajaxPageState']['js']); } // Remove css files. if (isset($scripts['#items']['settings']['data'][$delta]['ajaxPageState']['css'])) { unset($scripts['#items']['settings']['data'][$delta]['ajaxPageState']['css']); } // Cleanup. if (empty($scripts['#items']['settings']['data'][$delta]['ajaxPageState'])) { unset($scripts['#items']['settings']['data'][$delta]['ajaxPageState']); if (empty($scripts['#items']['settings']['data'][$delta])) { unset($scripts['#items']['settings']['data'][$delta]); } } } } } } /** * Generate a list of rules and exceptions for js files and inline. * * Controls inline wrapping and defer. Controls no async/defer file list. * Controls files that stay in the header. * * @param array $js * The JS array. * @param array $css * The CSS array. * * @return array * A multidimensional array. */ function advagg_mod_get_lists(array $js = array(), array $css = array()) { $lists = &drupal_static(__FUNCTION__); $js_count = count($js); $css_count = count($css); $key = $js_count . '.' . $css_count; if (!isset($lists[$key])) { // Do not move to footer file list. $header_file_list = array( // Modernizr js. '/modernizr.', // Html5shiv and html5shiv-printshiv. '/html5shiv.', '/html5shiv-printshiv.', // Google Admanager Needs to be in the header. '/google_service.', ); // Do not move to footer inline list. $header_inline_list = array( // Google Analytics should be in the header to verify for Webmaster tools. 'GoogleAnalyticsObject', 'window.google_analytics_uacct', // Google Admanager Needs to be in the header. 'GS_googleAddAdSenseService(', 'GS_googleEnableAllServices(', 'GA_googleAddSlot(', 'GA_googleFetchAds(', ); // Do not defer/async list. $no_async_defer_list = array( // Wistia js. '//fast.wistia.', // Maps. '//dev.virtualearth.net', '//api.maps.yahoo.com', // Google Admanager can't be forced defer/async. '/google_service.', ); // Openlayers. if (module_exists('openlayers')) { // Openlayers fix; external scripts can not be loaded out of order. // Cloudmade. $path = variable_get('openlayers_layers_cloudmade_js', ''); if (valid_url($path, TRUE)) { $no_async_defer_list['openlayers_layers_cloudmade'] = $path; } // Google. $mapdomain = variable_get('openlayers_layers_google_mapdomain', 'maps.google.com'); $no_async_defer_list['openlayers_layers_google'] = $mapdomain . '/maps'; } // Wrap inline js so it does not run until the condition is TRUE. // Inline search string => js condition. $inline_wrapper_list = array(); // Get inline wrap js skip list string and convert it to an array. $inline_js_wrap_skip_list = array_filter(array_map('trim', explode("\n", variable_get('advagg_mod_wrap_inline_js_skip_list', ADVAGG_MOD_WRAP_INLINE_JS_SKIP_LIST)))); $inline_js_wrap_skip_list[] = '.write('; $inline_js_wrap_skip_list[] = '._fbq'; $inline_js_wrap_skip_list[] = '.fbq'; $inline_js_wrap_skip_list[] = 'gtm.start'; $inline_js_wrap_skip_list[] = '_gaq.push(["_'; $inline_js_wrap_skip_list[] = 'ga("'; $inline_js_wrap_skip_list[] = "ga('"; $inline_js_wrap_skip_list[] = 'GoogleAnalyticsObject'; $inline_js_wrap_skip_list[] = 'window.google_analytics_uacct'; $inline_js_wrap_skip_list[] = 'function krumo('; $inline_js_wrap_skip_list[] = '// no advagg'; $inline_js_wrap_skip_list[] = '// noadvagg'; $inline_js_wrap_skip_list[] = '// no-advagg'; $inline_js_wrap_skip_list[] = '//no advagg'; $inline_js_wrap_skip_list[] = '//noadvagg'; $inline_js_wrap_skip_list[] = '//no-advagg'; // Google Admanager can not be wrapped in a callback function. $inline_js_wrap_skip_list[] = 'GS_googleAddAdSenseService('; $inline_js_wrap_skip_list[] = 'GS_googleEnableAllServices('; $inline_js_wrap_skip_list[] = 'GA_googleAddSlot('; $inline_js_wrap_skip_list[] = 'GA_googleFetchAds('; $inline_js_wrap_skip_list[] = 'GA_googleFillSlot('; $inline_js_wrap_skip_list[] = 'adsbygoogle'; $inline_js_wrap_skip_list[] = '_paq.push(["'; if (module_exists('h5p')) { $inline_js_wrap_skip_list[] = 'H5PIntegration'; } // Get inline defer js skip list string and convert it to an array. $inline_js_defer_skip_list = array_filter(array_map('trim', explode("\n", variable_get('advagg_mod_defer_inline_js_skip_list', ADVAGG_MOD_DEFER_INLINE_JS_SKIP_LIST)))); $inline_js_defer_skip_list[] = 'loadCSS('; $inline_js_defer_skip_list[] = '._fbq'; $inline_js_defer_skip_list[] = '.fbq'; $inline_js_defer_skip_list[] = 'gtm.start'; $inline_js_defer_skip_list[] = '_gaq.push(["_'; $inline_js_defer_skip_list[] = 'GoogleAnalyticsObject'; $inline_js_defer_skip_list[] = 'window.google_analytics_uacct'; $inline_js_defer_skip_list[] = '// no advagg'; $inline_js_defer_skip_list[] = '// noadvagg'; $inline_js_defer_skip_list[] = '// no-advagg'; $inline_js_defer_skip_list[] = '//no advagg'; $inline_js_defer_skip_list[] = '//noadvagg'; $inline_js_defer_skip_list[] = '//no-advagg'; // Google Admanager can not be wrapped in a callback function. $inline_js_defer_skip_list[] = 'GS_googleAddAdSenseService('; $inline_js_defer_skip_list[] = 'GS_googleEnableAllServices('; $inline_js_defer_skip_list[] = 'GA_googleAddSlot('; $inline_js_defer_skip_list[] = 'GA_googleFetchAds('; $inline_js_defer_skip_list[] = 'GA_googleFillSlot('; $inline_js_defer_skip_list[] = 'adsbygoogle'; $inline_js_defer_skip_list[] = '_paq.push(["'; if (module_exists('h5p')) { $inline_js_defer_skip_list[] = 'H5PIntegration'; } // If there is a fast clicker, ajax links might not work if ajax.js is // loaded in the footer. $all_in_footer_list = array( 'misc/ajax.js' => array( '/jquery.js', '/jquery.min.js', '/jquery.once.js', '/ajax.js', '/drupal.js', 'settings', ), ); $move_js_to_footer = variable_get('advagg_mod_js_footer', ADVAGG_MOD_JS_FOOTER); $defer_setting = variable_get('advagg_mod_js_defer', ADVAGG_MOD_JS_DEFER); $async_setting = variable_get('advagg_mod_js_async', ADVAGG_MOD_JS_ASYNC); $css_defer = variable_get('advagg_mod_css_defer', ADVAGG_MOD_CSS_DEFER); // Allow other modules to add/edit the above lists. // Call hook_advagg_mod_get_lists_alter(). $lists[$key] = array( $header_file_list, $header_inline_list, $no_async_defer_list, $inline_wrapper_list, $inline_js_wrap_skip_list, $inline_js_defer_skip_list, $all_in_footer_list, $move_js_to_footer, $defer_setting, $async_setting, $css_defer, ); drupal_alter('advagg_mod_get_lists', $lists[$key], $js, $css); } return $lists[$key]; } /** * Move JS to the footer. * * @param array $js * JS array. */ function advagg_mod_js_move_to_footer(array &$js) { // Move all JS to the footer. list($header_file_list, $header_inline_list, , , , , $all_in_footer_list, $move_js_to_footer) = advagg_mod_get_lists($js); if (empty($move_js_to_footer)) { return; } // Process all in footer list. if ($move_js_to_footer == 3 && !empty($all_in_footer_list)) { foreach ($all_in_footer_list as $key => $search_strings) { if (isset($js[$key])) { foreach ($js as $name => &$values) { foreach ($search_strings as $string) { if (strpos($name, $string) !== FALSE) { $values['scope_lock'] = TRUE; break; } if (is_string($values['data']) && strpos($values['data'], $string) !== FALSE) { $values['scope_lock'] = TRUE; break; } } } } } } foreach ($js as $key => &$values) { // If scope is not set, this js is not getting used. remove it. if (!isset($values['scope'])) { unset($js[$key]); continue; } if (strpos($values['scope'], ':') !== FALSE) { continue; } // Skip if a library and configured to do so. if ($move_js_to_footer == 1 && $values['group'] <= JS_LIBRARY) { continue; } // Skip if the scope has been locked. if (!empty($values['scope_lock'])) { continue; } // Allow certain scripts to be kept in the header. if ($values['type'] !== 'inline' && $values['type'] !== 'setting') { foreach ($header_file_list as $search_string) { if (stripos($values['data'], $search_string) !== FALSE) { continue 2; } } } // Allow certain inline scripts to be kept in the header. if ($values['type'] === 'inline') { foreach ($header_inline_list as $search_string) { if (strpos($values['data'], $search_string) !== FALSE) { continue 2; } } } // If group is not set, make it JS_DEFAULT (0). if (!isset($values['group'])) { $values['group'] = JS_DEFAULT; } // If weight is not set, make it 0. if (!isset($values['weight'])) { $values['weight'] = 0; } // If every_page is not set, make it FALSE. if (!isset($values['every_page'])) { $values['every_page'] = FALSE; } // If JS is not in the header increase group by 10000. if ($values['scope'] !== 'header') { $values['group'] += 10000; } // If JS is already in the footer increase group by 10000. if ($values['scope'] === 'footer') { $values['group'] += 10000; } $values['scope'] = 'footer'; } unset($values); } /** * Add the defer and or the async tag to js. * * @param array $js * JS array. * * @return bool * TRUE if jQuery is deferred. */ 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) { if ($values['type'] !== 'inline') { continue; } // JQuery UI. if (stripos($values['data'], 'window.jQuery.ui') !== FALSE && stripos($values['data'], 'document.write(" &$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) { 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; } /** * Defer inline js by using setTimeout. * * @param array $js * JS array. * @param bool $jquery_deferred * TRUE if jquery is deferred. */ function advagg_mod_inline_defer(array &$js, $jquery_deferred) { if ($jquery_deferred) { $bootstrap_rev = strrev('/bootstrap.js'); $bootstrap_min_rev = strrev('/bootstrap.min.js'); foreach ($js as &$values) { // Defer bootstrap if jquery is deferred. if (is_string($values['data']) && (stripos(strrev($values['data']), $bootstrap_rev) === 0 || stripos(strrev($values['data']), $bootstrap_min_rev) === 0 )) { $values['defer'] = TRUE; continue; } // Only do inline. if ($values['type'] === 'inline') { // Skip if advagg has already wrapped this inline code. if (strpos($values['data'], 'advagg_mod_') !== FALSE) { continue; } if (!empty($values['no_defer'])) { continue; } // Do not wrap inline js if it contains a named function definition. $pattern = '/\\s*function\\s+((?:[a-z][a-z0-9_]*))\\s*\\(.*\\)\\s*\\{/smix'; $match = preg_match($pattern, $values['data']); if (!$match) { // Defer inline scripts by wrapping the code in setTimeout callback. $matches[2] = $matches[0] = $values['data']; $values['data'] = advagg_mod_wrap_inline_js($matches); } elseif (stripos($values['data'], 'jQuery.') !== FALSE || stripos($values['data'], '(jQuery)') !== FALSE) { // Inline js has a named function that uses jQuery; // do not defer jQuery.js. $no_jquery_defer = TRUE; } } } unset($values); } elseif (variable_get('advagg_mod_js_defer_inline_alter', ADVAGG_MOD_JS_DEFER_INLINE_ALTER)) { foreach ($js as &$values) { // Skip if not inline. if ($values['type'] !== 'inline') { continue; } // Skip if advagg has already wrapped this inline code. if (strpos($values['data'], 'advagg_mod_') !== FALSE) { continue; } if (!empty($values['no_defer'])) { continue; } // Do not wrap inline js if it contains a named function definition. $pattern = '/\\s*function\\s+((?:[a-z][a-z0-9_]*))\\s*\\(.*\\)\\s*\\{/smix'; $match = preg_match($pattern, $values['data']); if (!$match) { // Defer the inline script by wrapping the code in setTimeout callback. $values['data'] = advagg_mod_defer_inline_js($values['data']); } } unset($values); } if (!empty($no_jquery_defer)) { $jquery_rev = strrev('/jquery.js'); $jquery_min_rev = strrev('/jquery.min.js'); foreach ($js as $name => &$values) { // Skip if not a file or external. if ($values['type'] !== 'file' && $values['type'] !== 'external') { continue; } // Special handling for jQuery. if (stripos(strrev($name), $jquery_rev) === 0 || stripos(strrev($name), $jquery_min_rev) === 0 ) { $values['defer'] = FALSE; } } } } /** * Callback for pre_render to inline all JavaScript on this page. * * @param array $elements * A render array containing: * - #items: The JavaScript items as returned by drupal_add_js() and * altered by drupal_get_js(). * - #group_callback: A function to call to group #items. Following * this function, #aggregate_callback is called to aggregate items within * the same group into a single file. * - #aggregate_callback: A function to call to aggregate the items within * the groups arranged by the #group_callback function. * * @return array * A render array that will render to a string of JavaScript tags. * * @see drupal_get_js() */ function _advagg_mod_pre_render_scripts(array $elements) { if (advagg_mod_inline_page() || advagg_mod_inline_page_js()) { advagg_mod_inline_js($elements['#items']); } return $elements; } /** * A #pre_render callback to inline all CSS on this page. * * @param array $elements * A render array containing: * - '#items': The CSS items as returned by drupal_add_css() and altered by * drupal_get_css(). * - '#group_callback': A function to call to group #items to enable the use * of fewer tags by aggregating files and/or using multiple @import * statements within a single tag. * - '#aggregate_callback': A function to call to aggregate the items within * the groups arranged by the #group_callback function. * * @return array * A render array that will render to a string of XHTML CSS tags. * * @see drupal_get_css() */ function _advagg_mod_pre_render_styles(array $elements) { if (!module_exists('advagg') || !advagg_enabled()) { return $elements; } if (advagg_mod_inline_page() || advagg_mod_inline_page_css()) { advagg_mod_inline_css($elements['#items']); } elseif (variable_get('advagg_mod_css_defer_skip_first_file', ADVAGG_MOD_CSS_DEFER_SKIP_FIRST_FILE) == 4) { list(, , , , , , , , , , $css_defer) = advagg_mod_get_lists(array(), $elements['#items']); $css_defer_admin = variable_get('advagg_mod_css_defer_admin', ADVAGG_MOD_CSS_DEFER_ADMIN); if (advagg_mod_css_defer_page() && !empty($css_defer) && (!empty($css_defer_admin) || !path_is_admin(current_path())) ) { advagg_mod_inline_css($elements['#items'], 0, variable_get('advagg_mod_css_defer_inline_size_limit', ADVAGG_MOD_CSS_DEFER_INLINE_SIZE_LIMIT)); } } return $elements; } /** * Use DOMDocument's loadHTML along with DOMXPath's query to find script tags. * * Once found, it will also wrap them in a javascript loader function. * * @param string $html * HTML fragments. * * @return string * The HTML fragment with less markup errors and script tags wrapped. */ function advagg_mod_xpath_script_wrapper($html) { // Do not throw errors when parsing the html. libxml_use_internal_errors(TRUE); $dom = new DOMDocument(); // Load html with full tags all around. $dom->loadHTML(' ' . $html . ''); $xpath = new DOMXPath($dom); // Get all script tags that // are not inside of a textarea // do not contain a src attribute // and the type is empty or has the type of javascript. $nodes = $xpath->query("//script[not(@src)][not(ancestor::textarea)][contains(translate(@type, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'javascript') or not(@type)]"); foreach ($nodes as $node) { $matches[2] = $node->nodeValue; // $matches[0] = $dom->saveHTML($node); $matches[0] = $node->nodeValue; $new_html = advagg_mod_wrap_inline_js($matches); $advagg = $dom->createElement('script'); $advagg->appendchild($dom->createTextNode($new_html)); $node->parentNode->replaceChild($advagg, $node); } // Render to HTML. $output = $dom->saveHTML(); // Remove the tags we added. $output = str_replace(array( ' ', '', ), array('', ''), $output); // Clear any errors. libxml_clear_errors(); return $output; } /** * Use DOMDocument's loadHTML along with DOMXPath's query to find script tags. * * Once found, it will add the src to the dns prefetch list. * * @param string $html * HTML fragments. */ function advagg_mod_xpath_script_external_dns($html) { // Do not throw errors when parsing the html. libxml_use_internal_errors(TRUE); $dom = new DOMDocument(); // Load html with full tags all around. $dom->loadHTML(' ' . $html . ''); $xpath = new DOMXPath($dom); // Get all script tags that // are not inside of a textarea // have a src attribute. $nodes = $xpath->query("//script[@src][not(ancestor::textarea)]"); // Add the src attribute to dns-prefetch. foreach ($nodes as $node) { advagg_add_dns_prefetch($node->attributes->getNamedItem('src')->nodeValue); } // Clear any errors. libxml_clear_errors(); } /** * Callback for preg_replace_callback. * * Used to wrap inline JS in a function in order to defer the inline js code. * * @param string $input * JavaScript code to wrap in setTimeout. * * @return string * Inline javascript code wrapped up in a loader. */ function advagg_mod_defer_inline_js($input) { if (variable_get('advagg_mod_js_defer_jquery', ADVAGG_MOD_JS_DEFER_JQUERY) !== FALSE && variable_get('jquery_update_jquery_cdn', 'none') !== 'none' && (stripos($input, 'jQuery') !== FALSE || strpos($input, '$(') !== FALSE || stripos($input, 'Drupal.') !== FALSE ) ) { $matches[2] = $matches[0] = $input; return advagg_mod_wrap_inline_js($matches); } // Get inline defer js skip list. list(, , , , , $inline_js_defer_skip_list) = advagg_mod_get_lists(); if (!empty($inline_js_defer_skip_list)) { // If the line is on the skip list then do not inline the script. foreach ($inline_js_defer_skip_list as $string_to_check) { if (stripos($input, $string_to_check) !== FALSE) { return $input; } } } // Use a counter in order to create unique function names. static $counter; ++$counter; // JS wrapper code. $new = " function advagg_mod_defer_${counter}() { ${input}; } window.setTimeout(advagg_mod_defer_${counter}, 0);"; return $new; } /** * Callback for preg_replace_callback. * * Used to wrap inline JS in a function in order to prevent js errors when JS is * moved to the footer, or when js is loaded async. * * @param array $matches * $matches[0] is the full string; $matches[2] is just the JavaScript. * @param string $check_string * JavaScript if statement; when true, run the inline code. * @param int $ms_wait * Default amount of time to wait until the required code is available. * * @return string * Inline javascript code wrapped up in a loader to prevent errors. */ function advagg_mod_wrap_inline_js(array $matches, $check_string = NULL, $ms_wait = 250) { list(, , , $inline_wrapper_list, $inline_js_wrap_skip_list) = advagg_mod_get_lists(); if (empty($check_string)) { foreach ($inline_wrapper_list as $search_string => $js_condition) { if (strpos($matches[2], $search_string) !== FALSE) { $check_string = $js_condition; break; } } if (empty($check_string)) { $check_string = 'window.jQuery && window.Drupal && window.Drupal.settings'; } } // Always wrap inline if it contains jquery or drupal. if (!empty($inline_js_wrap_skip_list) && stripos($matches[2], '(jQuery') === FALSE && stripos($matches[2], 'jQuery.') === FALSE && stripos($matches[2], '(Drupal') === FALSE && stripos($matches[2], 'Drupal.') === FALSE ) { // If the line is on the skip list then do not inline the script. foreach ($inline_js_wrap_skip_list as $string_to_check) { if (stripos($matches[2], $string_to_check) !== FALSE) { return $matches[0]; } } } // Use a counter in order to create unique function names. static $counter; ++$counter; // JS wrapper code. $new = " function advagg_mod_$counter() { // Count how many times this function is called. advagg_mod_$counter.count = ++advagg_mod_$counter.count || 1; try { if (advagg_mod_$counter.count <= 40) { $matches[2] // Set this to 100 so that this function only runs once. advagg_mod_$counter.count = 100; } } catch(e) { if (advagg_mod_$counter.count >= 40) { // Throw the exception if this still fails after running 40 times. throw e; } else { // Try again in $ms_wait ms. window.setTimeout(advagg_mod_$counter, $ms_wait); } } } function advagg_mod_${counter}_check() { if ($check_string) { advagg_mod_$counter(); } else { window.setTimeout(advagg_mod_${counter}_check, $ms_wait); } } advagg_mod_${counter}_check();"; $return = str_replace($matches[2], $new, $matches[0]); return $return; } /** * Rearrange CSS/JS so that aggregates are better grouped. * * This can move all external assets to the top, thus in one group. * This can move all inline assets to the bottom, thus in one group. * This can move all browser conditional assets together. * * @param array $array * The CSS or JS array. * @param string $type * String: css or js. */ function advagg_mod_sort_css_js(array &$array, $type) { if (($type === 'js' && variable_get('advagg_mod_js_adjust_sort_external', ADVAGG_MOD_JS_ADJUST_SORT_EXTERNAL)) || ($type === 'css' && variable_get('advagg_mod_css_adjust_sort_external', ADVAGG_MOD_CSS_ADJUST_SORT_EXTERNAL)) ) { // Find all external items. $external = array(); $group = NULL; $every_page = NULL; $weight = NULL; foreach ($array as $key => $value) { // Set values if not set. if (is_null($group)) { $group = $value['group']; } if (is_null($every_page)) { $every_page = $value['every_page']; } if (is_null($weight)) { $weight = $value['weight']; } // Find "lightest" item. if (isset($value['group']) && $value['group'] < $group) { $group = $value['group']; } if (!empty($value['every_page']) && !$every_page) { $every_page = $value['every_page']; } if (isset($value['weight']) && $value['weight'] < $weight) { $weight = $value['weight']; } if (!empty($value['type']) && $value['type'] === 'external') { $external[$key] = $value; unset($array[$key]); } if (!empty($value['type']) && $value['type'] === 'inline') { // Move jQuery fallback as well. if (strpos($value['data'], 'window.jQuery') === 0) { $external[$key] = $value; unset($array[$key]); } // Move jQuery ui fallback as well. if (strpos($value['data'], 'window.jQuery.ui') === 0) { $external[$key] = $value; unset($array[$key]); } } } // Sort the array so that it appears in the correct order. advagg_drupal_sort_css_js_stable($external); // Group all external together. $offset = 0.0001; $weight += -1; $found_jquery = FALSE; foreach ($external as $key => $value) { if (isset($value['movable']) && empty($value['movable'])) { $array[$key] = $value; continue; } // If bootstrap is used, it must be loaded after jquery. Don't move // bootstrap if jquery is not above it. if (strpos($value['data'], 'jquery.min.js') !== FALSE || strpos($value['data'], 'jquery.js') !== FALSE ) { $found_jquery = TRUE; } if (!$found_jquery && (strpos($value['data'], 'bootstrap.min.js') !== FALSE || strpos($value['data'], 'bootstrap.js') !== FALSE)) { $array[$key] = $value; continue; } $value['group'] = $group; $value['every_page'] = $every_page; $value['weight'] = $weight; $weight += $offset; $array[$key] = $value; } } if (($type === 'js' && variable_get('advagg_mod_js_adjust_sort_inline', ADVAGG_MOD_JS_ADJUST_SORT_INLINE)) || ($type === 'css' && variable_get('advagg_mod_css_adjust_sort_inline', ADVAGG_MOD_CSS_ADJUST_SORT_INLINE)) ) { // Find all inline items. $inline = array(); $group = NULL; $every_page = NULL; $weight = NULL; foreach ($array as $key => $value) { // Set values if not set. if (is_null($group)) { $group = $value['group']; } if (is_null($every_page)) { $every_page = $value['every_page']; } if (is_null($weight)) { $weight = $value['weight']; } // Find "heaviest" item. if (isset($value['group']) && $value['group'] > $group) { $group = $value['group']; } if (empty($value['every_page']) && $every_page) { $every_page = FALSE; } if (isset($value['weight']) && $value['weight'] > $weight) { $weight = $value['weight']; } if (!empty($value['type']) && $value['type'] === 'inline') { // Do not move jQuery fallback. if (strpos($value['data'], 'window.jQuery') === 0) { continue; } // Do not move jQuery.ui fallback. if (strpos($value['data'], 'window.jQuery.ui') === 0) { continue; } $inline[$key] = $value; unset($array[$key]); } } // Sort the array so that it appears in the correct order. advagg_drupal_sort_css_js_stable($inline); // Group all inline together. $offset = 0.0001; $weight += 1; foreach ($inline as $key => $value) { if (isset($value['movable']) && empty($value['movable'])) { $array[$key] = $value; continue; } $value['group'] = $group; $value['every_page'] = $every_page; $value['weight'] = $weight; $weight += $offset; $array[$key] = $value; } } if (($type === 'js' && variable_get('advagg_mod_js_adjust_sort_browsers', ADVAGG_MOD_JS_ADJUST_SORT_BROWSERS)) || ($type === 'css' && variable_get('advagg_mod_css_adjust_sort_browsers', ADVAGG_MOD_CSS_ADJUST_SORT_BROWSERS)) ) { // Get a list of browsers. $browsers_list = array(); foreach ($array as $key => $value) { if (isset($value['browsers']['IE']) && $value['browsers']['IE'] !== TRUE) { $browsers_list['IE'][] = $value['browsers']['IE']; } } // Group browsers CSS together. if (isset($browsers_list['IE'])) { $browsers_list['IE'] = array_values(array_unique($browsers_list['IE'])); foreach ($browsers_list['IE'] as $browser) { $browsers = array(); $group = NULL; $every_page = NULL; $weight = NULL; foreach ($array as $key => $value) { if (isset($value['browsers']['IE']) && $browser === $value['browsers']['IE']) { // Set values if not set. if (is_null($group)) { $group = $value['group']; } if (is_null($every_page)) { $every_page = $value['every_page']; } if (is_null($weight)) { $weight = $value['weight']; } // Find "heaviest" item. if ($value['group'] > $group) { $group = $value['group']; } if (!$value['every_page'] && $every_page) { $every_page = $value['every_page']; } if ($value['weight'] > $weight) { $weight = $value['weight']; } $browsers[$key] = $value; unset($array[$key]); } } // Sort the array so that it appears in the correct order. advagg_drupal_sort_css_js_stable($browsers); // Group all browsers together. $offset = 0.0001; foreach ($browsers as $key => $value) { if (isset($value['movable']) && empty($value['movable'])) { $array[$key] = $value; continue; } $value['group'] = $group; $value['every_page'] = $every_page; $value['weight'] = $weight; $weight += $offset; $array[$key] = $value; } } } } } /** * Returns TRUE if this page should have inline CSS and JS. * * @return bool * TRUE or FALSE. Default is FALSE. */ function advagg_mod_inline_page() { $visibility = variable_get('advagg_mod_inline_visibility', ADVAGG_MOD_VISIBILITY_LISTED); $pages = variable_get('advagg_mod_inline_pages', ''); return advagg_mod_match_path($pages, $visibility); } /** * Returns TRUE if this page should have inline CSS. * * @return bool * TRUE or FALSE. Default is FALSE. */ function advagg_mod_inline_page_css() { $visibility = variable_get('advagg_mod_inline_css_visibility', ADVAGG_MOD_VISIBILITY_LISTED); $pages = variable_get('advagg_mod_inline_css_pages', ''); return advagg_mod_match_path($pages, $visibility); } /** * Returns TRUE if this page should have inline JS. * * @return bool * TRUE or FALSE. Default is FALSE. */ function advagg_mod_inline_page_js() { $visibility = variable_get('advagg_mod_inline_js_visibility', ADVAGG_MOD_VISIBILITY_LISTED); $pages = variable_get('advagg_mod_inline_js_pages', ''); return advagg_mod_match_path($pages, $visibility); } /** * Returns TRUE if this page should have critical CSS inlined. * * @return bool * TRUE or FALSE. Default is FALSE. */ function advagg_mod_css_defer_page() { $visibility = variable_get('advagg_mod_css_defer_visibility', ADVAGG_MOD_VISIBILITY_LISTED); $pages = variable_get('advagg_mod_css_defer_pages', ''); return advagg_mod_match_path($pages, $visibility); } /** * Transforms all JS files into inline JS. * * @param array $js * JS array. */ function advagg_mod_inline_js(array &$js) { $aggregate_settings = advagg_current_hooks_hash_array(); foreach ($js as &$values) { // Only process files. if ($values['type'] !== 'file') { continue; } $filename = $values['data']; if (file_exists($filename)) { $contents = (string) @advagg_file_get_contents($filename); } // Allow other modules to modify this files contents. // Call hook_advagg_get_js_file_contents_alter(). drupal_alter('advagg_get_js_file_contents', $contents, $filename, $aggregate_settings); $values['data'] = $contents; $values['type'] = 'inline'; } unset($values); } /** * Transforms all CSS files into inline CSS. * * @param array $css * CSS array. * @param int $file_limit * The number of files to inline; 0 means no limit. * @param int $size_limit * The number of bytes to inline; 0 means no limit. * * @see advagg_get_css_aggregate_contents() * @see drupal_build_css_cache() */ function advagg_mod_inline_css(array &$css, $file_limit = 0, $size_limit = 0) { $aggregate_settings = advagg_current_hooks_hash_array(); $optimize = TRUE; module_load_include('inc', 'advagg', 'advagg'); $count = 0; $size = 0; foreach ($css as &$values) { // Only process files. if ($values['type'] !== 'file') { continue; } $file = $values['data']; if (file_exists($file)) { if (!empty($file_limit) && $count > $file_limit) { break; } $contents = advagg_load_css_stylesheet($file, $optimize, $aggregate_settings); // Allow other modules to modify this files contents. // Call hook_advagg_get_css_file_contents_alter(). drupal_alter('advagg_get_css_file_contents', $contents, $file, $aggregate_settings); // Per the W3C specification at // http://www.w3.org/TR/REC-CSS2/cascade.html#at-import, @import rules // must proceed any other style, so we move those to the top. $regexp = '/@import[^;]+;/i'; preg_match_all($regexp, $contents, $matches); $contents = preg_replace($regexp, '', $contents); $contents = implode('', $matches[0]) . $contents; $size += strlen($contents); if (!empty($size_limit) && $size > $size_limit) { break; } $values['data'] = $contents; $values['type'] = 'inline'; $count++; } } unset($values); } /** * Transforms all CSS files into inline CSS. * * @param string $pages * String from the advagg_mod_inline_pages variable. * @param int $visibility * Visibility setting from the advagg_mod_inline_visibility variable. * * @return bool * TRUE if the current path matches the given pages. * * @see block_block_list_alter() */ function advagg_mod_match_path($pages, $visibility) { // Default to not matching. $page_match = FALSE; // Limited visibility blocks must list at least one page. if (empty($pages) && $visibility <= ADVAGG_MOD_VISIBILITY_PHP) { if ($visibility == ADVAGG_MOD_VISIBILITY_NOTLISTED) { $page_match = TRUE; } } else { // Match on php. if ($visibility == ADVAGG_MOD_VISIBILITY_PHP) { if (module_exists('php')) { $page_match = php_eval($pages); } } // Match the given $pages. elseif ($visibility < ADVAGG_MOD_VISIBILITY_PHP) { $current_path = current_path(); // Convert path to lowercase. This allows comparison of the same path // with different case. Ex: /Page, /page, /PAGE. $pages = drupal_strtolower($pages); // Convert the Drupal path to lowercase. $path = drupal_strtolower(drupal_get_path_alias($current_path)); // Compare the lowercase internal and lowercase path alias (if any). $page_match = drupal_match_path($path, $pages); if ($path != $current_path) { $page_match = $page_match || drupal_match_path($current_path, $pages); } // When $visibility has a value of 0 (ADVAGG_MOD_VISIBILITY_NOTLISTED), // the block is displayed on all pages except those listed in $pages. // When set to 1 (ADVAGG_MOD_VISIBILITY_LISTED), it is displayed only on // those pages listed in $block->pages. $page_match = !($visibility xor $page_match); } } return $page_match; } /** * See if JavaScript file contains drupal and/or jquery. * * @param string $filename * Inline css, full URL, or filename. * @param string $type * (Optional) inline, external, or file. * * @return array * Returns an array stating if this JS file contains drupal or jquery. */ function advagg_mod_js_contains_jquery_drupal($filename, $type = '') { if (is_string($filename)) { if ($type === 'inline') { $contents = $filename; } elseif ($type === 'external' || strpos($filename, 'http://') === 0 || strpos($filename, 'https://') === 0 || strpos($filename, '//') === 0 ) { $result = drupal_http_request($filename); if (($result->code == 200 || (isset($result->redirect_code) && $result->redirect_code == 200)) && !empty($result->data)) { $contents = $result->data; } } elseif (file_exists($filename)) { $contents = (string) @advagg_file_get_contents($filename); } } $results = array(); if (!empty($contents) && stripos($contents, 'drupal.') !== FALSE) { $results['contents']['drupal'] = TRUE; if (stripos($contents, 'drupal.settings.') !== FALSE) { $results['contents']['drupal.settings'] = TRUE; } else { $results['contents']['drupal.settings'] = FALSE; } if (stripos($contents, 'drupal.behaviors.') !== FALSE) { $results['contents']['drupal.behaviors'] = TRUE; } else { $results['contents']['drupal.behaviors'] = FALSE; } } else { $results['contents']['drupal'] = FALSE; $results['contents']['drupal.settings'] = FALSE; $results['contents']['drupal.behaviors'] = FALSE; } if (!empty($contents) && stripos($contents, 'jquery') !== FALSE) { $results['contents']['jquery'] = TRUE; } else { $results['contents']['jquery'] = FALSE; } return $results; } /** * Move analytics.js to be a file instead of inline. * * @param array $js * JS array. */ function advagg_mod_ga_inline_to_file(array &$js) { // Do nothing if the googleanalytics module is not enabled. if (!module_exists('googleanalytics') || !is_callable('googleanalytics_api') || !is_callable('_googleanalytics_cache') ) { return; } // Get inline GA js and put it inside of an aggregrate. $ga_script = ''; $debug = variable_get('googleanalytics_debug', 0); $api = googleanalytics_api(); if ($api['api'] === 'analytics.js') { $library_tracker_url = '//www.google-analytics.com/' . ($debug ? 'analytics_debug.js' : 'analytics.js'); $library_cache_url = 'http:' . $library_tracker_url; } else { // Which version of the tracking library should be used? if ($trackdoubleclick = variable_get('googleanalytics_trackdoubleclick', FALSE)) { $library_tracker_url = 'stats.g.doubleclick.net/dc.js'; $library_cache_url = 'http://' . $library_tracker_url; } else { $library_tracker_url = '.google-analytics.com/ga.js'; $library_cache_url = 'http://www' . $library_tracker_url; } } $ga_script = _googleanalytics_cache($library_cache_url); if (variable_get('googleanalytics_cache', 0) && $ga_script) { $mod_base_url = substr($GLOBALS['base_root'] . $GLOBALS['base_path'], strpos($GLOBALS['base_root'] . $GLOBALS['base_path'], '//') + 2); $mod_base_url_len = strlen($mod_base_url); $ga_script = substr($ga_script, stripos($ga_script, $mod_base_url) + $mod_base_url_len); } else { $ga_script = $library_cache_url; if ($api['api'] === 'ga.js' && $GLOBALS['is_https']) { if (!empty($trackdoubleclick)) { $ga_script = str_replace('http://', 'https://', $ga_script); } else { $ga_script = str_replace('http://www', 'https://ssl', $ga_script); } } } if (!empty($ga_script)) { foreach ($js as $key => $value) { // Skip if not inline. if ($value['type'] !== 'inline') { continue; } $add_ga = FALSE; // GoogleAnalytics 2.x inline loader string. if ($api['api'] === 'analytics.js') { $start = strpos($value['data'], '(function(i,s,o,g,r,a,m){i["GoogleAnalyticsObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();'); $end = strpos($value['data'], '})(window,document,"script",'); if ($start === 0) { // Strip loader string. $js[$key]['data'] = substr($value['data'], 0, $start + 133) . substr($value['data'], $end); $js[$key]['data'] = advagg_mod_defer_inline_js($js[$key]['data']); $add_ga = TRUE; } } // GoogleAnalytics 1.x inline loader string. if ($api['api'] === 'ga.js') { $start = strpos($value['data'], '(function() {var ga = document.createElement("script");ga.type = "text/javascript";ga.async = true;ga.src ='); $end = strpos($value['data'], '";var s = document.getElementsByTagName("script")[0];s.parentNode.insertBefore(ga, s);})();'); if ($start !== FALSE && $end !== FALSE) { // Strip loader string. $js[$key]['data'] = substr($value['data'], 0, $start) . substr($value['data'], $end + 91); $js[$key]['no_defer'] = TRUE; $add_ga = TRUE; } } if ($add_ga) { // Add GA analytics.js file to the $js array. $js[$ga_script] = array( 'data' => $ga_script, 'type' => 'file', 'async' => TRUE, 'defer' => TRUE, ); $js[$ga_script] += $value; break; } } } } /** * Remove JS if not in use on current page. * * @param array $js * JS array. */ function advagg_mod_remove_js_if_not_used(array &$js) { $files_skiplist = array( 'drupal.js', 'jquery.js', 'jquery.min.js', 'jquery.once.js', ); $inline_skiplist = array(); if (module_exists('jquery_update')) { $inline_skiplist[] = 'document.write("