PageRenderTime 65ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/w3-total-cache/lib/CSSTidy/class.csstidy.php

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