csspp
[ class tree: csspp ] [ index: csspp ] [ all elements ]

Source for file css_parser2.php

Documentation is available at css_parser2.php

  1. <?php
  2. /**
  3. * CSSPP - CSS Parser and Optimiser
  4. *
  5. * CSS Parser class ( CSS PreProcessor or CSS PostProcessor, depends on the point of view )
  6. * This class represents a CSS parser which reads CSS code and saves it in an array.
  7. * In opposite to most other CSS parsers, it does not use regular expressions and
  8. * thus has full CSS2 support and a higher reliability. The downside of not using regular expressions
  9. * is a lower speed though.
  10. * Additional to that it applies some optimisations and fixes to the CSS code.
  11. * An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
  12. * @package csspp
  13. * @author Florian Schmitz (floele at gmail dot com) 2005
  14. */
  15.  
  16. /**
  17. * Various CSS data needed for correct optimisations etc.
  18. *
  19. * @version 1.0
  20. */
  21. require("data.inc.php");
  22.  
  23. /**
  24. * All fcuntions which are not directly related to the parser class
  25. *
  26. * @version 1.0
  27. */
  28. require("functions.inc.php");
  29.  
  30. /**
  31. * CSS Parser class ( CSS PreProcessor or CSS PostProcessor, depends on the point of view )
  32. *
  33. * This class represents a CSS parser which reads CSS code and saves it in an array.
  34. * In opposite to most other CSS parsers, it does not use regular expressions and
  35. * thus has full CSS2 support and a higher reliability. The downside of not using regular expressions
  36. * is a lower speed though.
  37. * Additional to that it applies some optimisations and fixes to the CSS code.
  38. * An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
  39. * @package csspp
  40. * @author Florian Schmitz (floele at gmail dot com) 2005
  41. * @version 0.98
  42. */
  43. class csspp {
  44.  
  45. /**
  46. * Saves the parsed CSS
  47. * @var array
  48. * @access public
  49. */
  50. var $css = array();
  51.  
  52. /**
  53. * Saves the charset (@charset)
  54. * @var string
  55. * @access private
  56. */
  57. var $charset = '';
  58.  
  59. /**
  60. * Saves all @import URLs
  61. * @var array
  62. * @access private
  63. */
  64. var $import = array();
  65.  
  66. /**
  67. * Saves the input CSS string
  68. * @var string
  69. * @access private
  70. */
  71. var $input_css = '';
  72.  
  73. /**
  74. * Saves the formatted CSS string
  75. * @var string
  76. * @access public
  77. */
  78. var $output_css = '';
  79.  
  80. /**
  81. * Saves the templates
  82. * @var array
  83. * @access private
  84. * @see http://cdburnerxp.se/cssparse/template.htm
  85. */
  86. var $template = array();
  87.  
  88. /**
  89. * A list of certain chars, needed for removing unnecessary backslashes
  90. * @var array
  91. * @access private
  92. */
  93. var $hex = array('a','b','c','d','e','f','\\',':','=');
  94.  
  95. /**
  96. * Contains the version of CSSPP
  97. * @var string
  98. * @access private
  99. */
  100. var $version = '0.98dev';
  101.  
  102. /**
  103. * Stores the settings
  104. * @var array
  105. * @access private
  106. */
  107. var $settings = array();
  108.  
  109. /**
  110. * Saves the parser-status:
  111. * wfs = wait for selector
  112. * is = in selector
  113. * wfp = wait for property
  114. * ip = in property
  115. * iv = in value
  116. * instr = in string (-> ",' ( => ignore } and ; etc.) )
  117. * ic = in comment (ignore everything)
  118. * at = in @-block
  119. *
  120. * @var string
  121. * @access private
  122. */
  123. var $status = 'wfs';
  124.  
  125.  
  126. /**
  127. * Saves the current at rule (@media)
  128. * @var string
  129. * @access private
  130. */
  131. var $cur_at = '';
  132.  
  133. /**
  134. * Saves the current selector
  135. * @var string
  136. * @access private
  137. */
  138. var $cur_selector = '';
  139.  
  140. /**
  141. * Saves the current property
  142. * @var string
  143. * @access private
  144. */
  145. var $cur_property = '';
  146.  
  147. /**
  148. * Saves the current value
  149. * @var string
  150. * @access private
  151. */
  152. var $cur_value = '';
  153.  
  154. /**
  155. * Saves the current sub-value
  156. *
  157. * Example for a subvalue:
  158. * background:url(foo.png) red no-repeat;
  159. * "url(foo.png)", "red", and "no-repeat" are subvalues,
  160. * seperated by whitespace
  161. * @var string
  162. * @access private
  163. */
  164. var $cur_sub_value = '';
  165.  
  166. /**
  167. * Array which saves all subvalues for a property.
  168. * @var string
  169. * @see cur_sub_value
  170. * @access private
  171. */
  172. var $cur_sub_value_arr = array();
  173.  
  174. /**
  175. * Saves the char which opened the last string
  176. * @var string
  177. * @access private
  178. */
  179. var $str_char = '';
  180.  
  181. /**
  182. * Status where the string has been started (is or iv)
  183. * @var string
  184. * @access private
  185. */
  186. var $str_from = '';
  187.  
  188. /**
  189. * Variable needed to manage string-in-strings, for example url("foo.png")
  190. * @var string
  191. * @access private
  192. */
  193. var $str_in_str = FALSE;
  194.  
  195. /**
  196. * Status where the comment has been started
  197. * @var string
  198. * @access private
  199. */
  200. var $comment_from = '';
  201.  
  202.  
  203. /**
  204. * Loads standard template and sets default settings
  205. * @access private
  206. * @version 1.0
  207. */
  208. function csspp()
  209. {
  210. $this->settings["remove_bslash"] = true;
  211. $this->settings["compress_colors"] = true;
  212. $this->settings["lowercase_s"] = false;
  213. $this->settings["save_ie_hacks"] = false;
  214. $this->settings["optimise_shorthands"] = true;
  215. $this->settings["remove_last_;"] = false;
  216. $this->settings["uppercase_properties"] = false;
  217. $this->settings["sort_properties"] = false;
  218. $this->settings["sort_selectors"] = false;
  219. $this->settings["merge_selectors"] = true;
  220.  
  221. $this->template[0] = '<span class="at">'; //string before @rule
  222. $this->template[1] = '</span> <span class="format">{</span>'."\n"; //bracket after @-rule
  223. $this->template[2] = '<span class="selector">'; //string before selector
  224. $this->template[3] = '</span> <span class="format">{</span>'."\n"; //bracket after selector
  225. $this->template[4] = '<span class="property">'; //string before property
  226. $this->template[5] = '</span><span class="value">'; //string after property+before value
  227. $this->template[6] = '</span><span class="format">;</span>'."\n"; //string after value
  228. $this->template[7] = '<span class="format">}</span>'; //closing bracket - selector
  229. $this->template[8] = "\n\n"; //after closing bracket (conditional)
  230. $this->template[9] = "\n".'<span class="format">}</span>'."\n\n"; //closing bracket @-rule
  231. $this->template[10] = ''; //indent in @-rule
  232. $this->template[11] = '</span> <span class="format">{</span>'."\n"; //indent in @-rule before selector bracket
  233. $this->template[12] = ''; // after @-rule
  234. }
  235.  
  236. /**
  237. * Get the value of a setting.
  238. * @param string $setting
  239. * @access public
  240. * @return bool
  241. * @version 1.0
  242. */
  243. function get_cfg($setting)
  244. {
  245. if(isset($this->settings[$setting]))
  246. {
  247. return $this->settings[$setting];
  248. }
  249. else
  250. {
  251. return false;
  252. }
  253. }
  254.  
  255. /**
  256. * Set the value of a setting.
  257. * @param string $setting
  258. * @param bool $value
  259. * @access public
  260. * @return bool
  261. * @version 1.0
  262. */
  263. function set_cfg($setting,$value)
  264. {
  265. if(isset($this->settings[$setting]) && ($value === TRUE || $value === FALSE))
  266. {
  267. $this->settings[$setting] = $value;
  268. return true;
  269. }
  270. return false;
  271. }
  272.  
  273. /**
  274. * Extract URL from @import value (if $opt == TRUE) and/or add missing http:// to URL
  275. * @param string $string
  276. * @param bool $opt
  277. * @access public
  278. * @return bool
  279. * @version 1.0
  280. */
  281. function parseurl($string,$opt = TRUE)
  282. {
  283. if(substr($string,0,4) == 'url(' && $opt)
  284. {
  285. $string = substr($string,4);
  286. $string = substr($string,0,(strlen($string)-1)-strpos(strrev($string),')'));
  287. }
  288. if(($string{0} == '"' || $string{0} == '\'') && $opt)
  289. {
  290. $string = substr($string,0,(strlen($string)-1)-strpos(strrev($string),$string{0}));
  291. $string = substr($string,1);
  292. }
  293. if(substr($string,0,7) != 'http://')
  294. {
  295. $string = 'http://'.$string;
  296. }
  297. return $string;
  298. }
  299.  
  300. /**
  301. * Compresses shorthand values. Example: margin:1px 1px 1px 1px -> margin:1px
  302. * @param string $value
  303. * @access private
  304. * @return string
  305. * @version 1.0
  306. */
  307. function shorthand($value)
  308. {
  309. $important = '';
  310. if(csspp::is_important($value))
  311. {
  312. $values = csspp::gvw_important($value);
  313. $important = ' !important';
  314. }
  315. else $values = $value;
  316. $values = explode(' ',$values);
  317. switch(count($values))
  318. {
  319. case 4:
  320. if($values[0] == $values[1] && $values[0] == $values[2] && $values[0] == $values[3])
  321. {
  322. return $values[0].$important;
  323. }
  324. elseif($values[1] == $values[3] && $values[0] == $values[2])
  325. {
  326. return $values[0].' '.$values[1].$important;
  327. }
  328. elseif($values[1] == $values[3])
  329. {
  330. return $values[0].' '.$values[1].' '.$values[2].$important;
  331. }
  332. else return $value;
  333. break;
  334. case 3:
  335. if($values[0] == $values[1] && $values[0] == $values[2])
  336. {
  337. return $values[0].$important;
  338. }
  339. elseif($values[0] == $values[2])
  340. {
  341. return $values[0].' '.$values[1].$important;
  342. }
  343. else return $value;
  344. break;
  345. case 2:
  346. if($values[0] == $values[1])
  347. {
  348. return $values[0].$important;
  349. }
  350. else return $value;
  351. break;
  352. default:
  353. return $value;
  354. }
  355. }
  356.  
  357. /**
  358. * Get compression ratio and prints the code if necessary.
  359. * @access public
  360. * @return float
  361. * @version 1.0
  362. */
  363. function get_ratio()
  364. {
  365. if(empty($this->output_css))
  366. {
  367. $this->print_code($this->css);
  368. }
  369. return $ratio = round(((strlen($this->input_css))-(strlen(strip_tags(html_entity_decode($this->output_css)))))/(strlen($this->input_css)),3)*100;
  370. }
  371.  
  372. /**
  373. * Get difference between the old and new code in bytes and prints the code if necessary.
  374. * @access public
  375. * @return string
  376. * @version 1.0
  377. */
  378. function get_diff()
  379. {
  380. if(empty($this->output_css))
  381. {
  382. $this->print_code($this->css);
  383. }
  384. $diff = (strlen(html_entity_decode(strip_tags($this->output_css))))-(strlen($this->input_css));
  385. if($diff > 0)
  386. {
  387. return '+'.$diff;
  388. }
  389. elseif($diff == 0)
  390. {
  391. return '+-'.$diff;
  392. }
  393. else
  394. {
  395. return $diff;
  396. }
  397. }
  398.  
  399. /**
  400. * Get the size of either input or output CSS in KB
  401. * @param string $loc default is "output"
  402. * @access public
  403. * @return integer
  404. * @version 1.0
  405. */
  406. function size($loc = 'output')
  407. {
  408. if($loc == 'output' && empty($this->output_css))
  409. {
  410. $this->print_code($this->css);
  411. }
  412. if($loc == 'input')
  413. {
  414. return (strlen($this->input_css)/1000);
  415. }
  416. else
  417. {
  418. return (strlen(html_entity_decode(strip_tags($this->output_css)))/1000);
  419. }
  420. }
  421.  
  422. /**
  423. * Loads a new template
  424. * @param string $content either filename (if $from_file == true) or content of a template file
  425. * @param bool $from_file uses $content as filename if true
  426. * @access public
  427. * @version 1.0
  428. * @see http://cdburnerxp.se/cssparse/template.htm
  429. */
  430. function load_template($content,$from_file=TRUE)
  431. {
  432. if($from_file)
  433. {
  434. $content = strip_tags(file_get_contents($content),'<span>');
  435. }
  436. $content = str_replace("\r\n","\n",$content); // Unify newlines (because the output also only uses \n)
  437. $template = explode('|',$content);
  438.  
  439. for ($i = 0, $size = count($this->template); $i < $size; $i++ )
  440. {
  441. $this->template[$i] = @$template[$i];
  442. }
  443. }
  444.  
  445. /**
  446. * Starts parsing from URL
  447. * @param string $url
  448. * @access public
  449. * @version 1.0
  450. */
  451. function parse_from_url($url)
  452. {
  453. $content = @file_get_contents($url);
  454. $this->parse($content);
  455. }
  456.  
  457. /**
  458. * Parses CSS in $string. The code is saved as array in $this->css
  459. * @param string $string the CSS code
  460. * @access public
  461. * @return bool
  462. * @version 0.98
  463. */
  464. function parse($string) {
  465.  
  466. global $shorthands;
  467. $string = str_replace("\r\n","\n",$string);
  468. $this->input_css = $string;
  469.  
  470. for ($i = 0, $size = strlen($string); $i < $size; $i++ ) {
  471. switch($this->status)
  472. {
  473. case 'wfs':
  474. if($string{$i} == '/' && @$string{$i+1} == '*')
  475. {
  476. $this->status = 'ic';
  477. $this->comment_from = 'wfs';
  478. }
  479. elseif($string{$i} == '@')
  480. {
  481. if(strtolower(substr($string,$i+1,4)) == 'page' || strtolower(substr($string,$i+1,9)) == 'font-face')
  482. {
  483. $this->cur_selector = '@';
  484. $this->status = 'is';
  485. }
  486. elseif(strtolower(substr($string,$i+1,7)) == 'charset')
  487. {
  488. $this->cur_selector = '@charset';
  489. $i += 7;
  490. $this->status = 'iv';
  491. }
  492. elseif(strtolower(substr($string,$i+1,6)) == 'import')
  493. {
  494. $this->cur_selector = '@import';
  495. $i += 6;
  496. $this->status = 'iv';
  497. }
  498. else
  499. {
  500. $this->cur_at = '@';
  501. $this->status = 'at';
  502. }
  503. }
  504. elseif($string{$i} == '}')
  505. {
  506. $this->cur_at = '';
  507. }
  508. elseif(!ctype_space($string{$i}))
  509. {
  510. $this->status = 'is';
  511. $this->cur_selector = $string{$i};
  512. }
  513. break;
  514. case 'at';
  515. if($string{$i} == '/' && @$string{$i+1} == '*')
  516. {
  517. $this->status = 'ic';
  518. $this->comment_from = 'at';
  519. }
  520. elseif($string{$i} != '{')
  521. {
  522. if($string{$i-1} == ',' && !ctype_space($string{$i}) || $string{$i-1} != ',')
  523. {
  524. if( ( !ctype_space($string{$i-1}) && ctype_space($string{$i}) ) || !ctype_space($string{$i}))
  525. {
  526. $this->cur_at .= $string{$i};
  527. }
  528. }
  529. }
  530. elseif($string{$i} == '{')
  531. {
  532. $this->status = 'wfs';
  533. }
  534. break;
  535. case 'is';
  536. if($string{$i} == '/' && @$string{$i+1} == '*')
  537. {
  538. $this->status = 'ic';
  539. $this->comment_from = 'is';
  540. }
  541. elseif(($string{$i} == '"' || $string{$i} == "'") && !csspp::escaped($string,$i))
  542. {
  543. $this->cur_selector .= $string{$i};
  544. $this->status = 'instr';
  545. $this->str_char = $string{$i};
  546. $this->str_from = 'is';
  547. }
  548. elseif($string{$i} != '{')
  549. {
  550. if($string{$i-1} == ',' && !ctype_space($string{$i}) || $string{$i-1} != ',')
  551. {
  552. if( ( !ctype_space($string{$i-1}) && ctype_space($string{$i}) ) || !ctype_space($string{$i}))
  553. {
  554. $this->cur_selector .= $string{$i};
  555. }
  556. }
  557. }
  558. elseif($string{$i} == '{' && !csspp::escaped($string,$i))
  559. {
  560. $this->status = 'wfp';
  561. }
  562. elseif($string{$i} == '{' && csspp::escaped($string,$i))
  563. {
  564. $this->cur_selector .= $string{$i};
  565. }
  566. break;
  567.  
  568. case 'wfp':
  569. if($string{$i} == '/' && @$string{$i+1} == '*')
  570. {
  571. $this->status = 'ic';
  572. $this->comment_from = 'wfp';
  573. }
  574. elseif($string{$i} == '}')
  575. {
  576. $this->status = 'wfs';
  577. $this->cur_selector = '';
  578. }
  579. elseif(!ctype_space($string{$i}))
  580. {
  581. $this->cur_property .= $string{$i};
  582. $this->status = 'ip';
  583. }
  584. break;
  585. case 'ip':
  586. if(!ctype_space($string{$i}) && $string{$i} != ':' && $string{$i} != '=')
  587. {
  588. if($string{$i} != '\\' || !$this->get_cfg('remove_bslash'))
  589. {
  590. $this->cur_property .= $string{$i};
  591. }
  592. elseif($this->get_cfg('remove_bslash'))
  593. {
  594. if(($string{$i} == '\\' && in_array(@$string{$i+1},$this->hex)) || csspp::escaped($string,$i))
  595. {
  596. $this->cur_property .= $string{$i};
  597. }
  598. }
  599. }
  600. if($string{$i} == ':' || $string{$i} == '=')
  601. {
  602. $this->status = 'iv';
  603. }
  604. break;
  605. case 'iv':
  606. if($string{$i} == '/' && @$string{$i+1} == '*')
  607. {
  608. $this->status = 'ic';
  609. $this->comment_from = 'iv';
  610. }
  611. elseif(($string{$i} == '"' || $string{$i} == "'" || $string{$i} == '(') && !csspp::escaped($string,$i))
  612. {
  613. if($this->cur_selector != '@charset')
  614. {
  615. $this->cur_sub_value .= $string{$i};
  616. }
  617. if($string{$i} == '(') $this->str_char = ')'; else $this->str_char = $string{$i};
  618. $this->status = 'instr';
  619. $this->str_from = 'iv';
  620. }
  621. elseif($string{$i} != ';' && $string{$i} != '}' && !( ($string[$i] == "\n" || $string[$i] == "\r") && csspp::property_is_next($string,$i+1)))
  622. {
  623. if($this->cur_selector == '@charset')
  624. {
  625. break;
  626. }
  627. $c = FALSE;
  628. if((($string{$i-1} != ' ') && $string{$i} == ' ') || $string{$i} != ' ')
  629. {
  630. $c = TRUE;
  631. $this->cur_sub_value .= $string{$i};
  632. }
  633. if(ctype_space($string{$i}) && $c)
  634. {
  635. if(trim($this->cur_sub_value) != '')
  636. {
  637. if($this->get_cfg('compress_colors'))
  638. {
  639. $this->cur_sub_value = cut_color($this->cur_sub_value);
  640. }
  641. compress_numbers($this->cur_sub_value);
  642. $this->cur_sub_value_arr[] = trim($this->cur_sub_value);
  643. }
  644. $this->cur_sub_value = '';
  645. }
  646. }
  647. elseif($string{$i} == ';' || ( ($string[$i] == "\n" || $string[$i] == "\r") && csspp::property_is_next($string,$i+1)))
  648. {
  649. if($this->cur_selector == '@charset')
  650. {
  651. $this->status = 'wfs';
  652. $this->charset = $this->cur_sub_value;
  653. $this->cur_sub_value = '';
  654. $this->cur_selector = '';
  655. }
  656. elseif($this->cur_selector == '@import')
  657. {
  658. $this->cur_sub_value_arr[] = trim($this->cur_sub_value);
  659. $this->status = 'wfs';
  660. $this->import[] = implode(' ',$this->cur_sub_value_arr);
  661. $this->cur_sub_value_arr = array();
  662. $this->cur_sub_value = '';
  663. $this->cur_selector = '';
  664. }
  665. else
  666. {
  667. $this->status = 'wfp';
  668. }
  669. }
  670. if(($string{$i} == '}' || $string{$i} == ';' || ( ($string[$i] == "\n" || $string[$i] == "\r") && csspp::property_is_next($string,$i+1))) && !empty($this->cur_selector))
  671. {
  672. if($this->cur_at == '')
  673. {
  674. $this->cur_at = 'standard';
  675. }
  676. // case settings
  677. if($this->get_cfg('lowercase_s'))
  678. {
  679. $this->cur_selector = strtolower($this->cur_selector);
  680. }
  681. $this->cur_property = strtolower($this->cur_property);
  682. if(trim($this->cur_sub_value) != '')
  683. {
  684. if($this->get_cfg('compress_colors'))
  685. {
  686. $this->cur_sub_value = cut_color($this->cur_sub_value);
  687. }
  688. compress_numbers($this->cur_sub_value);
  689. $this->cur_sub_value_arr[] = $this->cur_sub_value;
  690. $this->cur_sub_value = '';
  691. }
  692. $this->cur_value = implode(' ',$this->cur_sub_value_arr);
  693. $this->cur_selector = trim($this->cur_selector);
  694. // optimise shorthand properties
  695. if(isset($shorthands[$this->cur_property]))
  696. {
  697. $this->cur_value = csspp::shorthand($this->cur_value);
  698. }
  699. // Remove whitespace at ! important
  700. csspp::c_important($this->cur_value);
  701.  
  702. if(isset($this->css[$this->cur_at][$this->cur_selector]) && ($this->get_cfg('save_ie_hacks') || csspp::has_subkey($this->cur_property,$this->css[$this->cur_at][$this->cur_selector])))
  703. {
  704. $this->css_add_property($this->css[$this->cur_at][$this->cur_selector],$this->cur_property,$this->cur_value);
  705. }
  706. else
  707. {
  708. $this->css[$this->cur_at][$this->cur_selector][][$this->cur_property] = trim($this->cur_value);
  709. }
  710. // Further Optimisation
  711. if($this->cur_property === 'background' && $this->get_cfg('optimise_shorthands'))
  712. {
  713. $temp = dissolve_short_bg($this->cur_value);
  714. csspp::rm_subkey('background',$this->css[$this->cur_at][$this->cur_selector]);
  715. csspp::merge_css_blocks($this->css[$this->cur_at][$this->cur_selector],$temp);
  716. }
  717. if(isset($shorthands[$this->cur_property]) && $this->get_cfg('optimise_shorthands'))
  718. {
  719. $temp = dissolve_4value_shorthands($this->cur_property,$this->cur_value);
  720. csspp::merge_css_blocks($this->css[$this->cur_at][$this->cur_selector],$temp);
  721. if(is_array($shorthands[$this->cur_property]))
  722. {
  723. csspp::rm_subkey($this->cur_property,$this->css[$this->cur_at][$this->cur_selector]);
  724. }
  725. }
  726. $this->cur_property = '';
  727. $this->cur_sub_value_arr = array();
  728. $this->cur_value = '';
  729. }
  730. if($string{$i} == '}')
  731. {
  732. $this->status = 'wfs';
  733. $this->cur_selector = '';
  734. }
  735. break;
  736. case 'instr':
  737. if($this->str_char == ')' && $string{$i} == '"' && $this->str_in_str === FALSE && !csspp::escaped($string,$i))
  738. {
  739. $this->str_in_str = TRUE;
  740. }
  741. elseif($this->str_char == ')' && $string{$i} == '"' && $this->str_in_str === TRUE && !csspp::escaped($string,$i))
  742. {
  743. $this->str_in_str = FALSE;
  744. }
  745. if($string{$i} == $this->str_char && !csspp::escaped($string,$i) && $this->str_in_str === FALSE)
  746. {
  747. if($this->str_from == 'iv')
  748. {
  749. $this->status = 'iv';
  750. }
  751. elseif($this->str_from == 'is')
  752. {
  753. $this->status = 'is';
  754. }
  755. if($this->cur_selector == '@charset')
  756. {
  757. break;
  758. }
  759. }
  760. $temp_add = $string{$i};
  761.  
  762. if( ($string{$i} == "\n" || $string{$i} == "\r") && !($string{$i-1 } == '\\' && !csspp::escaped($string,$i-1)) )
  763. {
  764. $temp_add = "\\A";
  765. }
  766. if($this->str_from == 'iv')
  767. {
  768. $this->cur_sub_value .= $temp_add;
  769. }
  770. elseif($this->str_from == 'is')
  771. {
  772. $this->cur_selector .= $temp_add;
  773. }
  774. break;
  775. case 'ic':
  776. if($string{$i} == '/' && $string{$i-1} == '*' && !csspp::escaped($string,$i))
  777. {
  778. $this->status = $this->comment_from;
  779. }
  780. break;
  781. }
  782. }
  783. if($this->get_cfg('merge_selectors'))
  784. {
  785. foreach($this->css as $medium => $value)
  786. {
  787. for ($i = 0; $i < count($this->css); $i++)
  788. {
  789. $this->css[$medium] = csspp::merge_selectors($this->css[$medium]);
  790. }
  791. }
  792. }
  793.  
  794. if($this->get_cfg('optimise_shorthands'))
  795. {
  796. foreach($this->css as $medium => $value)
  797. {
  798. foreach($value as $selector => $value1)
  799. {
  800. $this->css[$medium][$selector] = merge_4value_shorthands($this->css[$medium][$selector]);
  801. $this->css[$medium][$selector] = merge_bg($this->css[$medium][$selector]);
  802. if(empty($this->css[$medium][$selector]))
  803. {
  804. unset($this->css[$medium][$selector]);
  805. }
  806. }
  807. }
  808. }
  809. return (empty($this->css)) ? FALSE : TRUE;
  810. }
  811.  
  812. /**
  813. * Checks if a character is escaped (and returns TRUE if it is)
  814. * @param string $string
  815. * @param integer $pos
  816. * @access public
  817. * @return bool
  818. * @version 1.0
  819. */
  820. function escaped(&$string,$pos)
  821. {
  822. if(@($string{$pos-1} != '\\'))
  823. {
  824. return FALSE;
  825. }
  826. elseif(csspp::escaped($string,$pos-1))
  827. {
  828. return FALSE;
  829. }
  830. else
  831. {
  832. return TRUE;
  833. }
  834. }
  835.  
  836. /**
  837. * Checks if $array has the key $find (array[x][$find]). If gv=1, the value of the key is returned.
  838. * @param string $find
  839. * @param array $array
  840. * @param integer $gv default is 0
  841. * @access public
  842. * @return mixed depends on $gv, either bool or string
  843. * @version 1.0
  844. */
  845. function has_subkey($find,$array,$gv=0) {
  846. foreach($array as $key => $value)
  847. {
  848. if(isset($array[$key][$find]))
  849. {
  850. return ($gv == 0) ? TRUE : $array[$key][$find];
  851. }
  852. }
  853. return FALSE;
  854. }
  855.  
  856. /**
  857. * Removes all keys $find in $array (array[x][$find]).
  858. * @param string $find
  859. * @param array $array
  860. * @param string $del_val if this is !== NULL, it only removes if the value of they key $find === $del_val
  861. * @access public
  862. * @version 1.0
  863. */
  864. function rm_subkey($find,&$array,$del_val = NULL)
  865. {
  866. foreach($array as $key => $value)
  867. {
  868. if(isset($array[$key][$find]) && ($del_val === NULL || $array[$key][$find] === $del_val))
  869. {
  870. unset($array[$key]);
  871. }
  872. }
  873. }
  874.  
  875. /**
  876. * Adds a property with value to an existing CSS block
  877. * @param array $css
  878. * @param string $property
  879. * @param string $new_val
  880. * @param bool $ie if $ie == FALSE IE Hacks are not saved even if it is enabled in settings
  881. * @access public
  882. * @version 1.0
  883. */
  884. function css_add_property(&$css,$property,$new_val,$ie=TRUE)
  885. {
  886. $temp = $css;
  887. if($this->get_cfg('save_ie_hacks') && $ie)
  888. {
  889. $overwrite = FALSE;
  890. foreach($css as $key => $value)
  891. {
  892. if(isset($css[$key][$property]) && (!csspp::is_important($css[$key][$property]) && !($property == 'voice-family' && $css[$key][$property] == '"\"}\""') ) )
  893. {
  894. $overwrite = TRUE;
  895. $temp[$key][$property] = trim($new_val);
  896. }
  897. }
  898. if(!$overwrite) $temp[][$property] = trim($new_val);
  899. }
  900. else
  901. {
  902. foreach($css as $key => $value)
  903. {
  904. if(isset($css[$key][$property]))
  905. {
  906. if((csspp::is_important($css[$key][$property]) && csspp::is_important($new_val)) || !csspp::is_important($css[$key][$property]))
  907. {
  908. unset($temp[$key]);
  909. $temp[][$property] = trim($new_val);
  910. }
  911. }
  912. }
  913. }
  914. $css = $temp;
  915. }
  916.  
  917. /**
  918. * Merges two CSS blocks
  919. * @param array $existing_css
  920. * @param array $css_add
  921. * @access public
  922. * @version 1.0
  923. */
  924. function merge_css_blocks(&$existing_css,$css_add)
  925. {
  926. foreach($css_add as $key => $value)
  927. {
  928. if(!csspp::has_subkey($key,$existing_css))
  929. {
  930. $existing_css[][$key] = $value;
  931. }
  932. else
  933. {
  934. $this->css_add_property($existing_css,$key,$value,false);
  935. }
  936. }
  937. }
  938.  
  939. /**
  940. * This function checks if the properties $needle also exist in other selectors $haystack and returs them as $keys
  941. * @param array $needle
  942. * @param array $haystack
  943. * @access public
  944. * @version 1.0
  945. */
  946. function in_array_prop($needle, $haystack)
  947. {
  948. $keys = array();
  949. foreach($haystack as $key => $value)
  950. {
  951. $i = 0;
  952. foreach($needle as $key1 => $value1)
  953. {
  954. if(in_array($needle[$key1],$haystack[$key]))
  955. $i++;
  956. }
  957. if($i == count($needle) && $i == count($haystack[$key])) $keys[] = $key;
  958. }
  959. if(empty($keys)) return FALSE; else return $keys;
  960. }
  961.  
  962. /**
  963. * Merges selectors with same properties. Example: a{color:red} b{color:red} -> a,b{color:red}
  964. * Very basic and has at least one bug. Hopefully there is a replacement soon.
  965. * @param array $array
  966. * @return array
  967. * @access public
  968. * @version 1.0
  969. */
  970. function merge_selectors($array)
  971. {
  972. foreach($array as $key => $value)
  973. {
  974. if(isset($array[$key]))
  975. {
  976. $newsel = '';
  977. $temp = $array;
  978. unset($temp[$key]);
  979. $result = csspp::in_array_prop($array[$key],$temp);
  980. if($result !== FALSE)
  981. {
  982. $newsel = $key;
  983. unset($array[$key]);
  984. foreach($result as $key1 => $value1)
  985. {
  986. unset($array[$value1]);
  987. $newsel .= ','.$value1;
  988. }
  989. $array[$newsel] = $value;
  990. }
  991. }
  992. }
  993. return $array;
  994. }
  995.  
  996.  
  997. /**
  998. * Checks if $value is !important.
  999. * @param string $value
  1000. * @return bool
  1001. * @access public
  1002. * @version 1.0
  1003. */
  1004. function is_important(&$value)
  1005. {
  1006. global $whitespace;
  1007. if(strtolower(substr(str_replace($whitespace,'',$value),-10,10)) === '!important')
  1008. {
  1009. return TRUE;
  1010. }
  1011. return FALSE;
  1012. }
  1013.  
  1014. /**
  1015. * Returns a value without !important
  1016. * @param string $value
  1017. * @return string
  1018. * @access public
  1019. * @version 1.0
  1020. */
  1021. function gvw_important($value)
  1022. {
  1023. if(csspp::is_important($value))
  1024. {
  1025. $value = trim($value);
  1026. $value = substr($value,0,-9);
  1027. $value = trim($value);
  1028. $value = substr($value,0,-1);
  1029. $value = trim($value);
  1030. return $value;
  1031. }
  1032. else
  1033. {
  1034. return $value;
  1035. }
  1036. }
  1037.  
  1038. /**
  1039. * Removes unnecessary whitespace in ! important
  1040. * @param string $string
  1041. * @access public
  1042. * @version 1.0
  1043. */
  1044. function c_important(&$string)
  1045. {
  1046. if(csspp::is_important($string))
  1047. {
  1048. $string = csspp::gvw_important($string) . ' !important';
  1049. }
  1050. }
  1051.  
  1052. /**
  1053. * Sort function for sorting the properties
  1054. * @param string $val1
  1055. * @param string $val2
  1056. * @return integer
  1057. * @access public
  1058. * @version 1.0
  1059. */
  1060. function usort_properties($val1,$val2)
  1061. {
  1062. if(key($val1) > key($val2))
  1063. {
  1064. return 1;
  1065. }
  1066. elseif(key($val1) < key($val2))
  1067. {
  1068. return -1;
  1069. }
  1070. else
  1071. {
  1072. return 0;
  1073. }
  1074. }
  1075.  
  1076. /**
  1077. * Returns the formatted CSS Code and saves it into $this->output_css
  1078. * @param array $css
  1079. * @return string
  1080. * @access public
  1081. * @version 1.1
  1082. */
  1083. function print_code($css = NULL)
  1084. {
  1085. $output = '';
  1086. if($css === NULL)
  1087. {
  1088. $css = $this->css;
  1089. }
  1090. if(!empty ($this->charset))
  1091. {
  1092. $output .= $this->template[0].'@charset '.$this->template[5].'"'.$this->charset.'"'.$this->template[6].$this->template[12];
  1093. }
  1094. if(!empty ($this->import))
  1095. {
  1096. for ($i = 0, $size = count($this->import); $i < $size; $i ++) {
  1097. $output .= $this->template[0].'@import '.$this->template[5].$this->import[$i].$this->template[6].$this->template[12];
  1098. }
  1099. }
  1100. ksort($css);
  1101. foreach($css as $medium => $val)
  1102. {
  1103. if ($medium !== 'standard')
  1104. {
  1105. $output .= $this->template[0].htmlspecialchars($medium).$this->template[1];
  1106. }
  1107. if ($this->get_cfg('sort_selectors')) ksort($val);
  1108. foreach($val as $selector => $vali)
  1109. {
  1110. if ($this->get_cfg('sort_properties')) usort($vali,array('csspp','usort_properties'));
  1111. if ($medium !== 'standard') $output .= $this->template[10];
  1112. $output .= ($selector{0} !== '@') ? $this->template[2].htmlspecialchars($selector) : $this->template[0].htmlspecialchars($selector);
  1113. $output .= ($medium !== 'standard') ? $this->template[11] : $this->template[3];
  1114. foreach($vali as $num_prop => $valj)
  1115. {
  1116. foreach($valj as $property => $value)
  1117. {
  1118. if ($medium !== 'standard') $output .= $this->template[10];
  1119. $output .= $this->template[4];
  1120. $output .= ($this->get_cfg('uppercase_properties')) ? htmlspecialchars(strtoupper($property)) : htmlspecialchars($property);
  1121. $output .= ':'.$this->template[5].htmlspecialchars($value);
  1122. $output .= ($this->get_cfg('remove_last_;') && end($vali) === $valj) ? str_replace(';','',$this->template[6]) : $this->template[6];
  1123. }
  1124. }
  1125. if ($medium !== 'standard') $output .= $this->template[10];
  1126. $output .= $this->template[7];
  1127. if (( end($val) !== $vali && $medium !== 'standard') || $medium === 'standard') $output .= $this->template[8];
  1128. }
  1129. if ($medium != 'standard') $output .= $this->template[9];
  1130. }
  1131.  
  1132. $output = trim($output);
  1133. $this->output_css = $output;
  1134. return $output;
  1135. }
  1136.  
  1137. /**
  1138. * Checks if the next word in a string from pos is a CSS property
  1139. * @param string $istring
  1140. * @param integer $pos
  1141. * @return bool
  1142. * @access public
  1143. * @version 1.1
  1144. */
  1145. function property_is_next($istring, $pos)
  1146. {
  1147. global $all_properties;
  1148. $istring = strtolower(ltrim(substr($istring,$pos,strlen($istring)-$pos)));
  1149. for($i = 0; $i < count($all_properties); $i++)
  1150. {
  1151. if(substr($istring,0,strlen($all_properties[$i])) == $all_properties[$i])
  1152. {
  1153. return true;
  1154. }
  1155. }
  1156. return false;
  1157. }
  1158.  
  1159. }
  1160.  
  1161. ?>

Documentation generated on Sat, 20 Aug 2005 17:04:07 +0200 by phpDocumentor 1.3.0RC3