PluginProbe
MaxButtons – Create buttons / 6.24
MaxButtons – Create buttons v6.24
6.23 6.24 6.25 6.26 6.26.1 6.27 6.28 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7.0 7.1 7.1.1 7.1.2 7.1.3 7.10 7.11 7.13 7.13.1 7.13.2 7.13.3 All 100 releases
maxbuttons / classes / maxCSSParser.php

maxCSSParser.php in MaxButtons – Create buttons 6.24, at classes/maxCSSParser.php

604 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace MaxButtons;
3 defined('ABSPATH') or die('No direct access permitted');
4 /* Class to Parse CSS. Load as array with diffent pseudo-types,
5 ability to add nested and new root blocks
6 parses via scss
7 ability to use complicated css stuff via scss mixins parsing like gradient
8 auto-discovery for -unit field types to set units (like px, or %)
9 auto-discovery of fields via domobj.
10
11 */
12
13 use \Exception as Exception;
14
15 class maxCSSParser
16 {
17 protected $struct = array();
18 protected $domObj = '';
19 protected $pseudo = array("hover","active","responsive");
20
21 protected $data;
22 protected $output_css;
23
24 protected $inline = array();
25 protected $responsive = array();
26
27 public $anchor_class = '.maxbutton'; // used for matching buttons in parse_part
28
29 // settings
30 protected $elements_ignore_zero = array(
31 'text-shadow-left',
32 'text_shadow-top',
33 'text-shadow-width',
34 'box-shadow-offset-left',
35 'box-shadow-offset-top',
36 'box-shadow-width',
37 'box-shadow-spread',
38 ); // items to ignore if value is zero, otherwise they become unremovable ( where 0 is still something on display)
39
40 protected $important = false;
41
42 // log possible problems and incidents for debugging;
43 protected $parse_log = array();
44
45 function __construct()
46 {
47 //$root[] = array("a" => array("hover","active","responsive"));
48
49 }
50
51 function loadDom($domObj)
52 {
53 $this->domObj = $domObj;
54
55 $root = $domObj->find(0,0);
56 $struct[$root->tag] = array();
57
58 $children = $root->children();
59
60 if (count($children) > 0)
61 $struct[$root->tag] = $this->loadRecursive(array(), $children);
62
63
64 $this->struct = $struct;
65
66
67 }
68
69 function loadRecursive($struct, $children)
70 {
71 foreach($children as $domChild)
72 {
73
74 $class = $domChild->class;
75 $class = str_replace(" ",".", $class); // combine seperate classes
76
77 $struct[$class]["tag"] = $domChild->tag;
78
79 $child_children = $domChild->children();
80
81 if (count($child_children) > 0)
82 {
83
84 $struct[$class]["children"] = $this->loadRecursive(array(), $child_children);
85 }
86 }
87
88 return $struct;
89 }
90
91
92 function parse($data)
93 {
94 $this->clear();
95
96 $struct = $this->struct;
97
98 $this->data = $data;
99
100
101 if (isset($data["settings"])) // room for settings in parser
102 {
103 $settings = $data["settings"];
104 $this->important = (isset($settings["important"])) ? $settings["important"] : false;
105
106 unset($this->data["settings"]);
107 }
108
109 $elements = array_shift($struct); // first element is a 'stub' root.
110
111 if ( is_null($elements) )
112 return;
113
114
115 foreach($elements as $el => $el_data)
116 {
117
118 $this->parse_part($el,$el_data);
119 }
120
121
122 $this->parse_responsive();
123
124 maxUtils::startTime('compile CSS');
125 $css = $this->compile($this->output_css);
126 maxUtils::endTime('compile CSS');
127
128
129 return $css;
130
131 }
132
133 // reset output values.
134 protected function clear()
135 {
136 $this->data = '';
137 $this->output_css = '';
138 $this->inline = array();
139 //$this->struct = array();
140 $this->responsive = array();
141
142 }
143
144 protected function compile($css)
145 {
146 $scss = new \Leafo\ScssPhp\Compiler();
147 $scss->setImportPaths(MB()->get_plugin_path() . "assets/scss");
148
149 $minify = get_option("maxbuttons_minify", 1);
150 if ($minify == 1)
151 $scss->setFormatter('\Leafo\ScssPhp\Formatter\Compressed');
152
153 $compile = " @import '_mixins.scss'; " . $css;
154 //maxUtils::addTime("CSSParser: Compile start ");
155
156 try
157 {
158 $css = $scss->compile($compile);
159 } catch (Exception $e) {
160
161 $css = $this->output_css;
162 }
163
164 //maxUtils::addTime("CSSParser: Compile end ");
165
166 return $css;
167 }
168
169 function parse_part($element, $el_data, $el_add = '')
170 {
171 maxUtils::addTime("CSSParser: Parse $element ");
172
173
174 $tag = $el_data["tag"];
175 $element_data = $this->findData($this->data, $element);
176
177 // not using scss selectors here since there is trouble w/ the :pseudo selector, which should be put on the maxbutton / a tag.
178 if ($element != '')
179 { $el_add .= " ." . $element;
180
181 }
182
183 if (isset($element_data["responsive"]))
184 {
185 $responsive = $element_data["responsive"]; // doing that at the end
186 unset($element_data["responsive"]);
187
188 $this->responsive[$el_add] = $responsive;
189 }
190
191 foreach($element_data as $pseudo => $values)
192 {
193
194 if ($pseudo != 'normal')
195 {
196 // select the maxbutton case, ending with either space or next class -dot.
197 // Anchor class in default situation should be .maxbutton
198 $anchor_class = $this->anchor_class;
199
200 $count = 0;
201
202 /* If PS Selector replacement doesn't match anchor class selector this probably means the parse is done in a higher level
203 e.g. container level, so no proper will be set. In case 0 count replacement, just put it on current */
204 $ps_selector = preg_replace('/' . $anchor_class . '$|' . $anchor_class . '([.| ])/i',"$anchor_class:$pseudo\$1",$el_add, -1, $count);
205
206 if ($count === 0)
207 {
208 $ps_selector = $el_add . ":" . $pseudo;
209 }
210
211
212 $this->output_css .= " $ps_selector { ";
213 }
214 else {
215 $this->output_css .= " $el_add { ";
216 }
217
218 $values = $this->doMixins($values);
219
220 foreach($values as $cssTag => $cssVal)
221 {
222
223 $statement = $this->parse_cssline($values, $cssTag,$cssVal); ///"$cssTag $css_sep $cssVal$unit$css_end ";
224
225 if ($statement)
226 {
227 $this->output_css .= $statement ;
228
229 if (! isset($this->inline[$pseudo][$element])) $this->inline[$pseudo][$element] = '';
230 $this->inline[$pseudo][$element] .= $statement;
231 }
232 }
233
234 $this->output_css .= " } ";
235 }
236 if (isset($el_data["children"]))
237 {
238 foreach($el_data["children"] as $child_id => $child_data)
239 {
240
241 $this->parse_part($child_id, $child_data, $el_add);
242 }
243 }
244
245 }
246
247 function parse_cssline($values, $cssTag, $cssVal, $css_end = ';')
248 {
249
250 // unit check - two ways; either unitable items is first or unit declaration.
251 if (isset($values[$cssTag . "_unit"]))
252 {
253 $unit = $values[$cssTag . "_unit"];
254 }
255 elseif(strpos($cssTag, "_unit") !== false)
256 {
257 return false; // no print, should be found under first def.
258 }
259 else $unit = '';
260
261
262 $important = ($this->is_important()) ? " !important" : "";
263 $important = ($cssTag == '@include') ? "" : $important; // mixin's problem, no checking here.
264
265 $css_sep = ($cssTag == '@include') ? $css_sep = '' : ':';
266
267 if ($cssVal == 0 && in_array($cssTag, $this->elements_ignore_zero))
268 return false;
269
270 if($cssVal !== '' && $cssTag !== '')
271 {
272 $statement = "$cssTag $css_sep $cssVal$unit$important$css_end ";
273 return $statement;
274 }
275 return false;
276
277 }
278
279 function parse_responsive()
280 {
281
282 $responsive = $this->responsive;
283 if (! is_array($responsive) || count($responsive) == 0)
284 return;
285
286 $media_queries = maxUtils::get_media_query(2); // query names
287
288 $output = '';
289
290 $query_array = array();
291
292
293 foreach($responsive as $element => $queries)
294 {
295 foreach($queries as $query => $qdata)
296 foreach($qdata as $index => $data)
297 {{
298 $query_array[$query][$index][$element] = $data;
299
300 }}
301 }
302
303
304
305 foreach($query_array as $query => $vdata):
306
307 if ($query == 'custom')
308 {
309
310 // first discover the custom size properties.
311 foreach($vdata as $index => $data):
312 foreach($data as $element => $values):
313
314 if (isset($values["custom_maxwidth"]) || isset($values["custom_minwidth"]) )
315 {
316
317 $minwidth = (isset($values["custom_minwidth"])) ? intval($values["custom_minwidth"]) : -1;
318 $maxwidth = (isset($values["custom_maxwidth"])) ? intval($values["custom_maxwidth"]) : -1;
319
320
321 unset($vdata[$index][$element]["custom_minwidth"]);
322 unset($vdata[$index][$element]["custom_maxwidth"]);
323
324
325 // make it always an integer
326 if ($minwidth == '') $minwidth = 0;
327 if ($maxwidth == '') $maxwidth = 0;
328
329 // if minwidth is not set and maxwidth zero or not set - ignore since it would result in an empty media line.
330 //if ($minwidth <= 0 && $maxwidth <= 0)
331 //{
332 //continue;
333 //}
334
335 if ($minwidth > 0 && $maxwidth > 0)
336 $qdef = "only screen and (min-width: $minwidth" . "px) and (max-width: $maxwidth" . "px)";
337 if ($minwidth >= 0 && $maxwidth <= 0)
338 $qdef = "only screen and (min-width: $minwidth" . "px) ";
339 if ($minwidth <= 0 && $maxwidth > 0)
340 $qdef = "only screen and (max-width: $maxwidth" . "px) ";
341
342 //break;
343
344 }
345 endforeach; //foreach data
346
347 // The problem is that every 'custom' query needs to run with a different qdef unlike other queries.
348
349 $qdata = array($vdata[$index]);
350
351 $output = $this->parse_responsive_definition($output, $qdef, $qdata);
352 endforeach; // foreach vdata
353 }
354 else
355 {
356
357 $qdef = $media_queries[$query];
358 $output = $this->parse_responsive_definition($output, $qdef, $vdata);
359 }
360
361 endforeach;
362
363
364 $this->output_css .= $output;
365 }
366
367 protected function parse_responsive_definition($output, $qdef, $vdata)
368 {
369
370 if (! isset($qdef) || $qdef == '') {
371
372 return; // no definition.
373 }
374
375
376 $output .= "@media ". $qdef . " { ";
377
378
379 foreach($vdata as $index => $data)
380 {
381
382 foreach($data as $element => $values) {
383 //foreach($vdat as $index => $values):
384 $output .= $element . " { ";
385 $css_end = ';';
386
387 // same as parse part, maybe merge in future
388 foreach($values as $cssTag => $cssVal)
389 {
390 // unit check - two ways; either unitable items is first or unit declaration.
391 $statement = $this->parse_cssline($values, $cssTag,$cssVal);
392 if($statement)
393 $output .= $statement;
394
395 }
396
397 $output .= " } ";
398 // endforeach;
399 }
400
401
402 }
403 $output .= " } ";
404
405 //endforeach;
406
407 return $output;
408 }
409
410 private function is_important()
411 {
412
413 if ($this->important == 1)
414 return true;
415 else
416 return false;
417 }
418
419 function findData($data, $el)
420 {
421 $classes = explode(".", $el);
422
423 foreach($data as $part => $values)
424 {
425 if (in_array($part, $classes))
426 return $data[$part];
427 }
428 return array();
429 }
430
431 function doMixins($values)
432 {
433 $mixins = array("gradient", "box-shadow", "text-shadow");
434
435 foreach($mixins as $mixin)
436 {
437
438 $results = preg_grep("/^$mixin/i",array_keys($values) );
439 if (count($results) === 0)
440 continue; // no mixins.
441
442 $mixin_array = array();
443 foreach($results as $result)
444 {
445 $mixin_array[$result] = $values[$result];
446 }
447
448 if (count($mixin_array) > 0)
449 {
450 switch($mixin)
451 {
452 case "gradient":
453 $values = $this->mixin_gradient($mixin_array, $values);
454
455 break;
456 case "box-shadow":
457 $values = $this->mixin_boxshadow($mixin_array, $values);
458 break;
459 case "text-shadow":
460 $values = $this->mixin_textshadow($mixin_array, $values);
461 break;
462 }
463 }
464
465
466 }
467 return $values;
468 }
469
470 function mixin_gradient($results, $values)
471 {
472
473 $start = isset($results["gradient-start-color"]) ? $results["gradient-start-color"] : '';
474 $end = isset( $results["gradient-end-color"] ) ? $results["gradient-end-color"] : '';
475 $start_opacity = isset( $results["gradient-start-opacity"] ) ? $results["gradient-start-opacity"] : '';
476 $end_opacity = isset( $results["gradient-end-opacity"] ) ? $results["gradient-end-opacity"] : '';
477 $stop = (isset( $results["gradient-stop"]) && $results["gradient-stop"] != '') ? $results["gradient-stop"] . "%" : '45%';
478 // default to use ( old situation )
479 $use_gradient = (isset($results['gradient-use-gradient']) && $results['gradient-use-gradient'] != '') ? $results['gradient-use-gradient'] : 1;
480
481 $start = maxUtils::hex2rgba($start, $start_opacity);
482 $end = maxUtils::hex2rgba($end, $end_opacity);
483
484 $important = ($this->is_important()) ? "!important" : "";
485 //$values = $this->add_include($values, "linear-gradient($start,$end,$stop,$important)");
486
487 if ($use_gradient == 1)
488 {
489 $values = $this->add_include($values, "linear-gradient($start,$end,$stop,$important)");
490 }
491 else {
492 $values['background-color'] = $start;
493 }
494
495 // remove the non-css keys from the value array ( field names )
496 $values = array_diff_key($values, $results);
497
498 return $values;
499
500 }
501
502 function mixin_boxshadow($results, $values)
503 {
504 $width = $results["box-shadow-width"];
505 $left = $results["box-shadow-offset-left"];
506 $top = $results["box-shadow-offset-top"];
507 $spread = isset($results['box-shadow-spread']) ? $results['box-shadow-spread'] : 0;
508 $color = isset($results["box-shadow-color"]) ? $results["box-shadow-color"] : '';
509
510 $important = ($this->is_important()) ? "!important" : "";
511
512 if ($width == 0 && $left == 0 && $top == 0)
513 return $values;
514
515 $values = $this->add_include($values, "box-shadow($left, $top, $width, $color,$spread, false, $important) ");
516 $values = array_diff_key($values, $results);
517
518 return $values;
519 }
520
521 function mixin_textshadow($results, $values)
522 {
523 $width = isset($results["text-shadow-width"]) ? $results["text-shadow-width"] : 0;
524 $left = isset($results["text-shadow-left"]) ? $results["text-shadow-left"] : 0;
525 $top = isset($results["text-shadow-top"]) ? $results["text-shadow-top"] : 0;
526 $color = isset($results["text-shadow-color"]) ? $results["text-shadow-color"] : '';
527 $important = ($this->is_important()) ? "!important" : "";
528
529 if ($width == 0 && $left == 0 && $top == 0)
530 {
531 $values = array_diff_key($values, $results); // remove them from the values, prevent incorrect output.
532 return $values;
533 }
534
535 $values = $this->add_include($values, "text-shadow ($left,$top,$width,$color $important)");
536
537 $values = array_diff_key($values, $results);
538
539 return $values;
540 }
541
542 private function add_include($values, $include)
543 {
544 if (isset($values["@include"]))
545 $values["@include"] .= "; @include " . $include;
546 else
547 $values["@include"] = $include;
548 return $values;
549 }
550
551 function outputInline($domObj, $pseudo = 'normal')
552 {
553 $domObj = $domObj->load($domObj->save());
554
555 $inline = $this->inline;
556
557 // ISSUE #43 Sometimes this breaks
558 if (! isset($inline[$pseudo]))
559 return $domObj;
560
561 $elements = array_keys($inline[$pseudo]);
562 if ($pseudo != 'normal') // gather all elements
563 $elements = array_merge($elements, array_keys($inline["normal"]));
564
565 foreach($elements as $element )
566 {
567 $styles = isset($inline[$pseudo][$element]) ? $inline[$pseudo][$element] : '';
568
569 if ($pseudo != 'normal') // parse all possible missing styles from pseudo el.
570 $normstyle = $this->compile($inline['normal'][$element]);
571
572 $normstyle = '';
573 if ($pseudo != 'normal') // parse all possible missing styles from pseudo el.
574 $normstyle = $this->compile($inline['normal'][$element]);
575
576 maxUtils::addTime("CSSParser: Parse inline done");
577
578 $styles = $normstyle . $this->compile($styles);
579
580 $element = trim(str_replace("."," ", $element)); // molten css class, seperator.
581
582 $el = $domObj->find('[class*="' . $element . '"]', 0);
583 if (is_null($el)) echo "NULL";
584 $el->style = $styles;
585
586 }
587
588 return $domObj;
589
590 }
591
592 function output()
593 {
594
595
596 }
597
598 }
599
600 class compileException extends Exception {
601 protected $code = -1;
602
603 }
604