. */ /* * A class to inline CSS. * * It honours !important attributes and doesn't choke on complex styles. * * */ class CSS { private $parsed_css = array(); public static function splitMediaQueries($css) { $start = 0; $queries = ''; while(($start = strpos($css, "@media", $start)) !== false) { // stack to manage brackets $s = array(); // get the first opening bracket $i = strpos($css, "{", $start); // if $i is false, then there is probably a css syntax error if($i !== false) { // push bracket onto stack array_push($s, $css[$i]); // move past first bracket $i++; while(!empty($s)) { // if the character is an opening bracket, push it onto the stack, otherwise pop the stack if($css[$i] == "{") { array_push($s, "{"); } else if($css[$i] == "}") { array_pop($s); } $i++; } $queries .= substr($css, $start-1, $i+1-$start) . "\n"; $css = substr($css, 0, $start-1) . substr($css, $i); $i = $start; } } return array($css, $queries); } public function parseCSS($text) { $css = new csstidy(); $css->settings['compress_colors'] = false; $css->parse($text); $rules = array(); $position = 0; foreach($css->css as $declarations) { foreach($declarations as $selectors => $properties) { foreach(explode(",", $selectors) as $selector) { $rules[] = array( 'position' => $position, 'specificity' => self::calculateCSSSpecifity($selector), 'selector' => $selector, 'properties' => $properties ); } $position += 1; } } usort($rules, function($a, $b) { if($a['specificity'] > $b['specificity']) { return 1; } else if($a['specificity'] < $b['specificity']) { return -1; } else { if($a['position'] > $b['position']) { return 1; } else { return -1; } } }); return $rules; } /** * The following function fomes from CssToInlineStyles.php - here is the original licence FOR THIS FUNCTION * * CSS to Inline Styles class * * @author Tijs Verkoyen * @version 1.2.1 * @copyright Copyright (c), Tijs Verkoyen. All rights reserved. * @license BSD License */ public static function calculateCSSSpecifity($selector) { // cleanup selector $selector = str_replace(array('>', '+'), array(' > ', ' + '), $selector); // init var $specifity = 0; // split the selector into chunks based on spaces $chunks = explode(' ', $selector); // loop chunks foreach ($chunks as $chunk) { // an ID is important, so give it a high specifity if(strstr($chunk, '#') !== false) $specifity += 100; // classes are more important than a tag, but less important then an ID elseif(strstr($chunk, '.')) $specifity += 10; // anything else isn't that important else $specifity += 1; } // return return $specifity; } /* * Turns a CSS style string (like: "border: 1px solid black; color:red") * into an array of properties (like: array("border" => "1px solid black", "color" => "red")) */ public static function styleToArray($str) { $array = array(); if(trim($str) === '') return $array; foreach(explode(';', $str) as $kv) { if($kv === '') { continue; } $key_value = explode(':', $kv); $array[trim($key_value[0])] = trim($key_value[1]); } return $array; } /* * Reverses what styleToArray does, see above. * array("border" => "1px solid black", "color" => "red") yields "border: 1px solid black; color:red" */ public static function arrayToStyle($array) { $parts = array(); foreach($array as $k => $v) { $parts[] = "$k:$v"; } return implode(';', $parts); } /* * The core of the algorithm, takes a URL and returns the HTML found there with the CSS inlined. * If you pass $contents then the original HTML is not downloaded and $contents is used instead. * $url is mandatory as it is used to resolve the links to the stylesheets found in the HTML. */ function inlineCSS($url, $contents=null) { $html = \pQuery::parseStr($contents); if(!is_object($html)) { return false; } $css_blocks = ''; // Find all