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

Source for file class.csstidy.php

Documentation is available at class.csstidy.php

  1. <?php
  2. /**
  3. * CSSTidy - CSS Parser and Optimiser
  4. *
  5. * CSS Parser class
  6. *
  7. * This file is part of CSSTidy.
  8. *
  9. * CSSTidy is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * CSSTidy is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with CSSTidy; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  24. * @package csstidy
  25. * @author Florian Schmitz (floele at gmail dot com) 2005-2006
  26. */
  27.  
  28. /**
  29. * Various CSS data needed for correct optimisations etc.
  30. *
  31. * @version 1.2
  32. */
  33. require('data.inc.php');
  34.  
  35. /**
  36. * Contains a class for printing CSS code
  37. *
  38. * @version 1.0
  39. */
  40. require('class.csstidy_print.php');
  41.  
  42. /**
  43. * Contains a class for optimising CSS code
  44. *
  45. * @version 1.0
  46. */
  47. require('class.csstidy_optimise.php');
  48.  
  49. /**
  50. * CSS Parser class
  51. *
  52. * This class represents a CSS parser which reads CSS code and saves it in an array.
  53. * In opposite to most other CSS parsers, it does not use regular expressions and
  54. * thus has full CSS2 support and a higher reliability.
  55. * Additional to that it applies some optimisations and fixes to the CSS code.
  56. * An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
  57. * @package csstidy
  58. * @author Florian Schmitz (floele at gmail dot com) 2005-2006
  59. * @version 1.2
  60. */
  61. class csstidy {
  62.  
  63. /**
  64. * Saves the parsed CSS
  65. * @var array
  66. * @access public
  67. */
  68. var $css = array();
  69.  
  70. /**
  71. * Saves the parsed CSS (raw)
  72. * @var array
  73. * @access private
  74. */
  75. var $tokens = array();
  76.  
  77. /**
  78. * Printer class
  79. * @see csstidy_print
  80. * @var object
  81. * @access public
  82. */
  83. var $print;
  84.  
  85. /**
  86. * Optimiser class
  87. * @see csstidy_optimise
  88. * @var object
  89. * @access private
  90. */
  91. var $optimise;
  92.  
  93. /**
  94. * Saves the CSS charset (@charset)
  95. * @var string
  96. * @access private
  97. */
  98. var $charset = '';
  99.  
  100. /**
  101. * Saves all @import URLs
  102. * @var array
  103. * @access private
  104. */
  105. var $import = array();
  106.  
  107. /**
  108. * Saves the namespace
  109. * @var string
  110. * @access private
  111. */
  112. var $namespace = '';
  113.  
  114. /**
  115. * Contains the version of csstidy
  116. * @var string
  117. * @access private
  118. */
  119. var $version = '1.2';
  120.  
  121. /**
  122. * Stores the settings
  123. * @var array
  124. * @access private
  125. */
  126. var $settings = array();
  127.  
  128. /**
  129. * Saves the parser-status.
  130. *
  131. * Possible values:
  132. * - is = in selector
  133. * - ip = in property
  134. * - iv = in value
  135. * - instr = in string (started at " or ' or ( )
  136. * - ic = in comment (ignore everything)
  137. * - at = in @-block
  138. *
  139. * @var string
  140. * @access private
  141. */
  142. var $status = 'is';
  143.  
  144.  
  145. /**
  146. * Saves the current at rule (@media)
  147. * @var string
  148. * @access private
  149. */
  150. var $at = '';
  151.  
  152. /**
  153. * Saves the current selector
  154. * @var string
  155. * @access private
  156. */
  157. var $selector = '';
  158.  
  159. /**
  160. * Saves the current property
  161. * @var string
  162. * @access private
  163. */
  164. var $property = '';
  165.  
  166. /**
  167. * Saves the position of , in selectors
  168. * @var array
  169. * @access private
  170. */
  171. var $sel_separate = array();
  172.  
  173. /**
  174. * Saves the current value
  175. * @var string
  176. * @access private
  177. */
  178. var $value = '';
  179.  
  180. /**
  181. * Saves the current sub-value
  182. *
  183. * Example for a subvalue:
  184. * background:url(foo.png) red no-repeat;
  185. * "url(foo.png)", "red", and "no-repeat" are subvalues,
  186. * seperated by whitespace
  187. * @var string
  188. * @access private
  189. */
  190. var $sub_value = '';
  191.  
  192. /**
  193. * Array which saves all subvalues for a property.
  194. * @var array
  195. * @see sub_value
  196. * @access private
  197. */
  198. var $sub_value_arr = array();
  199.  
  200. /**
  201. * Saves the char which opened the last string
  202. * @var string
  203. * @access private
  204. */
  205. var $str_char = '';
  206.  
  207. /**
  208. * Status from which the parser switched to ic or instr
  209. * @var string
  210. * @access private
  211. */
  212. var $from = '';
  213.  
  214. /**
  215. * Variable needed to manage string-in-strings, for example url("foo.png")
  216. * @var string
  217. * @access private
  218. */
  219. var $str_in_str = false;
  220.  
  221. /**
  222. * =true if in invalid at-rule
  223. * @var bool
  224. * @access private
  225. */
  226. var $invalid_at = false;
  227.  
  228. /**
  229. * =true if something has been added to the current selector
  230. * @var bool
  231. * @access private
  232. */
  233. var $added = false;
  234.  
  235. /**
  236. * Array which saves the message log
  237. * @var array
  238. * @access private
  239. */
  240. var $log = array();
  241.  
  242. /**
  243. * Saves the line number
  244. * @var integer
  245. * @access private
  246. */
  247. var $line = 1;
  248.  
  249. /**
  250. * Loads standard template and sets default settings
  251. * @access private
  252. * @version 1.2
  253. */
  254. function csstidy()
  255. {
  256. $this->settings['remove_bslash'] = true;
  257. $this->settings['compress_colors'] = true;
  258. $this->settings['compress_font-weight'] = true;
  259. $this->settings['lowercase_s'] = false;
  260. $this->settings['optimise_shorthands'] = 1;
  261. $this->settings['remove_last_;'] = false;
  262. $this->settings['case_properties'] = 1;
  263. $this->settings['sort_properties'] = false;
  264. $this->settings['sort_selectors'] = false;
  265. $this->settings['merge_selectors'] = 2;
  266. $this->settings['discard_invalid_properties'] = false;
  267. $this->settings['css_level'] = 'CSS2.1';
  268. $this->settings['preserve_css'] = false;
  269. $this->settings['timestamp'] = false;
  270.  
  271. $this->load_template('default');
  272. $this->print = new csstidy_print($this);
  273. $this->optimise = new csstidy_optimise($this);
  274. }
  275.  
  276. /**
  277. * Get the value of a setting.
  278. * @param string $setting
  279. * @access public
  280. * @return mixed
  281. * @version 1.0
  282. */
  283. function get_cfg($setting)
  284. {
  285. if(isset($this->settings[$setting]))
  286. {
  287. return $this->settings[$setting];
  288. }
  289. return false;
  290. }
  291.  
  292. /**
  293. * Set the value of a setting.
  294. * @param string $setting
  295. * @param mixed $value
  296. * @access public
  297. * @return bool
  298. * @version 1.0
  299. */
  300. function set_cfg($setting,$value)
  301. {
  302. if(isset($this->settings[$setting]) && $value !== '')
  303. {
  304. $this->settings[$setting] = $value;
  305. return true;
  306. }
  307. return false;
  308. }
  309.  
  310. /**
  311. * Adds a token to $this->tokens
  312. * @param mixed $type
  313. * @param string $data
  314. * @param bool $do add a token even if preserve_css is off
  315. * @access private
  316. * @version 1.0
  317. */
  318. function _add_token($type, $data, $do = false) {
  319. if($this->get_cfg('preserve_css') || $do) {
  320. $this->tokens[] = array($type, ($type == COMMENT) ? $data : trim($data));
  321. }
  322. }
  323. /**
  324. * Add a message to the message log
  325. * @param string $message
  326. * @param string $type
  327. * @param integer $line
  328. * @access private
  329. * @version 1.0
  330. */
  331. function log($message,$type,$line = -1)
  332. {
  333. if($line === -1)
  334. {
  335. $line = $this->line;
  336. }
  337. $line = intval($line);
  338. $add = array('m' => $message, 't' => $type);
  339. if(!isset($this->log[$line]) || !in_array($add,$this->log[$line]))
  340. {
  341. $this->log[$line][] = $add;
  342. }
  343. }
  344.  
  345. /**
  346. * Parse unicode notations and find a replacement character
  347. * @param string $string
  348. * @param integer $i
  349. * @access private
  350. * @return string
  351. * @version 1.2
  352. */
  353. function _unicode(&$string, &$i)
  354. {
  355. ++$i;
  356. $add = '';
  357. $tokens =& $GLOBALS['csstidy']['tokens'];
  358. $replaced = false;
  359. while($i < strlen($string) && (ctype_xdigit($string{$i}) || ctype_space($string{$i})) && strlen($add) < 6)
  360. {
  361. $add .= $string{$i};
  362.  
  363. if(ctype_space($string{$i})) {
  364. break;
  365. }
  366. $i++;
  367. }
  368.  
  369. if(hexdec($add) > 47 && hexdec($add) < 58 || hexdec($add) > 64 && hexdec($add) < 91 || hexdec($add) > 96 && hexdec($add) < 123)
  370. {
  371. $this->log('Replaced unicode notation: Changed \\'. $add .' to ' . chr(hexdec($add)),'Information');
  372. $add = chr(hexdec($add));
  373. $replaced = true;
  374. }
  375. else {
  376. $add = trim('\\'.$add);
  377. }
  378.  
  379. if(@ctype_xdigit($string{$i+1}) && ctype_space($string{$i})
  380. && !$replaced || !ctype_space($string{$i})) {
  381. $i--;
  382. }
  383. if($add != '\\' || !$this->get_cfg('remove_bslash') || strpos($tokens, $string{$i+1}) !== false) {
  384. return $add;
  385. }
  386. if($add == '\\') {
  387. $this->log('Removed unnecessary backslash','Information');
  388. }
  389. return '';
  390. }
  391.  
  392. /**
  393. * Loads a new template
  394. * @param string $content either filename (if $from_file == true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default"
  395. * @param bool $from_file uses $content as filename if true
  396. * @access public
  397. * @version 1.1
  398. * @see http://csstidy.sourceforge.net/templates.php
  399. */
  400. function load_template($content, $from_file=true)
  401. {
  402. $predefined_templates =& $GLOBALS['csstidy']['predefined_templates'];
  403. if($content == 'high_compression' || $content == 'default' || $content == 'highest_compression' || $content == 'low_compression')
  404. {
  405. $this->template = $predefined_templates[$content];
  406. return;
  407. }
  408. if($from_file)
  409. {
  410. $content = strip_tags(file_get_contents($content),'<span>');
  411. }
  412. $content = str_replace("\r\n","\n",$content); // Unify newlines (because the output also only uses \n)
  413. $template = explode('|',$content);
  414.  
  415. for ($i = 0; $i < count($template); $i++ )
  416. {
  417. $this->template[$i] = $template[$i];
  418. }
  419. }
  420.  
  421. /**
  422. * Starts parsing from URL
  423. * @param string $url
  424. * @access public
  425. * @version 1.0
  426. */
  427. function parse_from_url($url)
  428. {
  429. return $this->parse(@file_get_contents($url));
  430. }
  431.  
  432. /**
  433. * Checks if there is a token at the current position
  434. * @param string $string
  435. * @param integer $i
  436. * @access public
  437. * @version 1.11
  438. */
  439. function is_token(&$string, $i)
  440. {
  441. $tokens =& $GLOBALS['csstidy']['tokens'];
  442. return (strpos($tokens, $string{$i}) !== false && !csstidy::escaped($string,$i));
  443. }
  444.  
  445.  
  446. /**
  447. * Parses CSS in $string. The code is saved as array in $this->css
  448. * @param string $string the CSS code
  449. * @access public
  450. * @return bool
  451. * @version 1.1
  452. */
  453. function parse($string) {
  454. // PHP bug? Settings need to be refreshed in PHP4
  455. $this->print = new csstidy_print($this);
  456. $this->optimise = new csstidy_optimise($this);
  457. $all_properties =& $GLOBALS['csstidy']['all_properties'];
  458. $at_rules =& $GLOBALS['csstidy']['at_rules'];
  459.  
  460. $this->css = array();
  461. $this->print->input_css = $string;
  462. $string = str_replace("\r\n","\n",$string) . ' ';
  463. $cur_comment = '';
  464. for ($i = 0, $size = strlen($string); $i < $size; $i++ )
  465. {
  466. if($string{$i} == "\n" || $string{$i} == "\r")
  467. {
  468. ++$this->line;
  469. }
  470. switch($this->status)
  471. {
  472. /* Case in at-block */
  473. case 'at':
  474. if(csstidy::is_token($string,$i))
  475. {
  476. if($string{$i} == '/' && @$string{$i+1} == '*')
  477. {
  478. $this->status = 'ic'; ++$i;
  479. $this->from = 'at';
  480. }
  481. elseif($string{$i} == '{')
  482. {
  483. $this->status = 'is';
  484. $this->_add_token(AT_START, $this->at);
  485. }
  486. elseif($string{$i} == ',')
  487. {
  488. $this->at = trim($this->at).',';
  489. }
  490. elseif($string{$i} == '\\')
  491. {
  492. $this->at .= $this->_unicode($string,$i);
  493. }
  494. }
  495. else
  496. {
  497. $lastpos = strlen($this->at)-1;
  498. if(!( (ctype_space($this->at{$lastpos}) || csstidy::is_token($this->at,$lastpos) && $this->at{$lastpos} == ',') && ctype_space($string{$i})))
  499. {
  500. $this->at .= $string{$i};
  501. }
  502. }
  503. break;
  504. /* Case in-selector */
  505. case 'is':
  506. if(csstidy::is_token($string,$i))
  507. {
  508. if($string{$i} == '/' && @$string{$i+1} == '*' && trim($this->selector) == '')
  509. {
  510. $this->status = 'ic'; ++$i;
  511. $this->from = 'is';
  512. }
  513. elseif($string{$i} == '@' && trim($this->selector) == '')
  514. {
  515. // Check for at-rule
  516. $this->invalid_at = true;
  517. foreach($at_rules as $name => $type)
  518. {
  519. if(!strcasecmp(substr($string,$i+1,strlen($name)),$name))
  520. {
  521. ($type == 'at') ? $this->at = '@'.$name : $this->selector = '@'.$name;
  522. $this->status = $type;
  523. $i += strlen($name);
  524. $this->invalid_at = false;
  525. }
  526. }
  527. if($this->invalid_at)
  528. {
  529. $this->selector = '@';
  530. $invalid_at_name = '';
  531. for($j = $i+1; $j < $size; ++$j)
  532. {
  533. if(!ctype_alpha($string{$j}))
  534. {
  535. break;
  536. }
  537. $invalid_at_name .= $string{$j};
  538. }
  539. $this->log('Invalid @-rule: '.$invalid_at_name.' (removed)','Warning');
  540. }
  541. }
  542. elseif(($string{$i} == '"' || $string{$i} == "'"))
  543. {
  544. $this->selector .= $string{$i};
  545. $this->status = 'instr';
  546. $this->str_char = $string{$i};
  547. $this->from = 'is';
  548. }
  549. elseif($this->invalid_at && $string{$i} == ';')
  550. {
  551. $this->invalid_at = false;
  552. $this->status = 'is';
  553. }
  554. elseif($string{$i} == '{')
  555. {
  556. $this->status = 'ip';
  557. $this->_add_token(SEL_START, $this->selector);
  558. $this->added = false;
  559. }
  560. elseif($string{$i} == '}')
  561. {
  562. $this->_add_token(AT_END, $this->at);
  563. $this->at = '';
  564. $this->selector = '';
  565. $this->sel_separate = array();
  566. }
  567. elseif($string{$i} == ',')
  568. {
  569. $this->selector = trim($this->selector).',';
  570. $this->sel_separate[] = strlen($this->selector);
  571. }
  572. elseif($string{$i} == '\\')
  573. {
  574. $this->selector .= $this->_unicode($string,$i);
  575. }
  576. else $this->selector .= $string{$i};
  577. }
  578. else
  579. {
  580. $lastpos = strlen($this->selector)-1;
  581. if($lastpos == -1 || !( (ctype_space($this->selector{$lastpos}) || csstidy::is_token($this->selector,$lastpos) && $this->selector{$lastpos} == ',') && ctype_space($string{$i})))
  582. {
  583. $this->selector .= $string{$i};
  584. }
  585. }
  586. break;
  587. /* Case in-property */
  588. case 'ip':
  589. if(csstidy::is_token($string,$i))
  590. {
  591. if(($string{$i} == ':' || $string{$i} == '=') && $this->property != '')
  592. {
  593. $this->status = 'iv';
  594. if(csstidy::property_is_valid($this->property) || !$this->get_cfg('discard_invalid_properties')) {
  595. $this->_add_token(PROPERTY, $this->property);
  596. }
  597. }
  598. elseif($string{$i} == '/' && @$string{$i+1} == '*' && $this->property == '')
  599. {
  600. $this->status = 'ic'; ++$i;
  601. $this->from = 'ip';
  602. }
  603. elseif($string{$i} == '}')
  604. {
  605. $this->explode_selectors();
  606. $this->status = 'is';
  607. $this->invalid_at = false;
  608. $this->_add_token(SEL_END, $this->selector);
  609. $this->selector = '';
  610. $this->property = '';
  611. }
  612. elseif($string{$i} == ';')
  613. {
  614. $this->property = '';
  615. }
  616. elseif($string{$i} == '\\')
  617. {
  618. $this->property .= $this->_unicode($string,$i);
  619. }
  620. }
  621. elseif(!ctype_space($string{$i}))
  622. {
  623. $this->property .= $string{$i};
  624. }
  625. break;
  626. /* Case in-value */
  627. case 'iv':
  628. $pn = (($string{$i} == "\n" || $string{$i} == "\r") && $this->property_is_next($string,$i+1) || $i == strlen($string)-1);
  629. if(csstidy::is_token($string,$i) || $pn)
  630. {
  631. if($string{$i} == '/' && @$string{$i+1} == '*')
  632. {
  633. $this->status = 'ic'; ++$i;
  634. $this->from = 'iv';
  635. }
  636. elseif(($string{$i} == '"' || $string{$i} == "'" || $string{$i} == '('))
  637. {
  638. $this->sub_value .= $string{$i};
  639. $this->str_char = ($string{$i} == '(') ? ')' : $string{$i};
  640. $this->status = 'instr';
  641. $this->from = 'iv';
  642. }
  643. elseif($string{$i} == ',')
  644. {
  645. $this->sub_value = trim($this->sub_value).',';
  646. }
  647. elseif($string{$i} == '\\')
  648. {
  649. $this->sub_value .= $this->_unicode($string,$i);
  650. }
  651. elseif($string{$i} == ';' || $pn)
  652. {
  653. if($this->selector{0} == '@' && isset($at_rules[substr($this->selector,1)]) && $at_rules[substr($this->selector,1)] == 'iv')
  654. {
  655. $this->sub_value_arr[] = trim($this->sub_value);
  656. $this->status = 'is';
  657. switch($this->selector)
  658. {
  659. case '@charset': $this->charset = $this->sub_value_arr[0]; break;
  660. case '@namespace': $this->namespace = implode(' ',$this->sub_value_arr); break;
  661. case '@import': $this->import[] = implode(' ',$this->sub_value_arr); break;
  662. }
  663. $this->sub_value_arr = array();
  664. $this->sub_value = '';
  665. $this->selector = '';
  666. $this->sel_separate = array();
  667. }
  668. else
  669. {
  670. $this->status = 'ip';
  671. }
  672. }
  673. elseif($string{$i} != '}')
  674. {
  675. $this->sub_value .= $string{$i};
  676. }
  677. if(($string{$i} == '}' || $string{$i} == ';' || $pn) && !empty($this->selector))
  678. {
  679. if($this->at == '')
  680. {
  681. $this->at = DEFAULT_AT;
  682. }
  683. // case settings
  684. if($this->get_cfg('lowercase_s'))
  685. {
  686. $this->selector = strtolower($this->selector);
  687. }
  688. $this->property = strtolower($this->property);
  689. $this->optimise->subvalue();
  690. if($this->sub_value != '') {
  691. $this->sub_value_arr[] = $this->sub_value;
  692. $this->sub_value = '';
  693. }
  694. $this->value = implode(' ',$this->sub_value_arr);
  695.  
  696. $this->selector = trim($this->selector);
  697. $this->optimise->value();
  698. $valid = csstidy::property_is_valid($this->property);
  699. if((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid))
  700. {
  701. $this->css_add_property($this->at,$this->selector,$this->property,$this->value);
  702. $this->_add_token(VALUE, $this->value);
  703. $this->optimise->shorthands();
  704. }
  705. if(!$valid)
  706. {
  707. if($this->get_cfg('discard_invalid_properties'))
  708. {
  709. $this->log('Removed invalid property: '.$this->property,'Warning');
  710. }
  711. else
  712. {
  713. $this->log('Invalid property in '.strtoupper($this->get_cfg('css_level')).': '.$this->property,'Warning');
  714. }
  715. }
  716. $this->property = '';
  717. $this->sub_value_arr = array();
  718. $this->value = '';
  719. }
  720. if($string{$i} == '}')
  721. {
  722. $this->explode_selectors();
  723. $this->_add_token(SEL_END, $this->selector);
  724. $this->status = 'is';
  725. $this->invalid_at = false;
  726. $this->selector = '';
  727. }
  728. }
  729. elseif(!$pn)
  730. {
  731. $this->sub_value .= $string{$i};
  732.  
  733. if(ctype_space($string{$i}))
  734. {
  735. $this->optimise->subvalue();
  736. if($this->sub_value != '') {
  737. $this->sub_value_arr[] = $this->sub_value;
  738. $this->sub_value = '';
  739. }
  740. }
  741. }
  742. break;
  743. /* Case in string */
  744. case 'instr':
  745. if($this->str_char == ')' && $string{$i} == '"' && !$this->str_in_str && !csstidy::escaped($string,$i))
  746. {
  747. $this->str_in_str = true;
  748. }
  749. elseif($this->str_char == ')' && $string{$i} == '"' && $this->str_in_str && !csstidy::escaped($string,$i))
  750. {
  751. $this->str_in_str = false;
  752. }
  753. if($string{$i} == $this->str_char && !csstidy::escaped($string,$i) && !$this->str_in_str)
  754. {
  755. $this->status = $this->from;
  756. }
  757. $temp_add = $string{$i};
  758. // ...and no not-escaped backslash at the previous position
  759. if( ($string{$i} == "\n" || $string{$i} == "\r") && !($string{$i-1} == '\\' && !csstidy::escaped($string,$i-1)) )
  760. {
  761. $temp_add = "\\A ";
  762. $this->log('Fixed incorrect newline in string','Warning');
  763. }
  764. if($this->from == 'iv')
  765. {
  766. $this->sub_value .= $temp_add;
  767. }
  768. elseif($this->from == 'is')
  769. {
  770. $this->selector .= $temp_add;
  771. }
  772. break;
  773. /* Case in-comment */
  774. case 'ic':
  775. if($string{$i} == '*' && $string{$i+1} == '/')
  776. {
  777. $this->status = $this->from;
  778. $i++;
  779. $this->_add_token(COMMENT, $cur_comment);
  780. $cur_comment = '';
  781. }
  782. else
  783. {
  784. $cur_comment .= $string{$i};
  785. }
  786. break;
  787. }
  788. }
  789.  
  790. $this->optimise->postparse();
  791.  
  792. $this->print->_reset();
  793.  
  794. return !(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace));
  795. }
  796.  
  797. /**
  798. * Explodes selectors
  799. * @access private
  800. * @version 1.0
  801. */
  802. function explode_selectors()
  803. {
  804. // Explode multiple selectors
  805. if($this->get_cfg('merge_selectors') == 1)
  806. {
  807. $new_sels = array();
  808. $lastpos = 0;
  809. $this->sel_separate[] = strlen($this->selector);
  810. foreach($this->sel_separate as $num => $pos)
  811. {
  812. if($num == count($this->sel_separate)-1) {
  813. $pos += 1;
  814. }
  815. $new_sels[] = substr($this->selector,$lastpos,$pos-$lastpos-1);
  816. $lastpos = $pos;
  817. }
  818. if(count($new_sels) > 1)
  819. {
  820. foreach($new_sels as $selector)
  821. {
  822. $this->merge_css_blocks($this->at,$selector,$this->css[$this->at][$this->selector]);
  823. }
  824. unset($this->css[$this->at][$this->selector]);
  825. }
  826. }
  827. $this->sel_separate = array();
  828. }
  829.  
  830. /**
  831. * Checks if a character is escaped (and returns true if it is)
  832. * @param string $string
  833. * @param integer $pos
  834. * @access public
  835. * @return bool
  836. * @version 1.02
  837. */
  838. function escaped(&$string,$pos)
  839. {
  840. return !(@($string{$pos-1} != '\\') || csstidy::escaped($string,$pos-1));
  841. }
  842.  
  843. /**
  844. * Adds a property with value to the existing CSS code
  845. * @param string $media
  846. * @param string $selector
  847. * @param string $property
  848. * @param string $new_val
  849. * @access private
  850. * @version 1.2
  851. */
  852. function css_add_property($media,$selector,$property,$new_val)
  853. {
  854. if($this->get_cfg('preserve_css') || trim($new_val) == '') {
  855. return;
  856. }
  857.  
  858. $this->added = true;
  859. if(isset($this->css[$media][$selector][$property]))
  860. {
  861. if((csstidy::is_important($this->css[$media][$selector][$property]) && csstidy::is_important($new_val)) || !csstidy::is_important($this->css[$media][$selector][$property]))
  862. {
  863. unset($this->css[$media][$selector][$property]);
  864. $this->css[$media][$selector][$property] = trim($new_val);
  865. }
  866. }
  867. else
  868. {
  869. $this->css[$media][$selector][$property] = trim($new_val);
  870. }
  871. }
  872.  
  873. /**
  874. * Adds CSS to an existing media/selector
  875. * @param string $media
  876. * @param string $selector
  877. * @param array $css_add
  878. * @access private
  879. * @version 1.1
  880. */
  881. function merge_css_blocks($media,$selector,$css_add)
  882. {
  883. foreach($css_add as $property => $value)
  884. {
  885. $this->css_add_property($media,$selector,$property,$value,false);
  886. }
  887. }
  888.  
  889. /**
  890. * Checks if $value is !important.
  891. * @param string $value
  892. * @return bool
  893. * @access public
  894. * @version 1.0
  895. */
  896. function is_important(&$value)
  897. {
  898. return (!strcasecmp(substr(str_replace($GLOBALS['csstidy']['whitespace'],'',$value),-10,10),'!important'));
  899. }
  900.  
  901. /**
  902. * Returns a value without !important
  903. * @param string $value
  904. * @return string
  905. * @access public
  906. * @version 1.0
  907. */
  908. function gvw_important($value)
  909. {
  910. if(csstidy::is_important($value))
  911. {
  912. $value = trim($value);
  913. $value = substr($value,0,-9);
  914. $value = trim($value);
  915. $value = substr($value,0,-1);
  916. $value = trim($value);
  917. return $value;
  918. }
  919. return $value;
  920. }
  921.  
  922. /**
  923. * Checks if the next word in a string from pos is a CSS property
  924. * @param string $istring
  925. * @param integer $pos
  926. * @return bool
  927. * @access private
  928. * @version 1.2
  929. */
  930. function property_is_next($istring, $pos)
  931. {
  932. $all_properties =& $GLOBALS['csstidy']['all_properties'];
  933. $istring = substr($istring,$pos,strlen($istring)-$pos);
  934. $pos = strpos($istring,':');
  935. if($pos === false)
  936. {
  937. return false;
  938. }
  939. $istring = strtolower(trim(substr($istring,0,$pos)));
  940. if(isset($all_properties[$istring]))
  941. {
  942. $this->log('Added semicolon to the end of declaration','Warning');
  943. return true;
  944. }
  945. return false;
  946. }
  947.  
  948. /**
  949. * Checks if a property is valid
  950. * @param string $property
  951. * @return bool;
  952. * @access public
  953. * @version 1.0
  954. */
  955. function property_is_valid($property) {
  956. $all_properties =& $GLOBALS['csstidy']['all_properties'];
  957. return (isset($all_properties[$property]) && strpos($all_properties[$property],strtoupper($this->get_cfg('css_level'))) !== false );
  958. }
  959.  
  960. }
  961. ?>

Documentation generated on Mon, 15 May 2006 22:55:14 +0200 by phpDocumentor 1.3.0RC3