source: spip-zone/_core_/plugins/compresseur/lib/csstidy/class.csstidy_optimise.php @ 63080

Last change on this file since 63080 was 63080, checked in by cedric@…, 7 years ago

Remplacer l'external sur github qui marche pas bien partout par un import de la v1.4

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