diff --git a/includes/common.inc b/includes/common.inc index 89a2c4a365..1e1c912f97 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -4346,6 +4346,7 @@ function drupal_add_js($data = NULL, $options = NULL) { $javascript[$options['data']] = $options; } } + return $javascript; } @@ -4426,19 +4427,62 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS } // Filter out elements of the given scope. - $items = array(); - foreach ($javascript as $key => $item) { - if ($item['scope'] == $scope) { - $items[$key] = $item; + $items = array_filter( + $javascript, + function ($item) use ($scope) { + return $item['scope'] === $scope; } + ); + + // 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; + } + + $elements = array( + '#type' => 'scripts', + '#items' => $items, + ); + + // 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 drupal_render($elements); +} + +/** + * #pre_render callback to add the elements needed for scripts tags to be + * rendered. + * + * @param $elements + * A render array containing: + * - '#items': The CSS items as returned by drupal_add_css() and altered by + * drupal_get_css(). + * + * @return array + * A render array that will render to a string of XHTML CSS tags. + * + * @see drupal_get_js() + */ +function drupal_pre_render_scripts(array $elements) { + // Group and aggregate the items. + if (isset($elements['#group_callback'])) { + $elements['#groups'] = $elements['#group_callback']($elements['#items']); } - $output = ''; - // The index counter is used to keep aggregated and non-aggregated files in - // order by weight. - $index = 1; - $processed = array(); - $files = array(); $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); // A dummy query-string is added to filenames, to gain control over @@ -4459,65 +4503,68 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS // 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( + '#type' => 'html_tag', '#tag' => 'script', '#value' => '', '#attributes' => array( 'type' => 'text/javascript', ), ); - foreach ($items as $item) { - $query_string = empty($item['version']) ? $default_query_string : $js_version_string . $item['version']; + + $index = 1; + $items = array( + 'external' => array(), + 'file' => array(), + 'inline' => array(), + 'setting' => array(), + ); + $files = array(); + + foreach ($elements['#items'] as $item) { + $js_element = $element; + + $item += array( + '#attributes' => array(), + ); + $js_element['#attributes'] += $item['#attributes']; + + $query_string = empty($item['#version']) ? + $default_query_string : + $js_version_string . $item['#version']; switch ($item['type']) { case 'setting': - $js_element = $element; $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'] = '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)); + $items['setting'][$index++] = $js_element; break; case 'inline': - $js_element = $element; if ($item['defer']) { $js_element['#attributes']['defer'] = 'defer'; } $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)); + $items['inline'][$index++] = $js_element; break; case 'file': - $js_element = $element; if (!$item['preprocess'] || !$preprocess_js) { if ($item['defer']) { $js_element['#attributes']['defer'] = 'defer'; } $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)); + $items['file'][$index++] = $js_element; } else { // By increasing the index for each aggregated file, we maintain @@ -4528,19 +4575,18 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS // leading to better front-end performance of a website as a whole. // See drupal_add_js() for details. $key = 'aggregate_' . $item['group'] . '_' . $item['every_page'] . '_' . $index; - $processed[$key] = ''; $files[$key][$item['data']] = $item; + $items['file'][$index++] = ''; } break; case 'external': - $js_element = $element; // Preprocessing for external JavaScript files is ignored. if ($item['defer']) { $js_element['#attributes']['defer'] = 'defer'; } $js_element['#attributes']['src'] = $item['data']; - $processed[$index++] = theme('html_tag', array('element' => $js_element)); + $items['external'][$index++] = $js_element; break; } } @@ -4555,14 +4601,12 @@ 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)); + $items['file'][$key] = $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 $items; } /** diff --git a/modules/system/system.module b/modules/system/system.module index 53844d878f..779859c814 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -323,6 +323,10 @@ 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'), + ); // Input elements. $types['submit'] = array(