PluginProbe
MaxButtons – Create buttons / 6.23
MaxButtons – Create buttons v6.23
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.23, at classes/maxCSSParser.php

590 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 $this->struct = $struct;
64
65
66 }
67
68 function loadRecursive($struct, $children)
69 {
70 foreach($children as $domChild)
71 {
72
73 $class = $domChild->class;
74 $class = str_replace(" ",".", $class); // combine seperate classes
75
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 foreach($elements as $el => $el_data)
114 {
115
116 $this->parse_part($el,$el_data);
117 }
118
119
120 $this->parse_responsive();
121
122 maxUtils::startTime('compile CSS');
123 $css = $this->compile($this->output_css);
124 maxUtils::endTime('compile CSS');
125
126
127 return $css;
128
129 }
130
131 // reset output values.
132 protected function clear()
133 {
134 $this->data = '';
135 $this->output_css = '';
136 $this->inline = array();
137 //$this->struct = array();
138 $this->responsive = array();
139
140 }
141
142 protected function compile($css)
143 {
144 $scss = new \Leafo\ScssPhp\Compiler();
145 $scss->setImportPaths(MB()->get_plugin_path() . "assets/scss");
146
147 $minify = get_option("maxbuttons_minify", 1);
148 if ($minify == 1)
149 $scss->setFormatter('\Leafo\ScssPhp\Formatter\Compressed');
150
151 $compile = " @import '_mixins.scss'; " . $css;
152 //maxUtils::addTime("CSSParser: Compile start ");
153
154 try
155 {
156 $css = $scss->compile($compile);
157 } catch (Exception $e) {
158
159 $css = $this->output_css;
160 }
161
162 //maxUtils::addTime("CSSParser: Compile end ");
163
164 return $css;
165 }
166
167 function parse_part($element, $el_data, $el_add = '')
168 {
169 maxUtils::addTime("CSSParser: Parse $element ");
170
171 $tag = $el_data["tag"];
172 $element_data = $this->findData($this->data, $element);
173
174 // not using scss selectors here since there is trouble w/ the :pseudo selector, which should be put on the maxbutton / a tag.
175 if ($element != '')
176 { $el_add .= " ." . $element;
177
178 }
179
180 if (isset($element_data["responsive"]))
181 {
182 $responsive = $element_data["responsive"]; // doing that at the end
183 unset($element_data["responsive"]);
184
185 $this->responsive[$el_add] = $responsive;
186 }
187
188 foreach($element_data as $pseudo => $values)
189 {
190
191 if ($pseudo != 'normal')
192 {
193 // select the maxbutton case, ending with either space or next class -dot.
194 // Anchor class in default situation should be .maxbutton
195 $anchor_class = $this->anchor_class;
196
197 $ps_selector = preg_replace('/' . $anchor_class . '$|' . $anchor_class . '([.| ])/i',"$anchor_class:$pseudo\$1",$el_add);
198 $this->output_css .= " $ps_selector { ";
199 }
200 else {
201 $this->output_css .= " $el_add { ";
202 }
203
204 $values = $this->doMixins($values);
205
206 foreach($values as $cssTag => $cssVal)
207 {
208
209 $statement = $this->parse_cssline($values, $cssTag,$cssVal); ///"$cssTag $css_sep $cssVal$unit$css_end ";
210
211 if ($statement)
212 {
213 $this->output_css .= $statement ;
214
215 if (! isset($this->inline[$pseudo][$element])) $this->inline[$pseudo][$element] = '';
216 $this->inline[$pseudo][$element] .= $statement;
217 }
218 }
219
220 $this->output_css .= " } ";
221 }
222 if (isset($el_data["children"]))
223 {
224 foreach($el_data["children"] as $child_id => $child_data)
225 {
226
227 $this->parse_part($child_id, $child_data, $el_add);
228 }
229 }
230
231 }
232
233 function parse_cssline($values, $cssTag, $cssVal, $css_end = ';')
234 {
235
236 // unit check - two ways; either unitable items is first or unit declaration.
237 if (isset($values[$cssTag . "_unit"]))
238 {
239 $unit = $values[$cssTag . "_unit"];
240 }
241 elseif(strpos($cssTag, "_unit") !== false)
242 {
243 return false; // no print, should be found under first def.
244 }
245 else $unit = '';
246
247
248 $important = ($this->is_important()) ? " !important" : "";
249 $important = ($cssTag == '@include') ? "" : $important; // mixin's problem, no checking here.
250
251 $css_sep = ($cssTag == '@include') ? $css_sep = '' : ':';
252
253 if ($cssVal == 0 && in_array($cssTag, $this->elements_ignore_zero))
254 return false;
255
256 if($cssVal !== '' && $cssTag !== '')
257 {
258 $statement = "$cssTag $css_sep $cssVal$unit$important$css_end ";
259 return $statement;
260 }
261 return false;
262
263 }
264
265 function parse_responsive()
266 {
267
268 $responsive = $this->responsive;
269 if (! is_array($responsive) || count($responsive) == 0)
270 return;
271
272 $media_queries = maxUtils::get_media_query(2); // query names
273
274 $output = '';
275
276 $query_array = array();
277
278
279 foreach($responsive as $element => $queries)
280 {
281 foreach($queries as $query => $qdata)
282 foreach($qdata as $index => $data)
283 {{
284 $query_array[$query][$index][$element] = $data;
285
286 }}
287 }
288
289
290
291 foreach($query_array as $query => $vdata):
292
293 if ($query == 'custom')
294 {
295
296 // first discover the custom size properties.
297 foreach($vdata as $index => $data):
298 foreach($data as $element => $values):
299
300 if (isset($values["custom_maxwidth"]) || isset($values["custom_minwidth"]) )
301 {
302
303 $minwidth = (isset($values["custom_minwidth"])) ? intval($values["custom_minwidth"]) : -1;
304 $maxwidth = (isset($values["custom_maxwidth"])) ? intval($values["custom_maxwidth"]) : -1;
305
306
307 unset($vdata[$index][$element]["custom_minwidth"]);
308 unset($vdata[$index][$element]["custom_maxwidth"]);
309
310
311 // make it always an integer
312 if ($minwidth == '') $minwidth = 0;
313 if ($maxwidth == '') $maxwidth = 0;
314
315 // if minwidth is not set and maxwidth zero or not set - ignore since it would result in an empty media line.
316 //if ($minwidth <= 0 && $maxwidth <= 0)
317 //{
318 //continue;
319 //}
320
321 if ($minwidth > 0 && $maxwidth > 0)
322 $qdef = "only screen and (min-width: $minwidth" . "px) and (max-width: $maxwidth" . "px)";
323 if ($minwidth >= 0 && $maxwidth <= 0)
324 $qdef = "only screen and (min-width: $minwidth" . "px) ";
325 if ($minwidth <= 0 && $maxwidth > 0)
326 $qdef = "only screen and (max-width: $maxwidth" . "px) ";
327
328 //break;
329
330 }
331 endforeach; //foreach data
332
333 // The problem is that every 'custom' query needs to run with a different qdef unlike other queries.
334
335 $qdata = array($vdata[$index]);
336
337 $output = $this->parse_responsive_definition($output, $qdef, $qdata);
338 endforeach; // foreach vdata
339 }
340 else
341 {
342
343 $qdef = $media_queries[$query];
344 $output = $this->parse_responsive_definition($output, $qdef, $vdata);
345 }
346
347 endforeach;
348
349
350 $this->output_css .= $output;
351 }
352
353 protected function parse_responsive_definition($output, $qdef, $vdata)
354 {
355
356 if (! isset($qdef) || $qdef == '') {
357
358 return; // no definition.
359 }
360
361
362 $output .= "@media ". $qdef . " { ";
363
364
365 foreach($vdata as $index => $data)
366 {
367
368 foreach($data as $element => $values) {
369 //foreach($vdat as $index => $values):
370 $output .= $element . " { ";
371 $css_end = ';';
372
373 // same as parse part, maybe merge in future
374 foreach($values as $cssTag => $cssVal)
375 {
376 // unit check - two ways; either unitable items is first or unit declaration.
377 $statement = $this->parse_cssline($values, $cssTag,$cssVal);
378 if($statement)
379 $output .= $statement;
380
381 }
382
383 $output .= " } ";
384 // endforeach;
385 }
386
387
388 }
389 $output .= " } ";
390
391 //endforeach;
392
393 return $output;
394 }
395
396 private function is_important()
397 {
398
399 if ($this->important == 1)
400 return true;
401 else
402 return false;
403 }
404
405 function findData($data, $el)
406 {
407 $classes = explode(".", $el);
408
409 foreach($data as $part => $values)
410 {
411 if (in_array($part, $classes))
412 return $data[$part];
413 }
414 return array();
415 }
416
417 function doMixins($values)
418 {
419 $mixins = array("gradient", "box-shadow", "text-shadow");
420
421 foreach($mixins as $mixin)
422 {
423
424 $results = preg_grep("/^$mixin/i",array_keys($values) );
425 if (count($results) === 0)
426 continue; // no mixins.
427
428 $mixin_array = array();
429 foreach($results as $result)
430 {
431 $mixin_array[$result] = $values[$result];
432 }
433
434 if (count($mixin_array) > 0)
435 {
436 switch($mixin)
437 {
438 case "gradient":
439 $values = $this->mixin_gradient($mixin_array, $values);
440
441 break;
442 case "box-shadow":
443 $values = $this->mixin_boxshadow($mixin_array, $values);
444 break;
445 case "text-shadow":
446 $values = $this->mixin_textshadow($mixin_array, $values);
447 break;
448 }
449 }
450
451
452 }
453 return $values;
454 }
455
456 function mixin_gradient($results, $values)
457 {
458
459 $start = isset($results["gradient-start-color"]) ? $results["gradient-start-color"] : '';
460 $end = isset( $results["gradient-end-color"] ) ? $results["gradient-end-color"] : '';
461 $start_opacity = isset( $results["gradient-start-opacity"] ) ? $results["gradient-start-opacity"] : '';
462 $end_opacity = isset( $results["gradient-end-opacity"] ) ? $results["gradient-end-opacity"] : '';
463 $stop = (isset( $results["gradient-stop"]) && $results["gradient-stop"] != '') ? $results["gradient-stop"] . "%" : '45%';
464 // default to use ( old situation )
465 $use_gradient = (isset($results['gradient-use-gradient']) && $results['gradient-use-gradient'] != '') ? $results['gradient-use-gradient'] : 1;
466
467 $start = maxUtils::hex2rgba($start, $start_opacity);
468 $end = maxUtils::hex2rgba($end, $end_opacity);
469
470 $important = ($this->is_important()) ? "!important" : "";
471 //$values = $this->add_include($values, "linear-gradient($start,$end,$stop,$important)");
472
473 if ($use_gradient == 1)
474 {
475 $values = $this->add_include($values, "linear-gradient($start,$end,$stop,$important)");
476 }
477 else {
478 $values['background-color'] = $start;
479 }
480
481 // remove the non-css keys from the value array ( field names )
482 $values = array_diff_key($values, $results);
483
484 return $values;
485
486 }
487
488 function mixin_boxshadow($results, $values)
489 {
490 $width = $results["box-shadow-width"];
491 $left = $results["box-shadow-offset-left"];
492 $top = $results["box-shadow-offset-top"];
493 $spread = isset($results['box-shadow-spread']) ? $results['box-shadow-spread'] : 0;
494 $color = isset($results["box-shadow-color"]) ? $results["box-shadow-color"] : '';
495
496 $important = ($this->is_important()) ? "!important" : "";
497
498 if ($width == 0 && $left == 0 && $top == 0)
499 return $values;
500
501 $values = $this->add_include($values, "box-shadow($left, $top, $width, $color,$spread, false, $important) ");
502 $values = array_diff_key($values, $results);
503
504 return $values;
505 }
506
507 function mixin_textshadow($results, $values)
508 {
509 $width = isset($results["text-shadow-width"]) ? $results["text-shadow-width"] : 0;
510 $left = isset($results["text-shadow-left"]) ? $results["text-shadow-left"] : 0;
511 $top = isset($results["text-shadow-top"]) ? $results["text-shadow-top"] : 0;
512 $color = isset($results["text-shadow-color"]) ? $results["text-shadow-color"] : '';
513 $important = ($this->is_important()) ? "!important" : "";
514
515 if ($width == 0 && $left == 0 && $top == 0)
516 {
517 $values = array_diff_key($values, $results); // remove them from the values, prevent incorrect output.
518 return $values;
519 }
520
521 $values = $this->add_include($values, "text-shadow ($left,$top,$width,$color $important)");
522
523 $values = array_diff_key($values, $results);
524
525 return $values;
526 }
527
528 private function add_include($values, $include)
529 {
530 if (isset($values["@include"]))
531 $values["@include"] .= "; @include " . $include;
532 else
533 $values["@include"] = $include;
534 return $values;
535 }
536
537 function outputInline($domObj, $pseudo = 'normal')
538 {
539 $domObj = $domObj->load($domObj->save());
540
541 $inline = $this->inline;
542
543 // ISSUE #43 Sometimes this breaks
544 if (! isset($inline[$pseudo]))
545 return $domObj;
546
547 $elements = array_keys($inline[$pseudo]);
548 if ($pseudo != 'normal') // gather all elements
549 $elements = array_merge($elements, array_keys($inline["normal"]));
550
551 foreach($elements as $element )
552 {
553 $styles = isset($inline[$pseudo][$element]) ? $inline[$pseudo][$element] : '';
554
555 if ($pseudo != 'normal') // parse all possible missing styles from pseudo el.
556 $normstyle = $this->compile($inline['normal'][$element]);
557
558 $normstyle = '';
559 if ($pseudo != 'normal') // parse all possible missing styles from pseudo el.
560 $normstyle = $this->compile($inline['normal'][$element]);
561
562 maxUtils::addTime("CSSParser: Parse inline done");
563
564 $styles = $normstyle . $this->compile($styles);
565
566 $element = trim(str_replace("."," ", $element)); // molten css class, seperator.
567
568 $el = $domObj->find('[class*="' . $element . '"]', 0);
569 if (is_null($el)) echo "NULL";
570 $el->style = $styles;
571
572 }
573
574 return $domObj;
575
576 }
577
578 function output()
579 {
580
581
582 }
583
584 }
585
586 class compileException extends Exception {
587 protected $code = -1;
588
589 }
590