summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2013-07-04 09:38:19 +0100
committerNathaniel Catchpole2013-07-04 09:38:19 +0100
commit2c11efed11984c344d738512034e0ba7744e47f4 (patch)
treedfe0948fbae200d9f1bdb6843ebbc7a1c3fed842
parent36975492e9222753eba723cd5e07da1de1bb9ab1 (diff)
Issue #352951 by Wim Leers, mfer, mcjim, larowlan, RobLoach, sdboyer, Owen Barton, JohnAlbin: Make JS & CSS Preprocessing Pluggable.
-rw-r--r--core/core.services.yml22
-rw-r--r--core/includes/common.inc841
-rw-r--r--core/includes/install.core.inc4
-rw-r--r--core/lib/Drupal/Core/Asset/AssetCollectionGrouperInterface.php25
-rw-r--r--core/lib/Drupal/Core/Asset/AssetCollectionOptimizerInterface.php25
-rw-r--r--core/lib/Drupal/Core/Asset/AssetCollectionRendererInterface.php25
-rw-r--r--core/lib/Drupal/Core/Asset/AssetDumper.php52
-rw-r--r--core/lib/Drupal/Core/Asset/AssetDumperInterface.php27
-rw-r--r--core/lib/Drupal/Core/Asset/AssetOptimizerInterface.php25
-rw-r--r--core/lib/Drupal/Core/Asset/CssCollectionGrouper.php97
-rw-r--r--core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php179
-rw-r--r--core/lib/Drupal/Core/Asset/CssCollectionRenderer.php177
-rw-r--r--core/lib/Drupal/Core/Asset/CssOptimizer.php229
-rw-r--r--core/lib/Drupal/Core/Asset/JsCollectionGrouper.php80
-rw-r--r--core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php167
-rw-r--r--core/lib/Drupal/Core/Asset/JsCollectionRenderer.php97
-rw-r--r--core/lib/Drupal/Core/Asset/JsOptimizer.php31
-rw-r--r--core/modules/color/color.module10
-rw-r--r--core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php2
-rw-r--r--core/modules/simpletest/files/css_test_files/comment_hacks.css.unoptimized.css80
-rw-r--r--core/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css30
-rw-r--r--core/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css65
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php8
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsUnitTest.php63
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php49
-rw-r--r--core/modules/system/system.module4
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php216
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php572
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php227
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/css_test_files/comment_hacks.css (renamed from core/modules/simpletest/files/css_test_files/comment_hacks.css)0
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/css_test_files/comment_hacks.css.optimized.css (renamed from core/modules/simpletest/files/css_test_files/comment_hacks.css.optimized.css)0
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css (renamed from core/modules/simpletest/files/css_test_files/css_input_with_import.css)0
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.css (renamed from core/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css)0
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_without_import.css (renamed from core/modules/simpletest/files/css_test_files/css_input_without_import.css)0
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_without_import.css.optimized.css (renamed from core/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css)0
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/css_test_files/import1.css (renamed from core/modules/simpletest/files/css_test_files/import1.css)0
-rw-r--r--core/tests/Drupal/Tests/Core/Asset/css_test_files/import2.css (renamed from core/modules/simpletest/files/css_test_files/import2.css)0
37 files changed, 2349 insertions, 1080 deletions
diff --git a/core/core.services.yml b/core/core.services.yml
index d915e10..8f561ca 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -559,3 +559,25 @@ services:
tags:
- { name: event_subscriber }
arguments: ['@authentication']
+ asset.css.collection_renderer:
+ class: Drupal\Core\Asset\CssCollectionRenderer
+ asset.css.collection_optimizer:
+ class: Drupal\Core\Asset\CssCollectionOptimizer
+ arguments: [ '@asset.css.collection_grouper', '@asset.css.optimizer', '@asset.css.dumper', '@state' ]
+ asset.css.optimizer:
+ class: Drupal\Core\Asset\CssOptimizer
+ asset.css.collection_grouper:
+ class: Drupal\Core\Asset\CssCollectionGrouper
+ asset.css.dumper:
+ class: Drupal\Core\Asset\AssetDumper
+ asset.js.collection_renderer:
+ class: Drupal\Core\Asset\JsCollectionRenderer
+ asset.js.collection_optimizer:
+ class: Drupal\Core\Asset\JsCollectionOptimizer
+ arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer', '@asset.js.dumper', '@state' ]
+ asset.js.optimizer:
+ class: Drupal\Core\Asset\JsOptimizer
+ asset.js.collection_grouper:
+ class: Drupal\Core\Asset\JsCollectionGrouper
+ asset.js.dumper:
+ class: Drupal\Core\Asset\AssetDumper
diff --git a/core/includes/common.inc b/core/includes/common.inc
index e768bf4..3852870 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -26,6 +26,15 @@ use Drupal\Core\Template\Attribute;
use Zend\Feed\Writer\Writer;
use Zend\Feed\Reader\Reader;
+use Drupal\Core\Asset\CssCollectionRenderer;
+use Drupal\Core\Asset\CssCollectionOptimizer;
+use Drupal\Core\Asset\CssCollectionGrouper;
+use Drupal\Core\Asset\CssOptimizer;
+use Drupal\Core\Asset\JsCollectionRenderer;
+use Drupal\Core\Asset\JsCollectionOptimizer;
+use Drupal\Core\Asset\JsCollectionGrouper;
+use Drupal\Core\Asset\AssetDumper;
+
/**
* @file
* Common functions that many Drupal modules will need to reference.
@@ -1800,154 +1809,6 @@ function drupal_sort_css_js($a, $b) {
}
/**
- * Grouping callback: Groups CSS items by their types, media, and browsers.
- *
- * This function arranges the CSS items that are in the #items property of the
- * styles element into groups. Arranging the CSS items into groups serves two
- * purposes. When aggregation is enabled, files within a group are aggregated
- * into a single file, significantly improving page loading performance by
- * minimizing network traffic overhead. When aggregation is disabled, grouping
- * allows multiple files to be loaded from a single STYLE tag, enabling sites
- * with many modules enabled or a complex theme being used to stay within IE's
- * 31 CSS inclusion tag limit: http://drupal.org/node/228818.
- *
- * This function puts multiple items into the same group if they are groupable
- * and if they are for the same 'media' and 'browsers'. Items of the 'file' type
- * are groupable if their 'preprocess' flag is TRUE, items of the 'inline' type
- * are always groupable, and items of the 'external' type are never groupable.
- * This function also ensures that the process of grouping items does not change
- * their relative order. This requirement may result in multiple groups for the
- * same type, media, and browsers, if needed to accommodate other items in
- * between.
- *
- * @param $css
- * An array of CSS items, as returned by drupal_add_css(), but after
- * alteration performed by drupal_get_css().
- *
- * @return
- * An array of CSS groups. Each group contains the same keys (e.g., 'media',
- * 'data', etc.) as a CSS item from the $css parameter, with the value of
- * each key applying to the group as a whole. Each group also contains an
- * 'items' key, which is the subset of items from $css that are in the group.
- *
- * @see drupal_pre_render_styles()
- * @see system_element_info()
- */
-function drupal_group_css($css) {
- $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;
- // When creating a new group, we pre-increment $i, so by initializing it to
- // -1, the first group will have index 0.
- $i = -1;
- foreach ($css as $item) {
- // The browsers for which the CSS item needs to be loaded is part of the
- // information that determines when a new group is needed, but the order of
- // keys in the array doesn't matter, and we don't want a new group if all
- // that's different is that order.
- ksort($item['browsers']);
-
- // If the item can be grouped with other items, set $group_keys to an array
- // of information that must be the same for all items in its group. If the
- // item can't be grouped with other items, set $group_keys to FALSE. We
- // put items into a group that can be aggregated together: whether they will
- // be aggregated is up to the _drupal_css_aggregate() function or an
- // override of that function specified in hook_css_alter(), but regardless
- // of the details of that function, a group represents items that can be
- // aggregated. Since a group may be rendered with a single HTML tag, all
- // items in the group must share the same information that would need to be
- // part of that HTML tag.
- 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['media'], $item['browsers']) : FALSE;
- break;
- case 'inline':
- // Always group inline items.
- $group_keys = array($item['type'], $item['media'], $item['browsers']);
- break;
- case 'external':
- // Do not group external items.
- $group_keys = 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) {
- $i++;
- // 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[$i] = $item;
- unset($groups[$i]['data'], $groups[$i]['weight']);
- $groups[$i]['items'] = array();
- $current_group_keys = $group_keys ? $group_keys : NULL;
- }
-
- // Add the item to the current group.
- $groups[$i]['items'][] = $item;
- }
- return $groups;
-}
-
-/**
- * Aggregation callback: Aggregates CSS files and inline content.
- *
- * Having the browser load fewer CSS files results in much faster page loads
- * than when it loads many small files. This function aggregates files within
- * the same group into a single file unless the site-wide setting to do so is
- * disabled (commonly the case during site development). To optimize download,
- * it also compresses the aggregate files by removing comments, whitespace, and
- * other unnecessary content. Additionally, this functions aggregates inline
- * content together, regardless of the site-wide aggregation setting.
- *
- * @param $css_groups
- * An array of CSS groups as returned by drupal_group_css(). This function
- * modifies the group's 'data' property for each group that is aggregated.
- *
- * @see drupal_group_css()
- * @see drupal_pre_render_styles()
- * @see system_element_info()
- */
-function drupal_aggregate_css(&$css_groups) {
- // Only aggregate during normal site operation.
- if (defined('MAINTENANCE_MODE')) {
- $preprocess_css = FALSE;
- }
- else {
- $config = config('system.performance');
- $preprocess_css = $config->get('css.preprocess');
- }
-
- // For each group that needs aggregation, aggregate its items.
- foreach ($css_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_css) {
- $css_groups[$key]['data'] = drupal_build_css_cache($group['items']);
- }
- break;
- // Aggregate all inline CSS content into the group's data property.
- case 'inline':
- $css_groups[$key]['data'] = '';
- foreach ($group['items'] as $item) {
- $css_groups[$key]['data'] .= drupal_load_stylesheet_content($item['data'], $item['preprocess']);
- }
- break;
- }
- }
-}
-
-/**
* Pre-render callback: Adds the elements needed for CSS tags to be rendered.
*
* For production websites, LINK tags are preferable to STYLE tags with @import
@@ -1997,11 +1858,6 @@ function drupal_aggregate_css(&$css_groups) {
* 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
* A render array that will render to a string of XHTML CSS tags.
@@ -2009,393 +1865,13 @@ function drupal_aggregate_css(&$css_groups) {
* @see drupal_get_css()
*/
function drupal_pre_render_styles($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']);
- }
-
- // A dummy query-string is added to filenames, to gain control over
- // browser-caching. The string changes on every update or full cache
- // flush, forcing browsers to load a new copy of the files, as the
- // URL changed.
- $query_string = variable_get('css_js_query_string', '0');
-
- // For inline CSS to validate as XHTML, all CSS containing XHTML needs to be
- // wrapped in CDATA. To make that backwards compatible with HTML 4, we need to
- // comment out the CDATA-tag.
- $embed_prefix = "\n/* <![CDATA[ */\n";
- $embed_suffix = "\n/* ]]> */\n";
-
- // Defaults for LINK and STYLE elements.
- $link_element_defaults = array(
- '#type' => 'html_tag',
- '#tag' => 'link',
- '#attributes' => array(
- 'rel' => 'stylesheet',
- ),
- );
- $style_element_defaults = array(
- '#type' => 'html_tag',
- '#tag' => 'style',
- );
+ $css_assets = $elements['#items'];
- // Loop through each group.
- foreach ($elements['#groups'] as $group) {
- switch ($group['type']) {
- // For file items, there are three possibilites.
- // - The group has been aggregated: in this case, output a LINK tag for
- // the aggregate file.
- // - The group can be aggregated but has not been (most likely because
- // the site administrator disabled the site-wide setting): in this case,
- // output as few STYLE tags for the group as possible, using @import
- // statement for each file in the group. This enables us to stay within
- // IE's limit of 31 total CSS inclusion tags.
- // - The group contains items not eligible for aggregation (their
- // 'preprocess' flag has been set to FALSE): in this case, output a LINK
- // tag for each file.
- case 'file':
- // The group has been aggregated into a single file: output a LINK tag
- // for the aggregate file.
- if (isset($group['data'])) {
- $element = $link_element_defaults;
- $element['#attributes']['href'] = file_create_url($group['data']);
- $element['#attributes']['media'] = $group['media'];
- $element['#browsers'] = $group['browsers'];
- $elements[] = $element;
- }
- // The group can be aggregated, but hasn't been: combine multiple items
- // into as few STYLE tags as possible.
- elseif ($group['preprocess']) {
- $import = array();
- foreach ($group['items'] as $item) {
- // The dummy query string needs to be added to the URL to control
- // browser-caching. IE7 does not support a media type on the
- // @import statement, so we instead specify the media for the
- // group on the STYLE tag.
- $import[] = '@import url("' . check_plain(file_create_url($item['data']) . '?' . $query_string) . '");';
- }
- // In addition to IE's limit of 31 total CSS inclusion tags, it also
- // has a limit of 31 @import statements per STYLE tag.
- while (!empty($import)) {
- $import_batch = array_slice($import, 0, 31);
- $import = array_slice($import, 31);
- $element = $style_element_defaults;
- // This simplifies the JavaScript regex, allowing each line
- // (separated by \n) to be treated as a completely different string.
- // This means that we can use ^ and $ on one line at a time, and not
- // worry about style tags since they'll never match the regex.
- $element['#value'] = "\n" . implode("\n", $import_batch) . "\n";
- $element['#attributes']['media'] = $group['media'];
- $element['#browsers'] = $group['browsers'];
- $elements[] = $element;
- }
- }
- // The group contains items ineligible for aggregation: output a LINK
- // tag for each file.
- else {
- foreach ($group['items'] as $item) {
- $element = $link_element_defaults;
- // The dummy query string needs to be added to the URL to control
- // browser-caching.
- $query_string_separator = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
- $element['#attributes']['href'] = file_create_url($item['data']) . $query_string_separator . $query_string;
- $element['#attributes']['media'] = $item['media'];
- $element['#browsers'] = $group['browsers'];
- $elements[] = $element;
- }
- }
- break;
- // For inline content, the 'data' property contains the CSS content. If
- // the group's 'data' property is set, then output it in a single STYLE
- // tag. Otherwise, output a separate STYLE tag for each item.
- case 'inline':
- if (isset($group['data'])) {
- $element = $style_element_defaults;
- $element['#value'] = $group['data'];
- $element['#value_prefix'] = $embed_prefix;
- $element['#value_suffix'] = $embed_suffix;
- $element['#attributes']['media'] = $group['media'];
- $element['#browsers'] = $group['browsers'];
- $elements[] = $element;
- }
- else {
- foreach ($group['items'] as $item) {
- $element = $style_element_defaults;
- $element['#value'] = $item['data'];
- $element['#value_prefix'] = $embed_prefix;
- $element['#value_suffix'] = $embed_suffix;
- $element['#attributes']['media'] = $item['media'];
- $element['#browsers'] = $group['browsers'];
- $elements[] = $element;
- }
- }
- break;
- // Output a LINK tag for each external item. The item's 'data' property
- // contains the full URL.
- case 'external':
- foreach ($group['items'] as $item) {
- $element = $link_element_defaults;
- $element['#attributes']['href'] = $item['data'];
- $element['#attributes']['media'] = $item['media'];
- $element['#browsers'] = $group['browsers'];
- $elements[] = $element;
- }
- break;
- }
+ // Aggregate the CSS if necessary, but only during normal site operation.
+ if (!defined('MAINTENANCE_MODE') && config('system.performance')->get('css.preprocess')) {
+ $css_assets = \Drupal::service('asset.css.collection_optimizer')->optimize($css_assets);
}
-
- return $elements;
-}
-
-/**
- * Aggregates and optimizes CSS files into a cache file in the files directory.
- *
- * The file name for the CSS cache file is generated from the hash of the
- * aggregated contents of the files in $css. This forces proxies and browsers
- * to download new CSS when the CSS changes.
- *
- * The cache file name is retrieved on a page load via a lookup variable that
- * contains an associative array. The array key is the hash of the file names
- * in $css while the value is the cache file name. The cache file is generated
- * in two cases. First, if there is no file name value for the key, which will
- * happen if a new file name has been added to $css or after the lookup
- * variable is emptied to force a rebuild of the cache. Second, the cache file
- * is generated if it is missing on disk. Old cache files are not deleted
- * immediately when the lookup variable is emptied, but are deleted after a set
- * period by drupal_delete_file_if_stale(). This ensures that files referenced
- * by a cached page will still be available.
- *
- * @param $css
- * An array of CSS files to aggregate and compress into one file.
- *
- * @return
- * The URI of the CSS cache file, or FALSE if the file could not be saved.
- */
-function drupal_build_css_cache($css) {
- $data = '';
- $uri = '';
- $map = Drupal::state()->get('drupal_css_cache_files') ?: array();
- // Create a new array so that only the file names are used to create the hash.
- // This prevents new aggregates from being created unnecessarily.
- $css_data = array();
- foreach ($css as $css_file) {
- $css_data[] = $css_file['data'];
- }
- $key = hash('sha256', serialize($css_data));
- if (isset($map[$key])) {
- $uri = $map[$key];
- }
-
- if (empty($uri) || !file_exists($uri)) {
- // Build aggregate CSS file.
- foreach ($css as $stylesheet) {
- // Only 'file' stylesheets can be aggregated.
- if ($stylesheet['type'] == 'file') {
- $contents = drupal_load_stylesheet($stylesheet['data'], TRUE);
-
- // Get the parent directory of this file, relative to the Drupal root.
- $css_base_url = substr($stylesheet['data'], 0, strrpos($stylesheet['data'], '/'));
- _drupal_build_css_path(NULL, $css_base_url . '/');
- // Anchor all paths in the CSS with its base URL, ignoring external and absolute paths.
- $data .= preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', '_drupal_build_css_path', $contents);
- }
- }
-
- // 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, $data, $matches);
- $data = preg_replace($regexp, '', $data);
- $data = implode('', $matches[0]) . $data;
-
- // Prefix filename to prevent blocking by firewalls which reject files
- // starting with "ad*".
- $filename = 'css_' . Crypt::hashBase64($data) . '.css';
- // Create the css/ within the files folder.
- $csspath = 'public://css';
- $uri = $csspath . '/' . $filename;
- // Create the CSS file.
- file_prepare_directory($csspath, FILE_CREATE_DIRECTORY);
- if (!file_exists($uri) && !file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE)) {
- return FALSE;
- }
- // If CSS gzip compression is enabled and the zlib extension is available
- // then create a gzipped version of this file. This file is served
- // conditionally to browsers that accept gzip using .htaccess rules.
- // It's possible that the rewrite rules in .htaccess aren't working on this
- // server, but there's no harm (other than the time spent generating the
- // file) in generating the file anyway. Sites on servers where rewrite rules
- // aren't working can set css.gzip to FALSE in order to skip
- // generating a file that won't be used.
- if (config('system.performance')->get('css.gzip') && extension_loaded('zlib')) {
- if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
- return FALSE;
- }
- }
- // Save the updated map.
- $map[$key] = $uri;
- Drupal::state()->set('drupal_css_cache_files', $map);
- }
- return $uri;
-}
-
-/**
- * Prefixes all paths within a CSS file for drupal_build_css_cache().
- */
-function _drupal_build_css_path($matches, $base = NULL) {
- $_base = &drupal_static(__FUNCTION__);
- // Store base path for preg_replace_callback.
- if (isset($base)) {
- $_base = $base;
- }
-
- // Prefix with base and remove '../' segments where possible.
- $path = $_base . $matches[1];
- $last = '';
- while ($path != $last) {
- $last = $path;
- $path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path);
- }
- return 'url(' . file_create_url($path) . ')';
-}
-
-/**
- * Loads the stylesheet and resolves all @import commands.
- *
- * Loads a stylesheet and replaces @import commands with the contents of the
- * imported file. Use this instead of file_get_contents when processing
- * stylesheets.
- *
- * The returned contents are compressed removing white space and comments only
- * when CSS aggregation is enabled. This optimization will not apply for
- * color.module enabled themes with CSS aggregation turned off.
- *
- * @param $file
- * Name of the stylesheet to be processed.
- * @param $optimize
- * Defines if CSS contents should be compressed or not.
- * @param $reset_basepath
- * Used internally to facilitate recursive resolution of @import commands.
- *
- * @return
- * Contents of the stylesheet, including any resolved @import commands.
- */
-function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE) {
- // These statics are not cache variables, so we don't use drupal_static().
- static $_optimize, $basepath;
- if ($reset_basepath) {
- $basepath = '';
- }
- // Store the value of $optimize for preg_replace_callback with nested
- // @import loops.
- if (isset($optimize)) {
- $_optimize = $optimize;
- }
-
- // Stylesheets are relative one to each other. Start by adding a base path
- // prefix provided by the parent stylesheet (if necessary).
- if ($basepath && !file_uri_scheme($file)) {
- $file = $basepath . '/' . $file;
- }
- $basepath = dirname($file);
-
- // Load the CSS stylesheet. We suppress errors because themes may specify
- // stylesheets in their .info.yml file that don't exist in the theme's path,
- // but are merely there to disable certain module CSS files.
- if ($contents = @file_get_contents($file)) {
- // Return the processed stylesheet.
- return drupal_load_stylesheet_content($contents, $_optimize);
- }
-
- return '';
-}
-
-/**
- * Processes the contents of a stylesheet for aggregation.
- *
- * @param $contents
- * The contents of the stylesheet.
- * @param $optimize
- * (optional) Boolean whether CSS contents should be minified. Defaults to
- * FALSE.
- *
- * @return
- * Contents of the stylesheet including the imported stylesheets.
- */
-function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
- // Remove multiple charset declarations for standards compliance (and fixing Safari problems).
- $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
-
- if ($optimize) {
- // Perform some safe CSS optimizations.
- // Regexp to match comment blocks.
- $comment = '/\*[^*]*\*+(?:[^/*][^*]*\*+)*/';
- // Regexp to match double quoted strings.
- $double_quot = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
- // Regexp to match single quoted strings.
- $single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'";
- // Strip all comment blocks, but keep double/single quoted strings.
- $contents = preg_replace(
- "<($double_quot|$single_quot)|$comment>Ss",
- "$1",
- $contents
- );
- // Remove certain whitespace.
- // There are different conditions for removing leading and trailing
- // whitespace.
- // @see http://php.net/manual/regexp.reference.subpatterns.php
- $contents = preg_replace('<
- # Strip leading and trailing whitespace.
- \s*([@{};,])\s*
- # Strip only leading whitespace from:
- # - Closing parenthesis: Retain "@media (bar) and foo".
- | \s+([\)])
- # Strip only trailing whitespace from:
- # - Opening parenthesis: Retain "@media (bar) and foo".
- # - Colon: Retain :pseudo-selectors.
- | ([\(:])\s+
- >xS',
- // Only one of the three capturing groups will match, so its reference
- // will contain the wanted value and the references for the
- // two non-matching groups will be replaced with empty strings.
- '$1$2$3',
- $contents
- );
- // End the file with a new line.
- $contents = trim($contents);
- $contents .= "\n";
- }
-
- // Replaces @import commands with the actual stylesheet content.
- // This happens recursively but omits external files.
- $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\s*\)?\s*;/', '_drupal_load_stylesheet', $contents);
- return $contents;
-}
-
-/**
- * Loads stylesheets recursively and returns contents with corrected paths.
- *
- * This function is used for recursive loading of stylesheets and
- * returns the stylesheet content with all url() paths corrected.
- */
-function _drupal_load_stylesheet($matches) {
- $filename = $matches[1];
- // Load the imported stylesheet and replace @import commands in there as well.
- $file = drupal_load_stylesheet($filename, NULL, FALSE);
-
- // Determine the file's directory.
- $directory = dirname($filename);
- // If the file is in the current directory, make sure '.' doesn't appear in
- // the url() path.
- $directory = $directory == '.' ? '' : $directory .'/';
-
- // Alter all internal url() paths. Leave external paths alone. We don't need
- // to normalize absolute paths here (i.e. remove folder/... segments) because
- // that will be done later.
- return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)/i', 'url(\1'. $directory, $file);
+ return \Drupal::service('asset.css.collection_renderer')->render($css_assets);
}
/**
@@ -3017,214 +2493,14 @@ function drupal_merge_js_settings($settings_items) {
* @see drupal_get_js()
*/
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']);
- }
-
- // A dummy query-string is added to filenames, to gain control over
- // browser-caching. The string changes on every update or full cache
- // flush, forcing browsers to load a new copy of the files, as the
- // URL changed. Files that should not be cached (see drupal_add_js())
- // get REQUEST_TIME as query-string instead, to enforce reload on every
- // page request.
- $default_query_string = variable_get('css_js_query_string', '0');
-
- // For inline JavaScript to validate as XHTML, all JavaScript containing
- // XHTML needs to be wrapped in CDATA. To make that backwards compatible
- // with HTML 4, we need to comment out the CDATA-tag.
- $embed_prefix = "\n<!--//--><![CDATA[//><!--\n";
- $embed_suffix = "\n//--><!]]>\n";
-
- // 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=');
-
- // Defaults for each SCRIPT element.
- $element_defaults = array(
- '#type' => 'html_tag',
- '#tag' => 'script',
- '#value' => '',
- );
-
- // Loop through each group.
- foreach ($elements['#groups'] as $group) {
- // If a group of files has been aggregated into a single file,
- // $group['data'] contains the URI of the aggregate file. Add a single
- // script element for this file.
- if ($group['type'] == 'file' && isset($group['data'])) {
- $element = $element_defaults;
- $element['#attributes']['src'] = file_create_url($group['data']);
- $element['#browsers'] = $group['browsers'];
- $elements[] = $element;
- }
- // For non-file types, and non-aggregated files, add a script element per
- // item.
- else {
- foreach ($group['items'] as $item) {
- // Element properties that do not depend on item type.
- $element = $element_defaults;
- $element['#browsers'] = $item['browsers'];
-
- // Element properties that depend on item type.
- switch ($item['type']) {
- case 'setting':
- $element['#value_prefix'] = $embed_prefix;
- $element['#value'] = 'var drupalSettings = ' . drupal_json_encode(drupal_merge_js_settings($item['data'])) . ";";
- $element['#value_suffix'] = $embed_suffix;
- break;
-
- case 'inline':
- $element['#value_prefix'] = $embed_prefix;
- $element['#value'] = $item['data'];
- $element['#value_suffix'] = $embed_suffix;
- break;
-
- case 'file':
- $query_string = empty($item['version']) ? $default_query_string : $js_version_string . $item['version'];
- $query_string_separator = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
- $element['#attributes']['src'] = file_create_url($item['data']) . $query_string_separator . ($item['cache'] ? $query_string : REQUEST_TIME);
- break;
-
- case 'external':
- $element['#attributes']['src'] = $item['data'];
- break;
- }
+ $js_assets = $elements['#items'];
- // Attributes may only be set if this script is output independently.
- if (!empty($element['#attributes']['src']) && !empty($item['attributes'])) {
- $element['#attributes'] += $item['attributes'];
- }
-
- $elements[] = $element;
- }
- }
- }
-
- return $elements;
-}
-
-/**
- * Default callback to group JavaScript items.
- *
- * This function arranges the JavaScript items that are in the #items property
- * of the scripts element into groups. When aggregation is enabled, files within
- * a group are aggregated into a single file, significantly improving page
- * loading performance by minimizing network traffic overhead.
- *
- * This function puts multiple items into the same group if they are groupable
- * and if they are for the same browsers. Items of the 'file' type are groupable
- * if their 'preprocess' flag is TRUE. Items of the 'inline', 'settings', or
- * 'external' type are not groupable.
- *
- * This function also ensures that the process of grouping items does not change
- * their relative order. This requirement may result in multiple groups for the
- * same type and browsers, if needed to accommodate other items in
- * between.
- *
- * @param $javascript
- * An array of JavaScript items, as returned by drupal_add_js(), but after
- * alteration performed by drupal_get_js().
- *
- * @return
- * An array of JavaScript groups. Each group contains the same keys (e.g.,
- * 'data', etc.) as a JavaScript item from the $javascript parameter, with the
- * value of each key applying to the group as a whole. Each group also
- * contains an 'items' key, which is the subset of items from $javascript that
- * are in the group.
- *
- * @see drupal_pre_render_scripts()
- */
-function drupal_group_js($javascript) {
- $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 ($javascript as $item) {
- // The browsers for which the JavaScript item needs to be loaded is part of
- // the information that determines when a new group is needed, but the order
- // of keys in the array doesn't matter, and we don't want a new group if all
- // that's different is that order.
- 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_js() for details about that.
- $group_keys = $item['preprocess'] ? array($item['type'], $item['group'], $item['every_page'], $item['browsers']) : FALSE;
- break;
-
- case 'external':
- case 'setting':
- case 'inline':
- // Do not group external, settings, and inline items.
- $group_keys = 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;
-}
-
-/**
- * Default callback to aggregate JavaScript files.
- *
- * Having the browser load fewer JavaScript files results in much faster page
- * loads than when it loads many small files. This function aggregates files
- * within the same group into a single file unless the site-wide setting to do
- * so is disabled (commonly the case during site development). To optimize
- * download, it also compresses the aggregate files by removing comments,
- * whitespace, and other unnecessary content.
- *
- * @param $js_groups
- * An array of JavaScript groups as returned by drupal_group_js(). For each
- * group that is aggregated, this function sets the value of the group's
- * 'data' key to the URI of the aggregate file.
- *
- * @see drupal_group_js()
- * @see drupal_pre_render_scripts()
- */
-function drupal_aggregate_js(&$js_groups) {
- // Only aggregate during normal site operation.
- if (defined('MAINTENANCE_MODE')) {
- $preprocess_js = FALSE;
- }
- else {
- $config = config('system.performance');
- $preprocess_js = $config->get('js.preprocess');
- }
-
- if ($preprocess_js) {
- foreach ($js_groups as $key => $group) {
- if ($group['type'] == 'file' && $group['preprocess']) {
- $js_groups[$key]['data'] = drupal_build_js_cache($group['items']);
- }
- }
+ // Aggregate the JavaScript if necessary, but only during normal site
+ // operation.
+ if (!defined('MAINTENANCE_MODE') && config('system.performance')->get('js.preprocess')) {
+ $js_assets = \Drupal::service('asset.js.collection_optimizer')->optimize($js_assets);
}
+ return \Drupal::service('asset.js.collection_renderer')->render($js_assets);
}
/**
@@ -3727,83 +3003,6 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgro
}
/**
- * Aggregates JavaScript files into a cache file in the files directory.
- *
- * The file name for the JavaScript cache file is generated from the hash of
- * the aggregated contents of the files in $files. This forces proxies and
- * browsers to download new JavaScript when the JavaScript changes.
- *
- * The cache file name is retrieved on a page load via a lookup variable that
- * contains an associative array. The array key is the hash of the names in
- * $files while the value is the cache file name. The cache file is generated
- * in two cases. First, if there is no file name value for the key, which will
- * happen if a new file name has been added to $files or after the lookup
- * variable is emptied to force a rebuild of the cache. Second, the cache file
- * is generated if it is missing on disk. Old cache files are not deleted
- * immediately when the lookup variable is emptied, but are deleted after a set
- * period by drupal_delete_file_if_stale(). This ensures that files referenced
- * by a cached page will still be available.
- *
- * @param $files
- * An array of JavaScript files to aggregate and compress into one file.
- *
- * @return
- * The URI of the cache file, or FALSE if the file could not be saved.
- */
-function drupal_build_js_cache($files) {
- $contents = '';
- $uri = '';
- $map = Drupal::state()->get('system.js_cache_files') ?: array();
- // Create a new array so that only the file names are used to create the hash.
- // This prevents new aggregates from being created unnecessarily.
- $js_data = array();
- foreach ($files as $file) {
- $js_data[] = $file['data'];
- }
- $key = hash('sha256', serialize($js_data));
- if (isset($map[$key])) {
- $uri = $map[$key];
- }
-
- if (empty($uri) || !file_exists($uri)) {
- // Build aggregate JS file.
- foreach ($files as $info) {
- if ($info['preprocess']) {
- // Append a ';' and a newline after each JS file to prevent them from running together.
- $contents .= file_get_contents($info['data']) . ";\n";
- }
- }
- // Prefix filename to prevent blocking by firewalls which reject files
- // starting with "ad*".
- $filename = 'js_' . Crypt::hashBase64($contents) . '.js';
- // Create the js/ within the files folder.
- $jspath = 'public://js';
- $uri = $jspath . '/' . $filename;
- // Create the JS file.
- file_prepare_directory($jspath, FILE_CREATE_DIRECTORY);
- if (!file_exists($uri) && !file_unmanaged_save_data($contents, $uri, FILE_EXISTS_REPLACE)) {
- return FALSE;
- }
- // If JS gzip compression is enabled and the zlib extension is available
- // then create a gzipped version of this file. This file is served
- // conditionally to browsers that accept gzip using .htaccess rules.
- // It's possible that the rewrite rules in .htaccess aren't working on this
- // server, but there's no harm (other than the time spent generating the
- // file) in generating the file anyway. Sites on servers where rewrite rules
- // aren't working can set js.gzip to FALSE in order to skip
- // generating a file that won't be used.
- if (config('system.performance')->get('js.gzip') && extension_loaded('zlib')) {
- if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($contents, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
- return FALSE;
- }
- }
- $map[$key] = $uri;
- Drupal::state()->set('system.js_cache_files', $map);
- }
- return $uri;
-}
-
-/**
* Deletes old cached JavaScript files and variables.
*/
function drupal_clear_js_cache() {
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index ee5c0a4..4d0d6da 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -436,6 +436,10 @@ function install_begin_request(&$install_state) {
$container->register('url_generator', 'Drupal\Core\Routing\NullGenerator');
+ // Register the CSS and JavaScript asset collection renderers.
+ $container->register('asset.css.collection_renderer', 'Drupal\Core\Asset\CssCollectionRenderer');
+ $container->register('asset.js.collection_renderer', 'Drupal\Core\Asset\JsCollectionRenderer');
+
Drupal::setContainer($container);
}
diff --git a/core/lib/Drupal/Core/Asset/AssetCollectionGrouperInterface.php b/core/lib/Drupal/Core/Asset/AssetCollectionGrouperInterface.php
new file mode 100644
index 0000000..94f9e85
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetCollectionGrouperInterface.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Asset\AssetCollectionGrouperInterface.
+ */
+
+namespace Drupal\Core\Asset;
+
+/**
+ * Interface defining a service that logically groups a collection of assets.
+ */
+interface AssetCollectionGrouperInterface {
+
+ /**
+ * Groups a collection of assets into logical groups of asset collections.
+ *
+ * @param array $assets
+ * An asset collection.
+ *
+ * @return array
+ * A sorted array of asset groups.
+ */
+ public function group(array $assets);
+
+}
diff --git a/core/lib/Drupal/Core/Asset/AssetCollectionOptimizerInterface.php b/core/lib/Drupal/Core/Asset/AssetCollectionOptimizerInterface.php
new file mode 100644
index 0000000..f7102d2
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetCollectionOptimizerInterface.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Asset\AssetCollectionOptimizerInterface.
+ */
+
+namespace Drupal\Core\Asset;
+
+/**
+ * Interface defining a service that optimizes a collection of assets.
+ */
+interface AssetCollectionOptimizerInterface {
+
+ /**
+ * Optimizes a collection of assets.
+ *
+ * @param array $assets
+ * An asset collection.
+ *
+ * @return array
+ * An optimized asset collection.
+ */
+ public function optimize(array $assets);
+
+}
diff --git a/core/lib/Drupal/Core/Asset/AssetCollectionRendererInterface.php b/core/lib/Drupal/Core/Asset/AssetCollectionRendererInterface.php
new file mode 100644
index 0000000..9568ab0
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetCollectionRendererInterface.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Asset\AssetCollectionRendererInterface.
+ */
+
+namespace Drupal\Core\Asset;
+
+/**
+ * Interface defining a service that generates a render array to render assets.
+ */
+interface AssetCollectionRendererInterface {
+
+ /**
+ * Renders an asset collection.
+ *
+ * @param array $assets
+ * An asset collection.
+ *
+ * @return array
+ * A render array to render the asset collection.
+ */
+ public function render(array $assets);
+
+}
diff --git a/core/lib/Drupal/Core/Asset/AssetDumper.php b/core/lib/Drupal/Core/Asset/AssetDumper.php
new file mode 100644
index 0000000..b21beda
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetDumper.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\AssetDumper.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetDumperInterface;
+use Drupal\Component\Utility\Crypt;
+
+/**
+ * Dumps a CSS or JavaScript asset.
+ */
+class AssetDumper implements AssetDumperInterface {
+
+ /**
+ * {@inheritdoc}
+ *
+ * The file name for the CSS or JS cache file is generated from the hash of
+ * the aggregated contents of the files in $data. This forces proxies and
+ * browsers to download new CSS when the CSS changes.
+ */
+ public function dump($data, $file_extension) {
+ // Prefix filename to prevent blocking by firewalls which reject files
+ // starting with "ad*".
+ $filename = $file_extension. '_' . Crypt::hashBase64($data) . '.' . $file_extension;
+ // Create the css/ or js/ path within the files folder.
+ $path = 'public://' . $file_extension;
+ $uri = $path . '/' . $filename;
+ // Create the CSS or JS file.
+ file_prepare_directory($path, FILE_CREATE_DIRECTORY);
+ if (!file_exists($uri) && !file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE)) {
+ return FALSE;
+ }
+ // If CSS/JS gzip compression is enabled and the zlib extension is available
+ // then create a gzipped version of this file. This file is served
+ // conditionally to browsers that accept gzip using .htaccess rules.
+ // It's possible that the rewrite rules in .htaccess aren't working on this
+ // server, but there's no harm (other than the time spent generating the
+ // file) in generating the file anyway. Sites on servers where rewrite rules
+ // aren't working can set css.gzip to FALSE in order to skip
+ // generating a file that won't be used.
+ if (config('system.performance')->get($file_extension . '.gzip') && extension_loaded('zlib')) {
+ if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
+ return FALSE;
+ }
+ }
+ return $uri;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Asset/AssetDumperInterface.php b/core/lib/Drupal/Core/Asset/AssetDumperInterface.php
new file mode 100644
index 0000000..5f70aa6
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetDumperInterface.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Asset\AssetDumperInterface.
+ */
+
+namespace Drupal\Core\Asset;
+
+/**
+ * Interface defining a service that dumps an (optimized) asset.
+ */
+interface AssetDumperInterface {
+
+ /**
+ * Dumps an (optimized) asset to persistent storage.
+ *
+ * @param string $data
+ * An (optimized) asset's contents.
+ * @param string $file_extension
+ * The file extension of this asset.
+ *
+ * @return string
+ * An URI to access the dumped asset.
+ */
+ public function dump($data, $file_extension);
+
+}
diff --git a/core/lib/Drupal/Core/Asset/AssetOptimizerInterface.php b/core/lib/Drupal/Core/Asset/AssetOptimizerInterface.php
new file mode 100644
index 0000000..31c4b93
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/AssetOptimizerInterface.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Asset\AssetCollectionOptimizerInterface.
+ */
+
+namespace Drupal\Core\Asset;
+
+/**
+ * Interface defining a service that optimizes an asset.
+ */
+interface AssetOptimizerInterface {
+
+ /**
+ * Optimizes an asset.
+ *
+ * @param array $asset
+ * An asset.
+ *
+ * @return string
+ * The optimized asset's contents.
+ */
+ public function optimize(array $asset);
+
+}
diff --git a/core/lib/Drupal/Core/Asset/CssCollectionGrouper.php b/core/lib/Drupal/Core/Asset/CssCollectionGrouper.php
new file mode 100644
index 0000000..2ccabfa
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/CssCollectionGrouper.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\CssCollectionGrouper.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetCollectionGrouperInterface;
+
+/**
+ * Groups CSS assets.
+ */
+class CssCollectionGrouper implements AssetCollectionGrouperInterface {
+
+ /**
+ * {@inheritdoc}
+ *
+ * Puts multiple items into the same group if they are groupable and if they
+ * are for the same 'media' and 'browsers'. Items of the 'file' type are
+ * groupable if their 'preprocess' flag is TRUE, items of the 'inline' type
+ * are always groupable, and items of the 'external' type are never groupable.
+ *
+ * Also ensures that the process of grouping items does not change their
+ * relative order. This requirement may result in multiple groups for the same
+ * type, media, and browsers, if needed to accommodate other items in between.
+ */
+ public function group(array $css_assets) {
+ $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;
+ // When creating a new group, we pre-increment $i, so by initializing it to
+ // -1, the first group will have index 0.
+ $i = -1;
+ foreach ($css_assets as $item) {
+ // The browsers for which the CSS item needs to be loaded is part of the
+ // information that determines when a new group is needed, but the order
+ // of keys in the array doesn't matter, and we don't want a new group if
+ // all that's different is that order.
+ ksort($item['browsers']);
+
+ // If the item can be grouped with other items, set $group_keys to an
+ // array of information that must be the same for all items in its group.
+ // If the item can't be grouped with other items, set $group_keys to
+ // FALSE. We put items into a group that can be aggregated together:
+ // whether they will be aggregated is up to the _drupal_css_aggregate()
+ // function or an
+ // override of that function specified in hook_css_alter(), but regardless
+ // of the details of that function, a group represents items that can be
+ // aggregated. Since a group may be rendered with a single HTML tag, all
+ // items in the group must share the same information that would need to
+ // be part of that HTML tag.
+ 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['media'], $item['browsers']) : FALSE;
+ break;
+
+ case 'inline':
+ // Always group inline items.
+ $group_keys = array($item['type'], $item['media'], $item['browsers']);
+ break;
+
+ case 'external':
+ // Do not group external items.
+ $group_keys = 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) {
+ $i++;
+ // Initialize the new group with the same properties as the first item
+ // being placed into it. The item's 'data', 'weight' and 'basename'
+ // properties are unique to the item and should not be carried over to
+ // the group.
+ $groups[$i] = $item;
+ unset($groups[$i]['data'], $groups[$i]['weight'], $groups[$i]['basename']);
+ $groups[$i]['items'] = array();
+ $current_group_keys = $group_keys ? $group_keys : NULL;
+ }
+
+ // Add the item to the current group.
+ $groups[$i]['items'][] = $item;
+ }
+
+ return $groups;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php
new file mode 100644
index 0000000..5d43231
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\CssCollectionOptimizer.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetCollectionOptimizerInterface;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+
+/**
+ * Optimizes CSS assets.
+ */
+class CssCollectionOptimizer implements AssetCollectionOptimizerInterface {
+
+ /**
+ * A CSS asset grouper.
+ *
+ * @var \Drupal\Core\Asset\CssCollectionGrouper
+ */
+ protected $grouper;
+
+ /**
+ * A CSS asset optimizer.
+ *
+ * @var \Drupal\Core\Asset\CssOptimizer
+ */
+ protected $optimizer;
+
+ /**
+ * An asset dumper.
+ *
+ * @var \Drupal\Core\Asset\AssetDumper
+ */
+ protected $dumper;
+
+ /**
+ * The state key/value store.
+ *
+ * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+ */
+ protected $state;
+
+ /**
+ * Constructs a CssCollectionOptimizer.
+ *
+ * @param \Drupal\Core\Asset\AssetCollectionGrouperInterface
+ * The grouper for CSS assets.
+ * @param \Drupal\Core\Asset\AssetOptimizerInterface
+ * The optimizer for a single CSS asset.
+ * @param \Drupal\Core\Asset\AssetDumperInterface
+ * The dumper for optimized CSS assets.
+ * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+ * The state key/value store.
+ */
+ public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, KeyValueStoreInterface $state) {
+ $this->grouper = $grouper;
+ $this->optimizer = $optimizer;
+ $this->dumper = $dumper;
+ $this->state = $state;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * The cache file name is retrieved on a page load via a lookup variable that
+ * contains an associative array. The array key is the hash of the file names
+ * in $css while the value is the cache file name. The cache file is generated
+ * in two cases. First, if there is no file name value for the key, which will
+ * happen if a new file name has been added to $css or after the lookup
+ * variable is emptied to force a rebuild of the cache. Second, the cache file
+ * is generated if it is missing on disk. Old cache files are not deleted
+ * immediately when the lookup variable is emptied, but are deleted after a
+ * set period by drupal_delete_file_if_stale(). This ensures that files
+ * referenced by a cached page will still be available.
+ */
+ public function optimize(array $css_assets) {
+ // Group the assets.
+ $css_groups = $this->grouper->group($css_assets);
+
+ // Now optimize (concatenate + minify) and dump each asset group, unless
+ // that was already done, in which case it should appear in
+ // drupal_css_cache_files.
+ // Drupal contrib can override this default CSS aggregator to keep the same
+ // grouping, optimizing and dumping, but change the strategy that is used to
+ // determine when the aggregate should be rebuilt (e.g. mtime, HTTPS …).
+ $map = $this->state->get('drupal_css_cache_files') ?: array();
+ $css_assets = array();
+ foreach ($css_groups as $order => $css_group) {
+ // We have to return a single asset, not a group of assets. It is now up
+ // to one of the pieces of code in the switch statement below to set the
+ // 'data' property to the appropriate value.
+ $css_assets[$order] = $css_group;
+ unset($css_assets[$order]['items']);
+
+ switch ($css_group['type']) {
+ case 'file':
+ // No preprocessing, single CSS asset: just use the existing URI.
+ if (!$css_group['preprocess']) {
+ $uri = $css_group['items'][0]['data'];
+ $css_assets[$order]['data'] = $uri;
+ }
+ // Preprocess (aggregate), unless the aggregate file already exists.
+ else {
+ $key = $this->generateHash($css_group);
+ $uri = '';
+ if (isset($map[$key])) {
+ $uri = $map[$key];
+ }
+ if (empty($uri) || !file_exists($uri)) {
+ // Optimize each asset within the group.
+ $data = '';
+ foreach ($css_group['items'] as $css_asset) {
+ $data .= $this->optimizer->optimize($css_asset);
+ }
+ // 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, $data, $matches);
+ $data = preg_replace($regexp, '', $data);
+ $data = implode('', $matches[0]) . $data;
+ // Dump the optimized CSS for this group into an aggregate file.
+ $uri = $this->dumper->dump($data, 'css');
+ // Set the URI for this group's aggregate file.
+ $css_assets[$order]['data'] = $uri;
+ // Persist the URI for this aggregate file.
+ $map[$key] = $uri;
+ $this->state->set('drupal_css_cache_files', $map);
+ }
+ else {
+ // Use the persisted URI for the optimized CSS file.
+ $css_assets[$order]['data'] = $uri;
+ }
+ $css_assets[$order]['preprocessed'] = TRUE;
+ }
+ break;
+
+ case 'inline':
+ // We don't do any caching for inline CSS assets.
+ $data = '';
+ foreach ($css_group['items'] as $css_asset) {
+ $data .= $this->optimizer->optimize($css_asset);
+ }
+ unset($css_assets[$order]['data']['items']);
+ $css_assets[$order]['data'] = $data;
+ break;
+
+ case 'external':
+ // We don't do any aggregation and hence also no caching for external
+ // CSS assets.
+ $uri = $css_group['items'][0]['data'];
+ $css_assets[$order]['data'] = $uri;
+ break;
+ }
+ }
+
+ return $css_assets;
+ }
+
+ /**
+ * Generate a hash for a given group of CSS assets.
+ *
+ * @param array $css_group
+ * A group of CSS assets.
+ *
+ * @return string
+ * A hash to uniquely identify the given group of CSS assets.
+ */
+ protected function generateHash(array $css_group) {
+ $css_data = array();
+ foreach ($css_group['items'] as $css_file) {
+ $css_data[] = $css_file['data'];
+ }
+ return hash('sha256', serialize($css_data));
+ }
+}
diff --git a/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php b/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php
new file mode 100644
index 0000000..6d36ede
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\CssCollectionRenderer.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetCollectionRendererInterface;
+use Drupal\Component\Utility\String;
+
+/**
+ * Renders CSS assets.
+ */
+class CssCollectionRenderer implements AssetCollectionRendererInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function render(array $css_assets) {
+ $elements = array();
+
+ // A dummy query-string is added to filenames, to gain control over
+ // browser-caching. The string changes on every update or full cache
+ // flush, forcing browsers to load a new copy of the files, as the
+ // URL changed.
+ $query_string = variable_get('css_js_query_string', '0');
+
+ // Defaults for LINK and STYLE elements.
+ $link_element_defaults = array(
+ '#type' => 'html_tag',
+ '#tag' => 'link',
+ '#attributes' => array(
+ 'rel' => 'stylesheet',
+ ),
+ );
+ $style_element_defaults = array(
+ '#type' => 'html_tag',
+ '#tag' => 'style',
+ );
+
+ // For filthy IE hack.
+ $current_ie_group_keys = NULL;
+ $get_ie_group_key = function ($css_asset) {
+ return array($css_asset['type'], $css_asset['preprocess'], $css_asset['group'], $css_asset['every_page'], $css_asset['media'], $css_asset['browsers']);
+ };
+
+ // Loop through all CSS assets, by key, to allow for the special IE
+ // workaround.
+ $css_assets_keys = array_keys($css_assets);
+ for ($i = 0; $i < count($css_assets_keys); $i++) {
+ $css_asset = $css_assets[$css_assets_keys[$i]];
+ switch ($css_asset['type']) {
+ // For file items, there are three possibilites.
+ // - There are up to 31 CSS assets on the page (some of which may be
+ // aggregated). In this case, output a LINK tag for file CSS assets.
+ // - There are more than 31 CSS assets on the page, yet we must stay
+ // below IE<10's limit of 31 total CSS inclussion tags, we handle this
+ // in two ways:
+ // - file CSS assets that are not eligible for aggregation (their
+ // 'preprocess' flag has been set to FALSE): in this case, output a
+ // LINK tag.
+ // - file CSS assets that can be aggregated (and possibly have been):
+ // in this case, figure out which subsequent file CSS assets share
+ // the same key properties ('group', 'every_page', 'media' and
+ // 'browsers') and output this group into as few STYLE tags as
+ // possible (a STYLE tag may contain only 31 @import statements).
+ case 'file':
+ // The dummy query string needs to be added to the URL to control
+ // browser-caching.
+ $query_string_separator = (strpos($css_asset['data'], '?') !== FALSE) ? '&' : '?';
+
+ // As long as the current page will not run into IE's limit for CSS
+ // assets: output a LINK tag for a file CSS asset.
+ if (count($css_assets) <= 31) {
+ $element = $link_element_defaults;
+ $element['#attributes']['href'] = file_create_url($css_asset['data']) . $query_string_separator . $query_string;;
+ $element['#attributes']['media'] = $css_asset['media'];
+ $element['#browsers'] = $css_asset['browsers'];
+ $elements[] = $element;
+ }
+ // The current page will run into IE's limits for CSS assets: work
+ // around these limits by performing a light form of grouping.
+ // Once Drupal only needs to support IE10 and later, we can drop this.
+ else {
+ // The file CSS asset is ineligible for aggregation: output it in a
+ // LINK tag.
+ if (!$css_asset['preprocess']) {
+ $element = $link_element_defaults;
+ $element['#attributes']['href'] = file_create_url($css_asset['data']) . $query_string_separator . $query_string;
+ $element['#attributes']['media'] = $css_asset['media'];
+ $element['#browsers'] = $css_asset['browsers'];
+ $elements[] = $element;
+ }
+ // The file CSS asset can be aggregated, but hasn't been: combine
+ // multiple items into as few STYLE tags as possible.
+ else {
+ $import = array();
+ // Start with the current CSS asset, iterate over subsequent CSS
+ // assets and find which ones have the same 'type', 'group',
+ // 'every_page', 'preprocess', 'media' and 'browsers' properties.
+ $j = $i;
+ $next_css_asset = $css_asset;
+ $current_ie_group_key = $get_ie_group_key($css_asset);
+ do {
+ // The dummy query string needs to be added to the URL to
+ // control browser-caching. IE7 does not support a media type on
+ // the @import statement, so we instead specify the media for
+ // the group on the STYLE tag.
+ $import[] = '@import url("' . String::checkPlain(file_create_url($next_css_asset['data']) . '?' . $query_string) . '");';
+ // Move the outer for loop skip the next item, since we
+ // processed it here.
+ $i = $j;
+ // Retrieve next CSS asset, unless there is none: then break.
+ if ($j + 1 < count($css_assets_keys)) {
+ $j++;
+ $next_css_asset = $css_assets[$css_assets_keys[$j]];
+ }
+ else {
+ break;
+ }
+ } while ($get_ie_group_key($next_css_asset) == $current_ie_group_key);
+
+ // In addition to IE's limit of 31 total CSS inclusion tags, it
+ // also has a limit of 31 @import statements per STYLE tag.
+ while (!empty($import)) {
+ $import_batch = array_slice($import, 0, 31);
+ $import = array_slice($import, 31);
+ $element = $style_element_defaults;
+ // This simplifies the JavaScript regex, allowing each line
+ // (separated by \n) to be treated as a completely different
+ // string. This means that we can use ^ and $ on one line at a
+ // time, and not worry about style tags since they'll never
+ // match the regex.
+ $element['#value'] = "\n" . implode("\n", $import_batch) . "\n";
+ $element['#attributes']['media'] = $css_asset['media'];
+ $element['#browsers'] = $css_asset['browsers'];
+ $elements[] = $element;
+ }
+ }
+ }
+ break;
+
+ // Output a STYLE tag for an inline CSS asset. The asset's 'data'
+ // property contains the CSS content.
+ case 'inline':
+ $element = $style_element_defaults;
+ $element['#value'] = $css_asset['data'];
+ $element['#attributes']['media'] = $css_asset['media'];
+ $element['#browsers'] = $css_asset['browsers'];
+ // For inline CSS to validate as XHTML, all CSS containing XHTML needs
+ // to be wrapped in CDATA. To make that backwards compatible with HTML
+ // 4, we need to comment out the CDATA-tag.
+ $element['#value_prefix'] = "\n/* <![CDATA[ */\n";
+ $element['#value_suffix'] = "\n/* ]]> */\n";
+ $elements[] = $element;
+ break;
+
+ // Output a LINK tag for an external CSS asset. The asset's 'data'
+ // property contains the full URL.
+ case 'external':
+ $element = $link_element_defaults;
+ $element['#attributes']['href'] = $css_asset['data'];
+ $element['#attributes']['media'] = $css_asset['media'];
+ $element['#browsers'] = $css_asset['browsers'];
+ $elements[] = $element;
+ break;
+
+ default:
+ throw new \Exception('Invalid CSS asset type.');
+ }
+ }
+
+ return $elements;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Asset/CssOptimizer.php b/core/lib/Drupal/Core/Asset/CssOptimizer.php
new file mode 100644
index 0000000..2cf07e7
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/CssOptimizer.php
@@ -0,0 +1,229 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\CssOptimizer.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetOptimizerInterface;
+
+/**
+ * Optimizes a CSS asset.
+ */
+class CssOptimizer implements AssetOptimizerInterface {
+
+ /**
+ * The base path used by rewriteFileURI().
+ *
+ * @var string
+ */
+ public $rewriteFileURIBasePath;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function optimize(array $css_asset) {
+ if (!in_array($css_asset['type'], array('file', 'inline'))) {
+ throw new \Exception('Only file or inline CSS assets can be optimized.');
+ }
+ if ($css_asset['type'] === 'file' && !$css_asset['preprocess']) {
+ throw new \Exception('Only file CSS assets with preprocessing enabled can be optimized.');
+ }
+
+ if ($css_asset['type'] === 'file') {
+ return $this->processFile($css_asset);
+ }
+ else {
+ return $this->processCss($css_asset['data'], $css_asset['preprocess']);
+ }
+ }
+
+ /**
+ * Build aggregate CSS file.
+ */
+ protected function processFile($css_asset) {
+ $contents = $this->loadFile($css_asset['data'], TRUE);
+
+ // Get the parent directory of this file, relative to the Drupal root.
+ $css_base_path = substr($css_asset['data'], 0, strrpos($css_asset['data'], '/'));
+ // Store base path.
+ $this->rewriteFileURIBasePath = $css_base_path . '/';
+
+ // Anchor all paths in the CSS with its base URL, ignoring external and absolute paths.
+ return preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', array($this, 'rewriteFileURI'), $contents);
+ }
+
+ /**
+ * Loads the stylesheet and resolves all @import commands.
+ *
+ * Loads a stylesheet and replaces @import commands with the contents of the
+ * imported file. Use this instead of file_get_contents when processing
+ * stylesheets.
+ *
+ * The returned contents are compressed removing white space and comments only
+ * when CSS aggregation is enabled. This optimization will not apply for
+ * color.module enabled themes with CSS aggregation turned off.
+ *
+ * Note: the only reason this method is public is so color.module can call it;
+ * it is not on the AssetOptimizerInterface, so future refactorings can make
+ * it protected.
+ *
+ * @param $file
+ * Name of the stylesheet to be processed.
+ * @param $optimize
+ * Defines if CSS contents should be compressed or not.
+ * @param $reset_basepath
+ * Used internally to facilitate recursive resolution of @import commands.
+ *
+ * @return
+ * Contents of the stylesheet, including any resolved @import commands.
+ */
+ public function loadFile($file, $optimize = NULL, $reset_basepath = TRUE) {
+ // These statics are not cache variables, so we don't use drupal_static().
+ static $_optimize, $basepath;
+ if ($reset_basepath) {
+ $basepath = '';
+ }
+ // Store the value of $optimize for preg_replace_callback with nested
+ // @import loops.
+ if (isset($optimize)) {
+ $_optimize = $optimize;
+ }
+
+ // Stylesheets are relative one to each other. Start by adding a base path
+ // prefix provided by the parent stylesheet (if necessary).
+ if ($basepath && !file_uri_scheme($file)) {
+ $file = $basepath . '/' . $file;
+ }
+ $basepath = dirname($file);
+
+ // Load the CSS stylesheet. We suppress errors because themes may specify
+ // stylesheets in their .info.yml file that don't exist in the theme's path,
+ // but are merely there to disable certain module CSS files.
+ if ($contents = @file_get_contents($file)) {
+ // Return the processed stylesheet.
+ return $this->processCss($contents, $_optimize);
+ }
+
+ return '';
+ }
+
+ /**
+ * Loads stylesheets recursively and returns contents with corrected paths.
+ *
+ * This function is used for recursive loading of stylesheets and
+ * returns the stylesheet content with all url() paths corrected.
+ *
+ * @param array $matches
+ * An array of matches by a preg_replace_callback() call that scans for
+ * @import-ed CSS files, except for external CSS files.
+ * @return
+ * The contents of the CSS file at $matches[1], with corrected paths.
+ *
+ * @see Drupal\Core\Asset\AssetOptimizerInterface::loadFile()
+ */
+ protected function loadNestedFile($matches) {
+ $filename = $matches[1];
+ // Load the imported stylesheet and replace @import commands in there as
+ // well.
+ $file = $this->loadFile($filename, NULL, FALSE);
+
+ // Determine the file's directory.
+ $directory = dirname($filename);
+ // If the file is in the current directory, make sure '.' doesn't appear in
+ // the url() path.
+ $directory = $directory == '.' ? '' : $directory .'/';
+
+ // Alter all internal url() paths. Leave external paths alone. We don't need
+ // to normalize absolute paths here (i.e. remove folder/... segments)
+ // because that will be done later.
+ return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)/i', 'url(\1'. $directory, $file);
+ }
+
+ /**
+ * Processes the contents of a stylesheet for aggregation.
+ *
+ * @param $contents
+ * The contents of the stylesheet.
+ * @param $optimize
+ * (optional) Boolean whether CSS contents should be minified. Defaults to
+ * FALSE.
+ *
+ * @return
+ * Contents of the stylesheet including the imported stylesheets.
+ */
+ protected function processCss($contents, $optimize = FALSE) {
+ // Remove multiple charset declarations for standards compliance (and fixing Safari problems).
+ $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
+
+ if ($optimize) {
+ // Perform some safe CSS optimizations.
+ // Regexp to match comment blocks.
+ $comment = '/\*[^*]*\*+(?:[^/*][^*]*\*+)*/';
+ // Regexp to match double quoted strings.
+ $double_quot = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
+ // Regexp to match single quoted strings.
+ $single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'";
+ // Strip all comment blocks, but keep double/single quoted strings.
+ $contents = preg_replace(
+ "<($double_quot|$single_quot)|$comment>Ss",
+ "$1",
+ $contents
+ );
+ // Remove certain whitespace.
+ // There are different conditions for removing leading and trailing
+ // whitespace.
+ // @see http://php.net/manual/regexp.reference.subpatterns.php
+ $contents = preg_replace('<
+ # Strip leading and trailing whitespace.
+ \s*([@{};,])\s*
+ # Strip only leading whitespace from:
+ # - Closing parenthesis: Retain "@media (bar) and foo".
+ | \s+([\)])
+ # Strip only trailing whitespace from:
+ # - Opening parenthesis: Retain "@media (bar) and foo".
+ # - Colon: Retain :pseudo-selectors.
+ | ([\(:])\s+
+ >xS',
+ // Only one of the three capturing groups will match, so its reference
+ // will contain the wanted value and the references for the
+ // two non-matching groups will be replaced with empty strings.
+ '$1$2$3',
+ $contents
+ );
+ // End the file with a new line.
+ $contents = trim($contents);
+ $contents .= "\n";
+ }
+
+ // Replaces @import commands with the actual stylesheet content.
+ // This happens recursively but omits external files.
+ $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\s*\)?\s*;/', array($this, 'loadNestedFile'), $contents);
+
+ return $contents;
+ }
+
+ /**
+ * Prefixes all paths within a CSS file for processFile().
+ *
+ * @param array $matches
+ * An array of matches by a preg_replace_callback() call that scans for
+ * url() references in CSS files, except for external or absolute ones.
+ *
+ * Note: the only reason this method is public is so color.module can call it;
+ * it is not on the AssetOptimizerInterface, so future refactorings can make
+ * it protected.
+ */
+ public function rewriteFileURI($matches) {
+ // Prefix with base and remove '../' segments where possible.
+ $path = $this->rewriteFileURIBasePath . $matches[1];
+ $last = '';
+ while ($path != $last) {
+ $last = $path;
+ $path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path);
+ }
+ return 'url(' . file_create_url($path) . ')';
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Asset/JsCollectionGrouper.php b/core/lib/Drupal/Core/Asset/JsCollectionGrouper.php
new file mode 100644
index 0000000..9f0aed0
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/JsCollectionGrouper.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\JsCollectionGrouper.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetCollectionGrouperInterface;
+
+/**
+ * Groups JavaScript assets.
+ */
+class JsCollectionGrouper implements AssetCollectionGrouperInterface {
+
+ /**
+ * {@inheritdoc}
+ *
+ * Puts multiple items into the same group if they are groupable and if they
+ * are for the same browsers. Items of the 'file' type are groupable if their
+ * 'preprocess' flag is TRUE. Items of the 'inline', 'settings', or 'external'
+ * type are not groupable.
+ *
+ * Also ensures that the process of grouping items does not change their
+ * relative order. This requirement may result in multiple groups for the same
+ * type and browsers, if needed to accommodate other items in between.
+ */
+ public function group(array $js_assets) {
+ $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 ($js_assets as $item) {
+ // The browsers for which the JavaScript item needs to be loaded is part
+ // of the information that determines when a new group is needed, but the
+ // order of keys in the array doesn't matter, and we don't want a new
+ // group if all that's different is that order.
+ 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_js() for details about that.
+ $group_keys = $item['preprocess'] ? array($item['type'], $item['group'], $item['every_page'], $item['browsers']) : FALSE;
+ break;
+
+ case 'external':
+ case 'setting':
+ case 'inline':
+ // Do not group external, settings, and inline items.
+ $group_keys = 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;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
new file mode 100644
index 0000000..bf4cbf6
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
@@ -0,0 +1,167 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\JsCollectionOptimizer.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetCollectionOptimizerInterface;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+
+
+/**
+ * Optimizes JavaScript assets.
+ */
+class JsCollectionOptimizer implements AssetCollectionOptimizerInterface {
+
+ /**
+ * A JS asset grouper.
+ *
+ * @var \Drupal\Core\Asset\JsCollectionGrouper
+ */
+ protected $grouper;
+
+ /**
+ * A JS asset optimizer.
+ *
+ * @var \Drupal\Core\Asset\JsOptimizer
+ */
+ protected $optimizer;
+
+ /**
+ * An asset dumper.
+ *
+ * @var \Drupal\Core\Asset\AssetDumper
+ */
+ protected $dumper;
+
+ /**
+ * The state key/value store.
+ *
+ * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+ */
+ protected $state;
+
+ /**
+ * Constructs a JsCollectionOptimizer.
+ *
+ * @param \Drupal\Core\Asset\AssetCollectionGrouperInterface
+ * The grouper for JS assets.
+ * @param \Drupal\Core\Asset\AssetOptimizerInterface
+ * The optimizer for a single JS asset.
+ * @param \Drupal\Core\Asset\AssetDumperInterface
+ * The dumper for optimized JS assets.
+ * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+ * The state key/value store.
+ */
+ public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, KeyValueStoreInterface $state) {
+ $this->grouper = $grouper;
+ $this->optimizer = $optimizer;
+ $this->dumper = $dumper;
+ $this->state = $state;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * The cache file name is retrieved on a page load via a lookup variable that
+ * contains an associative array. The array key is the hash of the names in
+ * $files while the value is the cache file name. The cache file is generated
+ * in two cases. First, if there is no file name value for the key, which will
+ * happen if a new file name has been added to $files or after the lookup
+ * variable is emptied to force a rebuild of the cache. Second, the cache file
+ * is generated if it is missing on disk. Old cache files are not deleted
+ * immediately when the lookup variable is emptied, but are deleted after a
+ * set period by drupal_delete_file_if_stale(). This ensures that files
+ * referenced by a cached page will still be available.
+ */
+ public function optimize(array $js_assets) {
+ // Group the assets.
+ $js_groups = $this->grouper->group($js_assets);
+
+ // Now optimize (concatenate, not minify) and dump each asset group, unless
+ // that was already done, in which case it should appear in
+ // system.js_cache_files.
+ // Drupal contrib can override this default JS aggregator to keep the same
+ // grouping, optimizing and dumping, but change the strategy that is used to
+ // determine when the aggregate should be rebuilt (e.g. mtime, HTTPS …).
+ $map = $this->state->get('system.js_cache_files') ?: array();
+ $js_assets = array();
+ foreach ($js_groups as $order => $js_group) {
+ // We have to return a single asset, not a group of assets. It is now up
+ // to one of the pieces of code in the switch statement below to set the
+ // 'data' property to the appropriate value.
+ $js_assets[$order] = $js_group;
+ unset($js_assets[$order]['items']);
+
+ switch ($js_group['type']) {
+ case 'file':
+ // No preprocessing, single JS asset: just use the existing URI.
+ if (!$js_group['preprocess']) {
+ $uri = $js_group['items'][0]['data'];
+ $js_assets[$order]['data'] = $uri;
+ }
+ // Preprocess (aggregate), unless the aggregate file already exists.
+ else {
+ $key = $this->generateHash($js_group);
+ $uri = '';
+ if (isset($map[$key])) {
+ $uri = $map[$key];
+ }
+ if (empty($uri) || !file_exists($uri)) {
+ // Concatenate each asset within the group.
+ $data = '';
+ foreach ($js_group['items'] as $js_asset) {
+ $data .= $this->optimizer->optimize($js_asset);
+ // Append a ';' and a newline after each JS file to prevent them
+ // from running together.
+ $data .= ";\n";
+ }
+ // Dump the optimized JS for this group into an aggregate file.
+ $uri = $this->dumper->dump($data, 'js');
+ // Set the URI for this group's aggregate file.
+ $js_assets[$order]['data'] = $uri;
+ // Persist the URI for this aggregate file.
+ $map[$key] = $uri;
+ $this->state->set('system.js_cache_files', $map);
+ }
+ else {
+ // Use the persisted URI for the optimized JS file.
+ $js_assets[$order]['data'] = $uri;
+ }
+ $js_assets[$order]['preprocessed'] = TRUE;
+ }
+ break;
+
+ case 'external':
+ case 'setting':
+ case 'inline':
+ // We don't do any aggregation and hence also no caching for external,
+ // setting or inline JS assets.
+ $uri = $js_group['items'][0]['data'];
+ $js_assets[$order]['data'] = $uri;
+ break;
+ }
+ }
+
+ return $js_assets;
+ }
+
+ /**
+ * Generate a hash for a given group of JavaScript assets.
+ *
+ * @param array $js_group
+ * A group of JavaScript assets.
+ *
+ * @return string
+ * A hash to uniquely identify the given group of JavaScript assets.
+ */
+ protected function generateHash(array $js_group) {
+ $js_data = array();
+ foreach ($js_group['items'] as $js_file) {
+ $js_data[] = $js_file['data'];
+ }
+ return hash('sha256', serialize($js_data));
+ }
+}
diff --git a/core/lib/Drupal/Core/Asset/JsCollectionRenderer.php b/core/lib/Drupal/Core/Asset/JsCollectionRenderer.php
new file mode 100644
index 0000000..b9f6582
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/JsCollectionRenderer.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\JsCollectionRenderer.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetCollectionRendererInterface;
+
+/**
+ * Renders JavaScript assets.
+ */
+class JsCollectionRenderer implements AssetCollectionRendererInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function render(array $js_assets) {
+ $elements = array();
+
+ // A dummy query-string is added to filenames, to gain control over
+ // browser-caching. The string changes on every update or full cache
+ // flush, forcing browsers to load a new copy of the files, as the
+ // URL changed. Files that should not be cached (see drupal_add_js())
+ // get REQUEST_TIME as query-string instead, to enforce reload on every
+ // page request.
+ $default_query_string = variable_get('css_js_query_string', '0');
+
+ // For inline JavaScript to validate as XHTML, all JavaScript containing
+ // XHTML needs to be wrapped in CDATA. To make that backwards compatible
+ // with HTML 4, we need to comment out the CDATA-tag.
+ $embed_prefix = "\n<!--//--><![CDATA[//><!--\n";
+ $embed_suffix = "\n//--><!]]>\n";
+
+ // 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=');
+
+ // Defaults for each SCRIPT element.
+ $element_defaults = array(
+ '#type' => 'html_tag',
+ '#tag' => 'script',
+ '#value' => '',
+ );
+
+ // Loop through all JS assets.
+ foreach ($js_assets as $js_asset) {
+ // Element properties that do not depend on JS asset type.
+ $element = $element_defaults;
+ $element['#browsers'] = $js_asset['browsers'];
+
+ // Element properties that depend on item type.
+ switch ($js_asset['type']) {
+ case 'setting':
+ $element['#value_prefix'] = $embed_prefix;
+ $element['#value'] = 'var drupalSettings = ' . drupal_json_encode(drupal_merge_js_settings($js_asset['data'])) . ";";
+ $element['#value_suffix'] = $embed_suffix;
+ break;
+
+ case 'inline':
+ $element['#value_prefix'] = $embed_prefix;
+ $element['#value'] = $js_asset['data'];
+ $element['#value_suffix'] = $embed_suffix;
+ break;
+
+ case 'file':
+ $query_string = empty($js_asset['version']) ? $default_query_string : $js_version_string . $js_asset['version'];
+ $query_string_separator = (strpos($js_asset['data'], '?') !== FALSE) ? '&' : '?';
+ $element['#attributes']['src'] = file_create_url($js_asset['data']);
+ // Only add the cache-busting query string if this isn't an aggregate
+ // file.
+ if (!isset($js_asset['preprocessed'])) {
+ $element['#attributes']['src'] .= $query_string_separator . ($js_asset['cache'] ? $query_string : REQUEST_TIME);
+ }
+ break;
+
+ case 'external':
+ $element['#attributes']['src'] = $js_asset['data'];
+ break;
+
+ default:
+ throw new \Exception('Invalid JS asset type.');
+ }
+
+ // Attributes may only be set if this script is output independently.
+ if (!empty($element['#attributes']['src']) && !empty($js_asset['attributes'])) {
+ $element['#attributes'] += $js_asset['attributes'];
+ }
+
+ $elements[] = $element;
+ }
+
+ return $elements;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Asset/JsOptimizer.php b/core/lib/Drupal/Core/Asset/JsOptimizer.php
new file mode 100644
index 0000000..e6ad293
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/JsOptimizer.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\JsOptimizer.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Asset\AssetOptimizerInterface;
+
+/**
+ * Optimizes a JavaScript asset.
+ */
+class JsOptimizer implements AssetOptimizerInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function optimize(array $js_asset) {
+ if ($js_asset['type'] !== 'file') {
+ throw new \Exception('Only file JavaScript assets can be optimized.');
+ }
+ if ($js_asset['type'] === 'file' && !$js_asset['preprocess']) {
+ throw new \Exception('Only file JavaScript assets with preprocessing enabled can be optimized.');
+ }
+
+ // No-op optimizer: no optimizations are applied to JavaScript assets.
+ return file_get_contents($js_asset['data']);
+ }
+
+}
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 549376a..7660d6b 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -4,6 +4,8 @@
* Allows users to change the color scheme of themes.
*/
+use Drupal\Core\Asset\CssOptimizer;
+
/**
* Implements hook_help().
*/
@@ -378,18 +380,18 @@ function color_scheme_form_submit($form, &$form_state) {
}
foreach ($files as $file) {
+ $css_optimizer = new CssOptimizer();
// Aggregate @imports recursively for each configured top level CSS file
// without optimization. Aggregation and optimization will be
// handled by drupal_build_css_cache() only.
- $style = drupal_load_stylesheet($paths['source'] . $file, FALSE);
+ $style = $css_optimizer->loadFile($paths['source'] . $file, FALSE);
// Return the path to where this CSS file originated from, stripping
// off the name of the file at the end of the path.
- $base = base_path() . dirname($paths['source'] . $file) . '/';
- _drupal_build_css_path(NULL, $base);
+ $css_optimizer->rewriteFileURIBasePath = base_path() . dirname($paths['source'] . $file) . '/';
// Prefix all paths within this CSS file, ignoring absolute paths.
- $style = preg_replace_callback('/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i', '_drupal_build_css_path', $style);
+ $style = preg_replace_callback('/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i', array($css_optimizer, 'rewriteFileURI'), $style);
// Rewrite stylesheet with new colors.
$style = _color_rewrite_stylesheet($theme, $info, $paths, $palette, $style);
diff --git a/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php b/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php
index f41460c..287fb8f 100644
--- a/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php
+++ b/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php
@@ -105,6 +105,6 @@ class LayoutDerivativesTest extends WebTestBase {
$this->assertRaw('<div class="layout-region layout-col-right">');
// Ensure the CSS was added.
- $this->assertRaw('@import url("' . url('', array('absolute' => TRUE)) . drupal_get_path('theme', 'layout_test_theme') . '/layouts/static/two-col/two-col.css');
+ $this->assertRaw(url('', array('absolute' => TRUE)) . drupal_get_path('theme', 'layout_test_theme') . '/layouts/static/two-col/two-col.css');
}
}
diff --git a/core/modules/simpletest/files/css_test_files/comment_hacks.css.unoptimized.css b/core/modules/simpletest/files/css_test_files/comment_hacks.css.unoptimized.css
deleted file mode 100644
index 8b12d6c..0000000
--- a/core/modules/simpletest/files/css_test_files/comment_hacks.css.unoptimized.css
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-* A sample css file, designed to test the effectiveness and stability
-* of function <code>drupal_load_stylesheet_content()</code>.
-*
-*/
-/*
-A large comment block to test for segfaults and speed. This is 60K a's. Extreme but useful to demonstrate flaws in comment striping regexp. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/
-.test1 { display:block;}
-
-/* A multiline IE-mac hack (v.2) taken fron Zen theme*/
-/* Hides from IE-mac \*/
-html .clear-block {
- height: 1%;
-}
-.clear-block {
- display: block;
- font:italic bold 12px/30px Georgia, serif;
-}
-
-/* End hide from IE-mac */
-.test2 { display:block; }
-
-/* v1 of the commented backslash hack. This \ character between rules appears to have the effect
-that macIE5 ignores the following rule. Odd, but extremely useful. */
-.bkslshv1 { background-color: #c00; }
-.test3 { display:block; }
-
-/**************** A multiline, multistar comment ***************
-****************************************************************/
-.test4 { display:block;}
-
-/**************************************/
-.comment-in-double-quotes:before {
- content: "/* ";
-}
-.this_rule_must_stay {
- color: #f00;
- background-color: #fff;
-}
-.comment-in-double-quotes:after {
- content: " */";
-}
-/**************************************/
-.comment-in-single-quotes:before {
- content: '/*';
-}
-.this_rule_must_stay {
- color: #f00;
- background-color: #fff;
-}
-.comment-in-single-quotes:after {
- content: '*/';
-}
-/**************************************/
-.comment-in-mixed-quotes:before {
- content: '"/*"';
-}
-.this_rule_must_stay {
- color: #f00;
- background-color: #fff;
-}
-.comment-in-mixed-quotes:after {
- content: "'*/'";
-}
-/**************************************/
-.comment-in-quotes-with-escaped:before {
- content: '/* \" \' */';
-}
-.this_rule_must_stay {
- color: #f00;
- background-color: #fff;
-}
-.comment-in-quotes-with-escaped:after {
- content: "*/ \" \ '";
-}
-/************************************/
-/*
-"This has to go"
-'This has to go'
-*/
diff --git a/core/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css b/core/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css
deleted file mode 100644
index 4c905f5..0000000
--- a/core/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-body {
- margin: 0;
- padding: 0;
- background: #edf5fa;
- font: 76%/170% Verdana, sans-serif;
- color: #494949;
-}
-
-.this .is .a .test {
- font: 1em/100% Verdana, sans-serif;
- color: #494949;
-}
-.this
-.is
-.a
-.test {
-font: 1em/100% Verdana, sans-serif;
-color: #494949;
-}
-
-textarea, select {
- font: 1em/160% Verdana, sans-serif;
- color: #494949;
-}
-
diff --git a/core/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css b/core/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css
deleted file mode 100644
index 118dfa4..0000000
--- a/core/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css
+++ /dev/null
@@ -1,65 +0,0 @@
-
-/**
- * @file Basic css that does not use import
- */
-
-
-body {
- margin: 0;
- padding: 0;
- background: #edf5fa;
- font: 76%/170% Verdana, sans-serif;
- color: #494949;
-}
-
-.this .is .a .test {
- font: 1em/100% Verdana, sans-serif;
- color: #494949;
-}
-
-/**
- * CSS spec says that all whitespace is valid whitespace, so this selector
- * should be just as good as the one above.
- */
-.this
-.is
-.a
-.test {
-font: 1em/100% Verdana, sans-serif;
-color: #494949;
-}
-
-some :pseudo .thing {
- border-radius: 3px;
-}
-
-::-moz-selection {
- background: #000;
- color:#fff;
-
-}
-::selection {
- background: #000;
- color: #fff;
-}
-
-@media print {
- * {
- background: #000 !important;
- color: #fff !important;
- }
- @page {
- margin: 0.5cm;
- }
-}
-
-@media screen and (max-device-width: 480px) {
- background: #000;
- color: #fff;
-}
-
-textarea, select {
- font: 1em/160% Verdana, sans-serif;
- color: #494949;
-}
-
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php
index d7fbdbf..54abcba 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php
@@ -79,7 +79,7 @@ class CascadingStylesheetsTest extends DrupalUnitTestBase {
$this->assertTrue(strpos($styles, $css) > 0, 'Rendered CSS includes the added stylesheet.');
// Verify that newlines are properly added inside style tags.
$query_string = variable_get('css_js_query_string', '0');
- $css_processed = "<style media=\"all\">\n@import url(\"" . check_plain(file_create_url($css)) . "?" . $query_string ."\");\n</style>";
+ $css_processed = '<link rel="stylesheet" href="' . check_plain(file_create_url($css)) . "?" . $query_string . '" media="all" />';
$this->assertEqual(trim($styles), $css_processed, 'Rendered CSS includes newlines inside style tags for JavaScript use.');
}
@@ -99,8 +99,12 @@ class CascadingStylesheetsTest extends DrupalUnitTestBase {
* Tests rendering inline stylesheets with preprocessing on.
*/
function testRenderInlinePreprocess() {
+ // Turn on CSS aggregation to allow for preprocessing.
+ $config = $this->container->get('config.factory')->get('system.performance');
+ $config->set('css.preprocess', 1);
+
$css = 'body { padding: 0px; }';
- $css_preprocessed = '<style media="all">' . "\n/* <![CDATA[ */\n" . drupal_load_stylesheet_content($css, TRUE) . "\n/* ]]> */\n" . '</style>';
+ $css_preprocessed = '<style media="all">' . "\n/* <![CDATA[ */\n" . "body{padding:0px;}\n" . "\n/* ]]> */\n" . '</style>';
drupal_add_css($css, array('type' => 'inline'));
$styles = drupal_get_css();
$this->assertEqual(trim($styles), $css_preprocessed, 'Rendering preprocessed inline CSS adds it to the page.');
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsUnitTest.php
deleted file mode 100644
index 5ab3c0a..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsUnitTest.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Common\CascadingStylesheetsUnitTest.
- */
-
-namespace Drupal\system\Tests\Common;
-
-use Drupal\simpletest\UnitTestBase;
-
-/**
- * Tests CSS functions.
- */
-class CascadingStylesheetsUnitTest extends UnitTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'CSS Unit Tests',
- 'description' => 'Unit tests on CSS functions like aggregation.',
- 'group' => 'Common',
- );
- }
-
- /**
- * Tests CSS loading via drupal_load_stylesheet().
- *
- * This test loads CSS files with and without CSS optimization.
- * Known tests:
- * - Retain white-space in selectors. (http://drupal.org/node/472820)
- * - Proper URLs in imported files. (http://drupal.org/node/265719)
- * - Retain pseudo-selectors. (http://drupal.org/node/460448)
- */
- function testLoadCssBasic() {
- // Array of files to test living in 'simpletest/files/css_test_files/'.
- // - Original: name.css
- // - Unoptimized expected content: name.css.unoptimized.css
- // - Optimized expected content: name.css.optimized.css
- $testfiles = array(
- 'css_input_without_import.css',
- 'css_input_with_import.css',
- 'comment_hacks.css'
- );
- $path = drupal_get_path('module', 'simpletest') . '/files/css_test_files';
- foreach ($testfiles as $file) {
- $expected = file_get_contents("$path/$file.unoptimized.css");
- $unoptimized_output = drupal_load_stylesheet("$path/$file.unoptimized.css", FALSE);
- $this->assertEqual($unoptimized_output, $expected, format_string('Unoptimized CSS file has expected contents (@file)', array('@file' => $file)));
-
- $expected = file_get_contents("$path/$file.optimized.css");
- $optimized_output = drupal_load_stylesheet("$path/$file", TRUE);
- $this->assertEqual($optimized_output, $expected, format_string('Optimized CSS file has expected contents (@file)', array('@file' => $file)));
-
- // Repeat the tests by accessing the stylesheets by URL.
- $expected = file_get_contents("$path/$file.unoptimized.css");
- $unoptimized_output_url = drupal_load_stylesheet($GLOBALS['base_url'] . "/$path/$file.unoptimized.css", FALSE);
- $this->assertEqual($unoptimized_output, $expected, format_string('Unoptimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file)));
-
- $expected = file_get_contents("$path/$file.optimized.css");
- $optimized_output = drupal_load_stylesheet($GLOBALS['base_url'] . "/$path/$file", TRUE);
- $this->assertEqual($optimized_output, $expected, format_string('Optimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file)));
- }
- }
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
index 5a1d3f6..fc0e5ff 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
@@ -8,6 +8,7 @@
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\WebTestBase;
+use Drupal\Component\Utility\Crypt;
/**
* Tests the JavaScript system.
@@ -345,8 +346,8 @@ class JavaScriptTest extends WebTestBase {
$js_items = drupal_add_js();
$javascript = drupal_get_js();
$expected = implode("\n", array(
- '<script src="' . file_create_url(drupal_build_js_cache(array('core/misc/collapse.js' => $js_items['core/misc/collapse.js'], 'core/misc/batch.js' => $js_items['core/misc/batch.js']))) . '"></script>',
- '<script src="' . file_create_url(drupal_build_js_cache(array('core/misc/ajax.js' => $js_items['core/misc/ajax.js'], 'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']))) . '"></script>',
+ '<script src="' . $this->calculateAggregateFilename(array('core/misc/collapse.js' => $js_items['core/misc/collapse.js'], 'core/misc/batch.js' => $js_items['core/misc/batch.js'])) . '"></script>',
+ '<script src="' . $this->calculateAggregateFilename(array('core/misc/ajax.js' => $js_items['core/misc/ajax.js'], 'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js'])) . '"></script>',
));
$this->assertTrue(strpos($javascript, $expected) !== FALSE, 'JavaScript is aggregated in the expected groups and order.');
}
@@ -365,10 +366,14 @@ class JavaScriptTest extends WebTestBase {
drupal_add_js('core/misc/autocomplete.js');
$js_items = drupal_add_js();
- drupal_build_js_cache(array(
- 'core/misc/ajax.js' => $js_items['core/misc/ajax.js'],
- 'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
- ));
+ $scripts_html = array(
+ '#type' => 'scripts',
+ '#items' => array(
+ 'core/misc/ajax.js' => $js_items['core/misc/ajax.js'],
+ 'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
+ )
+ );
+ drupal_render($scripts_html);
// Store the expected key for the first item in the cache.
$cache = array_keys(\Drupal::state()->get('system.js_cache_files') ?: array());
@@ -384,10 +389,14 @@ class JavaScriptTest extends WebTestBase {
// Rebuild the cache.
$js_items = drupal_add_js();
- drupal_build_js_cache(array(
- 'core/misc/ajax.js' => $js_items['core/misc/ajax.js'],
- 'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
- ));
+ $scripts_html = array(
+ '#type' => 'scripts',
+ '#items' => array(
+ 'core/misc/ajax.js' => $js_items['core/misc/ajax.js'],
+ 'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
+ )
+ );
+ drupal_render($scripts_html);
// Compare the expected key for the first file to the current one.
$cache = array_keys(\Drupal::state()->get('system.js_cache_files') ?: array());
@@ -562,4 +571,24 @@ class JavaScriptTest extends WebTestBase {
$query_string = variable_get('css_js_query_string', '0');
$this->assertRaw(drupal_get_path('module', 'node') . '/node.js?' . $query_string, 'Query string was appended correctly to js.');
}
+
+ /**
+ * Calculates the aggregated file URI of a group of JavaScript assets.
+ *
+ * @param array $js_assets
+ * A group of JavaScript assets.
+ * @return string
+ * A file URI.
+ *
+ * @see testAggregation()
+ * @see testAggregationOrder()
+ */
+ protected function calculateAggregateFilename($js_assets) {
+ $data = '';
+ foreach ($js_assets as $js_asset) {
+ $data .= file_get_contents($js_asset['data']) . ";\n";
+ }
+ return file_create_url('public://js/js_' . Crypt::hashBase64($data) . '.js');
+ }
+
}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 0df34a0..a6a7786 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -283,14 +283,10 @@ function system_element_info() {
$types['styles'] = array(
'#items' => array(),
'#pre_render' => array('drupal_pre_render_styles'),
- '#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.
diff --git a/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php
new file mode 100644
index 0000000..adf5e88
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php
@@ -0,0 +1,216 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Asset\CssGrouperUnitTest.
+ */
+
+
+namespace Drupal\Tests\Core\Asset;
+
+use Drupal\Core\Asset\CssCollectionGrouper;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the CssCollectionGrouper class.
+ *
+ * @group Asset
+ */
+class CssCollectionGrouperUnitTest extends UnitTestCase {
+
+ /**
+ * A CSS asset grouper.
+ *
+ * @var \Drupal\Core\Asset\CssCollectionGrouper object.
+ */
+ protected $grouper;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'CSS asset collection grouper functionality',
+ 'description' => 'Tests the CSS asset collection grouper.',
+ 'group' => 'Asset handling',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ $this->grouper = new CssCollectionGrouper();
+ }
+
+ /**
+ * Tests \Drupal\Core\Asset\CssCollectionGrouper.
+ */
+ function testGrouper() {
+ $css_assets = array(
+ 'system.base.css' => array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'weight' => 0.012,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'core/modules/system/system.base.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'system.base.css',
+ ),
+ 'system.theme.css' => array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'weight' => 0.013,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'core/modules/system/system.theme.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'system.theme.css',
+ ),
+ 'jquery.ui.core.css' => array(
+ 'group' => -100,
+ 'type' => 'file',
+ 'weight' => 0.004,
+ 'every_page' => FALSE,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'core/misc/ui/themes/base/jquery.ui.core.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'jquery.ui.core.css',
+ ),
+ 0 => array(
+ 'type' => 'inline',
+ 'group' => 0,
+ 'weight' => 0.007,
+ 'every_page' => FALSE,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'body { padding: 0px; }',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ ),
+ 1 => array(
+ 'type' => 'inline',
+ 'group' => 0,
+ 'weight' => 0.007,
+ 'every_page' => FALSE,
+ 'media' => 'all',
+ 'preprocess' => FALSE,
+ 'data' => 'body { margin: 0px; }',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ ),
+ 'field.css' => array(
+ 'every_page' => TRUE,
+ 'group' => 0,
+ 'type' => 'file',
+ 'weight' => 0.011,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'core/modules/field/theme/field.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'field.css',
+ ),
+ 'external.css' => array(
+ 'every_page' => FALSE,
+ 'group' => 0,
+ 'type' => 'external',
+ 'weight' => 0.009,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'http://example.com/external.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'external.css',
+ ),
+ 'style.css' => array(
+ 'group' => 100,
+ 'every_page' => TRUE,
+ 'media' => 'all',
+ 'type' => 'file',
+ 'weight' => 0.001,
+ 'preprocess' => TRUE,
+ 'data' => 'core/themes/bartik/css/style.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'style.css',
+ ),
+ 'print.css' => array(
+ 'group' => 100,
+ 'every_page' => TRUE,
+ 'media' => 'print',
+ 'type' => 'file',
+ 'weight' => 0.003,
+ 'preprocess' => TRUE,
+ 'data' => 'core/themes/bartik/css/print.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'print.css',
+ ),
+ );
+
+ $groups = $this->grouper->group($css_assets);
+
+ $this->assertSame(count($groups), 7, "7 groups created.");
+
+ // Check group 1.
+ $this->assertSame($groups[0]['group'], -100);
+ $this->assertSame($groups[0]['every_page'], TRUE);
+ $this->assertSame($groups[0]['type'], 'file');
+ $this->assertSame($groups[0]['media'], 'all');
+ $this->assertSame($groups[0]['preprocess'], TRUE);
+ $this->assertSame(count($groups[0]['items']), 2);
+ $this->assertContains($css_assets['system.base.css'], $groups[0]['items']);
+ $this->assertContains($css_assets['system.theme.css'], $groups[0]['items']);
+
+ // Check group 2.
+ $this->assertSame($groups[1]['group'], -100);
+ $this->assertSame($groups[1]['every_page'], FALSE);
+ $this->assertSame($groups[1]['type'], 'file');
+ $this->assertSame($groups[1]['media'], 'all');
+ $this->assertSame($groups[1]['preprocess'], TRUE);
+ $this->assertSame(count($groups[1]['items']), 1);
+ $this->assertContains($css_assets['jquery.ui.core.css'], $groups[1]['items']);
+
+ // Check group 3.
+ $this->assertSame($groups[2]['group'], 0);
+ $this->assertSame($groups[2]['every_page'], FALSE);
+ $this->assertSame($groups[2]['type'], 'inline');
+ $this->assertSame($groups[2]['media'], 'all');
+ $this->assertSame($groups[2]['preprocess'], TRUE);
+ $this->assertSame(count($groups[2]['items']), 2);
+ $this->assertContains($css_assets[0], $groups[2]['items']);
+ $this->assertContains($css_assets[1], $groups[2]['items']);
+
+ // Check group 4.
+ $this->assertSame($groups[3]['group'], 0);
+ $this->assertSame($groups[3]['every_page'], TRUE);
+ $this->assertSame($groups[3]['type'], 'file');
+ $this->assertSame($groups[3]['media'], 'all');
+ $this->assertSame($groups[3]['preprocess'], TRUE);
+ $this->assertSame(count($groups[3]['items']), 1);
+ $this->assertContains($css_assets['field.css'], $groups[3]['items']);
+
+ // Check group 5.
+ $this->assertSame($groups[4]['group'], 0);
+ $this->assertSame($groups[4]['every_page'], FALSE);
+ $this->assertSame($groups[4]['type'], 'external');
+ $this->assertSame($groups[4]['media'], 'all');
+ $this->assertSame($groups[4]['preprocess'], TRUE);
+ $this->assertSame(count($groups[4]['items']), 1);
+ $this->assertContains($css_assets['external.css'], $groups[4]['items']);
+
+ // Check group 6.
+ $this->assertSame($groups[5]['group'], 100);
+ $this->assertSame($groups[5]['every_page'], TRUE);
+ $this->assertSame($groups[5]['type'], 'file');
+ $this->assertSame($groups[5]['media'], 'all');
+ $this->assertSame($groups[5]['preprocess'], TRUE);
+ $this->assertSame(count($groups[5]['items']), 1);
+ $this->assertContains($css_assets['style.css'], $groups[5]['items']);
+
+ // Check group 7.
+ $this->assertSame($groups[6]['group'], 100);
+ $this->assertSame($groups[6]['every_page'], TRUE);
+ $this->assertSame($groups[6]['type'], 'file');
+ $this->assertSame($groups[6]['media'], 'print');
+ $this->assertSame($groups[6]['preprocess'], TRUE);
+ $this->assertSame(count($groups[6]['items']), 1);
+ $this->assertContains($css_assets['print.css'], $groups[6]['items']);
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php
new file mode 100644
index 0000000..cd97a61
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php
@@ -0,0 +1,572 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Asset\CssCollectionRendererUnitTest.
+ */
+
+
+namespace {
+
+/**
+ * CssRenderer uses file_create_url(), which *is* available when using the
+ * Simpletest test runner, but not when using the PHPUnit test runner; hence
+ * this hack.
+ */
+if (!function_exists('file_create_url')) {
+
+ /**
+ * Temporary mock for file_create_url(), until that is moved into
+ * Component/Utility.
+ */
+ function file_create_url($uri) {
+ return 'file_create_url:' . $uri;
+ }
+
+}
+
+if (!function_exists('variable_get')) {
+
+ function variable_get($variable, $default) {
+ return $default;
+ }
+
+}
+
+if (!function_exists('check_plain')) {
+
+ function check_plain($text) {
+ return $text;
+ }
+
+}
+
+}
+
+
+
+
+namespace Drupal\Tests\Core\Asset {
+
+use Drupal\Core\Asset\CssCollectionRenderer;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the CssCollectionRenderer class.
+ *
+ * @group Asset
+ */
+class CssCollectionRendererUnitTest extends UnitTestCase {
+
+ /**
+ * A CSS asset renderer.
+ *
+ * @var \Drupal\Core\Asset\CssRenderer object.
+ */
+ protected $renderer;
+
+ /**
+ * A valid file CSS asset group.
+ *
+ * @var array
+ */
+ protected $file_css_group;
+
+ /**
+ * A valid inline CSS asset group.
+ *
+ * @var array
+ */
+ protected $inline_css_group;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'CSS asset collection renderer functionality',
+ 'description' => 'Tests the CSS asset collection renderer.',
+ 'group' => 'Asset handling',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ $this->renderer = new CssCollectionRenderer();
+ $this->file_css_group = array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'items' => array(
+ 0 => array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'weight' => 0.012,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'tests/Drupal/Tests/Core/Asset/foo.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'foo.css',
+ ),
+ 1 => array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'weight' => 0.013,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'tests/Drupal/Tests/Core/Asset/bar.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'bar.css',
+ ),
+ ),
+ );
+ $this->inline_css_group = array(
+ 'group' => 0,
+ 'every_page' => FALSE,
+ 'type' => 'inline',
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'items' => array(
+ 0 => array(
+ 'group' => 0,
+ 'every_page' => FALSE,
+ 'type' => 'inline',
+ 'weight' => 0.012,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => '.girlfriend { display: none; }',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ ),
+ 1 => array(
+ 'group' => 0,
+ 'every_page' => FALSE,
+ 'type' => 'file',
+ 'weight' => 0.013,
+ 'media' => 'all',
+ 'preprocess' => FALSE,
+ 'data' => '#home body { position: fixed; }',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Provides data for the CSS asset rendering test.
+ *
+ * @see testRender
+ */
+ function testRenderProvider() {
+ // Default for 'browsers' key in CSS asset.
+ $browsers_default = array('IE' => TRUE, '!IE' => TRUE);
+
+ // Defaults for LINK and STYLE elements.
+ $link_element_defaults = array(
+
+ );
+ $style_element_defaults = array(
+ '#type' => 'html_tag',
+ '#tag' => 'style',
+ );
+
+ $create_link_element = function($href, $media = 'all', $browsers = array()) {
+ return array(
+ '#type' => 'html_tag',
+ '#tag' => 'link',
+ '#attributes' => array(
+ 'rel' => 'stylesheet',
+ 'href' => $href,
+ 'media' => $media,
+ ),
+ '#browsers' => $browsers,
+ );
+ };
+ $create_style_element = function($value, $media, $browsers = array(), $wrap_in_cdata = FALSE) {
+ $style_element = array(
+ '#type' => 'html_tag',
+ '#tag' => 'style',
+ '#value' => $value,
+ '#attributes' => array(
+ 'media' => $media
+ ),
+ '#browsers' => $browsers,
+ );
+ if ($wrap_in_cdata) {
+ $style_element['#value_prefix'] = "\n/* <![CDATA[ */\n";
+ $style_element['#value_suffix'] = "\n/* ]]> */\n";
+ }
+ return $style_element;
+ };
+
+ $create_file_css_asset = function($data, $media = 'all', $preprocess = TRUE) {
+ return array('group' => 0, 'every_page' => FALSE, 'type' => 'file', 'media' => $media, 'preprocess' => $preprocess, 'data' => $data, 'browsers' => array());
+ };
+
+ return array(
+ // Single external CSS asset.
+ 0 => array(
+ // CSS assets.
+ array(
+ 0 => array('group' => 0, 'every_page' => TRUE, 'type' => 'external', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'http://example.com/popular.js', 'browsers' => array()),
+ ),
+ // Render elements.
+ array(
+ 0 => $create_link_element('http://example.com/popular.js', 'all'),
+ ),
+ ),
+ // Single inline CSS asset.
+ 1 => array(
+ array(
+ 0 => array('group' => 0, 'every_page' => FALSE, 'type' => 'inline', 'media' => 'all', 'preprocess' => FALSE, 'data' => '.girlfriend { display: none; }', 'browsers' => array()),
+ ),
+ array(
+ 0 => $create_style_element('.girlfriend { display: none; }', 'all', array(), TRUE),
+ ),
+ ),
+ // Single file CSS asset.
+ 2 => array(
+ array(
+ 0 => array('group' => 0, 'every_page' => TRUE, 'type' => 'file', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'public://css/file-every_page-all', 'browsers' => array()),
+ ),
+ array(
+ 0 => $create_link_element(file_create_url('public://css/file-every_page-all') . '?0', 'all'),
+ ),
+ ),
+ // 31 file CSS assets: expect 31 link elements.
+ 3 => array(
+ array(
+ 0 => $create_file_css_asset('public://css/1.css'),
+ 1 => $create_file_css_asset('public://css/2.css'),
+ 2 => $create_file_css_asset('public://css/3.css'),
+ 3 => $create_file_css_asset('public://css/4.css'),
+ 4 => $create_file_css_asset('public://css/5.css'),
+ 5 => $create_file_css_asset('public://css/6.css'),
+ 6 => $create_file_css_asset('public://css/7.css'),
+ 7 => $create_file_css_asset('public://css/8.css'),
+ 8 => $create_file_css_asset('public://css/9.css'),
+ 9 => $create_file_css_asset('public://css/10.css'),
+ 10 => $create_file_css_asset('public://css/11.css'),
+ 11 => $create_file_css_asset('public://css/12.css'),
+ 12 => $create_file_css_asset('public://css/13.css'),
+ 13 => $create_file_css_asset('public://css/14.css'),
+ 14 => $create_file_css_asset('public://css/15.css'),
+ 15 => $create_file_css_asset('public://css/16.css'),
+ 16 => $create_file_css_asset('public://css/17.css'),
+ 17 => $create_file_css_asset('public://css/18.css'),
+ 18 => $create_file_css_asset('public://css/19.css'),
+ 19 => $create_file_css_asset('public://css/20.css'),
+ 20 => $create_file_css_asset('public://css/21.css'),
+ 21 => $create_file_css_asset('public://css/22.css'),
+ 22 => $create_file_css_asset('public://css/23.css'),
+ 23 => $create_file_css_asset('public://css/24.css'),
+ 24 => $create_file_css_asset('public://css/25.css'),
+ 25 => $create_file_css_asset('public://css/26.css'),
+ 26 => $create_file_css_asset('public://css/27.css'),
+ 27 => $create_file_css_asset('public://css/28.css'),
+ 28 => $create_file_css_asset('public://css/29.css'),
+ 29 => $create_file_css_asset('public://css/30.css'),
+ 30 => $create_file_css_asset('public://css/31.css'),
+ ),
+ array(
+ 0 => $create_link_element(file_create_url('public://css/1.css') . '?0'),
+ 1 => $create_link_element(file_create_url('public://css/2.css') . '?0'),
+ 2 => $create_link_element(file_create_url('public://css/3.css') . '?0'),
+ 3 => $create_link_element(file_create_url('public://css/4.css') . '?0'),
+ 4 => $create_link_element(file_create_url('public://css/5.css') . '?0'),
+ 5 => $create_link_element(file_create_url('public://css/6.css') . '?0'),
+ 6 => $create_link_element(file_create_url('public://css/7.css') . '?0'),
+ 7 => $create_link_element(file_create_url('public://css/8.css') . '?0'),
+ 8 => $create_link_element(file_create_url('public://css/9.css') . '?0'),
+ 9 => $create_link_element(file_create_url('public://css/10.css') . '?0'),
+ 10 => $create_link_element(file_create_url('public://css/11.css') . '?0'),
+ 11 => $create_link_element(file_create_url('public://css/12.css') . '?0'),
+ 12 => $create_link_element(file_create_url('public://css/13.css') . '?0'),
+ 13 => $create_link_element(file_create_url('public://css/14.css') . '?0'),
+ 14 => $create_link_element(file_create_url('public://css/15.css') . '?0'),
+ 15 => $create_link_element(file_create_url('public://css/16.css') . '?0'),
+ 16 => $create_link_element(file_create_url('public://css/17.css') . '?0'),
+ 17 => $create_link_element(file_create_url('public://css/18.css') . '?0'),
+ 18 => $create_link_element(file_create_url('public://css/19.css') . '?0'),
+ 19 => $create_link_element(file_create_url('public://css/20.css') . '?0'),
+ 20 => $create_link_element(file_create_url('public://css/21.css') . '?0'),
+ 21 => $create_link_element(file_create_url('public://css/22.css') . '?0'),
+ 22 => $create_link_element(file_create_url('public://css/23.css') . '?0'),
+ 23 => $create_link_element(file_create_url('public://css/24.css') . '?0'),
+ 24 => $create_link_element(file_create_url('public://css/25.css') . '?0'),
+ 25 => $create_link_element(file_create_url('public://css/26.css') . '?0'),
+ 26 => $create_link_element(file_create_url('public://css/27.css') . '?0'),
+ 27 => $create_link_element(file_create_url('public://css/28.css') . '?0'),
+ 28 => $create_link_element(file_create_url('public://css/29.css') . '?0'),
+ 29 => $create_link_element(file_create_url('public://css/30.css') . '?0'),
+ 30 => $create_link_element(file_create_url('public://css/31.css') . '?0'),
+ ),
+ ),
+ // 32 file CSS assets with the same properties: expect 2 style elements.
+ 4 => array(
+ array(
+ 0 => $create_file_css_asset('public://css/1.css'),
+ 1 => $create_file_css_asset('public://css/2.css'),
+ 2 => $create_file_css_asset('public://css/3.css'),
+ 3 => $create_file_css_asset('public://css/4.css'),
+ 4 => $create_file_css_asset('public://css/5.css'),
+ 5 => $create_file_css_asset('public://css/6.css'),
+ 6 => $create_file_css_asset('public://css/7.css'),
+ 7 => $create_file_css_asset('public://css/8.css'),
+ 8 => $create_file_css_asset('public://css/9.css'),
+ 9 => $create_file_css_asset('public://css/10.css'),
+ 10 => $create_file_css_asset('public://css/11.css'),
+ 11 => $create_file_css_asset('public://css/12.css'),
+ 12 => $create_file_css_asset('public://css/13.css'),
+ 13 => $create_file_css_asset('public://css/14.css'),
+ 14 => $create_file_css_asset('public://css/15.css'),
+ 15 => $create_file_css_asset('public://css/16.css'),
+ 16 => $create_file_css_asset('public://css/17.css'),
+ 17 => $create_file_css_asset('public://css/18.css'),
+ 18 => $create_file_css_asset('public://css/19.css'),
+ 19 => $create_file_css_asset('public://css/20.css'),
+ 20 => $create_file_css_asset('public://css/21.css'),
+ 21 => $create_file_css_asset('public://css/22.css'),
+ 22 => $create_file_css_asset('public://css/23.css'),
+ 23 => $create_file_css_asset('public://css/24.css'),
+ 24 => $create_file_css_asset('public://css/25.css'),
+ 25 => $create_file_css_asset('public://css/26.css'),
+ 26 => $create_file_css_asset('public://css/27.css'),
+ 27 => $create_file_css_asset('public://css/28.css'),
+ 28 => $create_file_css_asset('public://css/29.css'),
+ 29 => $create_file_css_asset('public://css/30.css'),
+ 30 => $create_file_css_asset('public://css/31.css'),
+ 31 => $create_file_css_asset('public://css/32.css'),
+ ),
+ array(
+ 0 => $create_style_element('
+@import url("' . file_create_url('public://css/1.css') . '?0");
+@import url("' . file_create_url('public://css/2.css') . '?0");
+@import url("' . file_create_url('public://css/3.css') . '?0");
+@import url("' . file_create_url('public://css/4.css') . '?0");
+@import url("' . file_create_url('public://css/5.css') . '?0");
+@import url("' . file_create_url('public://css/6.css') . '?0");
+@import url("' . file_create_url('public://css/7.css') . '?0");
+@import url("' . file_create_url('public://css/8.css') . '?0");
+@import url("' . file_create_url('public://css/9.css') . '?0");
+@import url("' . file_create_url('public://css/10.css') . '?0");
+@import url("' . file_create_url('public://css/11.css') . '?0");
+@import url("' . file_create_url('public://css/12.css') . '?0");
+@import url("' . file_create_url('public://css/13.css') . '?0");
+@import url("' . file_create_url('public://css/14.css') . '?0");
+@import url("' . file_create_url('public://css/15.css') . '?0");
+@import url("' . file_create_url('public://css/16.css') . '?0");
+@import url("' . file_create_url('public://css/17.css') . '?0");
+@import url("' . file_create_url('public://css/18.css') . '?0");
+@import url("' . file_create_url('public://css/19.css') . '?0");
+@import url("' . file_create_url('public://css/20.css') . '?0");
+@import url("' . file_create_url('public://css/21.css') . '?0");
+@import url("' . file_create_url('public://css/22.css') . '?0");
+@import url("' . file_create_url('public://css/23.css') . '?0");
+@import url("' . file_create_url('public://css/24.css') . '?0");
+@import url("' . file_create_url('public://css/25.css') . '?0");
+@import url("' . file_create_url('public://css/26.css') . '?0");
+@import url("' . file_create_url('public://css/27.css') . '?0");
+@import url("' . file_create_url('public://css/28.css') . '?0");
+@import url("' . file_create_url('public://css/29.css') . '?0");
+@import url("' . file_create_url('public://css/30.css') . '?0");
+@import url("' . file_create_url('public://css/31.css') . '?0");
+', 'all'),
+ 1 => $create_style_element('
+@import url("' . file_create_url('public://css/32.css') . '?0");
+', 'all'),
+ ),
+ ),
+ // 32 file CSS assets with the same properties, except for the 10th and
+ // 20th files, they have different 'media' properties. Expect 5 style
+ // elements.
+ 5 => array(
+ array(
+ 0 => $create_file_css_asset('public://css/1.css'),
+ 1 => $create_file_css_asset('public://css/2.css'),
+ 2 => $create_file_css_asset('public://css/3.css'),
+ 3 => $create_file_css_asset('public://css/4.css'),
+ 4 => $create_file_css_asset('public://css/5.css'),
+ 5 => $create_file_css_asset('public://css/6.css'),
+ 6 => $create_file_css_asset('public://css/7.css'),
+ 7 => $create_file_css_asset('public://css/8.css'),
+ 8 => $create_file_css_asset('public://css/9.css'),
+ 9 => $create_file_css_asset('public://css/10.css', 'screen'),
+ 10 => $create_file_css_asset('public://css/11.css'),
+ 11 => $create_file_css_asset('public://css/12.css'),
+ 12 => $create_file_css_asset('public://css/13.css'),
+ 13 => $create_file_css_asset('public://css/14.css'),
+ 14 => $create_file_css_asset('public://css/15.css'),
+ 15 => $create_file_css_asset('public://css/16.css'),
+ 16 => $create_file_css_asset('public://css/17.css'),
+ 17 => $create_file_css_asset('public://css/18.css'),
+ 18 => $create_file_css_asset('public://css/19.css'),
+ 19 => $create_file_css_asset('public://css/20.css', 'print'),
+ 20 => $create_file_css_asset('public://css/21.css'),
+ 21 => $create_file_css_asset('public://css/22.css'),
+ 22 => $create_file_css_asset('public://css/23.css'),
+ 23 => $create_file_css_asset('public://css/24.css'),
+ 24 => $create_file_css_asset('public://css/25.css'),
+ 25 => $create_file_css_asset('public://css/26.css'),
+ 26 => $create_file_css_asset('public://css/27.css'),
+ 27 => $create_file_css_asset('public://css/28.css'),
+ 28 => $create_file_css_asset('public://css/29.css'),
+ 29 => $create_file_css_asset('public://css/30.css'),
+ 30 => $create_file_css_asset('public://css/31.css'),
+ 31 => $create_file_css_asset('public://css/32.css'),
+ ),
+ array(
+ 0 => $create_style_element('
+@import url("' . file_create_url('public://css/1.css') . '?0");
+@import url("' . file_create_url('public://css/2.css') . '?0");
+@import url("' . file_create_url('public://css/3.css') . '?0");
+@import url("' . file_create_url('public://css/4.css') . '?0");
+@import url("' . file_create_url('public://css/5.css') . '?0");
+@import url("' . file_create_url('public://css/6.css') . '?0");
+@import url("' . file_create_url('public://css/7.css') . '?0");
+@import url("' . file_create_url('public://css/8.css') . '?0");
+@import url("' . file_create_url('public://css/9.css') . '?0");
+', 'all'),
+ 1 => $create_style_element('
+@import url("' . file_create_url('public://css/10.css') . '?0");
+', 'screen'),
+ 2 => $create_style_element('
+@import url("' . file_create_url('public://css/11.css') . '?0");
+@import url("' . file_create_url('public://css/12.css') . '?0");
+@import url("' . file_create_url('public://css/13.css') . '?0");
+@import url("' . file_create_url('public://css/14.css') . '?0");
+@import url("' . file_create_url('public://css/15.css') . '?0");
+@import url("' . file_create_url('public://css/16.css') . '?0");
+@import url("' . file_create_url('public://css/17.css') . '?0");
+@import url("' . file_create_url('public://css/18.css') . '?0");
+@import url("' . file_create_url('public://css/19.css') . '?0");
+', 'all'),
+ 3 => $create_style_element('
+@import url("' . file_create_url('public://css/20.css') . '?0");
+', 'print'),
+ 4 => $create_style_element('
+@import url("' . file_create_url('public://css/21.css') . '?0");
+@import url("' . file_create_url('public://css/22.css') . '?0");
+@import url("' . file_create_url('public://css/23.css') . '?0");
+@import url("' . file_create_url('public://css/24.css') . '?0");
+@import url("' . file_create_url('public://css/25.css') . '?0");
+@import url("' . file_create_url('public://css/26.css') . '?0");
+@import url("' . file_create_url('public://css/27.css') . '?0");
+@import url("' . file_create_url('public://css/28.css') . '?0");
+@import url("' . file_create_url('public://css/29.css') . '?0");
+@import url("' . file_create_url('public://css/30.css') . '?0");
+@import url("' . file_create_url('public://css/31.css') . '?0");
+@import url("' . file_create_url('public://css/32.css') . '?0");
+', 'all'),
+ ),
+ ),
+ // 32 file CSS assets with the same properties, except for the 15th, which
+ // has 'preprocess' = FALSE. Expect 1 link element and 2 style elements.
+ 6 => array(
+ array(
+ 0 => $create_file_css_asset('public://css/1.css'),
+ 1 => $create_file_css_asset('public://css/2.css'),
+ 2 => $create_file_css_asset('public://css/3.css'),
+ 3 => $create_file_css_asset('public://css/4.css'),
+ 4 => $create_file_css_asset('public://css/5.css'),
+ 5 => $create_file_css_asset('public://css/6.css'),
+ 6 => $create_file_css_asset('public://css/7.css'),
+ 7 => $create_file_css_asset('public://css/8.css'),
+ 8 => $create_file_css_asset('public://css/9.css'),
+ 9 => $create_file_css_asset('public://css/10.css'),
+ 10 => $create_file_css_asset('public://css/11.css'),
+ 11 => $create_file_css_asset('public://css/12.css'),
+ 12 => $create_file_css_asset('public://css/13.css'),
+ 13 => $create_file_css_asset('public://css/14.css'),
+ 14 => $create_file_css_asset('public://css/15.css', 'all', FALSE),
+ 15 => $create_file_css_asset('public://css/16.css'),
+ 16 => $create_file_css_asset('public://css/17.css'),
+ 17 => $create_file_css_asset('public://css/18.css'),
+ 18 => $create_file_css_asset('public://css/19.css'),
+ 19 => $create_file_css_asset('public://css/20.css'),
+ 20 => $create_file_css_asset('public://css/21.css'),
+ 21 => $create_file_css_asset('public://css/22.css'),
+ 22 => $create_file_css_asset('public://css/23.css'),
+ 23 => $create_file_css_asset('public://css/24.css'),
+ 24 => $create_file_css_asset('public://css/25.css'),
+ 25 => $create_file_css_asset('public://css/26.css'),
+ 26 => $create_file_css_asset('public://css/27.css'),
+ 27 => $create_file_css_asset('public://css/28.css'),
+ 28 => $create_file_css_asset('public://css/29.css'),
+ 29 => $create_file_css_asset('public://css/30.css'),
+ 30 => $create_file_css_asset('public://css/31.css'),
+ 31 => $create_file_css_asset('public://css/32.css'),
+ ),
+ array(
+ 0 => $create_style_element('
+@import url("' . file_create_url('public://css/1.css') . '?0");
+@import url("' . file_create_url('public://css/2.css') . '?0");
+@import url("' . file_create_url('public://css/3.css') . '?0");
+@import url("' . file_create_url('public://css/4.css') . '?0");
+@import url("' . file_create_url('public://css/5.css') . '?0");
+@import url("' . file_create_url('public://css/6.css') . '?0");
+@import url("' . file_create_url('public://css/7.css') . '?0");
+@import url("' . file_create_url('public://css/8.css') . '?0");
+@import url("' . file_create_url('public://css/9.css') . '?0");
+@import url("' . file_create_url('public://css/10.css') . '?0");
+@import url("' . file_create_url('public://css/11.css') . '?0");
+@import url("' . file_create_url('public://css/12.css') . '?0");
+@import url("' . file_create_url('public://css/13.css') . '?0");
+@import url("' . file_create_url('public://css/14.css') . '?0");
+', 'all'),
+ 1 => $create_link_element(file_create_url('public://css/15.css') . '?0'),
+ 2 => $create_style_element('
+@import url("' . file_create_url('public://css/16.css') . '?0");
+@import url("' . file_create_url('public://css/17.css') . '?0");
+@import url("' . file_create_url('public://css/18.css') . '?0");
+@import url("' . file_create_url('public://css/19.css') . '?0");
+@import url("' . file_create_url('public://css/20.css') . '?0");
+@import url("' . file_create_url('public://css/21.css') . '?0");
+@import url("' . file_create_url('public://css/22.css') . '?0");
+@import url("' . file_create_url('public://css/23.css') . '?0");
+@import url("' . file_create_url('public://css/24.css') . '?0");
+@import url("' . file_create_url('public://css/25.css') . '?0");
+@import url("' . file_create_url('public://css/26.css') . '?0");
+@import url("' . file_create_url('public://css/27.css') . '?0");
+@import url("' . file_create_url('public://css/28.css') . '?0");
+@import url("' . file_create_url('public://css/29.css') . '?0");
+@import url("' . file_create_url('public://css/30.css') . '?0");
+@import url("' . file_create_url('public://css/31.css') . '?0");
+@import url("' . file_create_url('public://css/32.css') . '?0");
+', 'all'),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Tests CSS asset rendering.
+ *
+ * @dataProvider testRenderProvider
+ */
+ function testRender(array $css_assets, array $render_elements) {
+ $this->assertSame($render_elements, $this->renderer->render($css_assets));
+ }
+
+ /**
+ * Tests a CSS asset group with the invalid 'type' => 'internal'.
+ */
+ function testRenderInvalidType() {
+ $this->setExpectedException('Exception', 'Invalid CSS asset type.');
+
+ $css_group = array(
+ 'group' => 0,
+ 'every_page' => TRUE,
+ 'type' => 'internal',
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'browsers' => array(),
+ 'data' => 'http://example.com/popular.js'
+ );
+ $this->renderer->render($css_group);
+ }
+}
+}
diff --git a/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php
new file mode 100644
index 0000000..41eb529
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php
@@ -0,0 +1,227 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Asset\CssOptimizerUnitTest.
+ */
+
+
+namespace {
+
+/**
+ * CssOptimizer uses file_create_url(), which *is* available when using the
+ * Simpletest test runner, but not when using the PHPUnit test runner; hence
+ * this hack.
+ */
+if (!function_exists('file_create_url')) {
+
+ /**
+ * Temporary mock for file_create_url(), until that is moved into
+ * Component/Utility.
+ */
+ function file_create_url($uri) {
+ return 'file_create_url:' . $uri;
+ }
+
+}
+
+if (!function_exists('file_uri_scheme')) {
+
+ function file_uri_scheme($uri) {
+ return FALSE;
+ }
+
+}
+
+}
+
+
+namespace Drupal\Tests\Core\Asset {
+
+use Drupal\Core\Asset\CssOptimizer;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the CssOptimizer class.
+ *
+ * @group Asset
+ */
+class CssOptimizerUnitTest extends UnitTestCase {
+
+ /**
+ * A CSS asset optimizer.
+ *
+ * @var \Drupal\Core\Asset\CssOptimizer object.
+ */
+ protected $optimizer;
+
+ /**
+ * A valid file CSS asset group.
+ *
+ * @var array
+ */
+ protected $file_css_group;
+
+ /**
+ * A valid inline CSS asset group.
+ *
+ * @var array
+ */
+ protected $inline_css_group;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'CSS asset optimizer functionality',
+ 'description' => 'Tests the CSS asset optimizer.',
+ 'group' => 'Asset handling',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ $this->optimizer = new CssOptimizer();
+ }
+
+ /**
+ * Provides data for the CSS asset optimizing test.
+ */
+ function testOptimizeProvider() {
+ $path = dirname(__FILE__) . '/css_test_files/';
+ return array(
+ // File. Tests:
+ // - Stripped comments and white-space.
+ // - Retain white-space in selectors. (http://drupal.org/node/472820)
+ // - Retain pseudo-selectors. (http://drupal.org/node/460448)
+ 0 => array(
+ array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'weight' => 0.012,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => $path . 'css_input_without_import.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'css_input_without_import.css',
+ ),
+ file_get_contents($path . 'css_input_without_import.css.optimized.css'),
+ ),
+ // File. Tests:
+ // - Proper URLs in imported files. (http://drupal.org/node/265719)
+ // - A background image with relative paths, which must be rewritten.
+ // - The rewritten background image path must also be passed through
+ // file_create_url(). (https://drupal.org/node/1961340)
+ 1 => array(
+ array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'weight' => 0.013,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => $path . 'css_input_with_import.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'css_input_with_import.css',
+ ),
+ str_replace('url(images/icon.png)', 'url(' . file_create_url($path . 'images/icon.png') . ')', file_get_contents($path . 'css_input_with_import.css.optimized.css')),
+ ),
+ // File. Tests:
+ // - Retain comment hacks.
+ 2 => array(
+ array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'weight' => 0.013,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => $path . 'comment_hacks.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'comment_hacks.css',
+ ),
+ file_get_contents($path . 'comment_hacks.css.optimized.css'),
+ ),
+ // Inline. Preprocessing enabled.
+ 3 => array(
+ array(
+ 'group' => 0,
+ 'every_page' => FALSE,
+ 'type' => 'inline',
+ 'weight' => 0.012,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => '.girlfriend { display: none; }',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ ),
+ ".girlfriend{display:none;}\n",
+ ),
+ // Inline. Preprocessing disabled.
+ 4 => array(
+ array(
+ 'group' => 0,
+ 'every_page' => FALSE,
+ 'type' => 'inline',
+ 'weight' => 0.013,
+ 'media' => 'all',
+ 'preprocess' => FALSE,
+ 'data' => '#home body { position: fixed; }',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ ),
+ '#home body { position: fixed; }',
+ )
+ );
+ }
+
+ /**
+ * Tests optimizing a CSS asset group containing 'type' => 'file'.
+ *
+ * @dataProvider testOptimizeProvider
+ */
+ function testOptimize($css_asset, $expected) {
+ $this->assertEquals($expected, $this->optimizer->optimize($css_asset), 'Group of file CSS assets optimized correctly.');
+ }
+
+ /**
+ * Tests a file CSS asset with preprocessing disabled.
+ */
+ function testTypeFilePreprocessingDisabled() {
+ $this->setExpectedException('Exception', 'Only file CSS assets with preprocessing enabled can be optimized.');
+
+ $css_asset = array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ 'type' => 'file',
+ 'weight' => 0.012,
+ 'media' => 'all',
+ // Preprocessing disabled.
+ 'preprocess' => FALSE,
+ 'data' => 'tests/Drupal/Tests/Core/Asset/foo.css',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ 'basename' => 'foo.css',
+ );
+ $this->optimizer->optimize($css_asset);
+ }
+
+ /**
+ * Tests a CSS asset with 'type' => 'external'.
+ */
+ function testTypeExternal() {
+ $this->setExpectedException('Exception', 'Only file or inline CSS assets can be optimized.');
+
+ $css_asset = array(
+ 'group' => -100,
+ 'every_page' => TRUE,
+ // Type external.
+ 'type' => 'external',
+ 'weight' => 0.012,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => 'http://example.com/foo.js',
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ );
+ $this->optimizer->optimize($css_asset);
+ }
+
+}
+}
diff --git a/core/modules/simpletest/files/css_test_files/comment_hacks.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/comment_hacks.css
index 8b12d6c..8b12d6c 100644
--- a/core/modules/simpletest/files/css_test_files/comment_hacks.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/comment_hacks.css
diff --git a/core/modules/simpletest/files/css_test_files/comment_hacks.css.optimized.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/comment_hacks.css.optimized.css
index 4e6615a..4e6615a 100644
--- a/core/modules/simpletest/files/css_test_files/comment_hacks.css.optimized.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/comment_hacks.css.optimized.css
diff --git a/core/modules/simpletest/files/css_test_files/css_input_with_import.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css
index 87afcb3..87afcb3 100644
--- a/core/modules/simpletest/files/css_test_files/css_input_with_import.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css
diff --git a/core/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.css
index 698d9aa..698d9aa 100644
--- a/core/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.css
diff --git a/core/modules/simpletest/files/css_test_files/css_input_without_import.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_without_import.css
index 118dfa4..118dfa4 100644
--- a/core/modules/simpletest/files/css_test_files/css_input_without_import.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_without_import.css
diff --git a/core/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_without_import.css.optimized.css
index 6296378..6296378 100644
--- a/core/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_without_import.css.optimized.css
diff --git a/core/modules/simpletest/files/css_test_files/import1.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/import1.css
index 3d5842e..3d5842e 100644
--- a/core/modules/simpletest/files/css_test_files/import1.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/import1.css
diff --git a/core/modules/simpletest/files/css_test_files/import2.css b/core/tests/Drupal/Tests/Core/Asset/css_test_files/import2.css
index 367eb57..367eb57 100644
--- a/core/modules/simpletest/files/css_test_files/import2.css
+++ b/core/tests/Drupal/Tests/Core/Asset/css_test_files/import2.css