xref: /plugin/pagecss/vendor/csstidy-2.2.1/class.csstidy_print.php (revision 7d6669007238fef7e8728f167d637ed824899eb0)
1*7d666900SdWiGhT<?php
2*7d666900SdWiGhT
3*7d666900SdWiGhT/**
4*7d666900SdWiGhT * CSSTidy - CSS Parser and Optimiser
5*7d666900SdWiGhT *
6*7d666900SdWiGhT * CSS Printing class
7*7d666900SdWiGhT * This class prints CSS data generated by csstidy.
8*7d666900SdWiGhT *
9*7d666900SdWiGhT * Copyright 2005, 2006, 2007 Florian Schmitz
10*7d666900SdWiGhT *
11*7d666900SdWiGhT * This file is part of CSSTidy.
12*7d666900SdWiGhT *
13*7d666900SdWiGhT *   CSSTidy is free software; you can redistribute it and/or modify
14*7d666900SdWiGhT *   it under the terms of the GNU Lesser General Public License as published by
15*7d666900SdWiGhT *   the Free Software Foundation; either version 2.1 of the License, or
16*7d666900SdWiGhT *   (at your option) any later version.
17*7d666900SdWiGhT *
18*7d666900SdWiGhT *   CSSTidy is distributed in the hope that it will be useful,
19*7d666900SdWiGhT *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20*7d666900SdWiGhT *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21*7d666900SdWiGhT *   GNU Lesser General Public License for more details.
22*7d666900SdWiGhT *
23*7d666900SdWiGhT *   You should have received a copy of the GNU Lesser General Public License
24*7d666900SdWiGhT *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
25*7d666900SdWiGhT *
26*7d666900SdWiGhT * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
27*7d666900SdWiGhT * @package csstidy
28*7d666900SdWiGhT * @author Florian Schmitz (floele at gmail dot com) 2005-2007
29*7d666900SdWiGhT * @author Brett Zamir (brettz9 at yahoo dot com) 2007
30*7d666900SdWiGhT * @author Cedric Morin (cedric at yterium dot com) 2010-2012
31*7d666900SdWiGhT */
32*7d666900SdWiGhT
33*7d666900SdWiGhT/**
34*7d666900SdWiGhT * CSS Printing class
35*7d666900SdWiGhT *
36*7d666900SdWiGhT * This class prints CSS data generated by csstidy.
37*7d666900SdWiGhT *
38*7d666900SdWiGhT * @package csstidy
39*7d666900SdWiGhT * @author Florian Schmitz (floele at gmail dot com) 2005-2006
40*7d666900SdWiGhT * @version 1.1.0
41*7d666900SdWiGhT */
42*7d666900SdWiGhTclass csstidy_print {
43*7d666900SdWiGhT
44*7d666900SdWiGhT	/**
45*7d666900SdWiGhT	 * csstidy object
46*7d666900SdWiGhT	 * @var object
47*7d666900SdWiGhT	 */
48*7d666900SdWiGhT	public $parser;
49*7d666900SdWiGhT
50*7d666900SdWiGhT	/**
51*7d666900SdWiGhT	 * Saves the input CSS string
52*7d666900SdWiGhT	 * @var string
53*7d666900SdWiGhT	 * @access private
54*7d666900SdWiGhT	 */
55*7d666900SdWiGhT	public $input_css = '';
56*7d666900SdWiGhT	/**
57*7d666900SdWiGhT	 * Saves the formatted CSS string
58*7d666900SdWiGhT	 * @var string
59*7d666900SdWiGhT	 * @access public
60*7d666900SdWiGhT	 */
61*7d666900SdWiGhT	public $output_css = '';
62*7d666900SdWiGhT	/**
63*7d666900SdWiGhT	 * Saves the formatted CSS string (plain text)
64*7d666900SdWiGhT	 * @var string
65*7d666900SdWiGhT	 * @access public
66*7d666900SdWiGhT	 */
67*7d666900SdWiGhT	public $output_css_plain = '';
68*7d666900SdWiGhT
69*7d666900SdWiGhT	public $css;
70*7d666900SdWiGhT	public $template;
71*7d666900SdWiGhT	public $tokens;
72*7d666900SdWiGhT	public $charset;
73*7d666900SdWiGhT	public $import;
74*7d666900SdWiGhT	public $namespace;
75*7d666900SdWiGhT
76*7d666900SdWiGhT	/**
77*7d666900SdWiGhT	 * Constructor
78*7d666900SdWiGhT	 * @param array $css contains the class csstidy
79*7d666900SdWiGhT	 * @access private
80*7d666900SdWiGhT	 * @version 1.0
81*7d666900SdWiGhT	 */
82*7d666900SdWiGhT	public function __construct($css) {
83*7d666900SdWiGhT		$this->parser = $css;
84*7d666900SdWiGhT		$this->css = & $css->css;
85*7d666900SdWiGhT		$this->template = & $css->template;
86*7d666900SdWiGhT		$this->tokens = & $css->tokens;
87*7d666900SdWiGhT		$this->charset = & $css->charset;
88*7d666900SdWiGhT		$this->import = & $css->import;
89*7d666900SdWiGhT		$this->namespace = & $css->namespace;
90*7d666900SdWiGhT	}
91*7d666900SdWiGhT
92*7d666900SdWiGhT	/**
93*7d666900SdWiGhT	 * Resets output_css and output_css_plain (new css code)
94*7d666900SdWiGhT	 * @access private
95*7d666900SdWiGhT	 * @version 1.0
96*7d666900SdWiGhT	 */
97*7d666900SdWiGhT	public function _reset() {
98*7d666900SdWiGhT		$this->output_css = '';
99*7d666900SdWiGhT		$this->output_css_plain = '';
100*7d666900SdWiGhT	}
101*7d666900SdWiGhT
102*7d666900SdWiGhT	/**
103*7d666900SdWiGhT	 * Returns the CSS code as plain text
104*7d666900SdWiGhT	 * @param string $default_media default @media to add to selectors without any @media
105*7d666900SdWiGhT	 * @return string
106*7d666900SdWiGhT	 * @access public
107*7d666900SdWiGhT	 * @version 1.0
108*7d666900SdWiGhT	 */
109*7d666900SdWiGhT	public function plain($default_media='') {
110*7d666900SdWiGhT		$this->_print(true, $default_media);
111*7d666900SdWiGhT		return $this->output_css_plain;
112*7d666900SdWiGhT	}
113*7d666900SdWiGhT
114*7d666900SdWiGhT	/**
115*7d666900SdWiGhT	 * Returns the formatted CSS code
116*7d666900SdWiGhT	 * @param string $default_media default @media to add to selectors without any @media
117*7d666900SdWiGhT	 * @return string
118*7d666900SdWiGhT	 * @access public
119*7d666900SdWiGhT	 * @version 1.0
120*7d666900SdWiGhT	 */
121*7d666900SdWiGhT	public function formatted($default_media='') {
122*7d666900SdWiGhT		$this->_print(false, $default_media);
123*7d666900SdWiGhT		return $this->output_css;
124*7d666900SdWiGhT	}
125*7d666900SdWiGhT
126*7d666900SdWiGhT	/**
127*7d666900SdWiGhT	 * Returns the formatted CSS code to make a complete webpage
128*7d666900SdWiGhT	 * @param string $doctype shorthand for the document type
129*7d666900SdWiGhT	 * @param bool $externalcss indicates whether styles to be attached internally or as an external stylesheet
130*7d666900SdWiGhT	 * @param string $title title to be added in the head of the document
131*7d666900SdWiGhT	 * @param string $lang two-letter language code to be added to the output
132*7d666900SdWiGhT	 * @return string
133*7d666900SdWiGhT	 * @access public
134*7d666900SdWiGhT	 * @version 1.4
135*7d666900SdWiGhT	 */
136*7d666900SdWiGhT	public function formatted_page($doctype='html5', $externalcss=true, $title='', $lang='en') {
137*7d666900SdWiGhT		switch ($doctype) {
138*7d666900SdWiGhT			case 'html5':
139*7d666900SdWiGhT				$doctype_output = '<!DOCTYPE html>';
140*7d666900SdWiGhT				break;
141*7d666900SdWiGhT				case 'xhtml1.0strict':
142*7d666900SdWiGhT				$doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
143*7d666900SdWiGhT			"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
144*7d666900SdWiGhT				break;
145*7d666900SdWiGhT			case 'xhtml1.1':
146*7d666900SdWiGhT			default:
147*7d666900SdWiGhT				$doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
148*7d666900SdWiGhT				"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
149*7d666900SdWiGhT				break;
150*7d666900SdWiGhT		}
151*7d666900SdWiGhT
152*7d666900SdWiGhT		$output = $cssparsed = '';
153*7d666900SdWiGhT		$this->output_css_plain = & $output;
154*7d666900SdWiGhT
155*7d666900SdWiGhT		$output .= $doctype_output . "\n" . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $lang . '"';
156*7d666900SdWiGhT		$output .= ( $doctype === 'xhtml1.1') ? '>' : ' lang="' . $lang . '">';
157*7d666900SdWiGhT		$output .= "\n<head>\n    <title>$title</title>";
158*7d666900SdWiGhT
159*7d666900SdWiGhT		if ($externalcss) {
160*7d666900SdWiGhT			$output .= "\n    <style type=\"text/css\">\n";
161*7d666900SdWiGhT			$cssparsed = file_get_contents('cssparsed.css');
162*7d666900SdWiGhT			$output .= $cssparsed; // Adds an invisible BOM or something, but not in css_optimised.php
163*7d666900SdWiGhT			$output .= "\n</style>";
164*7d666900SdWiGhT		} else {
165*7d666900SdWiGhT			$output .= "\n" . '    <link rel="stylesheet" type="text/css" href="cssparsed.css" />';
166*7d666900SdWiGhT		}
167*7d666900SdWiGhT		$output .= "\n</head>\n<body><code id=\"copytext\">";
168*7d666900SdWiGhT		$output .= $this->formatted();
169*7d666900SdWiGhT		$output .= '</code>' . "\n" . '</body></html>';
170*7d666900SdWiGhT		return $this->output_css_plain;
171*7d666900SdWiGhT	}
172*7d666900SdWiGhT
173*7d666900SdWiGhT	/**
174*7d666900SdWiGhT	 * Returns the formatted CSS Code and saves it into $this->output_css and $this->output_css_plain
175*7d666900SdWiGhT	 * @param bool $plain plain text or not
176*7d666900SdWiGhT	 * @param string $default_media default @media to add to selectors without any @media
177*7d666900SdWiGhT	 * @access private
178*7d666900SdWiGhT	 * @version 2.0
179*7d666900SdWiGhT	 */
180*7d666900SdWiGhT	public function _print($plain = false, $default_media='') {
181*7d666900SdWiGhT		if ($this->output_css && $this->output_css_plain) {
182*7d666900SdWiGhT			return;
183*7d666900SdWiGhT		}
184*7d666900SdWiGhT
185*7d666900SdWiGhT		$output = '';
186*7d666900SdWiGhT		if (!$this->parser->get_cfg('preserve_css')) {
187*7d666900SdWiGhT			$this->_convert_raw_css($default_media);
188*7d666900SdWiGhT		}
189*7d666900SdWiGhT
190*7d666900SdWiGhT		$template = & $this->template;
191*7d666900SdWiGhT
192*7d666900SdWiGhT		if ($plain) {
193*7d666900SdWiGhT			$template = array_map('strip_tags', $template);
194*7d666900SdWiGhT		}
195*7d666900SdWiGhT
196*7d666900SdWiGhT		if ($this->parser->get_cfg('timestamp')) {
197*7d666900SdWiGhT			array_unshift($this->tokens, array(COMMENT, ' CSSTidy ' . $this->parser->version . ': ' . date('r') . ' '));
198*7d666900SdWiGhT		}
199*7d666900SdWiGhT
200*7d666900SdWiGhT		if (!empty($this->charset)) {
201*7d666900SdWiGhT			$output .= $template[0] . '@charset ' . $template[5] . $this->charset . $template[6] . $template[13];
202*7d666900SdWiGhT		}
203*7d666900SdWiGhT
204*7d666900SdWiGhT		if (!empty($this->import)) {
205*7d666900SdWiGhT			for ($i = 0, $size = count($this->import); $i < $size; $i++) {
206*7d666900SdWiGhT				if (substr($this->import[$i], 0, 4) === 'url(' && substr($this->import[$i], -1, 1) === ')') {
207*7d666900SdWiGhT					$this->import[$i] = '"' . substr($this->import[$i], 4, -1) . '"';
208*7d666900SdWiGhT					$this->parser->log('Optimised @import : Removed "url("', 'Information');
209*7d666900SdWiGhT				}
210*7d666900SdWiGhT				else if (!preg_match('/^".+"$/',$this->import[$i])) {
211*7d666900SdWiGhT					// fixes a bug for @import ".." instead of the expected @import url("..")
212*7d666900SdWiGhT					// If it comes in due to @import ".." the "" will be missing and the output will become @import .. (which is an error)
213*7d666900SdWiGhT					$this->import[$i] = '"' . $this->import[$i] . '"';
214*7d666900SdWiGhT				}
215*7d666900SdWiGhT
216*7d666900SdWiGhT				$output .= $template[0] . '@import ' . $template[5] . $this->import[$i] . $template[6] . $template[13];
217*7d666900SdWiGhT			}
218*7d666900SdWiGhT		}
219*7d666900SdWiGhT
220*7d666900SdWiGhT		if (!empty($this->namespace)) {
221*7d666900SdWiGhT			if (($p=strpos($this->namespace,"url("))!==false && substr($this->namespace, -1, 1) === ')') {
222*7d666900SdWiGhT				$this->namespace = substr_replace($this->namespace,'"',$p,4);
223*7d666900SdWiGhT				$this->namespace = substr($this->namespace, 0, -1) . '"';
224*7d666900SdWiGhT				$this->parser->log('Optimised @namespace : Removed "url("', 'Information');
225*7d666900SdWiGhT			}
226*7d666900SdWiGhT			$output .= $template[0] . '@namespace ' . $template[5] . $this->namespace . $template[6] . $template[13];
227*7d666900SdWiGhT		}
228*7d666900SdWiGhT
229*7d666900SdWiGhT		$in_at_out = [];
230*7d666900SdWiGhT		$out = & $output;
231*7d666900SdWiGhT		$indent_level = 0;
232*7d666900SdWiGhT
233*7d666900SdWiGhT		foreach ($this->tokens as $key => $token) {
234*7d666900SdWiGhT			switch ($token[0]) {
235*7d666900SdWiGhT				case AT_START:
236*7d666900SdWiGhT					$out .= $template[0] . $this->_htmlsp($token[1], $plain) . $template[1];
237*7d666900SdWiGhT					$indent_level++;
238*7d666900SdWiGhT					if (!isset($in_at_out[$indent_level])) {
239*7d666900SdWiGhT						$in_at_out[$indent_level] = '';
240*7d666900SdWiGhT					}
241*7d666900SdWiGhT					$out = & $in_at_out[$indent_level];
242*7d666900SdWiGhT					break;
243*7d666900SdWiGhT
244*7d666900SdWiGhT				case SEL_START:
245*7d666900SdWiGhT					if ($this->parser->get_cfg('lowercase_s'))
246*7d666900SdWiGhT						$token[1] = strtolower($token[1]);
247*7d666900SdWiGhT					$out .= ( $token[1][0] !== '@') ? $template[2] . $this->_htmlsp($token[1], $plain) : $template[0] . $this->_htmlsp($token[1], $plain);
248*7d666900SdWiGhT					$out .= $template[3];
249*7d666900SdWiGhT					break;
250*7d666900SdWiGhT
251*7d666900SdWiGhT				case PROPERTY:
252*7d666900SdWiGhT					if ($this->parser->get_cfg('case_properties') === 2) {
253*7d666900SdWiGhT						$token[1] = strtoupper($token[1]);
254*7d666900SdWiGhT					} elseif ($this->parser->get_cfg('case_properties') === 1) {
255*7d666900SdWiGhT						$token[1] = strtolower($token[1]);
256*7d666900SdWiGhT					}
257*7d666900SdWiGhT					$out .= $template[4] . $this->_htmlsp($token[1], $plain) . ':' . $template[5];
258*7d666900SdWiGhT					break;
259*7d666900SdWiGhT
260*7d666900SdWiGhT				case VALUE:
261*7d666900SdWiGhT					$out .= $this->_htmlsp($token[1], $plain);
262*7d666900SdWiGhT					if ($this->_seeknocomment($key, 1) == SEL_END && $this->parser->get_cfg('remove_last_;')) {
263*7d666900SdWiGhT						$out .= str_replace(';', '', $template[6]);
264*7d666900SdWiGhT					} else {
265*7d666900SdWiGhT						$out .= $template[6];
266*7d666900SdWiGhT					}
267*7d666900SdWiGhT					break;
268*7d666900SdWiGhT
269*7d666900SdWiGhT				case SEL_END:
270*7d666900SdWiGhT					$out .= $template[7];
271*7d666900SdWiGhT					if ($this->_seeknocomment($key, 1) != AT_END)
272*7d666900SdWiGhT						$out .= $template[8];
273*7d666900SdWiGhT					break;
274*7d666900SdWiGhT
275*7d666900SdWiGhT				case AT_END:
276*7d666900SdWiGhT					if (strlen($template[10])) {
277*7d666900SdWiGhT						// indent the bloc we are closing
278*7d666900SdWiGhT						$out = str_replace("\n\n", "\r\n", $out); // don't fill empty lines
279*7d666900SdWiGhT						$out = str_replace("\n", "\n" . $template[10], $out);
280*7d666900SdWiGhT						$out = str_replace("\r\n", "\n\n", $out);
281*7d666900SdWiGhT					}
282*7d666900SdWiGhT					if ($indent_level > 1) {
283*7d666900SdWiGhT						$out = & $in_at_out[$indent_level-1];
284*7d666900SdWiGhT					}
285*7d666900SdWiGhT					else {
286*7d666900SdWiGhT						$out = & $output;
287*7d666900SdWiGhT					}
288*7d666900SdWiGhT					$out .= $template[10] . $in_at_out[$indent_level];
289*7d666900SdWiGhT					if ($this->_seeknocomment($key, 1) != AT_END) {
290*7d666900SdWiGhT						$out .= $template[9];
291*7d666900SdWiGhT					}
292*7d666900SdWiGhT					else {
293*7d666900SdWiGhT						$out .= rtrim($template[9]);
294*7d666900SdWiGhT					}
295*7d666900SdWiGhT
296*7d666900SdWiGhT					unset($in_at_out[$indent_level]);
297*7d666900SdWiGhT					$indent_level--;
298*7d666900SdWiGhT					break;
299*7d666900SdWiGhT
300*7d666900SdWiGhT				case IMPORTANT_COMMENT:
301*7d666900SdWiGhT				case COMMENT:
302*7d666900SdWiGhT					$out .= $template[11] . '/*' . $this->_htmlsp($token[1], $plain) . '*/' . $template[12];
303*7d666900SdWiGhT					break;
304*7d666900SdWiGhT			}
305*7d666900SdWiGhT		}
306*7d666900SdWiGhT
307*7d666900SdWiGhT		$output = trim($output);
308*7d666900SdWiGhT
309*7d666900SdWiGhT		if (!$plain) {
310*7d666900SdWiGhT			$this->output_css = $output;
311*7d666900SdWiGhT			$this->_print(true);
312*7d666900SdWiGhT		} else {
313*7d666900SdWiGhT			// If using spaces in the template, don't want these to appear in the plain output
314*7d666900SdWiGhT			$this->output_css_plain = str_replace('&#160;', '', $output);
315*7d666900SdWiGhT		}
316*7d666900SdWiGhT	}
317*7d666900SdWiGhT
318*7d666900SdWiGhT	/**
319*7d666900SdWiGhT	 * Gets the next token type which is $move away from $key, excluding comments
320*7d666900SdWiGhT	 * @param integer $key current position
321*7d666900SdWiGhT	 * @param integer $move move this far
322*7d666900SdWiGhT	 * @return mixed a token type
323*7d666900SdWiGhT	 * @access private
324*7d666900SdWiGhT	 * @version 1.0
325*7d666900SdWiGhT	 */
326*7d666900SdWiGhT	public function _seeknocomment($key, $move) {
327*7d666900SdWiGhT		$go = ($move > 0) ? 1 : -1;
328*7d666900SdWiGhT		for ($i = $key + 1; abs($key - $i) - 1 < abs($move); $i += $go) {
329*7d666900SdWiGhT			if (!isset($this->tokens[$i])) {
330*7d666900SdWiGhT				return;
331*7d666900SdWiGhT			}
332*7d666900SdWiGhT			if ($this->tokens[$i][0] == COMMENT) {
333*7d666900SdWiGhT				$move += 1;
334*7d666900SdWiGhT				continue;
335*7d666900SdWiGhT			}
336*7d666900SdWiGhT			return $this->tokens[$i][0];
337*7d666900SdWiGhT		}
338*7d666900SdWiGhT	}
339*7d666900SdWiGhT
340*7d666900SdWiGhT	/**
341*7d666900SdWiGhT	 * Converts $this->css array to a raw array ($this->tokens)
342*7d666900SdWiGhT	 * @param string $default_media default @media to add to selectors without any @media
343*7d666900SdWiGhT	 * @access private
344*7d666900SdWiGhT	 * @version 1.0
345*7d666900SdWiGhT	 */
346*7d666900SdWiGhT	public function _convert_raw_css($default_media='') {
347*7d666900SdWiGhT		$this->tokens = array();
348*7d666900SdWiGhT		$sort_selectors = $this->parser->get_cfg('sort_selectors');
349*7d666900SdWiGhT		$sort_properties = $this->parser->get_cfg('sort_properties');
350*7d666900SdWiGhT
351*7d666900SdWiGhT		// important comment section ?
352*7d666900SdWiGhT		if (isset($this->css['!'])) {
353*7d666900SdWiGhT			$this->parser->_add_token(IMPORTANT_COMMENT, rtrim($this->css['!']), true);
354*7d666900SdWiGhT			unset($this->css['!']);
355*7d666900SdWiGhT		}
356*7d666900SdWiGhT
357*7d666900SdWiGhT		foreach ($this->css as $medium => $val) {
358*7d666900SdWiGhT			if ($sort_selectors)
359*7d666900SdWiGhT				ksort($val);
360*7d666900SdWiGhT			if (intval($medium) < DEFAULT_AT) {
361*7d666900SdWiGhT				// un medium vide (contenant @font-face ou autre @) ne produit aucun conteneur
362*7d666900SdWiGhT				if (strlen(trim($medium))) {
363*7d666900SdWiGhT					$parts_to_open = explode('{', $medium);
364*7d666900SdWiGhT					foreach ($parts_to_open as $part) {
365*7d666900SdWiGhT						$this->parser->_add_token(AT_START, $part, true);
366*7d666900SdWiGhT					}
367*7d666900SdWiGhT				}
368*7d666900SdWiGhT			} elseif ($default_media) {
369*7d666900SdWiGhT				$this->parser->_add_token(AT_START, $default_media, true);
370*7d666900SdWiGhT			}
371*7d666900SdWiGhT
372*7d666900SdWiGhT			foreach ($val as $selector => $vali) {
373*7d666900SdWiGhT				if ($sort_properties)
374*7d666900SdWiGhT					ksort($vali);
375*7d666900SdWiGhT				$this->parser->_add_token(SEL_START, $selector, true);
376*7d666900SdWiGhT
377*7d666900SdWiGhT				$invalid = array(
378*7d666900SdWiGhT					'*' => array(), // IE7 hacks first
379*7d666900SdWiGhT					'_' => array(), // IE6 hacks
380*7d666900SdWiGhT					'/' => array(), // IE6 hacks
381*7d666900SdWiGhT					'-' => array()  // IE6 hacks
382*7d666900SdWiGhT				);
383*7d666900SdWiGhT				foreach ($vali as $property => $valj) {
384*7d666900SdWiGhT					if (strncmp($property,"//",2)!==0) {
385*7d666900SdWiGhT						$matches = array();
386*7d666900SdWiGhT						if ($sort_properties && preg_match('/^(\*|_|\/|-)(?!(ms|moz|o\b|xv|atsc|wap|khtml|webkit|ah|hp|ro|rim|tc)-)/', $property, $matches)) {
387*7d666900SdWiGhT							$invalid[$matches[1]][$property] = $valj;
388*7d666900SdWiGhT						} else {
389*7d666900SdWiGhT							$this->parser->_add_token(PROPERTY, $property, true);
390*7d666900SdWiGhT							$this->parser->_add_token(VALUE, $valj, true);
391*7d666900SdWiGhT						}
392*7d666900SdWiGhT					}
393*7d666900SdWiGhT				}
394*7d666900SdWiGhT				foreach ($invalid as $prefix => $props) {
395*7d666900SdWiGhT					foreach ($props as $property => $valj) {
396*7d666900SdWiGhT						$this->parser->_add_token(PROPERTY, $property, true);
397*7d666900SdWiGhT						$this->parser->_add_token(VALUE, $valj, true);
398*7d666900SdWiGhT					}
399*7d666900SdWiGhT				}
400*7d666900SdWiGhT				$this->parser->_add_token(SEL_END, $selector, true);
401*7d666900SdWiGhT			}
402*7d666900SdWiGhT
403*7d666900SdWiGhT			if (intval($medium) < DEFAULT_AT) {
404*7d666900SdWiGhT				// un medium vide (contenant @font-face ou autre @) ne produit aucun conteneur
405*7d666900SdWiGhT				if (strlen(trim($medium))) {
406*7d666900SdWiGhT					$parts_to_close = explode('{', $medium);
407*7d666900SdWiGhT					foreach (array_reverse($parts_to_close) as $part) {
408*7d666900SdWiGhT						$this->parser->_add_token(AT_END, $part, true);
409*7d666900SdWiGhT					}
410*7d666900SdWiGhT				}
411*7d666900SdWiGhT			} elseif ($default_media) {
412*7d666900SdWiGhT				$this->parser->_add_token(AT_END, $default_media, true);
413*7d666900SdWiGhT			}
414*7d666900SdWiGhT		}
415*7d666900SdWiGhT	}
416*7d666900SdWiGhT
417*7d666900SdWiGhT	/**
418*7d666900SdWiGhT	 * Same as htmlspecialchars, only that chars are not replaced if $plain !== true. This makes  print_code() cleaner.
419*7d666900SdWiGhT	 * @param string $string
420*7d666900SdWiGhT	 * @param bool $plain
421*7d666900SdWiGhT	 * @return string
422*7d666900SdWiGhT	 * @see csstidy_print::_print()
423*7d666900SdWiGhT	 * @access private
424*7d666900SdWiGhT	 * @version 1.0
425*7d666900SdWiGhT	 */
426*7d666900SdWiGhT	public function _htmlsp($string, $plain) {
427*7d666900SdWiGhT		if (!$plain) {
428*7d666900SdWiGhT			return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
429*7d666900SdWiGhT		}
430*7d666900SdWiGhT		return $string;
431*7d666900SdWiGhT	}
432*7d666900SdWiGhT
433*7d666900SdWiGhT	/**
434*7d666900SdWiGhT	 * Get compression ratio
435*7d666900SdWiGhT	 * @access public
436*7d666900SdWiGhT	 * @return float
437*7d666900SdWiGhT	 * @version 1.2
438*7d666900SdWiGhT	 */
439*7d666900SdWiGhT	public function get_ratio() {
440*7d666900SdWiGhT		if (!$this->output_css_plain) {
441*7d666900SdWiGhT			$this->formatted();
442*7d666900SdWiGhT		}
443*7d666900SdWiGhT		return round((strlen($this->input_css) - strlen($this->output_css_plain)) / strlen($this->input_css), 3) * 100;
444*7d666900SdWiGhT	}
445*7d666900SdWiGhT
446*7d666900SdWiGhT	/**
447*7d666900SdWiGhT	 * Get difference between the old and new code in bytes and prints the code if necessary.
448*7d666900SdWiGhT	 * @access public
449*7d666900SdWiGhT	 * @return string
450*7d666900SdWiGhT	 * @version 1.1
451*7d666900SdWiGhT	 */
452*7d666900SdWiGhT	public function get_diff() {
453*7d666900SdWiGhT		if (!$this->output_css_plain) {
454*7d666900SdWiGhT			$this->formatted();
455*7d666900SdWiGhT		}
456*7d666900SdWiGhT
457*7d666900SdWiGhT		$diff = strlen($this->output_css_plain) - strlen($this->input_css);
458*7d666900SdWiGhT
459*7d666900SdWiGhT		if ($diff > 0) {
460*7d666900SdWiGhT			return '+' . $diff;
461*7d666900SdWiGhT		} elseif ($diff == 0) {
462*7d666900SdWiGhT			return '+-' . $diff;
463*7d666900SdWiGhT		}
464*7d666900SdWiGhT
465*7d666900SdWiGhT		return $diff;
466*7d666900SdWiGhT	}
467*7d666900SdWiGhT
468*7d666900SdWiGhT	/**
469*7d666900SdWiGhT	 * Get the size of either input or output CSS in KB
470*7d666900SdWiGhT	 * @param string $loc default is "output"
471*7d666900SdWiGhT	 * @access public
472*7d666900SdWiGhT	 * @return integer
473*7d666900SdWiGhT	 * @version 1.0
474*7d666900SdWiGhT	 */
475*7d666900SdWiGhT	public function size($loc = 'output') {
476*7d666900SdWiGhT		if ($loc === 'output' && !$this->output_css) {
477*7d666900SdWiGhT			$this->formatted();
478*7d666900SdWiGhT		}
479*7d666900SdWiGhT
480*7d666900SdWiGhT		if ($loc === 'input') {
481*7d666900SdWiGhT			return (strlen($this->input_css) / 1000);
482*7d666900SdWiGhT		} else {
483*7d666900SdWiGhT			return (strlen($this->output_css_plain) / 1000);
484*7d666900SdWiGhT		}
485*7d666900SdWiGhT	}
486*7d666900SdWiGhT
487*7d666900SdWiGhT}
488