PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.35.1
MailPoet – Newsletters, Email Marketing, and Automation v5.35.1
5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor-prefixed / cerdic / css-tidy / class.csstidy_optimise.php
mailpoet / vendor-prefixed / cerdic / css-tidy Last commit date
class.csstidy.php 3 years ago class.csstidy_optimise.php 2 years ago class.csstidy_print.php 3 years ago cssparse.css 4 years ago cssparsed.css 4 years ago data.inc.php 4 years ago index.php 3 years ago lang.inc.php 4 years ago template.tpl 4 years ago template1.tpl 4 years ago template2.tpl 4 years ago template3.tpl 4 years ago
class.csstidy_optimise.php
992 lines
1 <?php
2 namespace MailPoetVendor;
3 if (!defined('ABSPATH')) exit;
4 class csstidy_optimise
5 {
6 public $parser;
7 public $css;
8 public $sub_value;
9 public $at;
10 public $selector;
11 public $property;
12 public $value;
13 public function __construct($css)
14 {
15 $this->parser = $css;
16 $this->css =& $css->css;
17 $this->sub_value =& $css->sub_value;
18 $this->at =& $css->at;
19 $this->selector =& $css->selector;
20 $this->property =& $css->property;
21 $this->value =& $css->value;
22 }
23 public function postparse()
24 {
25 if ($this->parser->get_cfg('reverse_left_and_right') > 0) {
26 foreach ($this->css as $medium => $selectors) {
27 if (\is_array($selectors)) {
28 foreach ($selectors as $selector => $properties) {
29 $this->css[$medium][$selector] = $this->reverse_left_and_right($this->css[$medium][$selector]);
30 }
31 }
32 }
33 }
34 if ($this->parser->get_cfg('preserve_css')) {
35 return;
36 }
37 if ((int) $this->parser->get_cfg('merge_selectors') === 2) {
38 foreach ($this->css as $medium => $value) {
39 if (\is_array($value)) {
40 $this->merge_selectors($this->css[$medium]);
41 }
42 }
43 }
44 if ($this->parser->get_cfg('discard_invalid_selectors')) {
45 foreach ($this->css as $medium => $value) {
46 if (\is_array($value)) {
47 $this->discard_invalid_selectors($this->css[$medium]);
48 }
49 }
50 }
51 if ($this->parser->get_cfg('optimise_shorthands') > 0) {
52 foreach ($this->css as $medium => $value) {
53 if (\is_array($value)) {
54 foreach ($value as $selector => $value1) {
55 $this->css[$medium][$selector] = $this->merge_4value_shorthands($this->css[$medium][$selector]);
56 $this->css[$medium][$selector] = $this->merge_4value_radius_shorthands($this->css[$medium][$selector]);
57 if ($this->parser->get_cfg('optimise_shorthands') < 2) {
58 continue;
59 }
60 $this->css[$medium][$selector] = $this->merge_font($this->css[$medium][$selector]);
61 if ($this->parser->get_cfg('optimise_shorthands') < 3) {
62 continue;
63 }
64 $this->css[$medium][$selector] = $this->merge_bg($this->css[$medium][$selector]);
65 if (empty($this->css[$medium][$selector])) {
66 unset($this->css[$medium][$selector]);
67 }
68 }
69 }
70 }
71 }
72 }
73 public function value()
74 {
75 $shorthands =& $this->parser->data['csstidy']['shorthands'];
76 // optimise shorthand properties
77 if (isset($shorthands[$this->property])) {
78 $temp = $this->shorthand($this->value);
79 // FIXME - move
80 if ($temp != $this->value) {
81 $this->parser->log('Optimised shorthand notation (' . $this->property . '): Changed "' . $this->value . '" to "' . $temp . '"', 'Information');
82 }
83 $this->value = $temp;
84 }
85 // Remove whitespace at ! important
86 if ($this->value != $this->compress_important($this->value)) {
87 $this->parser->log('Optimised !important', 'Information');
88 }
89 }
90 public function shorthands()
91 {
92 $shorthands =& $this->parser->data['csstidy']['shorthands'];
93 if (!$this->parser->get_cfg('optimise_shorthands') || $this->parser->get_cfg('preserve_css')) {
94 return;
95 }
96 if ($this->property === 'font' && $this->parser->get_cfg('optimise_shorthands') > 1) {
97 $this->css[$this->at][$this->selector]['font'] = '';
98 $this->parser->merge_css_blocks($this->at, $this->selector, $this->dissolve_short_font($this->value));
99 }
100 if ($this->property === 'background' && $this->parser->get_cfg('optimise_shorthands') > 2) {
101 $this->css[$this->at][$this->selector]['background'] = '';
102 $this->parser->merge_css_blocks($this->at, $this->selector, $this->dissolve_short_bg($this->value));
103 }
104 if (isset($shorthands[$this->property])) {
105 $this->parser->merge_css_blocks($this->at, $this->selector, $this->dissolve_4value_shorthands($this->property, $this->value));
106 if (\is_array($shorthands[$this->property])) {
107 $this->css[$this->at][$this->selector][$this->property] = '';
108 }
109 }
110 }
111 public function subvalue()
112 {
113 $replace_colors =& $this->parser->data['csstidy']['replace_colors'];
114 $this->sub_value = \trim($this->sub_value);
115 if ($this->sub_value == '') {
116 // caution : '0'
117 return;
118 }
119 $important = '';
120 if ($this->parser->is_important($this->sub_value)) {
121 $important = '!important';
122 }
123 $this->sub_value = $this->parser->gvw_important($this->sub_value);
124 // Compress font-weight
125 if ($this->property === 'font-weight' && $this->parser->get_cfg('compress_font-weight')) {
126 if ($this->sub_value === 'bold') {
127 $this->sub_value = '700';
128 $this->parser->log('Optimised font-weight: Changed "bold" to "700"', 'Information');
129 } elseif ($this->sub_value === 'normal') {
130 $this->sub_value = '400';
131 $this->parser->log('Optimised font-weight: Changed "normal" to "400"', 'Information');
132 }
133 }
134 $temp = $this->compress_numbers($this->sub_value);
135 if (\strcasecmp($temp, $this->sub_value) !== 0) {
136 if (\strlen($temp) > \strlen($this->sub_value)) {
137 $this->parser->log('Fixed invalid number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning');
138 } else {
139 $this->parser->log('Optimised number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information');
140 }
141 $this->sub_value = $temp;
142 }
143 if ($this->parser->get_cfg('compress_colors')) {
144 $temp = $this->cut_color($this->sub_value);
145 if ($temp !== $this->sub_value) {
146 if (isset($replace_colors[$this->sub_value])) {
147 $this->parser->log('Fixed invalid color name: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning');
148 } else {
149 $this->parser->log('Optimised color: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information');
150 }
151 $this->sub_value = $temp;
152 }
153 }
154 $this->sub_value .= $important;
155 }
156 public function shorthand($value)
157 {
158 $important = '';
159 if ($this->parser->is_important($value)) {
160 $values = $this->parser->gvw_important($value);
161 $important = '!important';
162 } else {
163 $values = $value;
164 }
165 $values = \explode(' ', $values);
166 switch (\count($values)) {
167 case 4:
168 if ($values[0] == $values[1] && $values[0] == $values[2] && $values[0] == $values[3]) {
169 return $values[0] . $important;
170 } elseif ($values[1] == $values[3] && $values[0] == $values[2]) {
171 return $values[0] . ' ' . $values[1] . $important;
172 } elseif ($values[1] == $values[3]) {
173 return $values[0] . ' ' . $values[1] . ' ' . $values[2] . $important;
174 }
175 break;
176 case 3:
177 if ($values[0] == $values[1] && $values[0] == $values[2]) {
178 return $values[0] . $important;
179 } elseif ($values[0] == $values[2]) {
180 return $values[0] . ' ' . $values[1] . $important;
181 }
182 break;
183 case 2:
184 if ($values[0] == $values[1]) {
185 return $values[0] . $important;
186 }
187 break;
188 }
189 return $value;
190 }
191 public function compress_important(&$string)
192 {
193 if ($this->parser->is_important($string)) {
194 $important = $this->parser->get_cfg('space_before_important') ? ' !important' : '!important';
195 $string = $this->parser->gvw_important($string) . $important;
196 }
197 return $string;
198 }
199 public function cut_color($color)
200 {
201 $replace_colors =& $this->parser->data['csstidy']['replace_colors'];
202 // if it's a string, don't touch !
203 if (\strncmp($color, "'", 1) == 0 || \strncmp($color, '"', 1) == 0) {
204 return $color;
205 }
206 if (\strpos($color, '(') !== \false && (\strncasecmp($color, 'rgb(', 4) !== 0 and \strncasecmp($color, 'rgba(', 5) !== 0)) {
207 // on ne touche pas aux couleurs dans les expression ms, c'est trop sensible
208 if (\stripos($color, 'progid:') !== \false) {
209 return $color;
210 }
211 \preg_match_all(",rgba?\\([^)]+\\),i", $color, $matches, \PREG_SET_ORDER);
212 if (\count($matches)) {
213 foreach ($matches as $m) {
214 $color = \str_replace($m[0], $this->cut_color($m[0]), $color);
215 }
216 }
217 \preg_match_all(",#[0-9a-f]{6}(?=[^0-9a-f]),i", $color, $matches, \PREG_SET_ORDER);
218 if (\count($matches)) {
219 foreach ($matches as $m) {
220 $color = \str_replace($m[0], $this->cut_color($m[0]), $color);
221 }
222 }
223 return $color;
224 }
225 // rgb(0,0,0) -> #000000 (or #000 in this case later)
226 if (\strncasecmp($color, 'rgb(', 4) == 0 and \strpos($color, '(', 4) === \false or \strncasecmp($color, 'rgba(', 5) == 0 and \strpos($color, '(', 5) === \false) {
227 $color_tmp = \explode('(', $color, 2);
228 $color_tmp = \rtrim(\end($color_tmp), ')');
229 if (\strpos($color_tmp, '/') !== \false) {
230 $color_tmp = \explode('/', $color_tmp, 2);
231 $color_parts = \explode(' ', \trim(\reset($color_tmp)), 3);
232 while (\count($color_parts) < 3) {
233 $color_parts[] = 0;
234 }
235 $color_parts[] = \end($color_tmp);
236 } else {
237 $color_parts = \explode(',', $color_tmp, 4);
238 }
239 for ($i = 0; $i < \count($color_parts); $i++) {
240 $color_parts[$i] = \trim($color_parts[$i]);
241 if (\substr($color_parts[$i], -1) === '%') {
242 $color_parts[$i] = \round(255 * \intval($color_parts[$i]) / 100);
243 } elseif ($i > 2) {
244 // 4th argument is alpga layer between 0 and 1 (if not %)
245 $color_parts[$i] = \round(255 * \floatval($color_parts[$i]));
246 }
247 $color_parts[$i] = \intval($color_parts[$i]);
248 if ($color_parts[$i] > 255) {
249 $color_parts[$i] = 255;
250 }
251 }
252 $color = '#';
253 // 3 or 4 parts depending on alpha layer
254 $nb = \min(\max(\count($color_parts), 3), 4);
255 for ($i = 0; $i < $nb; $i++) {
256 if (!isset($color_parts[$i])) {
257 $color_parts[$i] = 0;
258 }
259 if ($color_parts[$i] < 16) {
260 $color .= '0' . \dechex($color_parts[$i]);
261 } else {
262 $color .= \dechex($color_parts[$i]);
263 }
264 }
265 }
266 // Fix bad color names
267 if (isset($replace_colors[\strtolower($color)])) {
268 $color = $replace_colors[\strtolower($color)];
269 }
270 // #aabbcc -> #abc
271 if (\strlen($color) == 7) {
272 $color_temp = \strtolower($color);
273 if ($color_temp[0] === '#' && $color_temp[1] == $color_temp[2] && $color_temp[3] == $color_temp[4] && $color_temp[5] == $color_temp[6]) {
274 $color = '#' . $color[1] . $color[3] . $color[5];
275 }
276 } elseif (\strlen($color) == 9) {
277 $color_temp = \strtolower($color);
278 if ($color_temp[0] === '#' && $color_temp[1] == $color_temp[2] && $color_temp[3] == $color_temp[4] && $color_temp[5] == $color_temp[6] && $color_temp[7] == $color_temp[8]) {
279 $color = '#' . $color[1] . $color[3] . $color[5] . $color[7];
280 }
281 }
282 switch (\strtolower($color)) {
283 case 'black':
284 return '#000';
285 case 'fuchsia':
286 return '#f0f';
287 case 'white':
288 return '#fff';
289 case 'yellow':
290 return '#ff0';
291 case '#800000':
292 return 'maroon';
293 case '#ffa500':
294 return 'orange';
295 case '#808000':
296 return 'olive';
297 case '#800080':
298 return 'purple';
299 case '#008000':
300 return 'green';
301 case '#000080':
302 return 'navy';
303 case '#008080':
304 return 'teal';
305 case '#c0c0c0':
306 return 'silver';
307 case '#808080':
308 return 'gray';
309 case '#f00':
310 return 'red';
311 }
312 return $color;
313 }
314 public function compress_numbers($subvalue)
315 {
316 $unit_values =& $this->parser->data['csstidy']['unit_values'];
317 $color_values =& $this->parser->data['csstidy']['color_values'];
318 // for font:1em/1em sans-serif...;
319 if ($this->property === 'font') {
320 $temp = \explode('/', $subvalue);
321 } else {
322 $temp = array($subvalue);
323 }
324 for ($l = 0; $l < \count($temp); $l++) {
325 // if we are not dealing with a number at this point, do not optimise anything
326 $number = $this->AnalyseCssNumber($temp[$l]);
327 if ($number === \false) {
328 return $subvalue;
329 }
330 // Fix bad colors
331 if (\in_array($this->property, $color_values)) {
332 $temp[$l] = '#' . $temp[$l];
333 continue;
334 }
335 if (\abs($number[0]) > 0) {
336 if ($number[1] == '' && \in_array($this->property, $unit_values, \true)) {
337 $number[1] = 'px';
338 }
339 } elseif ($number[1] != 's' && $number[1] != 'ms') {
340 $number[1] = '';
341 }
342 $temp[$l] = $number[0] . $number[1];
343 }
344 return \count($temp) > 1 ? $temp[0] . '/' . $temp[1] : $temp[0];
345 }
346 public function AnalyseCssNumber($string)
347 {
348 // most simple checks first
349 if (\strlen($string) == 0 || \ctype_alpha($string[0])) {
350 return \false;
351 }
352 $units =& $this->parser->data['csstidy']['units'];
353 $return = array(0, '');
354 $return[0] = \floatval($string);
355 if (\abs($return[0]) > 0 && \abs($return[0]) < 1) {
356 if ($return[0] < 0) {
357 $return[0] = '-' . \ltrim(\substr($return[0], 1), '0');
358 } else {
359 $return[0] = \ltrim($return[0], '0');
360 }
361 }
362 // Look for unit and split from value if exists
363 foreach ($units as $unit) {
364 $expectUnitAt = \strlen($string) - \strlen($unit);
365 if (!($unitInString = \stristr($string, $unit))) {
366 // mb_strpos() fails with "false"
367 continue;
368 }
369 $actualPosition = \strpos($string, $unitInString);
370 if ($expectUnitAt === $actualPosition) {
371 $return[1] = $unit;
372 $string = \substr($string, 0, -\strlen($unit));
373 break;
374 }
375 }
376 if (!\is_numeric($string)) {
377 return \false;
378 }
379 return $return;
380 }
381 public function merge_selectors(&$array)
382 {
383 $css = $array;
384 foreach ($css as $key => $value) {
385 if (!isset($css[$key])) {
386 continue;
387 }
388 $newsel = '';
389 // Check if properties also exist in another selector
390 $keys = array();
391 // PHP bug (?) without $css = $array; here
392 foreach ($css as $selector => $vali) {
393 if ($selector == $key) {
394 continue;
395 }
396 if ($css[$key] === $vali) {
397 $keys[] = $selector;
398 }
399 }
400 if (!empty($keys)) {
401 $newsel = $key;
402 unset($css[$key]);
403 foreach ($keys as $selector) {
404 unset($css[$selector]);
405 $newsel .= ',' . $selector;
406 }
407 $css[$newsel] = $value;
408 }
409 }
410 $array = $css;
411 }
412 public function discard_invalid_selectors(&$array)
413 {
414 $invalid = array('+' => \true, '~' => \true, ',' => \true, '>' => \true);
415 foreach ($array as $selector => $decls) {
416 $ok = \true;
417 $selectors = \array_map('trim', \explode(',', $selector));
418 foreach ($selectors as $s) {
419 $simple_selectors = \preg_split('/\\s*[+>~\\s]\\s*/', $s);
420 foreach ($simple_selectors as $ss) {
421 if ($ss === '') {
422 $ok = \false;
423 }
424 // could also check $ss for internal structure,
425 // but that probably would be too slow
426 }
427 }
428 if (!$ok) {
429 unset($array[$selector]);
430 }
431 }
432 }
433 public function dissolve_4value_shorthands($property, $value, $shorthands = null)
434 {
435 if (\is_null($shorthands)) {
436 $shorthands =& $this->parser->data['csstidy']['shorthands'];
437 }
438 if (!\is_array($shorthands[$property])) {
439 $return[$property] = $value;
440 return $return;
441 }
442 $important = '';
443 if ($this->parser->is_important($value)) {
444 $value = $this->parser->gvw_important($value);
445 $important = '!important';
446 }
447 $values = \explode(' ', $value);
448 $return = array();
449 if (\count($values) == 4) {
450 for ($i = 0; $i < 4; $i++) {
451 $return[$shorthands[$property][$i]] = $values[$i] . $important;
452 }
453 } elseif (\count($values) == 3) {
454 $return[$shorthands[$property][0]] = $values[0] . $important;
455 $return[$shorthands[$property][1]] = $values[1] . $important;
456 $return[$shorthands[$property][3]] = $values[1] . $important;
457 $return[$shorthands[$property][2]] = $values[2] . $important;
458 } elseif (\count($values) == 2) {
459 for ($i = 0; $i < 4; $i++) {
460 $return[$shorthands[$property][$i]] = $i % 2 != 0 ? $values[1] . $important : $values[0] . $important;
461 }
462 } else {
463 for ($i = 0; $i < 4; $i++) {
464 $return[$shorthands[$property][$i]] = $values[0] . $important;
465 }
466 }
467 return $return;
468 }
469 public function dissolve_4value_radius_shorthands($property, $value)
470 {
471 $shorthands =& $this->parser->data['csstidy']['radius_shorthands'];
472 if (!\is_array($shorthands[$property])) {
473 $return[$property] = $value;
474 return $return;
475 }
476 if (\strpos($value, '/') !== \false) {
477 $values = $this->explode_ws('/', $value);
478 if (\count($values) == 2) {
479 $r[0] = $this->dissolve_4value_shorthands($property, \trim($values[0]), $shorthands);
480 $r[1] = $this->dissolve_4value_shorthands($property, \trim($values[1]), $shorthands);
481 $return = array();
482 foreach ($r[0] as $p => $v) {
483 $return[$p] = $v;
484 if ($r[1][$p] !== $v) {
485 $return[$p] .= ' ' . $r[1][$p];
486 }
487 }
488 return $return;
489 }
490 }
491 $return = $this->dissolve_4value_shorthands($property, $value, $shorthands);
492 return $return;
493 }
494 public function explode_ws($sep, $string, $explode_in_parenthesis = \false)
495 {
496 $status = 'st';
497 $to = '';
498 $output = array(0 => '');
499 $num = 0;
500 for ($i = 0, $len = \strlen($string); $i < $len; $i++) {
501 switch ($status) {
502 case 'st':
503 if ($string[$i] == $sep && !$this->parser->escaped($string, $i)) {
504 ++$num;
505 } elseif ($string[$i] === '"' || $string[$i] === '\'' || !$explode_in_parenthesis && $string[$i] === '(' && !$this->parser->escaped($string, $i)) {
506 $status = 'str';
507 $to = $string[$i] === '(' ? ')' : $string[$i];
508 isset($output[$num]) ? $output[$num] .= $string[$i] : ($output[$num] = $string[$i]);
509 } else {
510 isset($output[$num]) ? $output[$num] .= $string[$i] : ($output[$num] = $string[$i]);
511 }
512 break;
513 case 'str':
514 if ($string[$i] == $to && !$this->parser->escaped($string, $i)) {
515 $status = 'st';
516 }
517 isset($output[$num]) ? $output[$num] .= $string[$i] : ($output[$num] = $string[$i]);
518 break;
519 }
520 }
521 return $output;
522 }
523 public function merge_4value_shorthands($array, $shorthands = null)
524 {
525 $return = $array;
526 if (\is_null($shorthands)) {
527 $shorthands =& $this->parser->data['csstidy']['shorthands'];
528 }
529 foreach ($shorthands as $key => $value) {
530 if ($value !== 0 && isset($array[$value[0]]) && isset($array[$value[1]]) && isset($array[$value[2]]) && isset($array[$value[3]])) {
531 $return[$key] = '';
532 $important = '';
533 for ($i = 0; $i < 4; $i++) {
534 $val = $array[$value[$i]];
535 if ($this->parser->is_important($val)) {
536 $important = '!important';
537 $return[$key] .= $this->parser->gvw_important($val) . ' ';
538 } else {
539 $return[$key] .= $val . ' ';
540 }
541 unset($return[$value[$i]]);
542 }
543 $return[$key] = $this->shorthand(\trim($return[$key] . $important));
544 }
545 }
546 return $return;
547 }
548 public function merge_4value_radius_shorthands($array)
549 {
550 $return = $array;
551 $shorthands =& $this->parser->data['csstidy']['radius_shorthands'];
552 foreach ($shorthands as $key => $value) {
553 if (isset($array[$value[0]]) && isset($array[$value[1]]) && isset($array[$value[2]]) && isset($array[$value[3]]) && $value !== 0) {
554 $return[$key] = '';
555 $a = array();
556 for ($i = 0; $i < 4; $i++) {
557 $v = $this->explode_ws(' ', \trim($array[$value[$i]]));
558 $a[0][$value[$i]] = \reset($v);
559 $a[1][$value[$i]] = \end($v);
560 }
561 $r = array();
562 $r[0] = $this->merge_4value_shorthands($a[0], $shorthands);
563 $r[1] = $this->merge_4value_shorthands($a[1], $shorthands);
564 if (isset($r[0][$key]) and isset($r[1][$key])) {
565 $return[$key] = $r[0][$key];
566 if ($r[1][$key] !== $r[0][$key]) {
567 $return[$key] .= ' / ' . $r[1][$key];
568 }
569 for ($i = 0; $i < 4; $i++) {
570 unset($return[$value[$i]]);
571 }
572 }
573 }
574 }
575 return $return;
576 }
577 public function dissolve_short_bg($str_value)
578 {
579 // don't try to explose background gradient !
580 if (\stripos($str_value, 'gradient(') !== \false) {
581 return array('background' => $str_value);
582 }
583 $background_prop_default =& $this->parser->data['csstidy']['background_prop_default'];
584 $repeat = array('repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'space');
585 $attachment = array('scroll', 'fixed', 'local');
586 $clip = array('border', 'padding');
587 $origin = array('border', 'padding', 'content');
588 $pos = array('top', 'center', 'bottom', 'left', 'right');
589 $important = '';
590 $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);
591 if ($this->parser->is_important($str_value)) {
592 $important = ' !important';
593 $str_value = $this->parser->gvw_important($str_value);
594 }
595 $str_value = $this->explode_ws(',', $str_value);
596 for ($i = 0; $i < \count($str_value); $i++) {
597 $have['clip'] = \false;
598 $have['pos'] = \false;
599 $have['color'] = \false;
600 $have['bg'] = \false;
601 if (\is_array($str_value[$i])) {
602 $str_value[$i] = $str_value[$i][0];
603 }
604 $str_value[$i] = $this->explode_ws(' ', \trim($str_value[$i]));
605 for ($j = 0; $j < \count($str_value[$i]); $j++) {
606 if ($have['bg'] === \false && (\substr($str_value[$i][$j], 0, 4) === 'url(' || $str_value[$i][$j] === 'none')) {
607 $return['background-image'] .= $str_value[$i][$j] . ',';
608 $have['bg'] = \true;
609 } elseif (\in_array($str_value[$i][$j], $repeat, \true)) {
610 $return['background-repeat'] .= $str_value[$i][$j] . ',';
611 } elseif (\in_array($str_value[$i][$j], $attachment, \true)) {
612 $return['background-attachment'] .= $str_value[$i][$j] . ',';
613 } elseif (\in_array($str_value[$i][$j], $clip, \true) && !$have['clip']) {
614 $return['background-clip'] .= $str_value[$i][$j] . ',';
615 $have['clip'] = \true;
616 } elseif (\in_array($str_value[$i][$j], $origin, \true)) {
617 $return['background-origin'] .= $str_value[$i][$j] . ',';
618 } elseif ($str_value[$i][$j][0] === '(') {
619 $return['background-size'] .= \substr($str_value[$i][$j], 1, -1) . ',';
620 } 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] === '.') {
621 $return['background-position'] .= $str_value[$i][$j];
622 if (!$have['pos']) {
623 $return['background-position'] .= ' ';
624 } else {
625 $return['background-position'] .= ',';
626 }
627 $have['pos'] = \true;
628 } elseif (!$have['color']) {
629 $return['background-color'] .= $str_value[$i][$j] . ',';
630 $have['color'] = \true;
631 }
632 }
633 }
634 foreach ($background_prop_default as $bg_prop => $default_value) {
635 if ($return[$bg_prop] !== null) {
636 $return[$bg_prop] = \substr($return[$bg_prop], 0, -1) . $important;
637 } else {
638 $return[$bg_prop] = $default_value . $important;
639 }
640 }
641 return $return;
642 }
643 public function merge_bg($input_css)
644 {
645 $background_prop_default =& $this->parser->data['csstidy']['background_prop_default'];
646 // Max number of background images. CSS3 not yet fully implemented
647 $number_of_values = @\max(\count($this->explode_ws(',', $input_css['background-image'])), \count($this->explode_ws(',', $input_css['background-color'])), 1);
648 // Array with background images to check if BG image exists
649 $bg_img_array = @$this->explode_ws(',', $this->parser->gvw_important($input_css['background-image']));
650 $new_bg_value = '';
651 $important = '';
652 // if background properties is here and not empty, don't try anything
653 if (isset($input_css['background']) && $input_css['background']) {
654 return $input_css;
655 }
656 for ($i = 0; $i < $number_of_values; $i++) {
657 foreach ($background_prop_default as $bg_property => $default_value) {
658 // Skip if property does not exist
659 if (!isset($input_css[$bg_property])) {
660 continue;
661 }
662 $cur_value = $input_css[$bg_property];
663 // skip all optimisation if gradient() somewhere
664 if (\stripos($cur_value, 'gradient(') !== \false) {
665 return $input_css;
666 }
667 // Skip some properties if there is no background image
668 if ((!isset($bg_img_array[$i]) || $bg_img_array[$i] === 'none') && ($bg_property === 'background-size' || $bg_property === 'background-position' || $bg_property === 'background-attachment' || $bg_property === 'background-repeat')) {
669 continue;
670 }
671 // Remove !important
672 if ($this->parser->is_important($cur_value)) {
673 $important = ' !important';
674 $cur_value = $this->parser->gvw_important($cur_value);
675 }
676 // Do not add default values
677 if ($cur_value === $default_value) {
678 continue;
679 }
680 $temp = $this->explode_ws(',', $cur_value);
681 if (isset($temp[$i])) {
682 if ($bg_property === 'background-size') {
683 $new_bg_value .= '(' . $temp[$i] . ') ';
684 } else {
685 $new_bg_value .= $temp[$i] . ' ';
686 }
687 }
688 }
689 $new_bg_value = \trim($new_bg_value);
690 if ($i != $number_of_values - 1) {
691 $new_bg_value .= ',';
692 }
693 }
694 // Delete all background-properties
695 foreach ($background_prop_default as $bg_property => $default_value) {
696 unset($input_css[$bg_property]);
697 }
698 // Add new background property
699 if ($new_bg_value !== '') {
700 $input_css['background'] = $new_bg_value . $important;
701 } elseif (isset($input_css['background'])) {
702 $input_css['background'] = 'none';
703 }
704 return $input_css;
705 }
706 public function dissolve_short_font($str_value)
707 {
708 $font_prop_default =& $this->parser->data['csstidy']['font_prop_default'];
709 $font_weight = array('normal', 'bold', 'bolder', 'lighter', 100, 200, 300, 400, 500, 600, 700, 800, 900);
710 $font_variant = array('normal', 'small-caps');
711 $font_style = array('normal', 'italic', 'oblique');
712 $important = '';
713 $return = array('font-style' => null, 'font-variant' => null, 'font-weight' => null, 'font-size' => null, 'line-height' => null, 'font-family' => null);
714 if ($this->parser->is_important($str_value)) {
715 $important = '!important';
716 $str_value = $this->parser->gvw_important($str_value);
717 }
718 $have['style'] = \false;
719 $have['variant'] = \false;
720 $have['weight'] = \false;
721 $have['size'] = \false;
722 // Detects if font-family consists of several words w/o quotes
723 $multiwords = \false;
724 // Workaround with multiple font-family
725 $str_value = $this->explode_ws(',', \trim($str_value));
726 $str_value[0] = $this->explode_ws(' ', \trim($str_value[0]));
727 for ($j = 0; $j < \count($str_value[0]); $j++) {
728 if ($have['weight'] === \false && \in_array($str_value[0][$j], $font_weight)) {
729 $return['font-weight'] = $str_value[0][$j];
730 $have['weight'] = \true;
731 } elseif ($have['variant'] === \false && \in_array($str_value[0][$j], $font_variant)) {
732 $return['font-variant'] = $str_value[0][$j];
733 $have['variant'] = \true;
734 } elseif ($have['style'] === \false && \in_array($str_value[0][$j], $font_style)) {
735 $return['font-style'] = $str_value[0][$j];
736 $have['style'] = \true;
737 } elseif ($have['size'] === \false && (\is_numeric($str_value[0][$j][0]) || $str_value[0][$j][0] === null || $str_value[0][$j][0] === '.')) {
738 $size = $this->explode_ws('/', \trim($str_value[0][$j]));
739 $return['font-size'] = $size[0];
740 if (isset($size[1])) {
741 $return['line-height'] = $size[1];
742 } else {
743 $return['line-height'] = '';
744 // don't add 'normal' !
745 }
746 $have['size'] = \true;
747 } else {
748 if (isset($return['font-family'])) {
749 $return['font-family'] .= ' ' . $str_value[0][$j];
750 $multiwords = \true;
751 } else {
752 $return['font-family'] = $str_value[0][$j];
753 }
754 }
755 }
756 // add quotes if we have several qords in font-family
757 if ($multiwords !== \false) {
758 $return['font-family'] = '"' . $return['font-family'] . '"';
759 }
760 $i = 1;
761 while (isset($str_value[$i])) {
762 $return['font-family'] .= ',' . \trim($str_value[$i]);
763 $i++;
764 }
765 // Fix for 100 and more font-size
766 if ($have['size'] === \false && isset($return['font-weight']) && \is_numeric($return['font-weight'][0])) {
767 $return['font-size'] = $return['font-weight'];
768 unset($return['font-weight']);
769 }
770 foreach ($font_prop_default as $font_prop => $default_value) {
771 if ($return[$font_prop] !== null) {
772 $return[$font_prop] = $return[$font_prop] . $important;
773 } else {
774 $return[$font_prop] = $default_value . $important;
775 }
776 }
777 return $return;
778 }
779 public function merge_font($input_css)
780 {
781 $font_prop_default =& $this->parser->data['csstidy']['font_prop_default'];
782 $new_font_value = '';
783 $important = '';
784 // Skip if not font-family and font-size set
785 if (isset($input_css['font-family']) && isset($input_css['font-size']) && $input_css['font-family'] != 'inherit') {
786 // fix several words in font-family - add quotes
787 if (isset($input_css['font-family'])) {
788 $families = \explode(',', $input_css['font-family']);
789 $result_families = array();
790 foreach ($families as $family) {
791 $family = \trim($family);
792 $len = \strlen($family);
793 if (\strpos($family, ' ') && !($family[0] === '"' && $family[$len - 1] === '"' || $family[0] === "'" && $family[$len - 1] === "'")) {
794 $family = '"' . $family . '"';
795 }
796 $result_families[] = $family;
797 }
798 $input_css['font-family'] = \implode(',', $result_families);
799 }
800 foreach ($font_prop_default as $font_property => $default_value) {
801 // Skip if property does not exist
802 if (!isset($input_css[$font_property])) {
803 continue;
804 }
805 $cur_value = $input_css[$font_property];
806 // Skip if default value is used
807 if ($cur_value === $default_value) {
808 continue;
809 }
810 // Remove !important
811 if ($this->parser->is_important($cur_value)) {
812 $important = '!important';
813 $cur_value = $this->parser->gvw_important($cur_value);
814 }
815 $new_font_value .= $cur_value;
816 // Add delimiter
817 $new_font_value .= $font_property === 'font-size' && isset($input_css['line-height']) ? '/' : ' ';
818 }
819 $new_font_value = \trim($new_font_value);
820 // Delete all font-properties
821 foreach ($font_prop_default as $font_property => $default_value) {
822 if ($font_property !== 'font' || !$new_font_value) {
823 unset($input_css[$font_property]);
824 }
825 }
826 // Add new font property
827 if ($new_font_value !== '') {
828 $input_css['font'] = $new_font_value . $important;
829 }
830 }
831 return $input_css;
832 }
833 public function reverse_left_and_right($array)
834 {
835 $return = array();
836 // change left <-> right in properties name and values
837 foreach ($array as $propertie => $value) {
838 if (\method_exists($this, $m = 'reverse_left_and_right_' . \str_replace('-', '_', \trim($propertie)))) {
839 $value = $this->{$m}($value);
840 }
841 // simple replacement for properties
842 $propertie = \str_ireplace(array('left', 'right', "\x01"), array("\x01", 'left', 'right'), $propertie);
843 // be careful for values, not modifying protected or quoted valued
844 foreach (array('left' => "\x01", 'right' => 'left', "\x01" => 'right') as $v => $r) {
845 if (\strpos($value, $v) !== \false) {
846 // attraper les left et right separes du reste (pas au milieu d'un mot)
847 if (\in_array($v, array('left', 'right'))) {
848 $value = \preg_replace(",\\b{$v}\\b,", "\x00", $value);
849 } else {
850 $value = \str_replace($v, "\x00", $value);
851 }
852 $value = $this->explode_ws("\x00", $value . ' ', \true);
853 $value = \rtrim(\implode($r, $value));
854 $value = \str_replace("\x00", $v, $value);
855 }
856 }
857 $return[$propertie] = $value;
858 }
859 return $return;
860 }
861 public function reverse_left_and_right_4value_shorthands($property, $value)
862 {
863 $shorthands =& $this->parser->data['csstidy']['shorthands'];
864 if (isset($shorthands[$property])) {
865 $property_right = $shorthands[$property][1];
866 $property_left = $shorthands[$property][3];
867 $v = $this->dissolve_4value_shorthands($property, $value);
868 if ($v[$property_left] !== $v[$property_right]) {
869 $r = $v[$property_right];
870 $v[$property_right] = $v[$property_left];
871 $v[$property_left] = $r;
872 $v = $this->merge_4value_shorthands($v);
873 if (isset($v[$property])) {
874 return $v[$property];
875 }
876 }
877 }
878 return $value;
879 }
880 public function reverse_left_and_right_4value_radius_shorthands($property, $value)
881 {
882 $shorthands =& $this->parser->data['csstidy']['radius_shorthands'];
883 if (isset($shorthands[$property])) {
884 $v = $this->dissolve_4value_radius_shorthands($property, $value);
885 if ($v[$shorthands[$property][0]] !== $v[$shorthands[$property][1]] or $v[$shorthands[$property][2]] !== $v[$shorthands[$property][3]]) {
886 $r = array($shorthands[$property][0] => $v[$shorthands[$property][1]], $shorthands[$property][1] => $v[$shorthands[$property][0]], $shorthands[$property][2] => $v[$shorthands[$property][3]], $shorthands[$property][3] => $v[$shorthands[$property][2]]);
887 $v = $this->merge_4value_radius_shorthands($r);
888 if (isset($v[$property])) {
889 return $v[$property];
890 }
891 }
892 }
893 return $value;
894 }
895 public function reverse_left_and_right_margin($value)
896 {
897 return $this->reverse_left_and_right_4value_shorthands('margin', $value);
898 }
899 public function reverse_left_and_right_padding($value)
900 {
901 return $this->reverse_left_and_right_4value_shorthands('padding', $value);
902 }
903 public function reverse_left_and_right_border_color($value)
904 {
905 return $this->reverse_left_and_right_4value_shorthands('border-color', $value);
906 }
907 public function reverse_left_and_right_border_style($value)
908 {
909 return $this->reverse_left_and_right_4value_shorthands('border-style', $value);
910 }
911 public function reverse_left_and_right_border_width($value)
912 {
913 return $this->reverse_left_and_right_4value_shorthands('border-width', $value);
914 }
915 public function reverse_left_and_right_border_radius($value)
916 {
917 return $this->reverse_left_and_right_4value_radius_shorthands('border-radius', $value);
918 }
919 public function reverse_left_and_right__moz_border_radius($value)
920 {
921 return $this->reverse_left_and_right_4value_radius_shorthands('border-radius', $value);
922 }
923 public function reverse_left_and_right__webkit_border_radius($value)
924 {
925 return $this->reverse_left_and_right_4value_radius_shorthands('border-radius', $value);
926 }
927 public function reverse_left_and_right_background($value)
928 {
929 $values = $this->dissolve_short_bg($value);
930 if (isset($values['background-position']) and $values['background-position']) {
931 $v = $this->reverse_left_and_right_background_position($values['background-position']);
932 if ($v !== $values['background-position']) {
933 if ($value == $values['background-position']) {
934 return $v;
935 } else {
936 $values['background-position'] = $v;
937 $x = $this->merge_bg($values);
938 if (isset($x['background'])) {
939 return $x['background'];
940 }
941 }
942 }
943 }
944 return $value;
945 }
946 public function reverse_left_and_right_background_position_x($value)
947 {
948 return $this->reverse_left_and_right_background_position($value);
949 }
950 public function reverse_left_and_right_background_position($value)
951 {
952 // multiple background case
953 if (\strpos($value, ',') !== \false) {
954 $values = $this->explode_ws(',', $value);
955 if (\count($values) > 1) {
956 foreach ($values as $k => $v) {
957 $values[$k] = $this->reverse_left_and_right_background_position($v);
958 }
959 return \implode(',', $values);
960 }
961 }
962 // if no explicit left or right value
963 if (\stripos($value, 'left') === \false and \stripos($value, 'right') === \false) {
964 $values = $this->explode_ws(' ', \trim($value));
965 $values = \array_map('trim', $values);
966 $values = \array_filter($values, function ($v) {
967 return \strlen($v);
968 });
969 $values = \array_values($values);
970 if (\count($values) == 1) {
971 if (\in_array($value, array('center', 'top', 'bottom', 'inherit', 'initial', 'unset'))) {
972 return $value;
973 }
974 return "left {$value}";
975 }
976 if ($values[1] == 'top' or $values[1] == 'bottom') {
977 if ($values[0] === 'center') {
978 return $value;
979 }
980 return 'left ' . \implode(' ', $values);
981 } else {
982 $last = \array_pop($values);
983 if ($last === 'center') {
984 return $value;
985 }
986 return \implode(' ', $values) . ' left ' . $last;
987 }
988 }
989 return $value;
990 }
991 }
992