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

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