PluginProbe
TablePress – Tables in WordPress made easy / 1.12
TablePress – Tables in WordPress made easy v1.12
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / csstidy / class.csstidy_print.php

class.csstidy_print.php in TablePress – Tables in WordPress made easy 1.12, at libraries/csstidy/class.csstidy_print.php

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