diff --git a/includes/common.inc b/includes/common.inc index d7189ab..826d972 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -4034,6 +4034,7 @@ function drupal_js_defaults($data = NULL) { 'preprocess' => TRUE, 'version' => NULL, 'data' => $data, + 'browsers' => array(), ); } @@ -4089,7 +4090,45 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS } } - $output = ''; + // Sort the JavaScript so that it appears in the correct order. + uasort($items, 'drupal_sort_css_js'); + + // Provide the page with information about the individual JavaScript files + // used, information not otherwise available when aggregation is enabled. + $setting['ajaxPageState']['js'] = array_fill_keys(array_keys($items), 1); + unset($setting['ajaxPageState']['js']['settings']); + drupal_add_js($setting, 'setting'); + + // If we're outputting the header scope, then this might be the final time + // that drupal_get_js() is running, so add the setting to this output as well + // as to the drupal_add_js() cache. If $items['settings'] doesn't exist, it's + // because drupal_get_js() was intentionally passed a $javascript argument + // stripped off settings, potentially in order to override how settings get + // output, so in this case, do not add the setting to this output. + if ($scope == 'header' && isset($items['settings'])) { + $items['settings']['data'][] = $setting; + } + + // Loop through the JavaScript to construct the rendered output. + $elements = array( + '#type' => 'scripts', + '#items' => $items, + ); + + return drupal_render($elements); +} + +function drupal_pre_render_scripts($elements) { + // Group and aggregate the items. + if (isset($elements['#group_callback'])) { + $elements['#groups'] = $elements['#group_callback']($elements['#items']); + } + if (isset($elements['#aggregate_callback'])) { + $elements['#aggregate_callback']($elements['#groups']); + } + + $items = $elements['#items']; + dsm($elements); // The index counter is used to keep aggregated and non-aggregated files in // order by weight. $index = 1; @@ -4114,34 +4153,19 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS // Since JavaScript may look for arguments in the URL and act on them, some // third-party code might require the use of a different query string. $js_version_string = variable_get('drupal_js_version_query_string', 'v='); - - // Sort the JavaScript so that it appears in the correct order. - uasort($items, 'drupal_sort_css_js'); - - // Provide the page with information about the individual JavaScript files - // used, information not otherwise available when aggregation is enabled. - $setting['ajaxPageState']['js'] = array_fill_keys(array_keys($items), 1); - unset($setting['ajaxPageState']['js']['settings']); - drupal_add_js($setting, 'setting'); - - // If we're outputting the header scope, then this might be the final time - // that drupal_get_js() is running, so add the setting to this output as well - // as to the drupal_add_js() cache. If $items['settings'] doesn't exist, it's - // because drupal_get_js() was intentionally passed a $javascript argument - // stripped off settings, potentially in order to override how settings get - // output, so in this case, do not add the setting to this output. - if ($scope == 'header' && isset($items['settings'])) { - $items['settings']['data'][] = $setting; - } - + // Loop through the JavaScript to construct the rendered output. $element = array( '#tag' => 'script', + '#type' => 'html_tag', '#value' => '', '#attributes' => array( 'type' => 'text/javascript', ), ); + + $js_elements = array(); + foreach ($items as $item) { $query_string = empty($item['version']) ? $default_query_string : $js_version_string . $item['version']; @@ -4151,7 +4175,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS $js_element['#value_prefix'] = $embed_prefix; $js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(drupal_array_merge_deep_array($item['data'])) . ");"; $js_element['#value_suffix'] = $embed_suffix; - $output .= theme('html_tag', array('element' => $js_element)); + $js_elements[] = $js_element; break; case 'inline': @@ -4162,7 +4186,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS $js_element['#value_prefix'] = $embed_prefix; $js_element['#value'] = $item['data']; $js_element['#value_suffix'] = $embed_suffix; - $processed[$index++] = theme('html_tag', array('element' => $js_element)); + $js_elements[] = $js_element; break; case 'file': @@ -4173,7 +4197,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS } $query_string_separator = (strpos($item['data'], '?') !== FALSE) ? '&' : '?'; $js_element['#attributes']['src'] = file_create_url($item['data']) . $query_string_separator . ($item['cache'] ? $query_string : REQUEST_TIME); - $processed[$index++] = theme('html_tag', array('element' => $js_element)); + $js_elements[] = $js_element; } else { // By increasing the index for each aggregated file, we maintain @@ -4196,7 +4220,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS $js_element['#attributes']['defer'] = 'defer'; } $js_element['#attributes']['src'] = $item['data']; - $processed[$index++] = theme('html_tag', array('element' => $js_element)); + $js_elements[] = $js_element; break; } } @@ -4211,14 +4235,69 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS $preprocess_file = file_create_url($uri); $js_element = $element; $js_element['#attributes']['src'] = $preprocess_file; - $processed[$key] = theme('html_tag', array('element' => $js_element)); + $js_elements[] = $js_element; } } } - // Keep the order of JS files consistent as some are preprocessed and others are not. - // Make sure any inline or JS setting variables appear last after libraries have loaded. - return implode('', $processed) . $output; + return $js_elements; +} + +function drupal_group_js($javascripts) { + $groups = array(); + // If a group can contain multiple items, we track the information that must + // be the same for each item in the group, so that when we iterate the next + // item, we can determine if it can be put into the current group, or if a + // new group needs to be made for it. + $current_group_keys = NULL; + $index = -1; + foreach ($javascripts as $item) { + ksort($item['browsers']); + + switch ($item['type']) { + case 'file': + // Group file items if their 'preprocess' flag is TRUE. + // Help ensure maximum reuse of aggregate files by only grouping + // together items that share the same 'group' value and 'every_page' + // flag. See drupal_add_css() for details about that. + $group_keys = $item['preprocess'] ? array($item['type'], $item['group'], $item['every_page'], $item['browsers']) : FALSE; + break; + } + + // If the group keys don't match the most recent group we're working with, + // then a new group must be made. + if ($group_keys !== $current_group_keys) { + $index++; + // Initialize the new group with the same properties as the first item + // being placed into it. The item's 'data' and 'weight' properties are + // unique to the item and should not be carried over to the group. + $groups[$index] = $item; + unset($groups[$index]['data'], $groups[$index]['weight']); + $groups[$index]['items'] = array(); + $current_group_keys = $group_keys ? $group_keys : NULL; + } + + // Add the item to the current group. + $groups[$index]['items'][] = $item; + } + + return $groups; +} + +function drupal_aggregate_js(&$js_groups) { + $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); + // For each group that needs aggregation, aggregate its items. + foreach ($js_groups as $key => $group) { + switch ($group['type']) { + // If a file group can be aggregated into a single file, do so, and set + // the group's data property to the file path of the aggregate file. + case 'file': + if ($group['preprocess'] || $preprocess_js) { + $js_groups[$key]['data'] = drupal_build_js_cache($group['items']); + } + break; + } + } } /** diff --git a/modules/system/system.module b/modules/system/system.module index 7d423ab..e9a025c 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -315,6 +315,12 @@ function system_element_info() { '#group_callback' => 'drupal_group_css', '#aggregate_callback' => 'drupal_aggregate_css', ); + $types['scripts'] = array( + '#items' => array(), + '#pre_render' => array('drupal_pre_render_scripts'), + '#group_callback' => 'drupal_group_js', + '#aggregate_callback' => 'drupal_aggregate_js', + ); // Input elements. $types['submit'] = array(