5 * Advanced aggregation modifier module.
8 // Core hook implementations.
10 * Implements hook_js_alter().
12 function advagg_mod_js_alter(&$js) {
13 if (\Drupal::moduleHandler()->moduleExists('advagg') && !advagg_enabled()) {
16 $config = \Drupal::config('advagg_mod.settings');
18 $css_defer = $config->get('css_defer');
20 // Use the current file system path for advagg_mod.
21 $module_path = drupal_get_path('module', 'advagg_mod');
22 if (advagg_mod_css_defer_active() && isset($js[$module_path . '/js/loadCSS.js'])) {
23 if ($css_defer == 3) {
24 $js[$module_path . '/js/loadCSS.js']['scope'] = 'header';
25 $js[$module_path . '/js/css_defer.js']['scope'] = 'header';
27 $css_defer_js_code = $config->get('css_defer_js_code');
28 if ($css_defer_js_code == 0) {
29 $js[$module_path . '/js/loadCSS.js']['inline'] = TRUE;
30 $js[$module_path . '/js/css_defer.js']['inline'] = TRUE;
32 elseif ($css_defer_js_code == 4) {
33 $js[$module_path . '/js/loadCSS.js']['type'] = 'external';
34 $js[$module_path . '/js/loadCSS.js']['data'] = '//cdn.rawgit.com/filamentgroup/loadCSS/master/src/loadCSS.js';
38 // Change sort order so aggregates do not get split up.
39 if ($config->get('js_adjust_sort_external') || $config->get('js_adjust_sort_browsers')) {
40 advagg_mod_sort_css_js($js, 'js');
43 // Move JS to the footer.
44 if ($config->get('js_footer')) {
45 advagg_mod_js_move_to_footer($js);
48 // Force all JS to be preprocessed.
49 if ($config->get('js_preprocess')) {
50 foreach ($js as $path => &$values) {
51 // However CKEditor must not be combined or errors *will* occur.
52 if ($path == 'core/assets/vendor/ckeditor/ckeditor.js') {
55 $values['preprocess'] = TRUE;
56 $values['cache'] = TRUE;
61 // Add the defer or the async tag to JS.
62 advagg_mod_js_async_defer($js);
64 // Move all async JS to the header.
65 if ($config->get('js_async_in_header')) {
66 foreach ($js as &$values) {
67 // Skip if not file or external.
68 if ($values['type'] !== 'file' && $values['type'] !== 'external') {
72 if (empty($values['async']) && empty($values['attributes']['async'])) {
76 // Move to the header with a group of 1000.
77 $values['scope'] = 'header';
78 $values['group'] = 1000;
85 * Implements hook_css_alter().
87 function advagg_mod_css_alter(&$css) {
88 if (\Drupal::moduleHandler()->moduleExists('advagg') && !advagg_enabled()) {
91 $config = \Drupal::config('advagg_mod.settings');
92 if ($config->get('css_adjust_sort_external') || $config->get('css_adjust_sort_browsers')) {
93 advagg_mod_sort_css_js($css, 'css');
96 // Force all CSS to be preprocessed.
97 if ($config->get('css_preprocess')) {
98 foreach ($css as &$values) {
99 $values['preprocess'] = TRUE;
106 * Implements hook_page_attachments_alter().
108 function advagg_mod_page_attachments_alter(array &$page) {
109 if (advagg_mod_css_defer_active()) {
110 $page['#attached']['library'][] = 'advagg_mod/css_defer';
114 // AdvAgg hook implementations.
116 * Implements hook_advagg_current_hooks_hash_array_alter().
118 function advagg_mod_advagg_current_hooks_hash_array_alter(array &$aggregate_settings) {
119 $aggregate_settings['variables']['advagg_mod'] = \Drupal::config('advagg_mod.settings')->get();
123 * Implements hook_advagg_asset_render_alter().
125 function advagg_mod_advagg_asset_render_alter(&$assets, $render_type, $asset_type) {
126 if (!advagg_enabled()) {
130 if ($render_type == 'html') {
131 if ($asset_type == 'styles') {
132 foreach ($assets as &$value) {
133 if (!empty($value['#inline'])) {
134 $value['#value'] = @file_get_contents($value['#attributes']['href']);
135 if ($value['#value']) {
136 unset($value['#attributes']['href']);
139 elseif (advagg_mod_css_defer_active()) {
140 // Skip prefetch links and inline styles.
141 if ($value['#tag'] == 'style') {
144 $value['#attributes']['class'][] = 'advagg-css-defer';
145 $value['#noscript'] = TRUE;
150 elseif ($asset_type == 'scripts' || $asset_type == 'scripts_bottom') {
151 foreach ($assets as &$value) {
152 if (!empty($value['#inline'])) {
153 $value['#value'] = @file_get_contents($value['#attributes']['src']);
154 if ($value['#value']) {
155 unset($value['#attributes']['src']);
164 * Implements hook_advagg_hooks_implemented_alter().
166 function advagg_mod_advagg_hooks_implemented_alter(&$hooks, $all) {
169 'advagg_mod_get_lists_alter' => [],
175 * Implements hook_advagg_asset_path_alter().
177 function advagg_mod_advagg_asset_path_alter(&$path, $extension) {
178 if ($dir = rtrim(\Drupal::config('advagg_mod.settings')->get('unified_multisite_dir'), '/')) {
179 if ($extension == 'js') {
180 $path = $dir . '/js';
182 elseif ($extension == 'css') {
183 $path = $dir . '/css';
190 * Generate a list of rules and exceptions for js files.
192 * Controls no async/defer file list.
195 * A multidimensional array.
197 function advagg_mod_get_lists() {
198 $lists = &drupal_static(__FUNCTION__);
199 if (!isset($lists)) {
200 // Do not defer/async list.
201 $no_async_defer_list = [
205 '//maps.googleapis.com',
206 '//dev.virtualearth.net',
207 '//api.maps.yahoo.com',
210 '//cdn.rawgit.com/stubbornella/csslint/master/release/csslint.js',
213 // Allow other modules to add/edit the above lists.
214 // Call hook_advagg_mod_get_lists_alter().
216 $no_async_defer_list,
219 \Drupal::moduleHandler()->alter('advagg_mod_get_lists', $lists);
225 * Move JS to the footer.
230 function advagg_mod_js_move_to_footer(array &$js) {
231 // Move all JS to the footer.
232 $move_js_to_footer = \Drupal::config('advagg_mod.settings')->get('js_footer');
234 'core/assets/vendor/modernizr/modernizr.min.js' => 0,
235 'core/assets/vendor/html5shiv/html5shiv.min.js' => 0,
238 foreach ($js as $key => &$values) {
239 // Skip if a core header file and configured to do so.
240 if ($move_js_to_footer == 3 && isset($core_header_js[$key])) {
244 // Skip if the scope has been locked.
245 if (!empty($values['scope_lock'])) {
249 // If JS is not in the header decrease weight by 10000.
250 if ($values['scope'] === 'header') {
251 $values['weight'] -= 10000;
253 // If JS is already in the footer decrease weight by 10000.
254 if ($values['scope'] !== 'footer') {
255 $values['weight'] -= 10000;
257 $values['scope'] = 'footer';
263 * Add the defer and or the async tag to js.
268 function advagg_mod_js_async_defer(array &$js) {
269 // Return early if this is disabled.
270 $config = \Drupal::config('advagg_mod.settings');
271 $defer = $config->get('js_defer');
272 $async = $config->get('js_async');
273 list($no_async_defer_list) = advagg_mod_get_lists();
275 // Make all scripts defer and/or async.
276 foreach ($js as $name => &$values) {
277 $values['attributes'] = [];
278 // Defer all scripts.
280 $values['attributes']['defer'] = TRUE;
282 // Do not defer external scripts setting.
283 if ($defer == 2 && $values['type'] === 'external') {
284 unset($values['attributes']['defer']);
288 // Async all scripts. On most browsers this will run instead of defer.
289 // On some older browsers if defer is also set they will run that instead
290 // if they don't support async.
292 $values['attributes']['async'] = TRUE;
295 // No async defer list.
296 foreach ($no_async_defer_list as $search_string) {
297 if (strpos($name, $search_string) !== FALSE) {
298 // Do not defer/async the loading this script.
300 unset($values['attributes']['async'], $values['attributes']['defer']);
309 * Rearrange CSS/JS so that aggregates are better grouped.
311 * This can move all external assets to the top, thus in one group.
312 * This can move all browser conditional assets together.
314 * @param array $assets
315 * The CSS or JS array.
316 * @param string $type
319 function advagg_mod_sort_css_js(array &$assets, $type) {
320 $config = \Drupal::config('advagg_mod.settings');
321 if ($config->get($type . '_adjust_sort_external')) {
322 // Find all external items.
326 foreach ($assets as $key => $value) {
327 // Set values if not set.
328 if (is_null($group)) {
329 $group = $value['group'];
331 if (is_null($weight)) {
332 $weight = $value['weight'];
335 // Find "lightest" item.
336 if ($value['group'] < $group) {
337 $group = $value['group'];
339 if ($value['weight'] < $weight) {
340 $weight = $value['weight'];
343 list(, $no_move) = advagg_mod_get_lists();
344 if (!empty($value['type']) && $value['type'] === 'external' && !in_array($key, $no_move) && empty($value['movable'])) {
345 $external[$key] = $value;
346 unset($assets[$key]);
349 // Sort the array so that it appears in the correct order.
350 advagg_drupal_sort_css_js_stable($external);
352 // Group all external together.
356 $found_jquery = FALSE;
357 foreach ($external as $key => $value) {
359 // If bootstrap is used, it must be loaded after jquery. Don't move
360 // bootstrap if jquery is not above it.
361 if ($key == 'assets/vendor/jquery/jquery.min.js') {
362 $found_jquery = TRUE;
364 if (!$found_jquery && (strpos($value['data'], 'bootstrap.min.js') !== FALSE || strpos($value['data'], 'bootstrap.js') !== FALSE)) {
365 $assets[$key] = $value;
368 $value['group'] = $group;
369 $value['weight'] = $weight;
371 $assets[$key] = $value;
375 if ($config->get($type . '_adjust_sort_browsers')) {
376 // Get a list of browsers.
378 foreach ($assets as $key => $value) {
379 if (isset($value['browsers']['IE']) && $value['browsers']['IE'] !== TRUE) {
380 $browsers_list['IE'][] = $value['browsers']['IE'];
384 // Group browsers CSS together.
385 if (isset($browsers_list['IE'])) {
386 $browsers_list['IE'] = array_values(array_unique($browsers_list['IE']));
387 foreach ($browsers_list['IE'] as $browser) {
391 foreach ($assets as $key => $value) {
392 if (isset($value['browsers']['IE']) && $browser === $value['browsers']['IE']) {
393 // Set values if not set.
394 if (is_null($group)) {
395 $group = $value['group'];
397 if (is_null($weight)) {
398 $weight = $value['weight'];
401 // Find "heaviest" item.
402 if ($value['group'] > $group) {
403 $group = $value['group'];
405 if ($value['weight'] > $weight) {
406 $weight = $value['weight'];
409 $browsers[$key] = $value;
410 unset($assets[$key]);
414 // Sort the array so that it appears in the correct order.
415 advagg_drupal_sort_css_js_stable($browsers);
417 // Group all browsers together.
420 foreach ($browsers as $key => $value) {
421 if (isset($value['movable']) && empty($value['movable'])) {
422 $assets[$key] = $value;
425 $value['group'] = $group;
426 $value['weight'] = $weight;
428 $assets[$key] = $value;
436 * Determines whether css defering should be active for the current request.
438 function advagg_mod_css_defer_active() {
439 $config = \Drupal::config('advagg_mod.settings');
440 if (!$config->get('css_defer')) {
443 $admin_route = \Drupal::service('router.admin_context')->isAdminRoute();
444 if ($admin_route && !$config->get('css_defer_admin')) {