csstidy_optimise

CSS Optimising Class.

Defined (1)

The class is defined in the following location(s).

/modules/custom-css/csstidy/class.csstidy_optimise.php  
  1. class csstidy_optimise { 
  2.  
  3. /** 
  4. * Constructor 
  5. * @param array $css contains the class csstidy 
  6. * @access private 
  7. * @version 1.0 
  8. */ 
  9. function csstidy_optimise(&$css) { 
  10. $this->parser = & $css; 
  11. $this->css = & $css->css; 
  12. $this->sub_value = & $css->sub_value; 
  13. $this->at = & $css->at; 
  14. $this->selector = & $css->selector; 
  15. $this->property = & $css->property; 
  16. $this->value = & $css->value; 
  17.  
  18. /** 
  19. * Optimises $css after parsing 
  20. * @access public 
  21. * @version 1.0 
  22. */ 
  23. function postparse() { 
  24. if ($this->parser->get_cfg('preserve_css')) { 
  25. return; 
  26.  
  27. if ($this->parser->get_cfg('merge_selectors') === 2) { 
  28. foreach ($this->css as $medium => $value) { 
  29. $this->merge_selectors($this->css[$medium]); 
  30.  
  31. if ($this->parser->get_cfg('discard_invalid_selectors')) { 
  32. foreach ($this->css as $medium => $value) { 
  33. $this->discard_invalid_selectors($this->css[$medium]); 
  34.  
  35. if ($this->parser->get_cfg('optimise_shorthands') > 0) { 
  36. foreach ($this->css as $medium => $value) { 
  37. foreach ($value as $selector => $value1) { 
  38. $this->css[$medium][$selector] = csstidy_optimise::merge_4value_shorthands($this->css[$medium][$selector]); 
  39.  
  40. if ($this->parser->get_cfg('optimise_shorthands') < 2) { 
  41. continue; 
  42.  
  43. $this->css[$medium][$selector] = csstidy_optimise::merge_font($this->css[$medium][$selector]); 
  44.  
  45. if ($this->parser->get_cfg('optimise_shorthands') < 3) { 
  46. continue; 
  47.  
  48. $this->css[$medium][$selector] = csstidy_optimise::merge_bg($this->css[$medium][$selector]); 
  49. if (empty($this->css[$medium][$selector])) { 
  50. unset($this->css[$medium][$selector]); 
  51.  
  52. /** 
  53. * Optimises values 
  54. * @access public 
  55. * @version 1.0 
  56. */ 
  57. function value() { 
  58. $shorthands = & $GLOBALS['csstidy']['shorthands']; 
  59.  
  60. // optimise shorthand properties 
  61. if (isset($shorthands[$this->property])) { 
  62. $temp = csstidy_optimise::shorthand($this->value); // FIXME - move 
  63. if ($temp != $this->value) { 
  64. $this->parser->log('Optimised shorthand notation (' . $this->property . '): Changed "' . $this->value . '" to "' . $temp . '"', 'Information'); 
  65. $this->value = $temp; 
  66.  
  67. // Remove whitespace at ! important 
  68. if ($this->value != $this->compress_important($this->value)) { 
  69. $this->parser->log('Optimised !important', 'Information'); 
  70.  
  71. /** 
  72. * Optimises shorthands 
  73. * @access public 
  74. * @version 1.0 
  75. */ 
  76. function shorthands() { 
  77. $shorthands = & $GLOBALS['csstidy']['shorthands']; 
  78.  
  79. if (!$this->parser->get_cfg('optimise_shorthands') || $this->parser->get_cfg('preserve_css')) { 
  80. return; 
  81.  
  82. if ($this->property === 'font' && $this->parser->get_cfg('optimise_shorthands') > 1) { 
  83. $this->css[$this->at][$this->selector]['font']=''; 
  84. $this->parser->merge_css_blocks($this->at, $this->selector, csstidy_optimise::dissolve_short_font($this->value)); 
  85. if ($this->property === 'background' && $this->parser->get_cfg('optimise_shorthands') > 2) { 
  86. $this->css[$this->at][$this->selector]['background']=''; 
  87. $this->parser->merge_css_blocks($this->at, $this->selector, csstidy_optimise::dissolve_short_bg($this->value)); 
  88. if (isset($shorthands[$this->property])) { 
  89. $this->parser->merge_css_blocks($this->at, $this->selector, csstidy_optimise::dissolve_4value_shorthands($this->property, $this->value)); 
  90. if (is_array($shorthands[$this->property])) { 
  91. $this->css[$this->at][$this->selector][$this->property] = ''; 
  92.  
  93. /** 
  94. * Optimises a sub-value 
  95. * @access public 
  96. * @version 1.0 
  97. */ 
  98. function subvalue() { 
  99. $replace_colors = & $GLOBALS['csstidy']['replace_colors']; 
  100.  
  101. $this->sub_value = trim($this->sub_value); 
  102. if ($this->sub_value == '') { // caution : '0' 
  103. return; 
  104.  
  105. $important = ''; 
  106. if (csstidy::is_important($this->sub_value)) { 
  107. $important = '!important'; 
  108. $this->sub_value = csstidy::gvw_important($this->sub_value); 
  109.  
  110. // Compress font-weight 
  111. if ($this->property === 'font-weight' && $this->parser->get_cfg('compress_font-weight')) { 
  112. if ($this->sub_value === 'bold') { 
  113. $this->sub_value = '700'; 
  114. $this->parser->log('Optimised font-weight: Changed "bold" to "700"', 'Information'); 
  115. } else if ($this->sub_value === 'normal') { 
  116. $this->sub_value = '400'; 
  117. $this->parser->log('Optimised font-weight: Changed "normal" to "400"', 'Information'); 
  118.  
  119. $temp = $this->compress_numbers($this->sub_value); 
  120. if (strcasecmp($temp, $this->sub_value) !== 0) { 
  121. if (strlen($temp) > strlen($this->sub_value)) { 
  122. $this->parser->log('Fixed invalid number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning'); 
  123. } else { 
  124. $this->parser->log('Optimised number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information'); 
  125. $this->sub_value = $temp; 
  126. if ($this->parser->get_cfg('compress_colors')) { 
  127. $temp = $this->cut_color($this->sub_value); 
  128. if ($temp !== $this->sub_value) { 
  129. if (isset($replace_colors[$this->sub_value])) { 
  130. $this->parser->log('Fixed invalid color name: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning'); 
  131. } else { 
  132. $this->parser->log('Optimised color: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information'); 
  133. $this->sub_value = $temp; 
  134. $this->sub_value .= $important; 
  135.  
  136. /** 
  137. * Compresses shorthand values. Example: margin:1px 1px 1px 1px -> margin:1px 
  138. * @param string $value 
  139. * @access public 
  140. * @return string 
  141. * @version 1.0 
  142. */ 
  143. function shorthand($value) { 
  144. $important = ''; 
  145. if (csstidy::is_important($value)) { 
  146. $values = csstidy::gvw_important($value); 
  147. $important = '!important'; 
  148. else 
  149. $values = $value; 
  150.  
  151. $values = explode(' ', $values); 
  152. switch (count($values)) { 
  153. case 4: 
  154. if ($values[0] == $values[1] && $values[0] == $values[2] && $values[0] == $values[3]) { 
  155. return $values[0] . $important; 
  156. } elseif ($values[1] == $values[3] && $values[0] == $values[2]) { 
  157. return $values[0] . ' ' . $values[1] . $important; 
  158. } elseif ($values[1] == $values[3]) { 
  159. return $values[0] . ' ' . $values[1] . ' ' . $values[2] . $important; 
  160. break; 
  161.  
  162. case 3: 
  163. if ($values[0] == $values[1] && $values[0] == $values[2]) { 
  164. return $values[0] . $important; 
  165. } elseif ($values[0] == $values[2]) { 
  166. return $values[0] . ' ' . $values[1] . $important; 
  167. break; 
  168.  
  169. case 2: 
  170. if ($values[0] == $values[1]) { 
  171. return $values[0] . $important; 
  172. break; 
  173.  
  174. return $value; 
  175.  
  176. /** 
  177. * Removes unnecessary whitespace in ! important 
  178. * @param string $string 
  179. * @return string 
  180. * @access public 
  181. * @version 1.1 
  182. */ 
  183. function compress_important(&$string) { 
  184. if (csstidy::is_important($string)) { 
  185. $string = csstidy::gvw_important($string) . ' !important'; } 
  186. return $string; 
  187.  
  188. /** 
  189. * Color compression function. Converts all rgb() values to #-values and uses the short-form if possible. Also replaces 4 color names by #-values. 
  190. * @param string $color 
  191. * @return string 
  192. * @version 1.1 
  193. */ 
  194. function cut_color($color) { 
  195. $replace_colors = & $GLOBALS['csstidy']['replace_colors']; 
  196.  
  197. // rgb(0, 0, 0) -> #000000 (or #000 in this case later) 
  198. if (strtolower(substr($color, 0, 4)) === 'rgb(') { 
  199. $color_tmp = substr($color, 4, strlen($color) - 5); 
  200. $color_tmp = explode(', ', $color_tmp); 
  201. for ($i = 0; $i < count($color_tmp); $i++) { 
  202. $color_tmp[$i] = trim($color_tmp[$i]); 
  203. if (substr($color_tmp[$i], -1) === '%') { 
  204. $color_tmp[$i] = round((255 * $color_tmp[$i]) / 100); 
  205. if ($color_tmp[$i] > 255) 
  206. $color_tmp[$i] = 255; 
  207. $color = '#'; 
  208. for ($i = 0; $i < 3; $i++) { 
  209. if ($color_tmp[$i] < 16) { 
  210. $color .= '0' . dechex($color_tmp[$i]); 
  211. } else { 
  212. $color .= dechex($color_tmp[$i]); 
  213.  
  214. // Fix bad color names 
  215. if (isset($replace_colors[strtolower($color)])) { 
  216. $color = $replace_colors[strtolower($color)]; 
  217.  
  218. // #aabbcc -> #abc 
  219. if (strlen($color) == 7) { 
  220. $color_temp = strtolower($color); 
  221. if ($color_temp{0} === '#' && $color_temp{1} == $color_temp{2} && $color_temp{3} == $color_temp{4} && $color_temp{5} == $color_temp{6}) { 
  222. $color = '#' . $color{1} . $color{3} . $color{5}; 
  223.  
  224. switch (strtolower($color)) { 
  225. /** color name -> hex code */ 
  226. case 'black': return '#000'; 
  227. case 'fuchsia': return '#f0f'; 
  228. case 'white': return '#fff'; 
  229. case 'yellow': return '#ff0'; 
  230.  
  231. /** hex code -> color name */ 
  232. case '#800000': return 'maroon'; 
  233. case '#ffa500': return 'orange'; 
  234. case '#808000': return 'olive'; 
  235. case '#800080': return 'purple'; 
  236. case '#008000': return 'green'; 
  237. case '#000080': return 'navy'; 
  238. case '#008080': return 'teal'; 
  239. case '#c0c0c0': return 'silver'; 
  240. case '#808080': return 'gray'; 
  241. case '#f00': return 'red'; 
  242.  
  243. return $color; 
  244.  
  245. /** 
  246. * Compresses numbers (ie. 1.0 becomes 1 or 1.100 becomes 1.1 ) 
  247. * @param string $subvalue 
  248. * @return string 
  249. * @version 1.2 
  250. */ 
  251. function compress_numbers($subvalue) { 
  252. $unit_values = & $GLOBALS['csstidy']['unit_values']; 
  253. $color_values = & $GLOBALS['csstidy']['color_values']; 
  254.  
  255. // for font:1em/1em sans-serif...; 
  256. if ($this->property === 'font') { 
  257. $temp = explode('/', $subvalue); 
  258. } else { 
  259. $temp = array($subvalue); 
  260. for ($l = 0; $l < count($temp); $l++) { 
  261. // if we are not dealing with a number at this point, do not optimise anything 
  262. $number = $this->AnalyseCssNumber($temp[$l]); 
  263. if ($number === false) { 
  264. return $subvalue; 
  265.  
  266. // Fix bad colors 
  267. if (in_array($this->property, $color_values)) { 
  268. if (strlen($temp[$l]) == 3 || strlen($temp[$l]) == 6) { 
  269. $temp[$l] = '#' . $temp[$l]; 
  270. else { 
  271. $temp[$l] = "0"; 
  272. continue; 
  273.  
  274. if (abs($number[0]) > 0) { 
  275. if ($number[1] == '' && in_array($this->property, $unit_values, true)) { 
  276. $number[1] = 'px'; 
  277. } else { 
  278. $number[1] = ''; 
  279.  
  280. $temp[$l] = $number[0] . $number[1]; 
  281.  
  282. return ((count($temp) > 1) ? $temp[0] . '/' . $temp[1] : $temp[0]); 
  283.  
  284. /** 
  285. * Checks if a given string is a CSS valid number. If it is,  
  286. * an array containing the value and unit is returned 
  287. * @param string $string 
  288. * @return array ('unit' if unit is found or '' if no unit exists, number value) or false if no number 
  289. */ 
  290. function AnalyseCssNumber($string) { 
  291. // most simple checks first 
  292. if (strlen($string) == 0 || ctype_alpha($string{0})) { 
  293. return false; 
  294.  
  295. $units = & $GLOBALS['csstidy']['units']; 
  296. $return = array(0, ''); 
  297.  
  298. $return[0] = floatval($string); 
  299. if (abs($return[0]) > 0 && abs($return[0]) < 1) { 
  300. if ($return[0] < 0) { 
  301. $return[0] = '-' . ltrim(substr($return[0], 1), '0'); 
  302. } else { 
  303. $return[0] = ltrim($return[0], '0'); 
  304.  
  305. // Look for unit and split from value if exists 
  306. foreach ($units as $unit) { 
  307. $expectUnitAt = strlen($string) - strlen($unit); 
  308. if (!($unitInString = stristr($string, $unit))) { // mb_strpos() fails with "false" 
  309. continue; 
  310. $actualPosition = strpos($string, $unitInString); 
  311. if ($expectUnitAt === $actualPosition) { 
  312. $return[1] = $unit; 
  313. $string = substr($string, 0, - strlen($unit)); 
  314. break; 
  315. if (!is_numeric($string)) { 
  316. return false; 
  317. return $return; 
  318.  
  319. /** 
  320. * Merges selectors with same properties. Example: a{color:red} b{color:red} -> a, b{color:red} 
  321. * Very basic and has at least one bug. Hopefully there is a replacement soon. 
  322. * @param array $array 
  323. * @return array 
  324. * @access public 
  325. * @version 1.2 
  326. */ 
  327. function merge_selectors(&$array) { 
  328. $css = $array; 
  329. foreach ($css as $key => $value) { 
  330. if (!isset($css[$key])) { 
  331. continue; 
  332. $newsel = ''; 
  333.  
  334. // Check if properties also exist in another selector 
  335. $keys = array(); 
  336. // PHP bug (?) without $css = $array; here 
  337. foreach ($css as $selector => $vali) { 
  338. if ($selector == $key) { 
  339. continue; 
  340.  
  341. if ($css[$key] === $vali) { 
  342. $keys[] = $selector; 
  343.  
  344. if (!empty($keys)) { 
  345. $newsel = $key; 
  346. unset($css[$key]); 
  347. foreach ($keys as $selector) { 
  348. unset($css[$selector]); 
  349. $newsel .= ', ' . $selector; 
  350. $css[$newsel] = $value; 
  351. $array = $css; 
  352.  
  353. /** 
  354. * Removes invalid selectors and their corresponding rule-sets as 
  355. * defined by 4.1.7 in REC-CSS2. This is a very rudimentary check 
  356. * and should be replaced by a full-blown parsing algorithm or 
  357. * regular expression 
  358. * @version 1.4 
  359. */ 
  360. function discard_invalid_selectors(&$array) { 
  361. $invalid = array('+' => true, '~' => true, ', ' => true, '>' => true); 
  362. foreach ($array as $selector => $decls) { 
  363. $ok = true; 
  364. $selectors = array_map('trim', explode(', ', $selector)); 
  365. foreach ($selectors as $s) { 
  366. $simple_selectors = preg_split('/\s*[+>~\s]\s*/', $s); 
  367. foreach ($simple_selectors as $ss) { 
  368. if ($ss === '') 
  369. $ok = false; 
  370. // could also check $ss for internal structure,  
  371. // but that probably would be too slow 
  372. if (!$ok) 
  373. unset($array[$selector]); 
  374.  
  375. /** 
  376. * Dissolves properties like padding:10px 10px 10px to padding-top:10px;padding-bottom:10px;... 
  377. * @param string $property 
  378. * @param string $value 
  379. * @return array 
  380. * @version 1.0 
  381. * @see merge_4value_shorthands() 
  382. */ 
  383. function dissolve_4value_shorthands($property, $value) { 
  384. $shorthands = & $GLOBALS['csstidy']['shorthands']; 
  385. if (!is_array($shorthands[$property])) { 
  386. $return[$property] = $value; 
  387. return $return; 
  388.  
  389. $important = ''; 
  390. if (csstidy::is_important($value)) { 
  391. $value = csstidy::gvw_important($value); 
  392. $important = '!important'; 
  393. $values = explode(' ', $value); 
  394.  
  395.  
  396. $return = array(); 
  397. if (count($values) == 4) { 
  398. for ($i = 0; $i < 4; $i++) { 
  399. $return[$shorthands[$property][$i]] = $values[$i] . $important; 
  400. } elseif (count($values) == 3) { 
  401. $return[$shorthands[$property][0]] = $values[0] . $important; 
  402. $return[$shorthands[$property][1]] = $values[1] . $important; 
  403. $return[$shorthands[$property][3]] = $values[1] . $important; 
  404. $return[$shorthands[$property][2]] = $values[2] . $important; 
  405. } elseif (count($values) == 2) { 
  406. for ($i = 0; $i < 4; $i++) { 
  407. $return[$shorthands[$property][$i]] = (($i % 2 != 0)) ? $values[1] . $important : $values[0] . $important; 
  408. } else { 
  409. for ($i = 0; $i < 4; $i++) { 
  410. $return[$shorthands[$property][$i]] = $values[0] . $important; 
  411.  
  412. return $return; 
  413.  
  414. /** 
  415. * Explodes a string as explode() does, however, not if $sep is escaped or within a string. 
  416. * @param string $sep seperator 
  417. * @param string $string 
  418. * @return array 
  419. * @version 1.0 
  420. */ 
  421. function explode_ws($sep, $string) { 
  422. $status = 'st'; 
  423. $to = ''; 
  424.  
  425. $output = array(); 
  426. $num = 0; 
  427. for ($i = 0, $len = strlen($string); $i < $len; $i++) { 
  428. switch ($status) { 
  429. case 'st': 
  430. if ($string{$i} == $sep && !csstidy::escaped($string, $i)) { 
  431. ++$num; 
  432. } elseif ($string{$i} === '"' || $string{$i} === '\'' || $string{$i} === '(' && !csstidy::escaped($string, $i)) { 
  433. $status = 'str'; 
  434. $to = ($string{$i} === '(') ? ')' : $string{$i}; 
  435. (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i}; 
  436. } else { 
  437. (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i}; 
  438. break; 
  439.  
  440. case 'str': 
  441. if ($string{$i} == $to && !csstidy::escaped($string, $i)) { 
  442. $status = 'st'; 
  443. (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i}; 
  444. break; 
  445.  
  446. if (isset($output[0])) { 
  447. return $output; 
  448. } else { 
  449. return array($output); 
  450.  
  451. /** 
  452. * Merges Shorthand properties again, the opposite of dissolve_4value_shorthands() 
  453. * @param array $array 
  454. * @return array 
  455. * @version 1.2 
  456. * @see dissolve_4value_shorthands() 
  457. */ 
  458. function merge_4value_shorthands($array) { 
  459. $return = $array; 
  460. $shorthands = & $GLOBALS['csstidy']['shorthands']; 
  461.  
  462. foreach ($shorthands as $key => $value) { 
  463. if (isset($array[$value[0]]) && isset($array[$value[1]]) 
  464. && isset($array[$value[2]]) && isset($array[$value[3]]) && $value !== 0) { 
  465. $return[$key] = ''; 
  466.  
  467. $important = ''; 
  468. for ($i = 0; $i < 4; $i++) { 
  469. $val = $array[$value[$i]]; 
  470. if (csstidy::is_important($val)) { 
  471. $important = '!important'; 
  472. $return[$key] .= csstidy::gvw_important($val) . ' '; 
  473. } else { 
  474. $return[$key] .= $val . ' '; 
  475. unset($return[$value[$i]]); 
  476. $return[$key] = csstidy_optimise::shorthand(trim($return[$key] . $important)); 
  477. return $return; 
  478.  
  479. /** 
  480. * Dissolve background property 
  481. * @param string $str_value 
  482. * @return array 
  483. * @version 1.0 
  484. * @see merge_bg() 
  485. * @todo full CSS 3 compliance 
  486. */ 
  487. function dissolve_short_bg($str_value) { 
  488. // don't try to explose background gradient ! 
  489. if (stripos($str_value, "gradient(")!==FALSE) 
  490. return array('background'=>$str_value); 
  491.  
  492. $background_prop_default = & $GLOBALS['csstidy']['background_prop_default']; 
  493. $repeat = array('repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'space'); 
  494. $attachment = array('scroll', 'fixed', 'local'); 
  495. $clip = array('border', 'padding'); 
  496. $origin = array('border', 'padding', 'content'); 
  497. $pos = array('top', 'center', 'bottom', 'left', 'right'); 
  498. $important = ''; 
  499. $return = array('background-image' => null, 'background-size' => null, 'background-repeat' => null, 'background-position' => null, 'background-attachment' => null, 'background-clip' => null, 'background-origin' => null, 'background-color' => null); 
  500.  
  501. if (csstidy::is_important($str_value)) { 
  502. $important = ' !important'; 
  503. $str_value = csstidy::gvw_important($str_value); 
  504.  
  505. $str_value = csstidy_optimise::explode_ws(', ', $str_value); 
  506. for ($i = 0; $i < count($str_value); $i++) { 
  507. $have['clip'] = false; 
  508. $have['pos'] = false; 
  509. $have['color'] = false; 
  510. $have['bg'] = false; 
  511.  
  512. if (is_array($str_value[$i])) { 
  513. $str_value[$i] = $str_value[$i][0]; 
  514. $str_value[$i] = csstidy_optimise::explode_ws(' ', trim($str_value[$i])); 
  515.  
  516. for ($j = 0; $j < count($str_value[$i]); $j++) { 
  517. if ($have['bg'] === false && (substr($str_value[$i][$j], 0, 4) === 'url(' || $str_value[$i][$j] === 'none')) { 
  518. $return['background-image'] .= $str_value[$i][$j] . ', '; 
  519. $have['bg'] = true; 
  520. } elseif (in_array($str_value[$i][$j], $repeat, true)) { 
  521. $return['background-repeat'] .= $str_value[$i][$j] . ', '; 
  522. } elseif (in_array($str_value[$i][$j], $attachment, true)) { 
  523. $return['background-attachment'] .= $str_value[$i][$j] . ', '; 
  524. } elseif (in_array($str_value[$i][$j], $clip, true) && !$have['clip']) { 
  525. $return['background-clip'] .= $str_value[$i][$j] . ', '; 
  526. $have['clip'] = true; 
  527. } elseif (in_array($str_value[$i][$j], $origin, true)) { 
  528. $return['background-origin'] .= $str_value[$i][$j] . ', '; 
  529. } elseif ($str_value[$i][$j]{0} === '(') { 
  530. $return['background-size'] .= substr($str_value[$i][$j], 1, -1) . ', '; 
  531. } elseif (in_array($str_value[$i][$j], $pos, true) || is_numeric($str_value[$i][$j]{0}) || $str_value[$i][$j]{0} === null || $str_value[$i][$j]{0} === '-' || $str_value[$i][$j]{0} === '.') { 
  532. $return['background-position'] .= $str_value[$i][$j]; 
  533. if (!$have['pos']) 
  534. $return['background-position'] .= ' '; else 
  535. $return['background-position'].= ', '; 
  536. $have['pos'] = true; 
  537. elseif (!$have['color']) { 
  538. $return['background-color'] .= $str_value[$i][$j] . ', '; 
  539. $have['color'] = true; 
  540.  
  541. foreach ($background_prop_default as $bg_prop => $default_value) { 
  542. if ($return[$bg_prop] !== null) { 
  543. $return[$bg_prop] = substr($return[$bg_prop], 0, -1) . $important; 
  544. else 
  545. $return[$bg_prop] = $default_value . $important; 
  546. return $return; 
  547.  
  548. /** 
  549. * Merges all background properties 
  550. * @param array $input_css 
  551. * @return array 
  552. * @version 1.0 
  553. * @see dissolve_short_bg() 
  554. * @todo full CSS 3 compliance 
  555. */ 
  556. function merge_bg($input_css) { 
  557. $background_prop_default = & $GLOBALS['csstidy']['background_prop_default']; 
  558. // Max number of background images. CSS3 not yet fully implemented 
  559. $number_of_values = @max(count(csstidy_optimise::explode_ws(', ', $input_css['background-image'])), count(csstidy_optimise::explode_ws(', ', $input_css['background-color'])), 1); 
  560. // Array with background images to check if BG image exists 
  561. $bg_img_array = @csstidy_optimise::explode_ws(', ', csstidy::gvw_important($input_css['background-image'])); 
  562. $new_bg_value = ''; 
  563. $important = ''; 
  564.  
  565. // if background properties is here and not empty, don't try anything 
  566. if (isset($input_css['background']) AND $input_css['background']) 
  567. return $input_css; 
  568.  
  569. for ($i = 0; $i < $number_of_values; $i++) { 
  570. foreach ($background_prop_default as $bg_property => $default_value) { 
  571. // Skip if property does not exist 
  572. if (!isset($input_css[$bg_property])) { 
  573. continue; 
  574.  
  575. $cur_value = $input_css[$bg_property]; 
  576. // skip all optimisation if gradient() somewhere 
  577. if (stripos($cur_value, "gradient(")!==FALSE) 
  578. return $input_css; 
  579.  
  580. // Skip some properties if there is no background image 
  581. if ((!isset($bg_img_array[$i]) || $bg_img_array[$i] === 'none') 
  582. && ($bg_property === 'background-size' || $bg_property === 'background-position' 
  583. || $bg_property === 'background-attachment' || $bg_property === 'background-repeat')) { 
  584. continue; 
  585.  
  586. // Remove !important 
  587. if (csstidy::is_important($cur_value)) { 
  588. $important = ' !important'; 
  589. $cur_value = csstidy::gvw_important($cur_value); 
  590.  
  591. // Do not add default values 
  592. if ($cur_value === $default_value) { 
  593. continue; 
  594.  
  595. $temp = csstidy_optimise::explode_ws(', ', $cur_value); 
  596.  
  597. if (isset($temp[$i])) { 
  598. if ($bg_property === 'background-size') { 
  599. $new_bg_value .= '(' . $temp[$i] . ') '; 
  600. } else { 
  601. $new_bg_value .= $temp[$i] . ' '; 
  602.  
  603. $new_bg_value = trim($new_bg_value); 
  604. if ($i != $number_of_values - 1) 
  605. $new_bg_value .= ', '; 
  606.  
  607. // Delete all background-properties 
  608. foreach ($background_prop_default as $bg_property => $default_value) { 
  609. unset($input_css[$bg_property]); 
  610.  
  611. // Add new background property 
  612. if ($new_bg_value !== '') 
  613. $input_css['background'] = $new_bg_value . $important; 
  614. elseif(isset ($input_css['background'])) 
  615. $input_css['background'] = 'none'; 
  616.  
  617. return $input_css; 
  618.  
  619. /** 
  620. * Dissolve font property 
  621. * @param string $str_value 
  622. * @return array 
  623. * @version 1.3 
  624. * @see merge_font() 
  625. */ 
  626. function dissolve_short_font($str_value) { 
  627. $font_prop_default = & $GLOBALS['csstidy']['font_prop_default']; 
  628. $font_weight = array('normal', 'bold', 'bolder', 'lighter', 100, 200, 300, 400, 500, 600, 700, 800, 900); 
  629. $font_variant = array('normal', 'small-caps'); 
  630. $font_style = array('normal', 'italic', 'oblique'); 
  631. $important = ''; 
  632. $return = array('font-style' => null, 'font-variant' => null, 'font-weight' => null, 'font-size' => null, 'line-height' => null, 'font-family' => null); 
  633.  
  634. if (csstidy::is_important($str_value)) { 
  635. $important = '!important'; 
  636. $str_value = csstidy::gvw_important($str_value); 
  637.  
  638. $have['style'] = false; 
  639. $have['variant'] = false; 
  640. $have['weight'] = false; 
  641. $have['size'] = false; 
  642. // Detects if font-family consists of several words w/o quotes 
  643. $multiwords = false; 
  644.  
  645. // Workaround with multiple font-family 
  646. $str_value = csstidy_optimise::explode_ws(', ', trim($str_value)); 
  647.  
  648. $str_value[0] = csstidy_optimise::explode_ws(' ', trim($str_value[0])); 
  649.  
  650. for ($j = 0; $j < count($str_value[0]); $j++) { 
  651. if ($have['weight'] === false && in_array($str_value[0][$j], $font_weight)) { 
  652. $return['font-weight'] = $str_value[0][$j]; 
  653. $have['weight'] = true; 
  654. } elseif ($have['variant'] === false && in_array($str_value[0][$j], $font_variant)) { 
  655. $return['font-variant'] = $str_value[0][$j]; 
  656. $have['variant'] = true; 
  657. } elseif ($have['style'] === false && in_array($str_value[0][$j], $font_style)) { 
  658. $return['font-style'] = $str_value[0][$j]; 
  659. $have['style'] = true; 
  660. } elseif ($have['size'] === false && (is_numeric($str_value[0][$j]{0}) || $str_value[0][$j]{0} === null || $str_value[0][$j]{0} === '.')) { 
  661. $size = csstidy_optimise::explode_ws('/', trim($str_value[0][$j])); 
  662. $return['font-size'] = $size[0]; 
  663. if (isset($size[1])) { 
  664. $return['line-height'] = $size[1]; 
  665. } else { 
  666. $return['line-height'] = ''; // don't add 'normal' ! 
  667. $have['size'] = true; 
  668. } else { 
  669. if (isset($return['font-family'])) { 
  670. $return['font-family'] .= ' ' . $str_value[0][$j]; 
  671. $multiwords = true; 
  672. } else { 
  673. $return['font-family'] = $str_value[0][$j]; 
  674. // add quotes if we have several qords in font-family 
  675. if ($multiwords !== false) { 
  676. $return['font-family'] = '"' . $return['font-family'] . '"'; 
  677. $i = 1; 
  678. while (isset($str_value[$i])) { 
  679. $return['font-family'] .= ', ' . trim($str_value[$i]); 
  680. $i++; 
  681.  
  682. // Fix for 100 and more font-size 
  683. if ($have['size'] === false && isset($return['font-weight']) && 
  684. is_numeric($return['font-weight']{0})) { 
  685. $return['font-size'] = $return['font-weight']; 
  686. unset($return['font-weight']); 
  687.  
  688. foreach ($font_prop_default as $font_prop => $default_value) { 
  689. if ($return[$font_prop] !== null) { 
  690. $return[$font_prop] = $return[$font_prop] . $important; 
  691. else 
  692. $return[$font_prop] = $default_value . $important; 
  693. return $return; 
  694.  
  695. /** 
  696. * Merges all fonts properties 
  697. * @param array $input_css 
  698. * @return array 
  699. * @version 1.3 
  700. * @see dissolve_short_font() 
  701. */ 
  702. function merge_font($input_css) { 
  703. $font_prop_default = & $GLOBALS['csstidy']['font_prop_default']; 
  704. $new_font_value = ''; 
  705. $important = ''; 
  706. // Skip if not font-family and font-size set 
  707. if (isset($input_css['font-family']) && isset($input_css['font-size'])) { 
  708. // fix several words in font-family - add quotes 
  709. if (isset($input_css['font-family'])) { 
  710. $families = explode(", ", $input_css['font-family']); 
  711. $result_families = array(); 
  712. foreach ($families as $family) { 
  713. $family = trim($family); 
  714. $len = strlen($family); 
  715. if (strpos($family, " ") && 
  716. !(($family{0} == '"' && $family{$len - 1} == '"') || 
  717. ($family{0} == "'" && $family{$len - 1} == "'"))) { 
  718. $family = '"' . $family . '"'; 
  719. $result_families[] = $family; 
  720. $input_css['font-family'] = implode(", ", $result_families); 
  721. foreach ($font_prop_default as $font_property => $default_value) { 
  722.  
  723. // Skip if property does not exist 
  724. if (!isset($input_css[$font_property])) { 
  725. continue; 
  726.  
  727. $cur_value = $input_css[$font_property]; 
  728.  
  729. // Skip if default value is used 
  730. if ($cur_value === $default_value) { 
  731. continue; 
  732.  
  733. // Remove !important 
  734. if (csstidy::is_important($cur_value)) { 
  735. $important = '!important'; 
  736. $cur_value = csstidy::gvw_important($cur_value); 
  737.  
  738. $new_font_value .= $cur_value; 
  739. // Add delimiter 
  740. $new_font_value .= ( $font_property === 'font-size' && 
  741. isset($input_css['line-height'])) ? '/' : ' '; 
  742.  
  743. $new_font_value = trim($new_font_value); 
  744.  
  745. // Delete all font-properties 
  746. foreach ($font_prop_default as $font_property => $default_value) { 
  747. if ($font_property!=='font' OR !$new_font_value) 
  748. unset($input_css[$font_property]); 
  749.  
  750. // Add new font property 
  751. if ($new_font_value !== '') { 
  752. $input_css['font'] = $new_font_value . $important; 
  753.  
  754. return $input_css; 
  755.