You are here

function advagg_font_get_replacements_array in Advanced CSS/JS Aggregation 7.2

Get the replacements array for the css.

Parameters

string $css_string: String of CSS.

Return value

array An array containing the replacemnts and the font class name.

4 calls to advagg_font_get_replacements_array()
advagg_css_alter in advagg_font/advagg_font.module
Implements hook_css_alter().
advagg_font_admin_settings_form in advagg_font/advagg_font.admin.inc
Form builder; Configure advagg settings.
advagg_font_advagg_get_css_file_contents_alter in advagg_font/advagg_font.advagg.inc
Implements hook_advagg_get_css_file_contents_alter().
advagg_font_advagg_get_info_on_files_alter in advagg_font/advagg_font.advagg.inc
Implements hook_advagg_get_info_on_files_alter().

File

advagg_font/advagg_font.module, line 319
Advanced aggregation font module.

Code

function advagg_font_get_replacements_array($css_string) {

  // Get the CSS that contains a font-family rule.
  $length = strlen($css_string);
  $property_position = 0;
  $property = 'font';
  $property_alt = 'font-family';
  $replacements = array();
  $fonts_with_no_replacements = array();
  $lower = strtolower($css_string);
  $safe_fonts_list = array(
    'georgia' => TRUE,
    'palatino' => TRUE,
    'times new roman' => TRUE,
    'times' => TRUE,
    'arial' => TRUE,
    'helvetica' => TRUE,
    'gadget' => TRUE,
    'verdana' => TRUE,
    'geneva' => TRUE,
    'tahoma' => TRUE,
    'garamond' => TRUE,
    'bookman' => TRUE,
    'comic sans ms' => TRUE,
    'cursive' => TRUE,
    'trebuchet ms' => TRUE,
    'arial black' => TRUE,
    'impact' => TRUE,
    'charcoal' => TRUE,
    'courier new' => TRUE,
    'courier' => TRUE,
    'monaco' => TRUE,
    'system' => TRUE,
  );
  while (($property_position = strpos($lower, $property, $property_position)) !== FALSE) {

    // Find the start of the values for the property.
    $start_of_values = strpos($css_string, ':', $property_position);

    // Get the property at this location of the css.
    $property_in_loop = trim(substr($css_string, $property_position, $start_of_values - $property_position));

    // Make sure this property is one of the ones we're looking for.
    if ($property_in_loop !== $property && $property_in_loop !== $property_alt) {
      $property_position += strlen($property);
      continue;
    }

    // Get position of the last closing bracket plus 1 (start of this section).
    $start = strrpos($css_string, '}', -($length - $property_position));
    if ($start === FALSE) {

      // Property is in the first selector and a declaration block (full rule
      // set).
      $start = 0;
    }
    else {

      // Add one to start after the }.
      $start++;
    }

    // Get closing bracket (end of this section).
    $end = strpos($css_string, '}', $property_position);
    if ($end === FALSE) {

      // The end is the end of this file.
      $end = $length;
    }

    // Get closing ; in order to get the end of the declaration of the property.
    $declaration_end_a = strpos($css_string, ';', $property_position);
    $declaration_end_b = strpos($css_string, '}', $property_position);
    if ($declaration_end_a === FALSE) {
      $declaration_end = $declaration_end_b;
    }
    else {
      $declaration_end = min($declaration_end_a, $declaration_end_b);
    }
    if ($declaration_end > $end) {
      $declaration_end = $end;
    }

    // Add one in order to capture the } when we ge the full rule set.
    $end++;

    // Advance position for the next run of the while loop.
    $property_position = $end;

    // Get values assigned to this property.
    $values_string = substr($css_string, $start_of_values + 1, $declaration_end - ($start_of_values + 1));

    // Parse values string into an array of values.
    $values_array = explode(',', $values_string);
    if (empty($values_array)) {
      continue;
    }

    // Values array, first element is a quoted string.
    $dq = strpos($values_array[0], '"');
    $sq = strpos($values_array[0], "'");
    $quote_pos = $sq !== FALSE ? $sq : $dq;

    // Skip if the first font is not quoted.
    if ($quote_pos === FALSE) {
      continue;
    }
    $values_array[0] = trim($values_array[0]);

    // Skip if only one font is listed.
    if (count($values_array) === 1) {
      $fonts_with_no_replacements[$values_array[0]] = '';
      continue;
    }

    // Save the first value to a variable; starting at the quote.
    $removed_value_original = substr($values_array[0], max($quote_pos - 1, 0));

    // Resave first value.
    if ($quote_pos > 1) {
      $values_array[0] = trim(substr($values_array[0], 0, $quote_pos - 1));
    }

    // Get value as a classname. Remove quotes, trim, lowercase, and replace
    // spaces with dashes.
    $removed_value_classname = strtolower(trim(str_replace(array(
      '"',
      "'",
    ), '', $removed_value_original)));
    $removed_value_classname = str_replace(' ', '-', $removed_value_classname);

    // Remove value if it contains a quote.
    $values_array_copy = $values_array;
    foreach ($values_array as $key => $value) {
      if (strpos($value, '"') !== FALSE || strpos($value, "'") !== FALSE) {
        unset($values_array[$key]);
      }
      elseif ($key !== 0) {
        break;
      }
    }
    if (empty($values_array)) {

      // See if there's a "safe" fallback that is quoted.
      $values_array = $values_array_copy;
      foreach ($values_array as $key => $value) {
        if (strpos($value, '"') !== FALSE || strpos($value, "'") !== FALSE) {
          if ($key !== 0) {
            $lower_key = trim(trim(strtolower(trim($value)), '"'), "'");
            if (!empty($safe_fonts_list[$lower_key])) {
              break;
            }
          }
          unset($values_array[$key]);
        }
        elseif ($key !== 0) {
          break;
        }
      }
      if (empty($values_array)) {

        // No unquoted values left; do not modify the css.
        $key = array_shift($values_array_copy);
        $fonts_with_no_replacements[$key] = implode(',', $values_array_copy);
        continue;
      }
    }
    $extra = '';
    if (isset($values_array[0])) {
      $extra = $values_array[0] . ' ';
      unset($values_array[0]);
    }

    // Rezero the keys.
    $values_array = array_values($values_array);

    // Save next value.
    $next_value_original = trim($values_array[0]);

    // Create the values string.
    $new_values_string = $extra . implode(',', $values_array);

    // Get all selectors.
    $end_of_selectors = strpos($css_string, '{', $start);
    $selectors = substr($css_string, $start, $end_of_selectors - $start);

    // Ensure selectors is not a media query.
    if (stripos($selectors, "@media") !== FALSE) {

      // Move the start to the end of the media query.
      $start = $end_of_selectors + 1;

      // Get the selectors again.
      $end_of_selectors = strpos($css_string, '{', $start);
      $selectors = substr($css_string, $start, $end_of_selectors - $start);
    }

    // From advagg_load_stylesheet_content().
    // Perform some safe CSS optimizations.
    // Regexp to match comment blocks.
    // Regexp to match double quoted strings.
    // Regexp to match single quoted strings.
    $comment = '/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/';
    $double_quot = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
    $single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'";

    // Strip all comment blocks, but keep double/single quoted strings.
    $selectors_stripped = preg_replace("<({$double_quot}|{$single_quot})|{$comment}>Ss", "\$1", $selectors);

    // Add css class to all the selectors.
    $selectors_array = explode(',', $selectors_stripped);
    foreach ($selectors_array as &$selector) {

      // Remove extra whitespace.
      $selector = trim($selector);
      $selector = " .{$removed_value_classname} {$selector}";
    }
    $new_selectors = implode(',', $selectors_array);

    // Get full rule set.
    $full_rule_set = substr($css_string, $start, $end - $start);

    // Replace values.
    $new_values_full_rule_set = str_replace($values_string, $new_values_string, $full_rule_set);

    // Add in old rule set with new selectors.
    $new_selectors_full_rule_set = $new_selectors . '{' . $property_in_loop . ': ' . $values_string . ';}';

    // Record info.
    $replacements[] = array(
      $full_rule_set,
      $new_values_full_rule_set,
      $new_selectors_full_rule_set,
      $removed_value_original,
      $removed_value_classname,
      $next_value_original,
    );
  }
  return array(
    $replacements,
    $fonts_with_no_replacements,
  );
}