Completed
Push — fix/9129-allow-analytics-js-wi... ( 41fabb...20f3a6 )
by
unknown
27:44 queued 11:52
created

custom-css/csstidy/class.csstidy_optimise.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * CSSTidy - CSS Parser and Optimiser
5
 *
6
 * CSS Optimising Class
7
 * This class optimises CSS data generated by csstidy.
8
 *
9
 * Copyright 2005, 2006, 2007 Florian Schmitz
10
 *
11
 * This file is part of CSSTidy.
12
 *
13
 *   CSSTidy is free software; you can redistribute it and/or modify
14
 *   it under the terms of the GNU Lesser General Public License as published by
15
 *   the Free Software Foundation; either version 2.1 of the License, or
16
 *   (at your option) any later version.
17
 *
18
 *   CSSTidy is distributed in the hope that it will be useful,
19
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 *   GNU Lesser General Public License for more details.
22
 *
23
 *   You should have received a copy of the GNU Lesser General Public License
24
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
 *
26
 * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
27
 * @package csstidy
28
 * @author Florian Schmitz (floele at gmail dot com) 2005-2007
29
 * @author Brett Zamir (brettz9 at yahoo dot com) 2007
30
 * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
31
 */
32
33
/**
34
 * CSS Optimising Class
35
 *
36
 * This class optimises CSS data generated by csstidy.
37
 *
38
 * @package csstidy
39
 * @author Florian Schmitz (floele at gmail dot com) 2005-2006
40
 * @version 1.0
41
 */
42
class csstidy_optimise {
43
	/**
44
	 * Constructor
45
	 * @param array $css contains the class csstidy
46
	 * @access private
47
	 * @version 1.0
48
	 */
49
	function __construct(&$css) {
50
		$this->parser = & $css;
51
		$this->css = & $css->css;
52
		$this->sub_value = & $css->sub_value;
53
		$this->at = & $css->at;
54
		$this->selector = & $css->selector;
55
		$this->property = & $css->property;
56
		$this->value = & $css->value;
0 ignored issues
show
The property value does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

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