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

571 lines 13.9 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 scssc();
136 $scss->setImportPaths(MB()->get_plugin_path() . "assets/scss");
137
138 $minify = get_option("maxbuttons_minify", 1);
139 if ($minify == 1)
140 $scss->setFormatter('scss_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 foreach($query_array as $query => $vdata):
280
281 if ($query == 'custom')
282 {
283
284 // first discover the custom size properties.
285 foreach($vdata as $index => $data):
286 foreach($data as $element => $values):
287
288 if (isset($values["custom_maxwidth"]) || isset($values["custom_minwidth"]) )
289 {
290
291 $minwidth = (isset($values["custom_minwidth"])) ? intval($values["custom_minwidth"]) : -1;
292 $maxwidth = (isset($values["custom_maxwidth"])) ? intval($values["custom_maxwidth"]) : -1;
293
294
295 unset($vdata[$index][$element]["custom_minwidth"]);
296 unset($vdata[$index][$element]["custom_maxwidth"]);
297
298
299 // make it always an integer
300 if ($minwidth == '') $minwidth = 0;
301 if ($maxwidth == '') $maxwidth = 0;
302
303 // if minwidth is not set and maxwidth zero or not set - ignore since it would result in an empty media line.
304 //if ($minwidth <= 0 && $maxwidth <= 0)
305 //{
306 //continue;
307 //}
308
309 if ($minwidth > 0 && $maxwidth > 0)
310 $qdef = "only screen and (min-width: $minwidth" . "px) and (max-width: $maxwidth" . "px)";
311 if ($minwidth >= 0 && $maxwidth <= 0)
312 $qdef = "only screen and (min-width: $minwidth" . "px) ";
313 if ($minwidth <= 0 && $maxwidth > 0)
314 $qdef = "only screen and (max-width: $maxwidth" . "px) ";
315
316 //break;
317
318 }
319 endforeach; //foreach data
320
321 // The problem is that every 'custom' query needs to run with a different qdef unlike other queries.
322
323 $qdata = array($vdata[$index]);
324
325 $output = $this->parse_responsive_definition($output, $qdef, $qdata);
326 endforeach; // foreach vdata
327 }
328 else
329 {
330
331 $qdef = $media_queries[$query];
332 $output = $this->parse_responsive_definition($output, $qdef, $vdata);
333 }
334
335 endforeach;
336
337
338 $this->output_css .= $output;
339 }
340
341 protected function parse_responsive_definition($output, $qdef, $vdata)
342 {
343
344 if (! isset($qdef) || $qdef == '') {
345
346 return; // no definition.
347 }
348
349
350 $output .= "@media ". $qdef . " { ";
351
352
353 foreach($vdata as $index => $data)
354 {
355
356 foreach($data as $element => $values) {
357 //foreach($vdat as $index => $values):
358 $output .= $element . " { ";
359 $css_end = ';';
360
361 // same as parse part, maybe merge in future
362 foreach($values as $cssTag => $cssVal)
363 {
364 // unit check - two ways; either unitable items is first or unit declaration.
365 $statement = $this->parse_cssline($values, $cssTag,$cssVal);
366 if($statement)
367 $output .= $statement;
368
369 }
370
371 $output .= " } ";
372 // endforeach;
373 }
374
375
376 }
377 $output .= " } ";
378
379 //endforeach;
380
381 return $output;
382 }
383
384 private function is_important()
385 {
386
387 if ($this->important == 1)
388 return true;
389 else
390 return false;
391 }
392
393 function findData($data, $el)
394 {
395 $classes = explode(".", $el);
396
397 foreach($data as $part => $values)
398 {
399 if (in_array($part, $classes))
400 return $data[$part];
401 }
402 return array();
403 }
404
405 function doMixins($values)
406 {
407 $mixins = array("gradient", "box-shadow", "text-shadow");
408
409 foreach($mixins as $mixin)
410 {
411
412 $results = preg_grep("/^$mixin/i",array_keys($values) );
413 $mixin_array = array();
414 foreach($results as $result)
415 {
416 $mixin_array[$result] = $values[$result];
417 }
418
419 if (count($mixin_array) > 0)
420 {
421 switch($mixin)
422 {
423 case "gradient":
424 $values = $this->mixin_gradient($mixin_array, $values);
425
426 break;
427 case "box-shadow":
428 $values = $this->mixin_boxshadow($mixin_array, $values);
429 break;
430 case "text-shadow":
431 $values = $this->mixin_textshadow($mixin_array, $values);
432 break;
433 }
434 }
435
436
437 }
438 return $values;
439 }
440
441 function mixin_gradient($results, $values)
442 {
443
444 $start = isset($results["gradient-start-color"]) ? $results["gradient-start-color"] : '';
445 $end = isset( $results["gradient-end-color"] ) ? $results["gradient-end-color"] : '';
446 $start_opacity = isset( $results["gradient-start-opacity"] ) ? $results["gradient-start-opacity"] : '';
447 $end_opacity = isset( $results["gradient-end-opacity"] ) ? $results["gradient-end-opacity"] : '';
448 $stop = (isset( $results["gradient-stop"]) && $results["gradient-stop"] != '') ? $results["gradient-stop"] . "%" : '45%';
449 // default to use ( old situation )
450 $use_gradient = (isset($results['gradient-use-gradient']) && $results['gradient-use-gradient'] != '') ? $results['gradient-use-gradient'] : 1;
451
452 $start = maxUtils::hex2rgba($start, $start_opacity);
453 $end = maxUtils::hex2rgba($end, $end_opacity);
454
455 $important = ($this->is_important()) ? "!important" : "";
456 //$values = $this->add_include($values, "linear-gradient($start,$end,$stop,$important)");
457
458 if ($use_gradient == 1)
459 {
460 $values = $this->add_include($values, "linear-gradient($start,$end,$stop,$important)");
461 }
462 else {
463 $values['background-color'] = $start;
464 }
465 // remove the non-css keys from the value array ( field names )
466 $values = array_diff_key($values, $results);
467
468 return $values;
469
470 }
471
472 function mixin_boxshadow($results, $values)
473 {
474 $width = $results["box-shadow-width"];
475 $left = $results["box-shadow-offset-left"];
476 $top = $results["box-shadow-offset-top"];
477 $spread = isset($results['box-shadow-spread']) ? $results['box-shadow-spread'] : 0;
478 $color = isset($results["box-shadow-color"]) ? $results["box-shadow-color"] : '';
479
480 $important = ($this->is_important()) ? "!important" : "";
481
482 if ($width == 0 && $left == 0 && $top == 0)
483 return $values;
484
485 $values = $this->add_include($values, "box-shadow($left, $top, $width, $color,$spread, false, $important) ");
486 $values = array_diff_key($values, $results);
487
488 return $values;
489 }
490
491 function mixin_textshadow($results, $values)
492 {
493 $width = isset($results["text-shadow-width"]) ? $results["text-shadow-width"] : 0;
494 $left = isset($results["text-shadow-left"]) ? $results["text-shadow-left"] : 0;
495 $top = isset($results["text-shadow-top"]) ? $results["text-shadow-top"] : 0;
496 $color = isset($results["text-shadow-color"]) ? $results["text-shadow-color"] : '';
497 $important = ($this->is_important()) ? "!important" : "";
498
499 if ($width == 0 && $left == 0 && $top == 0)
500 return $values;
501
502 $values = $this->add_include($values, "text-shadow ($left,$top,$width,$color $important)");
503
504 $values = array_diff_key($values, $results);
505
506 return $values;
507 }
508
509 private function add_include($values, $include)
510 {
511 if (isset($values["@include"]))
512 $values["@include"] .= "; @include " . $include;
513 else
514 $values["@include"] = $include;
515 return $values;
516 }
517
518 function outputInline($domObj, $pseudo = 'normal')
519 {
520 $domObj = $domObj->load($domObj->save());
521
522 $inline = $this->inline;
523
524 // ISSUE #43 Sometimes this breaks
525 if (! isset($inline[$pseudo]))
526 return $domObj;
527
528 $elements = array_keys($inline[$pseudo]);
529 if ($pseudo != 'normal') // gather all elements
530 $elements = array_merge($elements, array_keys($inline["normal"]));
531
532 foreach($elements as $element )
533 {
534 $styles = isset($inline[$pseudo][$element]) ? $inline[$pseudo][$element] : '';
535
536 if ($pseudo != 'normal') // parse all possible missing styles from pseudo el.
537 $normstyle = $this->compile($inline['normal'][$element]);
538
539 $normstyle = '';
540 if ($pseudo != 'normal') // parse all possible missing styles from pseudo el.
541 $normstyle = $this->compile($inline['normal'][$element]);
542
543 maxUtils::addTime("CSSParser: Parse inline done");
544
545 $styles = $normstyle . $this->compile($styles);
546
547 $element = trim(str_replace("."," ", $element)); // molten css class, seperator.
548
549 $el = $domObj->find('[class*="' . $element . '"]', 0);
550 if (is_null($el)) echo "NULL";
551 $el->style = $styles;
552
553 }
554
555 return $domObj;
556
557 }
558
559 function output()
560 {
561
562
563 }
564
565 }
566
567 class compileException extends Exception {
568 protected $code = -1;
569
570 }
571