diff --git a/core/core.services.yml b/core/core.services.yml index d915e10d731efeb7d81336e60fa97eb0f86e5745..8f561ca319f21f21965aa16e45537ccd059889bc 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 e768bf470265b91a7b5a918ef56409ff8254086b..385287086da589e78f25589782cafcb002696e6c 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -26,6 +26,15 @@ 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. @@ -1799,154 +1808,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. * @@ -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/* */\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\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); } /** @@ -3726,83 +3002,6 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgro drupal_add_js($settings, 'setting'); } -/** - * 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. */ diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index ee5c0a4737f56e0f9460bba5f00cd68d6aea8c62..4d0d6da2b5272ee33522765da2025aa5be23e02f 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 0000000000000000000000000000000000000000..94f9e855b78ac1199bf5e20718654f2b85c2d453 --- /dev/null +++ b/core/lib/Drupal/Core/Asset/AssetCollectionGrouperInterface.php @@ -0,0 +1,25 @@ +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 0000000000000000000000000000000000000000..5f70aa66ea7f6110be6f9e30aded60492de79b74 --- /dev/null +++ b/core/lib/Drupal/Core/Asset/AssetDumperInterface.php @@ -0,0 +1,27 @@ +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 0000000000000000000000000000000000000000..6d36eded11317088a4efca3e369a09115fc5ee30 --- /dev/null +++ b/core/lib/Drupal/Core/Asset/CssCollectionRenderer.php @@ -0,0 +1,177 @@ + '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/* */\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 0000000000000000000000000000000000000000..2cf07e7908774d14cb6c4085f69d78558e789096 --- /dev/null +++ b/core/lib/Drupal/Core/Asset/CssOptimizer.php @@ -0,0 +1,229 @@ +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 0000000000000000000000000000000000000000..9f0aed0d9754d4a5e164b934783c5e8bbb7ece1e --- /dev/null +++ b/core/lib/Drupal/Core/Asset/JsCollectionGrouper.php @@ -0,0 +1,80 @@ +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 0000000000000000000000000000000000000000..b9f65824b11e449a3a38e3e53021bd73d9dc2e90 --- /dev/null +++ b/core/lib/Drupal/Core/Asset/JsCollectionRenderer.php @@ -0,0 +1,97 @@ +\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 0000000000000000000000000000000000000000..e6ad2939e4811444e7b57bd16e5f88c56791b2fe --- /dev/null +++ b/core/lib/Drupal/Core/Asset/JsOptimizer.php @@ -0,0 +1,31 @@ +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 f41460cb2ffd0452cf279d463aea844faecbed27..287fb8fdd98d9bebe0a5d40127120a5e453b2472 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 @@ function testPageLayout() { $this->assertRaw('
drupal_load_stylesheet_content()
.
-*
-*/
-/*
-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 4c905f5620756f75f67ba4e4ce8a76950a8fcf7c..0000000000000000000000000000000000000000
--- 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 118dfa4dc5b77b05081a4adb9c209be0d31e05e8..0000000000000000000000000000000000000000
--- 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 d7fbdbf0b0406aa61a216828f513fdeffb18470a..54abcbaa37c96a98d480fed59fc7875e6704faa9 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 @@ function testRenderFile() {
$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 = "";
+ $css_processed = '';
$this->assertEqual(trim($styles), $css_processed, 'Rendered CSS includes newlines inside style tags for JavaScript use.');
}
@@ -99,8 +99,12 @@ function testRenderExternal() {
* 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 = '';
+ $css_preprocessed = '';
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 5ab3c0a163728d26cf480216e4c756b9fd5124f9..0000000000000000000000000000000000000000
--- a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsUnitTest.php
+++ /dev/null
@@ -1,63 +0,0 @@
- '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 5a1d3f6ef45dbc22e41d835732b76f47cc616f03..fc0e5ff68b6dd81aa5206ce7788835604ca1158e 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 @@ function testAggregation() {
$js_items = drupal_add_js();
$javascript = drupal_get_js();
$expected = implode("\n", array(
- '',
- '',
+ '',
+ '',
));
$this->assertTrue(strpos($javascript, $expected) !== FALSE, 'JavaScript is aggregated in the expected groups and order.');
}
@@ -365,10 +366,14 @@ function testAggregationOrder() {
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 @@ function testAggregationOrder() {
// 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 @@ function testAddJsFileWithQueryString() {
$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 0df34a040fa7c4c5c81d15ec6d81c0daeb0f09cd..a6a77861e0eba71e9ffefe7575e176c2da77a3e7 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 0000000000000000000000000000000000000000..adf5e888dc11f44f8009401541f65b86e4b04d86
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php
@@ -0,0 +1,216 @@
+ '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 0000000000000000000000000000000000000000..cd97a61a7789bfeab2563e8171d810f641abe1eb
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php
@@ -0,0 +1,572 @@
+ '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/* */\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 0000000000000000000000000000000000000000..41eb529d78324aca47343eaa85c48556c4b02cb9
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php
@@ -0,0 +1,227 @@
+ '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
similarity index 100%
rename from core/modules/simpletest/files/css_test_files/comment_hacks.css
rename to 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
similarity index 100%
rename from core/modules/simpletest/files/css_test_files/comment_hacks.css.optimized.css
rename to 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
similarity index 100%
rename from core/modules/simpletest/files/css_test_files/css_input_with_import.css
rename to 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
similarity index 100%
rename from core/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css
rename to 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
similarity index 100%
rename from core/modules/simpletest/files/css_test_files/css_input_without_import.css
rename to 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
similarity index 100%
rename from core/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css
rename to 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
similarity index 100%
rename from core/modules/simpletest/files/css_test_files/import1.css
rename to 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
similarity index 100%
rename from core/modules/simpletest/files/css_test_files/import2.css
rename to core/tests/Drupal/Tests/Core/Asset/css_test_files/import2.css