C_NAME: $pending_stylesheet['group'] = 'amp-keyframes'; break; } unset( $pending_stylesheet['serialized'] ); $stylesheets[] = $pending_stylesheet; } return compact( 'stylesheets' ); } /** * Update admin bar. */ public function add_css_budget_to_admin_bar() { if ( ! is_admin_bar_showing() ) { return; } $validity_li_element = $this->dom->getElementById( 'wp-admin-bar-amp-validity' ); if ( ! $validity_li_element instanceof DOMElement ) { return; } /** * Cloned
  • element that we can modify to include stylesheet information. * * @var DOMElement $stylesheets_li_element */ $stylesheets_li_element = $validity_li_element->cloneNode( true ); $stylesheets_li_element->setAttribute( 'id', 'wp-admin-bar-amp-stylesheets' ); $stylesheets_a_element = $stylesheets_li_element->getElementsByTagName( 'a' )->item( 0 ); if ( ! ( $stylesheets_a_element instanceof DOMElement ) ) { return; } $stylesheets_a_element->setAttribute( 'href', $stylesheets_a_element->getAttribute( 'href' ) . '#amp_stylesheets' ); while ( $stylesheets_a_element->firstChild ) { $stylesheets_a_element->removeChild( $stylesheets_a_element->firstChild ); } $total_size = 0; foreach ( $this->pending_stylesheets as $pending_stylesheet ) { if ( empty( $pending_stylesheet['duplicate'] ) ) { $total_size += $pending_stylesheet['final_size']; } } $css_usage_percentage = ceil( ( $total_size / $this->style_custom_cdata_spec['max_bytes'] ) * 100 ); $menu_item_text = __( 'CSS Usage', 'amp' ) . ': '; $menu_item_text .= $css_usage_percentage . '%'; $stylesheets_a_element->appendChild( $this->dom->createTextNode( $menu_item_text ) ); if ( $css_usage_percentage > 100 ) { $icon = Icon::INVALID; } elseif ( $css_usage_percentage >= self::CSS_BUDGET_WARNING_PERCENTAGE ) { $icon = Icon::WARNING; } if ( isset( $icon ) ) { $span = $this->dom->createElement( 'span' ); $span->setAttribute( 'class', 'ab-icon amp-icon ' . $icon ); $stylesheets_a_element->appendChild( $span ); } $validity_li_element->parentNode->insertBefore( $stylesheets_li_element, $validity_li_element->nextSibling ); } /** * Convert CSS selectors and remove obsolete selector hacks for IE. * * @param DeclarationBlock $ruleset Ruleset. * @return array Validation results. */ private function ampify_ruleset_selectors( $ruleset ) { $selectors = []; $results = []; $has_changed_selectors = false; $language = strtolower( get_bloginfo( 'language' ) ); foreach ( $ruleset->getSelectors() as $old_selector ) { $selector = $old_selector->getSelector(); // Strip out selectors that contain the disallowed prefix 'i-amphtml-'. if ( preg_match( '/(^|\W)i-amphtml-/', $selector ) ) { $error = [ 'code' => self::CSS_DISALLOWED_SELECTOR, 'type' => AMP_Validation_Error_Taxonomy::CSS_ERROR_TYPE, 'css_selector' => $selector, ]; $sanitized = $this->should_sanitize_validation_error( $error ); $results[] = compact( 'error', 'sanitized' ); if ( $sanitized ) { $has_changed_selectors = true; continue; } } // Automatically tree-shake IE6/IE7 hacks for selectors with `* html` and `*+html`. if ( preg_match( '/^\*\s*\+?\s*html/', $selector ) ) { $has_changed_selectors = true; continue; } // Automatically remove selectors with html[lang] that are for another language (and thus are irrelevant). This is safe because amp-bind'ed [lang] is not allowed. $is_other_language_root = ( preg_match( '/^html\[lang(?P\^)?=([\'"]?)(?P.+?)\2\]/', strtolower( $selector ), $matches ) && ( empty( $matches['starts_with'] ) ? $language !== $matches['lang'] : substr( $language, 0, strlen( $matches['lang'] ) ) !== $matches['lang'] ) ); if ( $is_other_language_root ) { $has_changed_selectors = true; continue; } // Remove selectors with :lang() for another language (and thus irrelevant). if ( preg_match( '/:lang\((?P.+?)\)/', $selector, $matches ) ) { $has_matching_language = 0; $selector_languages = array_map( static function ( $selector_language ) { return trim( $selector_language, '"\'' ); }, preg_split( '/\s*,\s*/', strtolower( trim( $matches['languages'] ) ) ) ); foreach ( $selector_languages as $selector_language ) { /* * The following logic accounts for the following conditions, where all but the last is a match: * * N: en && fr * Y: en && en * Y: en && en-US * Y: en-US && en * N: en-US && en-UK */ if ( substr( $language, 0, strlen( $selector_language ) ) === $selector_language || substr( $selector_language, 0, strlen( $language ) ) === $language ) { $has_matching_language = true; break; } } if ( ! $has_matching_language ) { $has_changed_selectors = true; continue; } } // An element (type) either starts a selector or is preceded by combinator, comma, opening paren, or closing brace. $before_type_selector_pattern = '(?<=^|\(|\s|>|\+|~|,|})'; $after_type_selector_pattern = '(?=$|[^a-zA-Z0-9_-])'; // Replace focus selectors with :focus-within. if ( $this->focus_class_name_selector_pattern ) { $count = 0; $selector = preg_replace_callback( $this->focus_class_name_selector_pattern, static function ( $matches ) { $replacement = ':focus-within'; if ( 'focus' === $matches['class'] && ( ! empty( $matches['beginning'] ) || ( ! empty( $matches['combinator'] ) && '' === trim( $matches['combinator'] ) ) ) ) { /* * If a descendant combinator precedes the focus selector, prefix the pseudo class selector * with a class selector that's known to be common among themes that use the focus selector. * This is to prevent the pseudo class selector being applied to the ancestor selector, * which can cause unintended behavior on the page. */ $replacement = '.menu-item-has-children' . $replacement; } // Ensure preceding combinator is preserved. if ( ! empty( $matches['combinator'] ) ) { $replacement = $matches['combinator'] . $replacement; } return $replacement; }, $selector, -1, $count ); if ( $count > 0 ) { $has_changed_selectors = true; } } // Replace the somewhat-meta [style] attribute selectors with attribute selector using the data attribute the original styles are copied into. if ( $this->args['transform_important_qualifiers'] ) { $selector = preg_replace( '/(?<=\[)style(?=([*$~]?=.*?)?])/is', self::ORIGINAL_STYLE_ATTRIBUTE_NAME, $selector, - 1, $count ); if ( $count > 0 ) { $has_changed_selectors = true; } } /* * Loop over each selector mappings. A single HTML tag can map to multiple AMP tags (e.g. img could be amp-img or amp-anim). * The $selector_mappings array contains ~6 items, so rest easy your O(n^3) eyes when seeing triple nested loops! */ $edited_selectors = [ $selector ]; foreach ( $this->selector_mappings as $html_tag => $amp_tags ) { // Create pattern for determining whether a mapped HTML element is present in this selector. $html_pattern = '/' . $before_type_selector_pattern . preg_quote( $html_tag, '/' ) . $after_type_selector_pattern . '/i'; /* * Iterate over each selector and perform the tag mapping replacements. * Note that $edited_selectors array contains only item in the normal case. * Note also that the size of $edited_selectors can grow while iterating, hence disabling sniffs. */ for ( $i = 0; $i < count( $edited_selectors ); $i++ ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed, Squiz.PHP.DisallowSizeFunctionsInLoops.Found // Skip doing any replacement if the AMP tag is already present, as this indicates the selector was written for AMP already. $amp_tag_pattern = '/' . $before_type_selector_pattern . implode( '|', $amp_tags ) . $after_type_selector_pattern . '/i'; if ( preg_match( $amp_tag_pattern, $edited_selectors[ $i ], $matches ) && in_array( $matches[0], $amp_tags, true ) ) { continue; } // Replace the HTML tag with the first first mapped AMP tag. $edited_selector = preg_replace( $html_pattern, $amp_tags[0], $edited_selectors[ $i ], -1, $count ); // If the HTML tag was not found, then short-circuit. if ( 0 === $count ) { continue; } $edited_selectors_from_selector = [ $edited_selector ]; // Replace the HTML tag with the the remaining mapped AMP tags. foreach ( array_slice( $amp_tags, 1 ) as $amp_tag ) { // Note: This array contains only a couple items. $edited_selectors_from_selector[] = preg_replace( $html_pattern, $amp_tag, $edited_selectors[ $i ] ); } // Replace the current edited selector with all the new edited selectors resulting from the mapping replacement. array_splice( $edited_selectors, $i, 1, $edited_selectors_from_selector ); $has_changed_selectors = true; } } $selectors = array_merge( $selectors, $edited_selectors ); } if ( $has_changed_selectors ) { $ruleset->setSelectors( $selectors ); } return $results; } /** * Given a list of class names, create a regular expression pattern to match them in a selector. * * @since 1.4 * @since 2.0 In addition to the class, now includes capture groups for an immediately-preceding combinator or whether the class begins the selector. * * @param string[] $class_names Class names. * @return string Regular expression pattern. */ private static function get_class_name_selector_pattern( $class_names ) { $class_pattern = implode( '|', array_map( static function ( $class_name ) { return preg_quote( $class_name, '/' ); }, (array) $class_names ) ); return "/(?:(?^\s*\.)|(?[>+~\s]*)\.)(?{$class_pattern})(?=$|[^a-zA-Z0-9_-])/s"; } /** * Finalize a stylesheet group (amp-custom or amp-keyframes). * * @since 1.2 * * @param int $group Group name (either self::STYLE_AMP_CUSTOM_GROUP_INDEX or self::STYLE_AMP_KEYFRAMES_GROUP_INDEX ). * @param array $group_config Group config. * @return array { * Finalized group info. * * @type int $included_count Number of included stylesheets in group. * @type bool $is_excessive_size Whether the total is greater than the max bytes allowed. * @type int $important_count Number of !important qualifiers. * @type int $kept_error_count Number of validation errors whose markup was kept. * @type string[] $preload_font_urls Font URLs to preload. * } */ private function finalize_stylesheet_group( $group, $group_config ) { $max_bytes = $group_config['cdata_spec']['max_bytes'] - strlen( $group_config['source_map_comment'] ); $included_count = 0; $is_excessive_size = false; $concatenated_size = 0; $important_count = 0; $kept_error_count = 0; $preload_font_urls = []; $previously_seen_stylesheet_index = []; foreach ( $this->pending_stylesheets as $pending_stylesheet_index => &$pending_stylesheet ) { if ( $group !== $pending_stylesheet['group'] ) { continue; } $start_time = microtime( true ); $shaken_tokens = []; foreach ( $pending_stylesheet['tokens'] as $token ) { if ( is_string( $token ) ) { $shaken_tokens[] = [ true, $token ]; continue; } list( $selectors_parsed, $declaration_block ) = $token; $used_selector_count = 0; $selectors = []; foreach ( $selectors_parsed as $selector => $parsed_selector ) { $should_include = $this->args['skip_tree_shaking'] || ( // If all class names are used in the doc. ( empty( $parsed_selector[ self::SELECTOR_EXTRACTED_CLASSES ] ) || $this->has_used_class_name( $parsed_selector[ self::SELECTOR_EXTRACTED_CLASSES ] ) ) && // If all IDs are used in the doc. ( empty( $parsed_selector[ self::SELECTOR_EXTRACTED_IDS ] ) || 0 === count( array_filter( $parsed_selector[ self::SELECTOR_EXTRACTED_IDS ], function( $id ) { return ! $this->dom->getElementById( $id ); } ) ) ) && // If tag names are present in the doc. ( empty( $parsed_selector[ self::SELECTOR_EXTRACTED_TAGS ] ) || $this->has_used_tag_names( $parsed_selector[ self::SELECTOR_EXTRACTED_TAGS ] ) ) && // If all attribute names are used in the doc. ( empty( $parsed_selector[ self::SELECTOR_EXTRACTED_ATTRIBUTES ] ) || $this->has_used_attributes( $parsed_selector[ self::SELECTOR_EXTRACTED_ATTRIBUTES ] ) ) ); $selectors[ $selector ] = $should_include; if ( $should_include ) { $used_selector_count++; } } $shaken_tokens[] = [ 0 !== $used_selector_count, $selectors, $declaration_block, ]; } // Strip empty at-rules after tree shaking. $stylesheet_part_count = count( $shaken_tokens ); for ( $i = 0; $i < $stylesheet_part_count; $i++ ) { // Skip anything that isn't an at-rule. if ( ! is_string( $shaken_tokens[ $i ][1] ) || '@' !== substr( $shaken_tokens[ $i ][1], 0, 1 ) ) { continue; } // Delete empty at-rules. if ( '{}' === substr( $shaken_tokens[ $i ][1], -2 ) ) { $shaken_tokens[ $i ][0] = false; continue; } // Delete at-rules that were emptied due to tree-shaking. if ( '{' === substr( $shaken_tokens[ $i ][1], -1 ) ) { $open_braces = 1; for ( $j = $i + 1; $j < $stylesheet_part_count; $j++ ) { if ( is_array( $shaken_tokens[ $j ][1] ) ) { // Is declaration block. if ( true === $shaken_tokens[ $j ][0] ) { // The declaration block has selectors which survived tree shaking, so the contained at- // rule cannot be removed and so we must abort. break; } else { // Continue to the next stylesheet part as this declaration block can be included in the // list of parts that may be part of an at-rule that is now empty and should be removed. continue; } } $is_at_rule = '@' === substr( $shaken_tokens[ $j ][1], 0, 1 ); if ( $is_at_rule && '{}' === substr( $shaken_tokens[ $j ][1], -2 ) ) { continue; // The rule opened is empty from the start. } if ( $is_at_rule && '{' === substr( $shaken_tokens[ $j ][1], -1 ) ) { $open_braces++; } elseif ( '}' === $shaken_tokens[ $j ][1] ) { $open_braces--; } else { break; } // Splice out the parts that are empty. if ( 0 === $open_braces ) { for ( $k = $i; $k <= $j; $k++ ) { $shaken_tokens[ $k ][0] = false; } $i = $j; // Jump the outer loop ahead to skip over what has been already marked as removed. continue 2; } } } } $pending_stylesheet['shaken_tokens'] = $shaken_tokens; unset( $pending_stylesheet['tokens'], $shaken_tokens ); // @todo After this point we could unset( $pending_stylesheet['tokens'] ) since they wouldn't be used in the course of generating a page, though they would still be useful for other purposes. $pending_stylesheet['serialized'] = implode( '', array_map( static function ( $shaken_token ) { if ( is_array( $shaken_token[1] ) ) { // Construct a declaration block. $selectors = array_keys( array_filter( $shaken_token[1] ) ); if ( empty( $selectors ) ) { return ''; } else { return implode( ',', $selectors ) . '{' . implode( ';', $shaken_token[2] ) . '}'; } } else { // Pass through parts other than declaration blocks. return $shaken_token[1]; } }, // Include the stylesheet parts that were not marked for exclusion during tree shaking. array_filter( $pending_stylesheet['shaken_tokens'], static function( $shaken_token ) { return false !== $shaken_token[0]; } ) ) ); $pending_stylesheet['included'] = null; // To be determined below. $pending_stylesheet['final_size'] = strlen( $pending_stylesheet['serialized'] ); // If this stylesheet is a duplicate of something that came before, mark the previous as not included automatically. if ( isset( $previously_seen_stylesheet_index[ $pending_stylesheet['hash'] ] ) ) { $this->pending_stylesheets[ $previously_seen_stylesheet_index[ $pending_stylesheet['hash'] ] ]['included'] = false; $this->pending_stylesheets[ $previously_seen_stylesheet_index[ $pending_stylesheet['hash'] ] ]['duplicate'] = true; } $previously_seen_stylesheet_index[ $pending_stylesheet['hash'] ] = $pending_stylesheet_index; $pending_stylesheet['shake_time'] = microtime( true ) - $start_time; } // End foreach pending_stylesheets. unset( $pending_stylesheet ); // Determine which stylesheets are included based on their priorities. $pending_stylesheet_indices = array_keys( $this->pending_stylesheets ); usort( $pending_stylesheet_indices, function ( $a, $b ) { return $this->pending_stylesheets[ $a ]['priority'] - $this->pending_stylesheets[ $b ]['priority']; } ); foreach ( $pending_stylesheet_indices as $i ) { if ( $group !== $this->pending_stylesheets[ $i ]['group'] ) { continue; } // Skip duplicates. if ( false === $this->pending_stylesheets[ $i ]['included'] ) { continue; } // Skip stylesheets that were completely tree-shaken and mark as included. if ( 0 === $this->pending_stylesheets[ $i ]['final_size'] ) { $this->pending_stylesheets[ $i ]['included'] = true; continue; } $is_stylesheet_excessive = $concatenated_size + $this->pending_stylesheets[ $i ]['final_size'] > $max_bytes; // Report validation error if size is now too big. if ( ! $this->args['allow_excessive_css'] && $is_stylesheet_excessive ) { $validation_error = [ 'code' => self::STYLESHEET_TOO_LONG, 'type' => AMP_Validation_Error_Taxonomy::CSS_ERROR_TYPE, 'spec_name' => self::STYLE_AMP_KEYFRAMES_GROUP_INDEX === $group ? self::STYLE_AMP_KEYFRAMES_SPEC_NAME : self::STYLE_AMP_CUSTOM_SPEC_NAME, ]; if ( isset( $this->pending_stylesheets[ $i ]['sources'] ) ) { $validation_error['sources'] = $this->pending_stylesheets[ $i ]['sources']; } $data = [ 'node' => $this->pending_stylesheets[ $i ]['element'], ]; if ( $this->should_sanitize_validation_error( $validation_error, $data ) ) { $this->pending_stylesheets[ $i ]['included'] = false; continue; // Skip to the next stylesheet. } } if ( ! isset( $this->pending_stylesheets[ $i ]['included'] ) ) { $this->pending_stylesheets[ $i ]['included'] = true; $included_count++; $concatenated_size += $this->pending_stylesheets[ $i ]['final_size']; $preload_font_urls = array_merge( $preload_font_urls, $this->pending_stylesheets[ $i ]['preload_font_urls'] ); if ( $is_stylesheet_excessive ) { $is_excessive_size = true; } // Note: the following two may be incorrect because the !important property or erroneous rule may have // actually been tree-shaken and thus is no longer in the document. $important_count += $this->pending_stylesheets[ $i ]['important_count']; $kept_error_count += $this->pending_stylesheets[ $i ]['kept_error_count']; } } return compact( 'included_count', 'is_excessive_size', 'important_count', 'kept_error_count', 'preload_font_urls' ); } /** * Creates and inserts a meta[name="viewport"] tag if there are @viewport style rules. * * These rules aren't valid in CSS, but they might be valid in that meta tag. * So this adds them to the content attribute of a new meta tag. * These are later processed, to merge the content values into a single meta tag. * * @param DOMElement $element An element. * @param array $viewport_rules An associative array of $rule_name => $rule_value. */ private function create_meta_viewport( DOMElement $element, $viewport_rules ) { if ( empty( $viewport_rules ) ) { return; } $viewport_meta = $this->dom->createElement( 'meta' ); $viewport_meta->setAttribute( 'name', 'viewport' ); $viewport_meta->setAttribute( 'content', implode( ',', array_map( static function ( $property_name ) use ( $viewport_rules ) { return $property_name . '=' . $viewport_rules[ $property_name ]; }, array_keys( $viewport_rules ) ) ) ); // Inject a potential duplicate meta viewport element, to later be merged in AMP_Meta_Sanitizer. $element->parentNode->insertBefore( $viewport_meta, $element ); } } Nigeria’s inflation rate hits 5-year high at 18.60%

    Business

    Nigeria’s inflation rate hits 5-year high at 18.60%

    The consumer price index (CPI), which measures the rate of change in prices of goods and services, surged to 18.60 percent in June 2022, up from 17.71 percent in the previous month.

    This is the highest since January 2017.

    The National Bureau of Statistics (NBS) said this in its consumer price index (CPI) report for June 2022, released on Friday.

    The figure is also 0.84 percent points higher compared to June 2021, which is 17.75 percent.

    The development means that the headline inflation rate increased in June 2022 when compared to the same month in the previous year (June 2021).

    According to the report, increases were recorded in all classifications of individual consumption according to purpose (COICOP) divisions that yielded the headline index.

    “On a month-on-month basis, the headline inflation rate increased to 1.82 percent in June 2022, this is 0.03 percent higher than the rate recorded in May 2022 (1.78 percent),” the report reads.

    “The percentage change in the average composite CPI for the twelve months ending June 2022 over the average of the CPI for the previous twelve months period is 16.54 percent, showing a 0.62 percent increase compared to 15.93 percent recorded in June 2021.”

    The report added that food inflation rose to 20.60 percent in June 2022 on a year-on-year basis but the rate of changes in average price level declined by 1.23 percent when compared to 21.83 percent in June 2021.

    The report further explained that the rate of changes in food prices compared to the same period last year was higher due to higher food prices volatility caused by COVID 19.

    “This rise in the food index was caused by increases in prices of bread and cereals, food products n.e.c, Potatoes, yam, and other tubers, meat, fish, oil and fat, and wine,” it added.

    “On a month-on-month basis, the food sub-index increased to 2.05 percent in June 2022, up by 0.03 percent points from 2.01 percent recorded in May 2022.”

    In analysing price movements for states, the report said Bauchi and Kogi state were the highest.

    “In June 2022, all items inflation on a year-on-year basis was highest in Bauchi (21.99 percent), Kogi (21.37 percent), Ebonyi (20.73 percent) while Adamawa (16.14 percent), Sokoto (16.31 percent) and Jigawa (16.37 percent) recorded the slowest rise in headline year-on-year inflation,” it said.

    “On a month-on-month basis, however, June 2022, recorded the highest increases in Kogi (2.69 percent), Ondo (2.65 percent), and Kaduna (2.61 percent), while Adamawa (-0.26 percent), Abuja (-.0.03 percent) and Sokoto (0.79 percent) recorded the slowest rise on month-on-month inflation.”

    Click to comment

    Trending

    Copyright © 2022 The Harmattan News. All rights reserved.