PluginProbe
MaxButtons – Create buttons / 7.1
MaxButtons – Create buttons v7.1
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 7.1, at classes/maxCSSParser.php

607 lines 14.5 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 $struct[$class]["tag"] = $domChild->tag;
77
78 $child_children = $domChild->children();
79
80 if (count($child_children) > 0)
81 {
82
83 $struct[$class]["children"] = $this->loadRecursive(array(), $child_children);
84 }
85 }
86
87 return $struct;
88 }
89
90
91 function parse($data)
92 {
93 $this->clear();
94
95 $struct = $this->struct;
96
97 $this->data = $data;
98
99
100 if (isset($data["settings"])) // room for settings in parser
101 {
102 $settings = $data["settings"];
103 $this->important = (isset($settings["important"])) ? $settings["important"] : false;
104
105 unset($this->data["settings"]);
106 }
107
108 $elements = array_shift($struct); // first element is a 'stub' root.
109
110 if ( is_null($elements) )
111 return;
112
113
114 foreach($elements as $el => $el_data)
115 {
116
117 $this->parse_part($el,$el_data);
118 }
119
120
121 $this->parse_responsive();
122
123 maxUtils::startTime('compile CSS');
124 $css = $this->compile($this->output_css);
125
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
207 if ($count === 0)
208 {
209 $ps_selector = $el_add . ":" . $pseudo;
210 }
211
212
213 $this->output_css .= " $ps_selector { ";
214 }
215 else {
216 $this->output_css .= " $el_add { ";
217 }
218
219 $values = $this->doMixins($values);
220
221 foreach($values as $cssTag => $cssVal)
222 {
223
224 $statement = $this->parse_cssline($values, $cssTag,$cssVal); ///"$cssTag $css_sep $cssVal$unit$css_end ";
225
226 if ($statement)
227 {
228 $this->output_css .= $statement ;
229
230 if (! isset($this->inline[$pseudo][$element])) $this->inline[$pseudo][$element] = '';
231 $this->inline[$pseudo][$element] .= $statement;
232 }
233 }
234
235 $this->output_css .= " } ";
236 }
237 if (isset($el_data["children"]))
238 {
239 foreach($el_data["children"] as $child_id => $child_data)
240 {
241
242 $this->parse_part($child_id, $child_data, $el_add);
243 }
244 }
245
246 }
247
248 function parse_cssline($values, $cssTag, $cssVal, $css_end = ';')
249 {
250
251 // unit check - two ways; either unitable items is first or unit declaration.
252 if (isset($values[$cssTag . "_unit"]))
253 {
254 $unit = $values[$cssTag . "_unit"];
255 }
256 elseif(strpos($cssTag, "_unit") !== false)
257 {
258 return false; // no print, should be found under first def.
259 }
260 else $unit = '';
261
262
263 $important = ($this->is_important()) ? " !important" : "";
264 $important = ($cssTag == '@include') ? "" : $important; // mixin's problem, no checking here.
265
266 $css_sep = ($cssTag == '@include') ? $css_sep = '' : ':';
267
268 if ($cssVal == 0 && in_array($cssTag, $this->elements_ignore_zero))
269 return false;
270
271 if($cssVal !== '' && $cssTag !== '')
272 {
273 $statement = "$cssTag $css_sep $cssVal$unit$important$css_end ";
274 return $statement;
275 }
276 return false;
277
278 }
279
280 function parse_responsive()
281 {
282
283 $responsive = $this->responsive;
284 if (! is_array($responsive) || count($responsive) == 0)
285 return;
286
287 $media_queries = maxUtils::get_media_query(2); // query names
288
289 $output = '';
290
291 $query_array = array();
292
293
294 foreach($responsive as $element => $queries)
295 {
296 foreach($queries as $query => $qdata)
297 foreach($qdata as $index => $data)
298 {{
299 $query_array[$query][$index][$element] = $data;
300
301 }}
302 }
303
304
305
306 foreach($query_array as $query => $vdata):
307
308 if ($query == 'custom')
309 {
310
311 // first discover the custom size properties.
312 foreach($vdata as $index => $data):
313 foreach($data as $element => $values):
314
315 if (isset($values["custom_maxwidth"]) || isset($values["custom_minwidth"]) )
316 {
317
318 $minwidth = (isset($values["custom_minwidth"])) ? intval($values["custom_minwidth"]) : -1;
319 $maxwidth = (isset($values["custom_maxwidth"])) ? intval($values["custom_maxwidth"]) : -1;
320
321
322 unset($vdata[$index][$element]["custom_minwidth"]);
323 unset($vdata[$index][$element]["custom_maxwidth"]);
324
325
326 // make it always an integer
327 if ($minwidth == '') $minwidth = 0;
328 if ($maxwidth == '') $maxwidth = 0;
329
330 // if minwidth is not set and maxwidth zero or not set - ignore since it would result in an empty media line.
331 //if ($minwidth <= 0 && $maxwidth <= 0)
332 //{
333 //continue;
334 //}
335
336 if ($minwidth > 0 && $maxwidth > 0)
337 $qdef = "only screen and (min-width: $minwidth" . "px) and (max-width: $maxwidth" . "px)";
338 if ($minwidth >= 0 && $maxwidth <= 0)
339 $qdef = "only screen and (min-width: $minwidth" . "px) ";
340 if ($minwidth <= 0 && $maxwidth > 0)
341 $qdef = "only screen and (max-width: $maxwidth" . "px) ";
342
343 //break;
344
345 }
346 endforeach; //foreach data
347
348 // The problem is that every 'custom' query needs to run with a different qdef unlike other queries.
349
350 $qdata = array($vdata[$index]);
351
352 $output = $this->parse_responsive_definition($output, $qdef, $qdata);
353 endforeach; // foreach vdata
354 }
355 else
356 {
357
358 $qdef = $media_queries[$query];
359 $output = $this->parse_responsive_definition($output, $qdef, $vdata);
360 }
361
362 endforeach;
363
364
365 $this->output_css .= $output;
366 }
367
368 protected function parse_responsive_definition($output, $qdef, $vdata)
369 {
370
371 if (! isset($qdef) || $qdef == '') {
372
373 return; // no definition.
374 }
375
376
377 $output .= "@media ". $qdef . " { ";
378
379
380 foreach($vdata as $index => $data)
381 {
382
383 foreach($data as $element => $values) {
384 //foreach($vdat as $index => $values):
385 $output .= $element . " { ";
386 $css_end = ';';
387
388 // same as parse part, maybe merge in future
389 foreach($values as $cssTag => $cssVal)
390 {
391 // unit check - two ways; either unitable items is first or unit declaration.
392 $statement = $this->parse_cssline($values, $cssTag,$cssVal);
393 if($statement)
394 $output .= $statement;
395
396 }
397
398 $output .= " } ";
399 // endforeach;
400 }
401
402
403 }
404 $output .= " } ";
405
406 //endforeach;
407
408 return $output;
409 }
410
411 private function is_important()
412 {
413
414 if ($this->important == 1)
415 return true;
416 else
417 return false;
418 }
419
420 function findData($data, $el)
421 {
422 $classes = explode(".", $el);
423
424 foreach($data as $part => $values)
425 {
426 if (in_array($part, $classes))
427 {
428 return $data[$part];
429 }
430 }
431 return array();
432 }
433
434 function doMixins($values)
435 {
436 $mixins = array("gradient", "box-shadow", "text-shadow");
437
438 foreach($mixins as $mixin)
439 {
440
441 $results = preg_grep("/^$mixin/i",array_keys($values) );
442 if (count($results) === 0)
443 continue; // no mixins.
444
445 $mixin_array = array();
446 foreach($results as $result)
447 {
448 $mixin_array[$result] = $values[$result];
449 }
450
451 if (count($mixin_array) > 0)
452 {
453 switch($mixin)
454 {
455 case "gradient":
456 $values = $this->mixin_gradient($mixin_array, $values);
457
458 break;
459 case "box-shadow":
460 $values = $this->mixin_boxshadow($mixin_array, $values);
461 break;
462 case "text-shadow":
463 $values = $this->mixin_textshadow($mixin_array, $values);
464 break;
465 }
466 }
467
468
469 }
470 return $values;
471 }
472
473 function mixin_gradient($results, $values)
474 {
475
476 $start = isset($results["gradient-start-color"]) ? $results["gradient-start-color"] : '';
477 $end = isset( $results["gradient-end-color"] ) ? $results["gradient-end-color"] : '';
478 $start_opacity = isset( $results["gradient-start-opacity"] ) ? $results["gradient-start-opacity"] : '';
479 $end_opacity = isset( $results["gradient-end-opacity"] ) ? $results["gradient-end-opacity"] : '';
480 $stop = (isset( $results["gradient-stop"]) && $results["gradient-stop"] != '') ? $results["gradient-stop"] . "%" : '45%';
481 // default to use ( old situation )
482 $use_gradient = (isset($results['gradient-use-gradient']) && $results['gradient-use-gradient'] != '') ? $results['gradient-use-gradient'] : 1;
483
484 $start = maxUtils::hex2rgba($start, $start_opacity);
485 $end = maxUtils::hex2rgba($end, $end_opacity);
486
487 $important = ($this->is_important()) ? "!important" : "";
488 //$values = $this->add_include($values, "linear-gradient($start,$end,$stop,$important)");
489
490 if ($use_gradient == 1)
491 {
492 $values = $this->add_include($values, "linear-gradient($start,$end,$stop,$important)");
493 }
494 else {
495 $values['background-color'] = $start;
496 }
497
498 // remove the non-css keys from the value array ( field names )
499 $values = array_diff_key($values, $results);
500
501 return $values;
502
503 }
504
505 function mixin_boxshadow($results, $values)
506 {
507 $width = isset($results["box-shadow-width"]) ? $results["box-shadow-width"] : 0;
508 $left = isset($results["box-shadow-offset-left"]) ? $results["box-shadow-offset-left"] : 0;
509 $top = isset($results["box-shadow-offset-top"]) ? $results["box-shadow-offset-top"] : 0;
510 $spread = isset($results['box-shadow-spread']) ? $results['box-shadow-spread'] : 0;
511 $color = isset($results["box-shadow-color"]) ? $results["box-shadow-color"] : '';
512
513 $important = ($this->is_important()) ? "!important" : "";
514
515 if ($width == 0 && $left == 0 && $top == 0)
516 return $values;
517
518 $values = $this->add_include($values, "box-shadow($left, $top, $width, $color,$spread, false, $important) ");
519 $values = array_diff_key($values, $results);
520
521 return $values;
522 }
523
524 function mixin_textshadow($results, $values)
525 {
526 $width = isset($results["text-shadow-width"]) ? $results["text-shadow-width"] : 0;
527 $left = isset($results["text-shadow-left"]) ? $results["text-shadow-left"] : 0;
528 $top = isset($results["text-shadow-top"]) ? $results["text-shadow-top"] : 0;
529 $color = isset($results["text-shadow-color"]) ? $results["text-shadow-color"] : '';
530 $important = ($this->is_important()) ? "!important" : "";
531
532 if ($width == 0 && $left == 0 && $top == 0)
533 {
534 $values = array_diff_key($values, $results); // remove them from the values, prevent incorrect output.
535 return $values;
536 }
537
538 $values = $this->add_include($values, "text-shadow ($left,$top,$width,$color $important)");
539
540 $values = array_diff_key($values, $results);
541
542 return $values;
543 }
544
545 private function add_include($values, $include)
546 {
547 if (isset($values["@include"]))
548 $values["@include"] .= "; @include " . $include;
549 else
550 $values["@include"] = $include;
551 return $values;
552 }
553
554 function outputInline($domObj, $pseudo = 'normal')
555 {
556 $domObj = $domObj->load($domObj->save());
557
558 $inline = $this->inline;
559
560 // ISSUE #43 Sometimes this breaks
561 if (! isset($inline[$pseudo]))
562 return $domObj;
563
564 $elements = array_keys($inline[$pseudo]);
565 if ($pseudo != 'normal') // gather all elements
566 $elements = array_merge($elements, array_keys($inline["normal"]));
567
568 foreach($elements as $element )
569 {
570 $styles = isset($inline[$pseudo][$element]) ? $inline[$pseudo][$element] : '';
571
572 if ($pseudo != 'normal') // parse all possible missing styles from pseudo el.
573 $normstyle = $this->compile($inline['normal'][$element]);
574
575 $normstyle = '';
576 if ($pseudo != 'normal') // parse all possible missing styles from pseudo el.
577 $normstyle = $this->compile($inline['normal'][$element]);
578
579 maxUtils::addTime("CSSParser: Parse inline done");
580
581 $styles = $normstyle . $this->compile($styles);
582
583 $element = trim(str_replace("."," ", $element)); // molten css class, seperator.
584
585 $el = $domObj->find('[class*="' . $element . '"]', 0);
586
587 $el->style = $styles;
588
589 }
590
591 return $domObj;
592
593 }
594
595 function output()
596 {
597
598
599 }
600
601 }
602
603 class compileException extends Exception {
604 protected $code = -1;
605
606 }
607