PageRenderTime 177ms CodeModel.GetById 92ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/includes/csstidy/class.csstidy_print.php

https://github.com/KenBoyer/CompactCMS
PHP | 446 lines | 252 code | 47 blank | 147 comment | 53 complexity | e232066941d797773fc0555e677dd7d2 MD5 | raw file
  1. <?php
  2. /**
  3. * CSSTidy - CSS Parser and Optimiser
  4. *
  5. * CSS Printing class
  6. * This class prints CSS data generated by csstidy.
  7. *
  8. * Copyright 2005, 2006, 2007 Florian Schmitz
  9. *
  10. * This file is part of CSSTidy.
  11. *
  12. * CSSTidy is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU Lesser General Public License as published by
  14. * the Free Software Foundation; either version 2.1 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * CSSTidy is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Lesser General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Lesser General Public License
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  24. *
  25. * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
  26. * @package csstidy
  27. * @author Florian Schmitz (floele at gmail dot com) 2005-2007
  28. * @author Brett Zamir (brettz9 at yahoo dot com) 2007
  29. */
  30. /**
  31. * CSS Printing class
  32. *
  33. * This class prints CSS data generated by csstidy.
  34. *
  35. * @package csstidy
  36. * @author Florian Schmitz (floele at gmail dot com) 2005-2006
  37. * @version 1.0
  38. */
  39. class csstidy_print
  40. {
  41. /**
  42. * Saves the input CSS string
  43. * @var string
  44. * @access private
  45. */
  46. var $input_css = '';
  47. /**
  48. * Saves the formatted CSS string
  49. * @var string
  50. * @access public
  51. */
  52. var $output_css = '';
  53. /**
  54. * Saves the formatted CSS string (plain text)
  55. * @var string
  56. * @access public
  57. */
  58. var $output_css_plain = '';
  59. /* [i_a] E_STRICT fix */
  60. var $css;
  61. var $template;
  62. var $tokens;
  63. var $charset;
  64. var $import;
  65. var $namespace;
  66. /**
  67. * Constructor
  68. * @param array $css contains the class csstidy
  69. * @access private
  70. * @version 1.0
  71. */
  72. function csstidy_print(&$css)
  73. {
  74. $this->parser =& $css;
  75. $this->css =& $css->css;
  76. $this->template =& $css->template;
  77. $this->tokens =& $css->tokens;
  78. $this->charset =& $css->charset;
  79. $this->import =& $css->import;
  80. $this->namespace =& $css->namespace;
  81. }
  82. /**
  83. * Resets output_css and output_css_plain (new css code)
  84. * @access private
  85. * @version 1.0
  86. */
  87. function _reset()
  88. {
  89. $this->output_css = '';
  90. $this->output_css_plain = '';
  91. }
  92. /**
  93. * Returns the CSS code as plain text
  94. * @return string
  95. * @access public
  96. * @version 1.0
  97. */
  98. function plain()
  99. {
  100. $this->_print(true);
  101. return $this->output_css_plain;
  102. }
  103. /**
  104. * Returns the formatted CSS code
  105. * @return string
  106. * @access public
  107. * @version 1.0
  108. */
  109. function formatted()
  110. {
  111. $this->_print(false);
  112. return $this->output_css;
  113. }
  114. /**
  115. * Returns the formatted CSS code to make a complete webpage
  116. * @param string $doctype shorthand for the document type
  117. * @param bool $externalcss indicates whether styles to be attached internally or as an external stylesheet
  118. * @param string $title title to be added in the head of the document
  119. * @param string $lang two-letter language code to be added to the output
  120. * @return string
  121. * @access public
  122. * @version 1.4
  123. */
  124. function formatted_page($doctype='xhtml1.1', $externalcss=true, $title='', $lang='en')
  125. {
  126. switch ($doctype) {
  127. case 'xhtml1.0strict':
  128. $doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  129. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
  130. break;
  131. case 'xhtml1.1':
  132. default:
  133. $doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  134. "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
  135. break;
  136. }
  137. $output = $cssparsed = '';
  138. $this->output_css_plain =& $output;
  139. $output .= $doctype_output."\n".'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="'.$lang.'"';
  140. $output .= ($doctype === 'xhtml1.1') ? '>' : ' lang="'.$lang.'">';
  141. $output .= "\n<head>\n <title>$title</title>";
  142. if ($externalcss) {
  143. $output .= "\n <style type=\"text/css\">\n";
  144. $cssparsed = file_get_contents('cssparsed.css');
  145. $output .= $cssparsed; // Adds an invisible BOM or something, but not in css_optimised.php
  146. $output .= "\n</style>";
  147. }
  148. else {
  149. $output .= "\n".' <link rel="stylesheet" type="text/css" href="cssparsed.css" />';
  150. // }
  151. }
  152. $output .= "\n</head>\n<body><code id=\"copytext\">";
  153. $output .= $this->formatted();
  154. $output .= '</code>'."\n".'</body></html>';
  155. return $this->output_css_plain;
  156. }
  157. /**
  158. * Returns the formatted CSS Code and saves it into $this->output_css and $this->output_css_plain
  159. * @param bool $plain plain text or not
  160. * @access private
  161. * @version 2.0
  162. */
  163. function _print($plain = false)
  164. {
  165. if ($this->output_css && $this->output_css_plain) {
  166. return;
  167. }
  168. $output = '';
  169. if (!$this->parser->get_cfg('preserve_css')) {
  170. $this->_convert_raw_css();
  171. }
  172. $template =& $this->template;
  173. if ($plain) {
  174. $template = array_map('strip_tags', $template);
  175. }
  176. if ($this->parser->get_cfg('timestamp')) {
  177. array_unshift($this->tokens, array(COMMENT, ' CSSTidy ' . $this->parser->version . ': ' . date('r') . ' '));
  178. }
  179. if (!empty($this->charset)) {
  180. $output .= $template[0].'@charset '.$template[5].$this->charset.$template[6];
  181. }
  182. if (!empty($this->import)) {
  183. for ($i = 0, $size = count($this->import); $i < $size; $i ++) {
  184. /*
  185. @import statements have already had all their imports wrapped in " double quotes before we get here.
  186. This would've happened around line 830 in class.csstidy.php:
  187. $this->sub_value_arr[] = '"' . trim($this->sub_value) . '"';
  188. That change was added 2009-jun-08, git commit 1897649efa7fc6a2b2d7
  189. */
  190. if(substr($this->import[$i], 0, 5) === '"url(' && substr($this->import[$i], -2, 2) === ')"') {
  191. $this->import[$i] = '\''.substr($this->import[$i], 5, -2).'\'';
  192. $this->parser->log('Optimised @import : Removed "url("','Information');
  193. }
  194. $output .= $template[0].'@import '.$template[5].$this->import[$i].$template[6];
  195. }
  196. }
  197. if (!empty($this->namespace)) {
  198. if(substr($this->namespace, 0, 4) === 'url(' && substr($this->namespace, -1, 1) === ')') {
  199. $this->namespace = '\''.substr($this->namespace, 4, -1).'\'';
  200. $this->parser->log('Optimised @namespace : Removed "url("','Information');
  201. }
  202. $output .= $template[0].'@namespace '.$template[5].$this->namespace.$template[6];
  203. }
  204. $output .= $template[13];
  205. $in_at_out = '';
  206. $out =& $output;
  207. foreach ($this->tokens as $key => $token)
  208. {
  209. switch ($token[0])
  210. {
  211. case AT_START:
  212. $out .= $template[0].$this->_htmlsp($token[1], $plain).$template[1];
  213. $out =& $in_at_out;
  214. break;
  215. case SEL_START:
  216. if($this->parser->get_cfg('lowercase_s')) $token[1] = strtolower($token[1]);
  217. // remove fake counter from @font-face
  218. if (substr($token[1], 0, 10) === '@font-face') {
  219. $token[1] = '@font-face';
  220. }
  221. $out .= ($token[1]{0} !== '@') ? $template[2].$this->_htmlsp($token[1], $plain) : $template[0].$this->_htmlsp($token[1], $plain);
  222. $out .= $template[3];
  223. break;
  224. case PROPERTY:
  225. if($this->parser->get_cfg('case_properties') === 2)
  226. {
  227. $token[1] = strtoupper($token[1]);
  228. // remove fake counter cursor property
  229. if (substr($token[1], 0, 6) == 'CURSOR') {
  230. $token[1] = 'CURSOR';
  231. }
  232. }
  233. elseif($this->parser->get_cfg('case_properties') === 1)
  234. {
  235. $token[1] = strtolower($token[1]);
  236. // remove fake counter cursor property
  237. if (substr($token[1], 0, 6) == 'cursor') {
  238. $token[1] = 'cursor';
  239. }
  240. } else {
  241. // remove fake counter cursor property
  242. if (preg_match("/cursor/i", substr($token[1], 0, 6))) {
  243. $token[1] = preg_replace("/(cursor)_[0-9]+/i", "$1", $token[1]);
  244. }
  245. }
  246. $out .= $template[4] . $this->_htmlsp($token[1], $plain) . ':' . $template[5];
  247. break;
  248. case VALUE:
  249. $out .= $this->_htmlsp($token[1], $plain);
  250. if($this->_seeknocomment($key, 1) == SEL_END && $this->parser->get_cfg('remove_last_;')) {
  251. $out .= str_replace(';', '', $template[6]);
  252. } else {
  253. $out .= $template[6];
  254. }
  255. break;
  256. case SEL_END:
  257. $out .= $template[7];
  258. if($this->_seeknocomment($key, 1) != AT_END) $out .= $template[8];
  259. break;
  260. case AT_END:
  261. $out =& $output;
  262. $out .= $template[10] . str_replace("\n", "\n" . $template[10], $in_at_out);
  263. $in_at_out = '';
  264. $out .= $template[9];
  265. break;
  266. case COMMENT:
  267. $out .= $template[11] . '/*' . $this->_htmlsp($token[1], $plain) . '*/' . $template[12];
  268. break;
  269. }
  270. }
  271. $output = trim($output);
  272. if (!$plain) {
  273. $this->output_css = $output;
  274. $this->_print(true);
  275. } else {
  276. // If using spaces in the template, don't want these to appear in the plain output
  277. $this->output_css_plain = str_replace('&#160;', '', $output);
  278. }
  279. }
  280. /**
  281. * Gets the next token type which is $move away from $key, excluding comments
  282. * @param integer $key current position
  283. * @param integer $move move this far
  284. * @return mixed a token type
  285. * @access private
  286. * @version 1.0
  287. */
  288. function _seeknocomment($key, $move) {
  289. $go = ($move > 0) ? 1 : -1;
  290. for ($i = $key + 1; abs($key-$i)-1 < abs($move); $i += $go) {
  291. if (!isset($this->tokens[$i])) {
  292. return;
  293. }
  294. if ($this->tokens[$i][0] == COMMENT) {
  295. $move += 1;
  296. continue;
  297. }
  298. return $this->tokens[$i][0];
  299. }
  300. }
  301. /**
  302. * Converts $this->css array to a raw array ($this->tokens)
  303. * @access private
  304. * @version 1.0
  305. */
  306. function _convert_raw_css()
  307. {
  308. $this->tokens = array();
  309. foreach ($this->css as $medium => $val)
  310. {
  311. if ($this->parser->get_cfg('sort_selectors')) ksort($val);
  312. if ($medium != DEFAULT_AT) {
  313. $this->parser->_add_token(AT_START, $medium, true);
  314. }
  315. foreach ($val as $selector => $vali)
  316. {
  317. if ($this->parser->get_cfg('sort_properties')) ksort($vali);
  318. $this->parser->_add_token(SEL_START, $selector, true);
  319. foreach ($vali as $property => $valj)
  320. {
  321. $this->parser->_add_token(PROPERTY, $property, true);
  322. $this->parser->_add_token(VALUE, $valj, true);
  323. }
  324. $this->parser->_add_token(SEL_END, $selector, true);
  325. }
  326. if ($medium != DEFAULT_AT) {
  327. $this->parser->_add_token(AT_END, $medium, true);
  328. }
  329. }
  330. }
  331. /**
  332. * Same as htmlspecialchars, only that chars are not replaced if $plain !== true. This makes print_code() cleaner.
  333. * @param string $string
  334. * @param bool $plain
  335. * @return string
  336. * @see csstidy_print::_print()
  337. * @access private
  338. * @version 1.0
  339. */
  340. function _htmlsp($string, $plain)
  341. {
  342. if (!$plain) {
  343. return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
  344. }
  345. return $string;
  346. }
  347. /**
  348. * Get compression ratio
  349. * @access public
  350. * @return float
  351. * @version 1.2
  352. */
  353. function get_ratio()
  354. {
  355. if (!$this->output_css_plain) {
  356. $this->formatted();
  357. }
  358. return round((strlen($this->input_css) - strlen($this->output_css_plain)) / strlen($this->input_css), 3) * 100;
  359. }
  360. /**
  361. * Get difference between the old and new code in bytes and prints the code if necessary.
  362. * @access public
  363. * @return string
  364. * @version 1.1
  365. */
  366. function get_diff()
  367. {
  368. if (!$this->output_css_plain) {
  369. $this->formatted();
  370. }
  371. $diff = strlen($this->output_css_plain) - strlen($this->input_css);
  372. if ($diff > 0) {
  373. return '+' . $diff;
  374. } elseif ($diff == 0) {
  375. return '+-' . $diff;
  376. }
  377. return $diff;
  378. }
  379. /**
  380. * Get the size of either input or output CSS in KB
  381. * @param string $loc default is "output"
  382. * @access public
  383. * @return integer
  384. * @version 1.0
  385. */
  386. function size($loc = 'output')
  387. {
  388. if ($loc === 'output' && !$this->output_css) {
  389. $this->formatted();
  390. }
  391. if ($loc === 'input') {
  392. return (strlen($this->input_css) / 1000);
  393. } else {
  394. return (strlen($this->output_css_plain) / 1000);
  395. }
  396. }
  397. }