MDL-33399 files: Fixed up handling of empty sort in File API methods.
[moodle.git] / lib / csslib.php
blobc5bc0f56d40717cc7210e710ed45a453b979c8ed
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * This file contains CSS related class, and function for the CSS optimiser
20 * Please see the {@see css_optimiser} class for greater detail.
22 * @package core_css
23 * @category css
24 * @copyright 2012 Sam Hemelryk
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28 //NOTE: do not verify MOODLE_INTERNAL here, this is used from themes too
30 /**
31 * Stores CSS in a file at the given path.
33 * This function either succeeds or throws an exception.
35 * @param theme_config $theme The theme that the CSS belongs to.
36 * @param string $csspath The path to store the CSS at.
37 * @param array $cssfiles The CSS files to store.
39 function css_store_css(theme_config $theme, $csspath, array $cssfiles) {
40 global $CFG;
42 if (!empty($CFG->enablecssoptimiser)) {
43 // This is an experimental feature introduced in Moodle 2.3
44 // The CSS optimiser organises the CSS in order to reduce the overall number
45 // of rules and styles being sent to the client. It does this by collating
46 // the CSS before it is cached removing excess styles and rules and stripping
47 // out any extraneous content such as comments and empty rules.
48 $optimiser = new css_optimiser;
49 $css = '';
50 foreach ($cssfiles as $file) {
51 $css .= file_get_contents($file)."\n";
53 $css = $theme->post_process($css);
54 $css = $optimiser->process($css);
56 // If cssoptimisestats is set then stats from the optimisation are collected
57 // and output at the beginning of the CSS
58 if (!empty($CFG->cssoptimiserstats)) {
59 $css = $optimiser->output_stats_css().$css;
61 } else {
62 // This is the default behaviour.
63 // The cssoptimise setting was introduced in Moodle 2.3 and will hopefully
64 // in the future be changed from an experimental setting to the default.
65 // The css_minify_css will method will use the Minify library remove
66 // comments, additional whitespace and other minor measures to reduce the
67 // the overall CSS being sent.
68 // However it has the distinct disadvantage of having to minify the CSS
69 // before running the post process functions. Potentially things may break
70 // here if theme designers try to push things with CSS post processing.
71 $css = $theme->post_process(css_minify_css($cssfiles));
74 clearstatcache();
75 if (!file_exists(dirname($csspath))) {
76 @mkdir(dirname($csspath), $CFG->directorypermissions, true);
79 // Prevent serving of incomplete file from concurrent request,
80 // the rename() should be more atomic than fwrite().
81 ignore_user_abort(true);
82 if ($fp = fopen($csspath.'.tmp', 'xb')) {
83 fwrite($fp, $css);
84 fclose($fp);
85 rename($csspath.'.tmp', $csspath);
86 @chmod($csspath, $CFG->filepermissions);
87 @unlink($csspath.'.tmp'); // just in case anything fails
89 ignore_user_abort(false);
90 if (connection_aborted()) {
91 die;
95 /**
96 * Sends IE specific CSS
98 * In writing the CSS parser I have a theory that we could optimise the CSS
99 * then split it based upon the number of selectors to ensure we dont' break IE
100 * and that we include only as many sub-stylesheets as we require.
101 * Of course just a theory but may be fun to code.
103 * @param string $themename The name of the theme we are sending CSS for.
104 * @param string $rev The revision to ensure we utilise the cache.
105 * @param string $etag The revision to ensure we utilise the cache.
106 * @param bool $slasharguments
108 function css_send_ie_css($themename, $rev, $etag, $slasharguments) {
109 global $CFG;
111 $lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
113 $relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot);
115 $css = "/** Unfortunately IE6/7 does not support more than 4096 selectors in one CSS file, which means we have to use some ugly hacks :-( **/";
116 if ($slasharguments) {
117 $css .= "\n@import url($relroot/styles.php/$themename/$rev/plugins);";
118 $css .= "\n@import url($relroot/styles.php/$themename/$rev/parents);";
119 $css .= "\n@import url($relroot/styles.php/$themename/$rev/theme);";
120 } else {
121 $css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=plugins);";
122 $css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=parents);";
123 $css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=theme);";
126 header('Etag: '.$etag);
127 header('Content-Disposition: inline; filename="styles.php"');
128 header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
129 header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
130 header('Pragma: ');
131 header('Cache-Control: public, max-age='.$lifetime);
132 header('Accept-Ranges: none');
133 header('Content-Type: text/css; charset=utf-8');
134 header('Content-Length: '.strlen($css));
136 echo $css;
137 die;
141 * Sends a cached CSS file
143 * This function sends the cached CSS file. Remember it is generated on the first
144 * request, then optimised/minified, and finally cached for serving.
146 * @param string $csspath The path to the CSS file we want to serve.
147 * @param string $etag The revision to make sure we utilise any caches.
149 function css_send_cached_css($csspath, $etag) {
150 $lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
152 header('Etag: '.$etag);
153 header('Content-Disposition: inline; filename="styles.php"');
154 header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($csspath)) .' GMT');
155 header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
156 header('Pragma: ');
157 header('Cache-Control: public, max-age='.$lifetime);
158 header('Accept-Ranges: none');
159 header('Content-Type: text/css; charset=utf-8');
160 if (!min_enable_zlib_compression()) {
161 header('Content-Length: '.filesize($csspath));
164 readfile($csspath);
165 die;
169 * Sends CSS directly without caching it.
171 * This function takes a raw CSS string, optimises it if required, and then
172 * serves it.
173 * Turning both themedesignermode and CSS optimiser on at the same time is aweful
174 * for performance because of the optimiser running here. However it was done so
175 * that theme designers could utilise the optimised output during development to
176 * help them optimise their CSS... not that they should write lazy CSS.
178 * @param string CSS
180 function css_send_uncached_css($css) {
181 global $CFG;
183 header('Content-Disposition: inline; filename="styles_debug.php"');
184 header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
185 header('Expires: '. gmdate('D, d M Y H:i:s', time() + THEME_DESIGNER_CACHE_LIFETIME) .' GMT');
186 header('Pragma: ');
187 header('Accept-Ranges: none');
188 header('Content-Type: text/css; charset=utf-8');
190 if (is_array($css)) {
191 $css = implode("\n\n", $css);
194 if (!empty($CFG->enablecssoptimiser)) {
195 $css = str_replace("\n", "\r\n", $css);
197 $optimiser = new css_optimiser;
198 $css = $optimiser->process($css);
199 if (!empty($CFG->cssoptimiserstats)) {
200 $css = $optimiser->output_stats_css().$css;
204 echo $css;
206 die;
210 * Send file not modified headers
211 * @param int $lastmodified
212 * @param string $etag
214 function css_send_unmodified($lastmodified, $etag) {
215 $lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
216 header('HTTP/1.1 304 Not Modified');
217 header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
218 header('Cache-Control: public, max-age='.$lifetime);
219 header('Content-Type: text/css; charset=utf-8');
220 header('Etag: '.$etag);
221 if ($lastmodified) {
222 header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
224 die;
228 * Sends a 404 message about CSS not being found.
230 function css_send_css_not_found() {
231 header('HTTP/1.0 404 not found');
232 die('CSS was not found, sorry.');
236 * Uses the minify library to compress CSS.
238 * This is used if $CFG->enablecssoptimiser has been turned off. This was
239 * the original CSS optimisation library.
240 * It removes whitespace and shrinks things but does no apparent optimisation.
241 * Note the minify library is still being used for JavaScript.
243 * @param array $files An array of files to minify
244 * @return string The minified CSS
246 function css_minify_css($files) {
247 global $CFG;
249 if (empty($files)) {
250 return '';
253 set_include_path($CFG->libdir . '/minify/lib' . PATH_SEPARATOR . get_include_path());
254 require_once('Minify.php');
256 if (0 === stripos(PHP_OS, 'win')) {
257 Minify::setDocRoot(); // IIS may need help
259 // disable all caching, we do it in moodle
260 Minify::setCache(null, false);
262 $options = array(
263 'bubbleCssImports' => false,
264 // Don't gzip content we just want text for storage
265 'encodeOutput' => false,
266 // Maximum age to cache, not used but required
267 'maxAge' => (60*60*24*20),
268 // The files to minify
269 'files' => $files,
270 // Turn orr URI rewriting
271 'rewriteCssUris' => false,
272 // This returns the CSS rather than echoing it for display
273 'quiet' => true
276 $error = 'unknown';
277 try {
278 $result = Minify::serve('Files', $options);
279 if ($result['success']) {
280 return $result['content'];
282 } catch (Exception $e) {
283 $error = $e->getMessage();
284 $error = str_replace("\r", ' ', $error);
285 $error = str_replace("\n", ' ', $error);
288 // minification failed - try to inform the theme developer and include the non-minified version
289 $css = <<<EOD
290 /* Error: $error */
291 /* Problem detected during theme CSS minimisation, please review the following code */
292 /* ================================================================================ */
295 EOD;
296 foreach ($files as $cssfile) {
297 $css .= file_get_contents($cssfile)."\n";
299 return $css;
303 * Determines if the given value is a valid CSS colour.
305 * A CSS colour can be one of the following:
306 * - Hex colour: #AA66BB
307 * - RGB colour: rgb(0-255, 0-255, 0-255)
308 * - RGBA colour: rgba(0-255, 0-255, 0-255, 0-1)
309 * - HSL colour: hsl(0-360, 0-100%, 0-100%)
310 * - HSLA colour: hsla(0-360, 0-100%, 0-100%, 0-1)
312 * Or a recognised browser colour mapping {@see css_optimiser::$htmlcolours}
314 * @param string $value The colour value to check
315 * @return bool
317 function css_is_colour($value) {
318 $value = trim($value);
319 if (in_array(strtolower($value), array('inherit'))) {
320 return true;
321 } else if (preg_match('/^#([a-fA-F0-9]{1,3}|[a-fA-F0-9]{6})$/', $value)) {
322 return true;
323 } else if (in_array(strtolower($value), array_keys(css_optimiser::$htmlcolours))) {
324 return true;
325 } else if (preg_match('#^rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$#i', $value, $m) && $m[1] < 256 && $m[2] < 256 && $m[3] < 256) {
326 // It is an RGB colour
327 return true;
328 } else if (preg_match('#^rgba\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1}(\.\d+)?)\s*\)$#i', $value, $m) && $m[1] < 256 && $m[2] < 256 && $m[3] < 256) {
329 // It is an RGBA colour
330 return true;
331 } else if (preg_match('#^hsl\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\%\s*,\s*(\d{1,3})\%\s*\)$#i', $value, $m) && $m[1] <= 360 && $m[2] <= 100 && $m[3] <= 100) {
332 // It is an HSL colour
333 return true;
334 } else if (preg_match('#^hsla\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\%\s*,\s*(\d{1,3})\%\s*,\s*(\d{1}(\.\d+)?)\s*\)$#i', $value, $m) && $m[1] <= 360 && $m[2] <= 100 && $m[3] <= 100) {
335 // It is an HSLA colour
336 return true;
338 // Doesn't look like a colour.
339 return false;
343 * Returns true is the passed value looks like a CSS width.
344 * In order to pass this test the value must be purely numerical or end with a
345 * valid CSS unit term.
347 * @param string|int $value
348 * @return boolean
350 function css_is_width($value) {
351 $value = trim($value);
352 if (in_array(strtolower($value), array('auto', 'inherit'))) {
353 return true;
355 if (preg_match('#^(\-\s*)?(\d*\.)?(\d+)\s*(em|px|pt|%|in|cm|mm|ex|pc)?$#i', $value)) {
356 return true;
358 return false;
362 * A simple sorting function to sort two array values on the number of items they contain
364 * @param array $a
365 * @param array $b
366 * @return int
368 function css_sort_by_count(array $a, array $b) {
369 $a = count($a);
370 $b = count($b);
371 if ($a == $b) {
372 return 0;
374 return ($a > $b) ? -1 : 1;
378 * A basic CSS optimiser that strips out unwanted things and then processing the
379 * CSS organising styles and moving duplicates and useless CSS.
381 * This CSS optimiser works by reading through a CSS string one character at a
382 * time and building an object structure of the CSS.
383 * As part of that processing styles are expanded out as much as they can be to
384 * ensure we collect all mappings, at the end of the processing those styles are
385 * then combined into an optimised form to keep them as short as possible.
387 * @package core_css
388 * @category css
389 * @copyright 2012 Sam Hemelryk
390 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
392 class css_optimiser {
395 * Used when the processor is about to start processing.
396 * Processing states. Used internally.
398 const PROCESSING_START = 0;
401 * Used when the processor is currently processing a selector.
402 * Processing states. Used internally.
404 const PROCESSING_SELECTORS = 0;
407 * Used when the processor is currently processing a style.
408 * Processing states. Used internally.
410 const PROCESSING_STYLES = 1;
413 * Used when the processor is currently processing a comment.
414 * Processing states. Used internally.
416 const PROCESSING_COMMENT = 2;
419 * Used when the processor is currently processing an @ rule.
420 * Processing states. Used internally.
422 const PROCESSING_ATRULE = 3;
425 * The raw string length before optimisation.
426 * Stats variables set during and after processing
427 * @var int
429 protected $rawstrlen = 0;
432 * The number of comments that were removed during optimisation.
433 * Stats variables set during and after processing
434 * @var int
436 protected $commentsincss = 0;
439 * The number of rules in the CSS before optimisation.
440 * Stats variables set during and after processing
441 * @var int
443 protected $rawrules = 0;
446 * The number of selectors using in CSS rules before optimisation.
447 * Stats variables set during and after processing
448 * @var int
450 protected $rawselectors = 0;
453 * The string length after optimisation.
454 * Stats variables set during and after processing
455 * @var int
457 protected $optimisedstrlen = 0;
460 * The number of rules after optimisation.
461 * Stats variables set during and after processing
462 * @var int
464 protected $optimisedrules = 0;
467 * The number of selectors used in rules after optimisation.
468 * Stats variables set during and after processing
469 * @var int
471 protected $optimisedselectors = 0;
474 * The start time of the optimisation.
475 * Stats variables set during and after processing
476 * @var int
478 protected $timestart = 0;
481 * The end time of the optimisation.
482 * Stats variables set during and after processing
483 * @var int
485 protected $timecomplete = 0;
488 * Will be set to any errors that may have occured during processing.
489 * This is updated only at the end of processing NOT during.
491 * @var array
493 protected $errors = array();
496 * Processes incoming CSS optimising it and then returning it.
498 * @param string $css The raw CSS to optimise
499 * @return string The optimised CSS
501 public function process($css) {
502 global $CFG;
504 $this->reset_stats();
505 $this->timestart = microtime(true);
506 $this->rawstrlen = strlen($css);
508 // First up we need to remove all line breaks - this allows us to instantly
509 // reduce our processing requirements and as we will process everything
510 // into a new structure there's really nothing lost.
511 $css = preg_replace('#\r?\n#', ' ', $css);
513 // Next remove the comments... no need to them in an optimised world and
514 // knowing they're all gone allows us to REALLY make our processing simpler
515 $css = preg_replace('#/\*(.*?)\*/#m', '', $css, -1, $this->commentsincss);
517 $medias = array(
518 'all' => new css_media()
520 $imports = array();
521 $charset = false;
523 $currentprocess = self::PROCESSING_START;
524 $currentrule = css_rule::init();
525 $currentselector = css_selector::init();
526 $inquotes = false; // ' or "
527 $inbraces = false; // {
528 $inbrackets = false; // [
529 $inparenthesis = false; // (
530 $currentmedia = $medias['all'];
531 $currentatrule = null;
532 $suspectatrule = false;
534 $buffer = '';
535 $char = null;
537 // Next we are going to iterate over every single character in $css.
538 // This is why we removed line breaks and comments!
539 for ($i = 0; $i < $this->rawstrlen; $i++) {
540 $lastchar = $char;
541 $char = substr($css, $i, 1);
542 if ($char == '@' && $buffer == '') {
543 $suspectatrule = true;
545 switch ($currentprocess) {
546 // Start processing an at rule e.g. @media, @page
547 case self::PROCESSING_ATRULE:
548 switch ($char) {
549 case ';':
550 if (!$inbraces) {
551 $buffer .= $char;
552 if ($currentatrule == 'import') {
553 $imports[] = $buffer;
554 $currentprocess = self::PROCESSING_SELECTORS;
555 } else if ($currentatrule == 'charset') {
556 $charset = $buffer;
557 $currentprocess = self::PROCESSING_SELECTORS;
560 $buffer = '';
561 $currentatrule = false;
562 // continue 1: The switch processing chars
563 // continue 2: The switch processing the state
564 // continue 3: The for loop
565 continue 3;
566 case '{':
567 if ($currentatrule == 'media' && preg_match('#\s*@media\s*([a-zA-Z0-9]+(\s*,\s*[a-zA-Z0-9]+)*)#', $buffer, $matches)) {
568 $mediatypes = str_replace(' ', '', $matches[1]);
569 if (!array_key_exists($mediatypes, $medias)) {
570 $medias[$mediatypes] = new css_media($mediatypes);
572 $currentmedia = $medias[$mediatypes];
573 $currentprocess = self::PROCESSING_SELECTORS;
574 $buffer = '';
576 // continue 1: The switch processing chars
577 // continue 2: The switch processing the state
578 // continue 3: The for loop
579 continue 3;
581 break;
582 // Start processing selectors
583 case self::PROCESSING_START:
584 case self::PROCESSING_SELECTORS:
585 switch ($char) {
586 case '[':
587 $inbrackets ++;
588 $buffer .= $char;
589 // continue 1: The switch processing chars
590 // continue 2: The switch processing the state
591 // continue 3: The for loop
592 continue 3;
593 case ']':
594 $inbrackets --;
595 $buffer .= $char;
596 // continue 1: The switch processing chars
597 // continue 2: The switch processing the state
598 // continue 3: The for loop
599 continue 3;
600 case ' ':
601 if ($inbrackets) {
602 // continue 1: The switch processing chars
603 // continue 2: The switch processing the state
604 // continue 3: The for loop
605 continue 3;
607 if (!empty($buffer)) {
608 if ($suspectatrule && preg_match('#@(media|import|charset)\s*#', $buffer, $matches)) {
609 $currentatrule = $matches[1];
610 $currentprocess = self::PROCESSING_ATRULE;
611 $buffer .= $char;
612 } else {
613 $currentselector->add($buffer);
614 $buffer = '';
617 $suspectatrule = false;
618 // continue 1: The switch processing chars
619 // continue 2: The switch processing the state
620 // continue 3: The for loop
621 continue 3;
622 case '{':
623 if ($inbrackets) {
624 // continue 1: The switch processing chars
625 // continue 2: The switch processing the state
626 // continue 3: The for loop
627 continue 3;
630 $currentselector->add($buffer);
631 $currentrule->add_selector($currentselector);
632 $currentselector = css_selector::init();
633 $currentprocess = self::PROCESSING_STYLES;
635 $buffer = '';
636 // continue 1: The switch processing chars
637 // continue 2: The switch processing the state
638 // continue 3: The for loop
639 continue 3;
640 case '}':
641 if ($inbrackets) {
642 // continue 1: The switch processing chars
643 // continue 2: The switch processing the state
644 // continue 3: The for loop
645 continue 3;
647 if ($currentatrule == 'media') {
648 $currentmedia = $medias['all'];
649 $currentatrule = false;
650 $buffer = '';
652 // continue 1: The switch processing chars
653 // continue 2: The switch processing the state
654 // continue 3: The for loop
655 continue 3;
656 case ',':
657 if ($inbrackets) {
658 // continue 1: The switch processing chars
659 // continue 2: The switch processing the state
660 // continue 3: The for loop
661 continue 3;
663 $currentselector->add($buffer);
664 $currentrule->add_selector($currentselector);
665 $currentselector = css_selector::init();
666 $buffer = '';
667 // continue 1: The switch processing chars
668 // continue 2: The switch processing the state
669 // continue 3: The for loop
670 continue 3;
672 break;
673 // Start processing styles
674 case self::PROCESSING_STYLES:
675 if ($char == '"' || $char == "'") {
676 if ($inquotes === false) {
677 $inquotes = $char;
679 if ($inquotes === $char && $lastchar !== '\\') {
680 $inquotes = false;
683 if ($inquotes) {
684 $buffer .= $char;
685 continue 2;
687 switch ($char) {
688 case ';':
689 if ($inparenthesis) {
690 $buffer .= $char;
691 // continue 1: The switch processing chars
692 // continue 2: The switch processing the state
693 // continue 3: The for loop
694 continue 3;
696 $currentrule->add_style($buffer);
697 $buffer = '';
698 $inquotes = false;
699 // continue 1: The switch processing chars
700 // continue 2: The switch processing the state
701 // continue 3: The for loop
702 continue 3;
703 case '}':
704 $currentrule->add_style($buffer);
705 $this->rawselectors += $currentrule->get_selector_count();
707 $currentmedia->add_rule($currentrule);
709 $currentrule = css_rule::init();
710 $currentprocess = self::PROCESSING_SELECTORS;
711 $this->rawrules++;
712 $buffer = '';
713 $inquotes = false;
714 $inparenthesis = false;
715 // continue 1: The switch processing chars
716 // continue 2: The switch processing the state
717 // continue 3: The for loop
718 continue 3;
719 case '(':
720 $inparenthesis = true;
721 $buffer .= $char;
722 // continue 1: The switch processing chars
723 // continue 2: The switch processing the state
724 // continue 3: The for loop
725 continue 3;
726 case ')':
727 $inparenthesis = false;
728 $buffer .= $char;
729 // continue 1: The switch processing chars
730 // continue 2: The switch processing the state
731 // continue 3: The for loop
732 continue 3;
734 break;
736 $buffer .= $char;
739 $css = '';
740 if (!empty($charset)) {
741 $imports[] = $charset;
743 if (!empty($imports)) {
744 $css .= implode("\n", $imports);
745 $css .= "\n\n";
747 foreach ($medias as $media) {
748 $media->organise_rules_by_selectors();
749 $this->optimisedrules += $media->count_rules();
750 $this->optimisedselectors += $media->count_selectors();
751 if ($media->has_errors()) {
752 $this->errors[] = $media->get_errors();
754 $css .= $media->out();
756 $this->optimisedstrlen = strlen($css);
758 $this->timecomplete = microtime(true);
759 return trim($css);
763 * Returns an array of stats from the last processing run
764 * @return string
766 public function get_stats() {
767 $stats = array(
768 'timestart' => $this->timestart,
769 'timecomplete' => $this->timecomplete,
770 'timetaken' => round($this->timecomplete - $this->timestart, 4),
771 'commentsincss' => $this->commentsincss,
772 'rawstrlen' => $this->rawstrlen,
773 'rawselectors' => $this->rawselectors,
774 'rawrules' => $this->rawrules,
775 'optimisedstrlen' => $this->optimisedstrlen,
776 'optimisedrules' => $this->optimisedrules,
777 'optimisedselectors' => $this->optimisedselectors,
778 'improvementstrlen' => round(100 - ($this->optimisedstrlen / $this->rawstrlen) * 100, 1).'%',
779 'improvementrules' => round(100 - ($this->optimisedrules / $this->rawrules) * 100, 1).'%',
780 'improvementselectors' => round(100 - ($this->optimisedselectors / $this->rawselectors) * 100, 1).'%',
782 return $stats;
786 * Returns true if any errors have occured during processing
788 * @return bool
790 public function has_errors() {
791 return !empty($this->errors);
795 * Returns an array of errors that have occured
797 * @return array
799 public function get_errors() {
800 return $this->errors;
804 * Returns any errors as a string that can be included in CSS.
806 * @return string
808 public function output_errors_css() {
809 $computedcss = "/****************************************\n";
810 $computedcss .= " *--- Errors found during processing ----\n";
811 foreach ($this->errors as $error) {
812 $computedcss .= preg_replace('#^#m', '* ', $error);
814 $computedcss .= " ****************************************/\n\n";
815 return $computedcss;
819 * Returns a string to display stats about the last generation within CSS output
821 * @return string
823 public function output_stats_css() {
824 $stats = $this->get_stats();
826 $strlenimprovement = round(100 - ($this->optimisedstrlen / $this->rawstrlen) * 100, 1);
827 $ruleimprovement = round(100 - ($this->optimisedrules / $this->rawrules) * 100, 1);
828 $selectorimprovement = round(100 - ($this->optimisedselectors / $this->rawselectors) * 100, 1);
829 $timetaken = round($this->timecomplete - $this->timestart, 4);
831 $computedcss = "/****************************************\n";
832 $computedcss .= " *------- CSS Optimisation stats --------\n";
833 $computedcss .= " * ".date('r')."\n";
834 $computedcss .= " * {$stats['commentsincss']} \t comments removed\n";
835 $computedcss .= " * Optimisation took {$stats['timetaken']} seconds\n";
836 $computedcss .= " *--------------- before ----------------\n";
837 $computedcss .= " * {$stats['rawstrlen']} \t chars read in\n";
838 $computedcss .= " * {$stats['rawrules']} \t rules read in\n";
839 $computedcss .= " * {$stats['rawselectors']} \t total selectors\n";
840 $computedcss .= " *---------------- after ----------------\n";
841 $computedcss .= " * {$stats['optimisedstrlen']} \t chars once optimized\n";
842 $computedcss .= " * {$stats['optimisedrules']} \t optimized rules\n";
843 $computedcss .= " * {$stats['optimisedselectors']} \t total selectors once optimized\n";
844 $computedcss .= " *---------------- stats ----------------\n";
845 $computedcss .= " * {$stats['improvementstrlen']} \t reduction in chars\n";
846 $computedcss .= " * {$stats['improvementrules']} \t reduction in rules\n";
847 $computedcss .= " * {$stats['improvementselectors']} \t reduction in selectors\n";
848 $computedcss .= " ****************************************/\n\n";
850 return $computedcss;
854 * Resets the stats ready for another fresh processing
856 public function reset_stats() {
857 $this->commentsincss = 0;
858 $this->optimisedrules = 0;
859 $this->optimisedselectors = 0;
860 $this->optimisedstrlen = 0;
861 $this->rawrules = 0;
862 $this->rawselectors = 0;
863 $this->rawstrlen = 0;
864 $this->timecomplete = 0;
865 $this->timestart = 0;
869 * An array of the common HTML colours that are supported by most browsers.
871 * This reference table is used to allow us to unify colours, and will aid
872 * us in identifying buggy CSS using unsupported colours.
874 * @staticvar array
875 * @var array
877 public static $htmlcolours = array(
878 'aliceblue' => '#F0F8FF',
879 'antiquewhite' => '#FAEBD7',
880 'aqua' => '#00FFFF',
881 'aquamarine' => '#7FFFD4',
882 'azure' => '#F0FFFF',
883 'beige' => '#F5F5DC',
884 'bisque' => '#FFE4C4',
885 'black' => '#000000',
886 'blanchedalmond' => '#FFEBCD',
887 'blue' => '#0000FF',
888 'blueviolet' => '#8A2BE2',
889 'brown' => '#A52A2A',
890 'burlywood' => '#DEB887',
891 'cadetblue' => '#5F9EA0',
892 'chartreuse' => '#7FFF00',
893 'chocolate' => '#D2691E',
894 'coral' => '#FF7F50',
895 'cornflowerblue' => '#6495ED',
896 'cornsilk' => '#FFF8DC',
897 'crimson' => '#DC143C',
898 'cyan' => '#00FFFF',
899 'darkblue' => '#00008B',
900 'darkcyan' => '#008B8B',
901 'darkgoldenrod' => '#B8860B',
902 'darkgray' => '#A9A9A9',
903 'darkgrey' => '#A9A9A9',
904 'darkgreen' => '#006400',
905 'darkKhaki' => '#BDB76B',
906 'darkmagenta' => '#8B008B',
907 'darkolivegreen' => '#556B2F',
908 'arkorange' => '#FF8C00',
909 'darkorchid' => '#9932CC',
910 'darkred' => '#8B0000',
911 'darksalmon' => '#E9967A',
912 'darkseagreen' => '#8FBC8F',
913 'darkslateblue' => '#483D8B',
914 'darkslategray' => '#2F4F4F',
915 'darkslategrey' => '#2F4F4F',
916 'darkturquoise' => '#00CED1',
917 'darkviolet' => '#9400D3',
918 'deeppink' => '#FF1493',
919 'deepskyblue' => '#00BFFF',
920 'dimgray' => '#696969',
921 'dimgrey' => '#696969',
922 'dodgerblue' => '#1E90FF',
923 'firebrick' => '#B22222',
924 'floralwhite' => '#FFFAF0',
925 'forestgreen' => '#228B22',
926 'fuchsia' => '#FF00FF',
927 'gainsboro' => '#DCDCDC',
928 'ghostwhite' => '#F8F8FF',
929 'gold' => '#FFD700',
930 'goldenrod' => '#DAA520',
931 'gray' => '#808080',
932 'grey' => '#808080',
933 'green' => '#008000',
934 'greenyellow' => '#ADFF2F',
935 'honeydew' => '#F0FFF0',
936 'hotpink' => '#FF69B4',
937 'indianred ' => '#CD5C5C',
938 'indigo ' => '#4B0082',
939 'ivory' => '#FFFFF0',
940 'khaki' => '#F0E68C',
941 'lavender' => '#E6E6FA',
942 'lavenderblush' => '#FFF0F5',
943 'lawngreen' => '#7CFC00',
944 'lemonchiffon' => '#FFFACD',
945 'lightblue' => '#ADD8E6',
946 'lightcoral' => '#F08080',
947 'lightcyan' => '#E0FFFF',
948 'lightgoldenrodyellow' => '#FAFAD2',
949 'lightgray' => '#D3D3D3',
950 'lightgrey' => '#D3D3D3',
951 'lightgreen' => '#90EE90',
952 'lightpink' => '#FFB6C1',
953 'lightsalmon' => '#FFA07A',
954 'lightseagreen' => '#20B2AA',
955 'lightskyblue' => '#87CEFA',
956 'lightslategray' => '#778899',
957 'lightslategrey' => '#778899',
958 'lightsteelblue' => '#B0C4DE',
959 'lightyellow' => '#FFFFE0',
960 'lime' => '#00FF00',
961 'limegreen' => '#32CD32',
962 'linen' => '#FAF0E6',
963 'magenta' => '#FF00FF',
964 'maroon' => '#800000',
965 'mediumaquamarine' => '#66CDAA',
966 'mediumblue' => '#0000CD',
967 'mediumorchid' => '#BA55D3',
968 'mediumpurple' => '#9370D8',
969 'mediumseagreen' => '#3CB371',
970 'mediumslateblue' => '#7B68EE',
971 'mediumspringgreen' => '#00FA9A',
972 'mediumturquoise' => '#48D1CC',
973 'mediumvioletred' => '#C71585',
974 'midnightblue' => '#191970',
975 'mintcream' => '#F5FFFA',
976 'mistyrose' => '#FFE4E1',
977 'moccasin' => '#FFE4B5',
978 'navajowhite' => '#FFDEAD',
979 'navy' => '#000080',
980 'oldlace' => '#FDF5E6',
981 'olive' => '#808000',
982 'olivedrab' => '#6B8E23',
983 'orange' => '#FFA500',
984 'orangered' => '#FF4500',
985 'orchid' => '#DA70D6',
986 'palegoldenrod' => '#EEE8AA',
987 'palegreen' => '#98FB98',
988 'paleturquoise' => '#AFEEEE',
989 'palevioletred' => '#D87093',
990 'papayawhip' => '#FFEFD5',
991 'peachpuff' => '#FFDAB9',
992 'peru' => '#CD853F',
993 'pink' => '#FFC0CB',
994 'plum' => '#DDA0DD',
995 'powderblue' => '#B0E0E6',
996 'purple' => '#800080',
997 'red' => '#FF0000',
998 'rosybrown' => '#BC8F8F',
999 'royalblue' => '#4169E1',
1000 'saddlebrown' => '#8B4513',
1001 'salmon' => '#FA8072',
1002 'sandybrown' => '#F4A460',
1003 'seagreen' => '#2E8B57',
1004 'seashell' => '#FFF5EE',
1005 'sienna' => '#A0522D',
1006 'silver' => '#C0C0C0',
1007 'skyblue' => '#87CEEB',
1008 'slateblue' => '#6A5ACD',
1009 'slategray' => '#708090',
1010 'slategrey' => '#708090',
1011 'snow' => '#FFFAFA',
1012 'springgreen' => '#00FF7F',
1013 'steelblue' => '#4682B4',
1014 'tan' => '#D2B48C',
1015 'teal' => '#008080',
1016 'thistle' => '#D8BFD8',
1017 'tomato' => '#FF6347',
1018 'transparent' => 'transparent',
1019 'turquoise' => '#40E0D0',
1020 'violet' => '#EE82EE',
1021 'wheat' => '#F5DEB3',
1022 'white' => '#FFFFFF',
1023 'whitesmoke' => '#F5F5F5',
1024 'yellow' => '#FFFF00',
1025 'yellowgreen' => '#9ACD32'
1030 * Used to prepare CSS strings
1032 * @package core_css
1033 * @category css
1034 * @copyright 2012 Sam Hemelryk
1035 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1037 abstract class css_writer {
1040 * The current indent level
1041 * @var int
1043 protected static $indent = 0;
1046 * Returns true if the output should still maintain minimum formatting.
1047 * @return bool
1049 protected static function is_pretty() {
1050 global $CFG;
1051 return (!empty($CFG->cssoptimiserpretty));
1055 * Returns the indenting char to use for indenting things nicely.
1056 * @return string
1058 protected static function get_indent() {
1059 if (self::is_pretty()) {
1060 return str_repeat(" ", self::$indent);
1062 return '';
1066 * Increases the current indent
1068 protected static function increase_indent() {
1069 self::$indent++;
1073 * Decreases the current indent
1075 protected static function decrease_indent() {
1076 self::$indent--;
1080 * Returns the string to use as a separator
1081 * @return string
1083 protected static function get_separator() {
1084 return (self::is_pretty())?"\n":' ';
1088 * Returns CSS for media
1090 * @param string $typestring
1091 * @param array $rules An array of css_rule objects
1092 * @return string
1094 public static function media($typestring, array &$rules) {
1095 $nl = self::get_separator();
1097 $output = '';
1098 if ($typestring !== 'all') {
1099 $output .= $nl.$nl."@media {$typestring} {".$nl;
1100 self::increase_indent();
1102 foreach ($rules as $rule) {
1103 $output .= $rule->out().$nl;
1105 if ($typestring !== 'all') {
1106 self::decrease_indent();
1107 $output .= '}';
1109 return $output;
1113 * Returns CSS for a rule
1115 * @param string $selector
1116 * @param string $styles
1117 * @return string
1119 public static function rule($selector, $styles) {
1120 $css = self::get_indent()."{$selector}{{$styles}}";
1121 return $css;
1125 * Returns CSS for the selectors of a rule
1127 * @param array $selectors Array of css_selector objects
1128 * @return string
1130 public static function selectors(array $selectors) {
1131 $nl = self::get_separator();
1132 $selectorstrings = array();
1133 foreach ($selectors as $selector) {
1134 $selectorstrings[] = $selector->out();
1136 return join(','.$nl, $selectorstrings);
1140 * Returns a selector given the components that make it up.
1142 * @param array $components
1143 * @return string
1145 public static function selector(array $components) {
1146 return trim(join(' ', $components));
1150 * Returns a CSS string for the provided styles
1152 * @param array $styles Array of css_style objects
1153 * @return string
1155 public static function styles(array $styles) {
1156 $bits = array();
1157 foreach ($styles as $style) {
1158 $bits[] = $style->out();
1160 return join('', $bits);
1164 * Returns a style CSS
1166 * @param string $name
1167 * @param string $value
1168 * @param bool $important
1169 * @return string
1171 public static function style($name, $value, $important = false) {
1172 if ($important && strpos($value, '!important') === false) {
1173 $value .= ' !important';
1175 return "{$name}:{$value};";
1180 * A structure to represent a CSS selector.
1182 * The selector is the classes, id, elements, and psuedo bits that make up a CSS
1183 * rule.
1185 * @package core_css
1186 * @category css
1187 * @copyright 2012 Sam Hemelryk
1188 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1190 class css_selector {
1193 * An array of selector bits
1194 * @var array
1196 protected $selectors = array();
1199 * The number of selectors.
1200 * @var int
1202 protected $count = 0;
1205 * Initialises a new CSS selector
1206 * @return css_selector
1208 public static function init() {
1209 return new css_selector();
1213 * CSS selectors can only be created through the init method above.
1215 protected function __construct() {}
1218 * Adds a selector to the end of the current selector
1219 * @param string $selector
1221 public function add($selector) {
1222 $selector = trim($selector);
1223 $count = 0;
1224 $count += preg_match_all('/(\.|#)/', $selector, $matchesarray);
1225 if (strpos($selector, '.') !== 0 && strpos($selector, '#') !== 0) {
1226 $count ++;
1228 $this->count = $count;
1229 $this->selectors[] = $selector;
1232 * Returns the number of individual components that make up this selector
1233 * @return int
1235 public function get_selector_count() {
1236 return $this->count;
1240 * Returns the selector for use in a CSS rule
1241 * @return string
1243 public function out() {
1244 return css_writer::selector($this->selectors);
1249 * A structure to represent a CSS rule.
1251 * @package core_css
1252 * @category css
1253 * @copyright 2012 Sam Hemelryk
1254 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1256 class css_rule {
1259 * An array of CSS selectors {@see css_selector}
1260 * @var array
1262 protected $selectors = array();
1265 * An array of CSS styles {@see css_style}
1266 * @var array
1268 protected $styles = array();
1271 * Created a new CSS rule. This is the only way to create a new CSS rule externally.
1272 * @return css_rule
1274 public static function init() {
1275 return new css_rule();
1279 * Constructs a new css rule.
1281 * @param string $selector The selector or array of selectors that make up this rule.
1282 * @param array $styles An array of styles that belong to this rule.
1284 protected function __construct($selector = null, array $styles = array()) {
1285 if ($selector != null) {
1286 if (is_array($selector)) {
1287 $this->selectors = $selector;
1288 } else {
1289 $this->selectors = array($selector);
1291 $this->add_styles($styles);
1296 * Adds a new CSS selector to this rule
1298 * e.g. $rule->add_selector('.one #two.two');
1300 * @param css_selector $selector Adds a CSS selector to this rule.
1302 public function add_selector(css_selector $selector) {
1303 $this->selectors[] = $selector;
1307 * Adds a new CSS style to this rule.
1309 * @param css_style|string $style Adds a new style to this rule
1311 public function add_style($style) {
1312 if (is_string($style)) {
1313 $style = trim($style);
1314 if (empty($style)) {
1315 return;
1317 $bits = explode(':', $style, 2);
1318 if (count($bits) == 2) {
1319 list($name, $value) = array_map('trim', $bits);
1321 if (isset($name) && isset($value) && $name !== '' && $value !== '') {
1322 $style = css_style::init_automatic($name, $value);
1324 } else if ($style instanceof css_style) {
1325 // Clone the style as it may be coming from another rule and we don't
1326 // want references as it will likely be overwritten by proceeding
1327 // rules
1328 $style = clone($style);
1330 if ($style instanceof css_style) {
1331 $name = $style->get_name();
1332 if (array_key_exists($name, $this->styles)) {
1333 $this->styles[$name]->set_value($style->get_value());
1334 } else {
1335 $this->styles[$name] = $style;
1337 } else if (is_array($style)) {
1338 // We probably shouldn't worry about processing styles here but to
1339 // be truthful it doesn't hurt.
1340 foreach ($style as $astyle) {
1341 $this->add_style($astyle);
1347 * An easy method of adding several styles at once. Just calls add_style.
1349 * This method simply iterates over the array and calls {@see css_rule::add_style()}
1350 * with each.
1352 * @param array $styles Adds an array of styles
1354 public function add_styles(array $styles) {
1355 foreach ($styles as $style) {
1356 $this->add_style($style);
1361 * Returns the array of selectors
1363 * @return array
1365 public function get_selectors() {
1366 return $this->selectors;
1370 * Returns the array of styles
1372 * @return array
1374 public function get_styles() {
1375 return $this->styles;
1379 * Outputs this rule as a fragment of CSS
1381 * @return string
1383 public function out() {
1384 $selectors = css_writer::selectors($this->selectors);
1385 $styles = css_writer::styles($this->get_consolidated_styles());
1386 return css_writer::rule($selectors, $styles);
1390 * Consolidates all styles associated with this rule
1392 * @return array An array of consolidated styles
1394 public function get_consolidated_styles() {
1395 $finalstyles = array();
1396 $consolidate = array();
1397 foreach ($this->styles as $style) {
1398 $consolidatetoclass = $style->consolidate_to();
1399 if ($style->is_valid() && !empty($consolidatetoclass) && class_exists('css_style_'.$consolidatetoclass)) {
1400 $class = 'css_style_'.$consolidatetoclass;
1401 if (!array_key_exists($class, $consolidate)) {
1402 $consolidate[$class] = array();
1404 $consolidate[$class][] = $style;
1405 } else {
1406 $finalstyles[] = $style;
1410 foreach ($consolidate as $class => $styles) {
1411 $styles = $class::consolidate($styles);
1412 foreach ($styles as $style) {
1413 $finalstyles[] = $style;
1416 return $finalstyles;
1420 * Splits this rules into an array of CSS rules. One for each of the selectors
1421 * that make up this rule.
1423 * @return array(css_rule)
1425 public function split_by_selector() {
1426 $return = array();
1427 foreach ($this->selectors as $selector) {
1428 $return[] = new css_rule($selector, $this->styles);
1430 return $return;
1434 * Splits this rule into an array of rules. One for each of the styles that
1435 * make up this rule
1437 * @return array Array of css_rule objects
1439 public function split_by_style() {
1440 $return = array();
1441 foreach ($this->styles as $style) {
1442 $return[] = new css_rule($this->selectors, array($style));
1444 return $return;
1448 * Gets a hash for the styles of this rule
1450 * @return string
1452 public function get_style_hash() {
1453 return md5(css_writer::styles($this->styles));
1457 * Gets a hash for the selectors of this rule
1459 * @return string
1461 public function get_selector_hash() {
1462 return md5(css_writer::selectors($this->selectors));
1466 * Gets the number of selectors that make up this rule.
1468 * @return int
1470 public function get_selector_count() {
1471 $count = 0;
1472 foreach ($this->selectors as $selector) {
1473 $count += $selector->get_selector_count();
1475 return $count;
1479 * Returns true if there are any errors with this rule.
1481 * @return bool
1483 public function has_errors() {
1484 foreach ($this->styles as $style) {
1485 if ($style->has_error()) {
1486 return true;
1489 return false;
1493 * Returns the error strings that were recorded when processing this rule.
1495 * Before calling this function you should first call {@see css_rule::has_errors()}
1496 * to make sure there are errors (hopefully there arn't).
1498 * @return string
1500 public function get_error_string() {
1501 $css = $this->out();
1502 $errors = array();
1503 foreach ($this->styles as $style) {
1504 if ($style->has_error()) {
1505 $errors[] = " * ".$style->get_last_error();
1508 return $css." has the following errors:\n".join("\n", $errors);
1514 * A media class to organise rules by the media they apply to.
1516 * @package core_css
1517 * @category css
1518 * @copyright 2012 Sam Hemelryk
1519 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1521 class css_media {
1524 * An array of the different media types this instance applies to.
1525 * @var array
1527 protected $types = array();
1530 * An array of rules within this media instance
1531 * @var array
1533 protected $rules = array();
1536 * Initalises a new media instance
1538 * @param string $for The media that the contained rules are destined for.
1540 public function __construct($for = 'all') {
1541 $types = explode(',', $for);
1542 $this->types = array_map('trim', $types);
1546 * Adds a new CSS rule to this media instance
1548 * @param css_rule $newrule
1550 public function add_rule(css_rule $newrule) {
1551 foreach ($newrule->split_by_selector() as $rule) {
1552 $hash = $rule->get_selector_hash();
1553 if (!array_key_exists($hash, $this->rules)) {
1554 $this->rules[$hash] = $rule;
1555 } else {
1556 $this->rules[$hash]->add_styles($rule->get_styles());
1562 * Returns the rules used by this
1564 * @return array
1566 public function get_rules() {
1567 return $this->rules;
1571 * Organises rules by gropuing selectors based upon the styles and consolidating
1572 * those selectors into single rules.
1574 * @return bool True if the CSS was optimised by this method
1576 public function organise_rules_by_selectors() {
1577 $optimised = array();
1578 $beforecount = count($this->rules);
1579 foreach ($this->rules as $rule) {
1580 $hash = $rule->get_style_hash();
1581 if (!array_key_exists($hash, $optimised)) {
1582 $optimised[$hash] = clone($rule);
1583 } else {
1584 foreach ($rule->get_selectors() as $selector) {
1585 $optimised[$hash]->add_selector($selector);
1589 $this->rules = $optimised;
1590 $aftercount = count($this->rules);
1591 return ($beforecount < $aftercount);
1595 * Returns the total number of rules that exist within this media set
1597 * @return int
1599 public function count_rules() {
1600 return count($this->rules);
1604 * Returns the total number of selectors that exist within this media set
1606 * @return int
1608 public function count_selectors() {
1609 $count = 0;
1610 foreach ($this->rules as $rule) {
1611 $count += $rule->get_selector_count();
1613 return $count;
1617 * Returns the CSS for this media and all of its rules.
1619 * @return string
1621 public function out() {
1622 return css_writer::media(join(',', $this->types), $this->rules);
1626 * Returns an array of media that this media instance applies to
1628 * @return array
1630 public function get_types() {
1631 return $this->types;
1635 * Returns true if the media has any rules that have errors
1637 * @return boolean
1639 public function has_errors() {
1640 foreach ($this->rules as $rule) {
1641 if ($rule->has_errors()) {
1642 return true;
1645 return false;
1649 * Returns any errors that have happened within rules in this media set.
1651 * @return string
1653 public function get_errors() {
1654 $errors = array();
1655 foreach ($this->rules as $rule) {
1656 if ($rule->has_errors()) {
1657 $errors[] = $rule->get_error_string();
1660 return join("\n", $errors);
1665 * An absract class to represent CSS styles
1667 * @package core_css
1668 * @category css
1669 * @copyright 2012 Sam Hemelryk
1670 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1672 abstract class css_style {
1675 * The name of the style
1676 * @var string
1678 protected $name;
1681 * The value for the style
1682 * @var mixed
1684 protected $value;
1687 * If set to true this style was defined with the !important rule.
1688 * Only trolls use !important.
1689 * Don't hide under bridges.. its not good for your skin. Do the proper thing
1690 * and fix the issue don't just force a fix that will undoubtedly one day
1691 * lead to further frustration.
1692 * @var bool
1694 protected $important = false;
1697 * Gets set to true if this style has an error
1698 * @var bool
1700 protected $error = false;
1703 * The last error message that occured
1704 * @var string
1706 protected $errormessage = null;
1710 * Initialises a new style.
1712 * This is the only public way to create a style to ensure they that appropriate
1713 * style class is used if it exists.
1715 * @param string $name The name of the style.
1716 * @param string $value The value of the style.
1717 * @return css_style_generic
1719 public static function init_automatic($name, $value) {
1720 $specificclass = 'css_style_'.preg_replace('#[^a-zA-Z0-9]+#', '', $name);
1721 if (class_exists($specificclass)) {
1722 return $specificclass::init($value);
1724 return new css_style_generic($name, $value);
1728 * Creates a new style when given its name and value
1730 * @param string $name The name of the style.
1731 * @param string $value The value of the style.
1733 protected function __construct($name, $value) {
1734 $this->name = $name;
1735 $this->set_value($value);
1739 * Sets the value for the style
1741 * @param string $value
1743 final public function set_value($value) {
1744 $value = trim($value);
1745 $important = preg_match('#(\!important\s*;?\s*)$#', $value, $matches);
1746 if ($important) {
1747 $value = substr($value, 0, -(strlen($matches[1])));
1749 if (!$this->important || $important) {
1750 $this->value = $this->clean_value($value);
1751 $this->important = $important;
1753 if (!$this->is_valid()) {
1754 $this->set_error('Invalid value for '.$this->name);
1759 * Returns true if the value associated with this style is valid
1761 * @return bool
1763 public function is_valid() {
1764 return true;
1768 * Returns the name for the style
1770 * @return string
1772 public function get_name() {
1773 return $this->name;
1777 * Returns the value for the style
1779 * @return string
1781 public function get_value() {
1782 $value = $this->value;
1783 if ($this->important) {
1784 $value .= ' !important';
1786 return $value;
1790 * Returns the style ready for use in CSS
1792 * @param string|null $value A value to use to override the value for this style.
1793 * @return string
1795 public function out($value = null) {
1796 if (is_null($value)) {
1797 $value = $this->get_value();
1799 return css_writer::style($this->name, $value, $this->important);
1803 * This can be overridden by a specific style allowing it to clean its values
1804 * consistently.
1806 * @param mixed $value
1807 * @return mixed
1809 protected function clean_value($value) {
1810 return $value;
1814 * If this particular style can be consolidated into another style this function
1815 * should return the style that it can be consolidated into.
1817 * @return string|null
1819 public function consolidate_to() {
1820 return null;
1824 * Sets the last error message.
1826 * @param string $message
1828 protected function set_error($message) {
1829 $this->error = true;
1830 $this->errormessage = $message;
1834 * Returns true if an error has occured
1836 * @return bool
1838 public function has_error() {
1839 return $this->error;
1843 * Returns the last error that occured or null if no errors have happened.
1845 * @return string
1847 public function get_last_error() {
1848 return $this->errormessage;
1853 * A generic CSS style class to use when a more specific class does not exist.
1855 * @package core_css
1856 * @category css
1857 * @copyright 2012 Sam Hemelryk
1858 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1860 class css_style_generic extends css_style {
1863 * Cleans incoming values for typical things that can be optimised.
1865 * @param mixed $value Cleans the provided value optimising it if possible
1866 * @return string
1868 protected function clean_value($value) {
1869 if (trim($value) == '0px') {
1870 $value = 0;
1871 } else if (preg_match('/^#([a-fA-F0-9]{3,6})/', $value, $matches)) {
1872 $value = '#'.strtoupper($matches[1]);
1874 return $value;
1879 * A colour CSS style
1881 * @package core_css
1882 * @category css
1883 * @copyright 2012 Sam Hemelryk
1884 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1886 class css_style_color extends css_style {
1889 * Creates a new colour style
1891 * @param mixed $value Initialises a new colour style
1892 * @return css_style_color
1894 public static function init($value) {
1895 return new css_style_color('color', $value);
1899 * Cleans the colour unifing it to a 6 char hash colour if possible
1900 * Doing this allows us to associate identical colours being specified in
1901 * different ways. e.g. Red, red, #F00, and #F00000
1903 * @param mixed $value Cleans the provided value optimising it if possible
1904 * @return string
1906 protected function clean_value($value) {
1907 $value = trim($value);
1908 if (css_is_colour($value)) {
1909 if (preg_match('/#([a-fA-F0-9]{6})/', $value, $matches)) {
1910 $value = '#'.strtoupper($matches[1]);
1911 } else if (preg_match('/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/', $value, $matches)) {
1912 $value = $matches[1] . $matches[1] . $matches[2] . $matches[2] . $matches[3] . $matches[3];
1913 $value = '#'.strtoupper($value);
1914 } else if (array_key_exists(strtolower($value), css_optimiser::$htmlcolours)) {
1915 $value = css_optimiser::$htmlcolours[strtolower($value)];
1918 return $value;
1922 * Returns the colour style for use within CSS.
1923 * Will return an optimised hash colour.
1925 * e.g #123456
1926 * #123 instead of #112233
1927 * #F00 instead of red
1929 * @param string $overridevalue If provided then this value will be used instead
1930 * of the styles current value.
1931 * @return string
1933 public function out($overridevalue = null) {
1934 if ($overridevalue === null) {
1935 $overridevalue = $this->value;
1937 return parent::out(self::shrink_value($overridevalue));
1941 * Shrinks the colour value is possible.
1943 * @param string $value Shrinks the current value to an optimial form if possible
1944 * @return string
1946 public static function shrink_value($value) {
1947 if (preg_match('/#([a-fA-F0-9])\1([a-fA-F0-9])\2([a-fA-F0-9])\3/', $value, $matches)) {
1948 return '#'.$matches[1].$matches[2].$matches[3];
1950 return $value;
1954 * Returns true if the value is a valid colour.
1956 * @return bool
1958 public function is_valid() {
1959 return css_is_colour($this->value);
1964 * A width style
1966 * @package core_css
1967 * @category css
1968 * @copyright 2012 Sam Hemelryk
1969 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1971 class css_style_width extends css_style {
1974 * Checks if the width is valid
1975 * @return bool
1977 public function is_valid() {
1978 return css_is_width($this->value);
1982 * Cleans the provided value
1984 * @param mixed $value Cleans the provided value optimising it if possible
1985 * @return string
1987 protected function clean_value($value) {
1988 if (!css_is_width($value)) {
1989 // Note we don't actually change the value to something valid. That
1990 // would be bad for futureproofing.
1991 $this->set_error('Invalid width specified for '.$this->name);
1992 } else if (preg_match('#^0\D+$#', $value)) {
1993 $value = 0;
1995 return trim($value);
1999 * Initialises a new width style
2001 * @param mixed $value The value this style has
2002 * @return css_style_width
2004 public static function init($value) {
2005 return new css_style_width('width', $value);
2010 * A margin style
2012 * @package core_css
2013 * @category css
2014 * @copyright 2012 Sam Hemelryk
2015 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2017 class css_style_margin extends css_style_width {
2020 * Initialises a margin style.
2022 * In this case we split the margin into several other margin styles so that
2023 * we can properly condense overrides and then reconsolidate them later into
2024 * an optimal form.
2026 * @param string $value The value the style has
2027 * @return array An array of margin values that can later be consolidated
2029 public static function init($value) {
2030 $important = '';
2031 if (strpos($value, '!important') !== false) {
2032 $important = ' !important';
2033 $value = str_replace('!important', '', $value);
2036 $value = preg_replace('#\s+#', ' ', trim($value));
2037 $bits = explode(' ', $value, 4);
2039 $top = $right = $bottom = $left = null;
2040 if (count($bits) > 0) {
2041 $top = $right = $bottom = $left = array_shift($bits);
2043 if (count($bits) > 0) {
2044 $right = $left = array_shift($bits);
2046 if (count($bits) > 0) {
2047 $bottom = array_shift($bits);
2049 if (count($bits) > 0) {
2050 $left = array_shift($bits);
2052 return array(
2053 new css_style_margintop('margin-top', $top.$important),
2054 new css_style_marginright('margin-right', $right.$important),
2055 new css_style_marginbottom('margin-bottom', $bottom.$important),
2056 new css_style_marginleft('margin-left', $left.$important)
2061 * Consolidates individual margin styles into a single margin style
2063 * @param array $styles
2064 * @return array An array of consolidated styles
2066 public static function consolidate(array $styles) {
2067 if (count($styles) != 4) {
2068 return $styles;
2070 $top = $right = $bottom = $left = null;
2071 foreach ($styles as $style) {
2072 switch ($style->get_name()) {
2073 case 'margin-top' : $top = $style->get_value();break;
2074 case 'margin-right' : $right = $style->get_value();break;
2075 case 'margin-bottom' : $bottom = $style->get_value();break;
2076 case 'margin-left' : $left = $style->get_value();break;
2079 if ($top == $bottom && $left == $right) {
2080 if ($top == $left) {
2081 return array(new css_style_margin('margin', $top));
2082 } else {
2083 return array(new css_style_margin('margin', "{$top} {$left}"));
2085 } else if ($left == $right) {
2086 return array(new css_style_margin('margin', "{$top} {$right} {$bottom}"));
2087 } else {
2088 return array(new css_style_margin('margin', "{$top} {$right} {$bottom} {$left}"));
2094 * A margin top style
2096 * @package core_css
2097 * @category css
2098 * @copyright 2012 Sam Hemelryk
2099 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2101 class css_style_margintop extends css_style_margin {
2104 * A simple init, just a single style
2106 * @param string $value The value the style has
2107 * @return css_style_margintop
2109 public static function init($value) {
2110 return new css_style_margintop('margin-top', $value);
2114 * This style can be consolidated into a single margin style
2116 * @return string
2118 public function consolidate_to() {
2119 return 'margin';
2124 * A margin right style
2126 * @package core_css
2127 * @category css
2128 * @copyright 2012 Sam Hemelryk
2129 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2131 class css_style_marginright extends css_style_margin {
2134 * A simple init, just a single style
2136 * @param string $value The value the style has
2137 * @return css_style_margintop
2139 public static function init($value) {
2140 return new css_style_marginright('margin-right', $value);
2144 * This style can be consolidated into a single margin style
2146 * @return string
2148 public function consolidate_to() {
2149 return 'margin';
2154 * A margin bottom style
2156 * @package core_css
2157 * @category css
2158 * @copyright 2012 Sam Hemelryk
2159 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2161 class css_style_marginbottom extends css_style_margin {
2164 * A simple init, just a single style
2166 * @param string $value The value the style has
2167 * @return css_style_margintop
2169 public static function init($value) {
2170 return new css_style_marginbottom('margin-bottom', $value);
2174 * This style can be consolidated into a single margin style
2176 * @return string
2178 public function consolidate_to() {
2179 return 'margin';
2184 * A margin left style
2186 * @package core_css
2187 * @category css
2188 * @copyright 2012 Sam Hemelryk
2189 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2191 class css_style_marginleft extends css_style_margin {
2194 * A simple init, just a single style
2196 * @param string $value The value the style has
2197 * @return css_style_margintop
2199 public static function init($value) {
2200 return new css_style_marginleft('margin-left', $value);
2204 * This style can be consolidated into a single margin style
2206 * @return string
2208 public function consolidate_to() {
2209 return 'margin';
2214 * A border style
2216 * @package core_css
2217 * @category css
2218 * @copyright 2012 Sam Hemelryk
2219 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2221 class css_style_border extends css_style {
2224 * Initalises the border style into an array of individual style compontents
2226 * @param string $value The value the style has
2227 * @return css_style_bordercolor
2229 public static function init($value) {
2230 $value = preg_replace('#\s+#', ' ', $value);
2231 $bits = explode(' ', $value, 3);
2233 $return = array();
2234 if (count($bits) > 0) {
2235 $width = array_shift($bits);
2236 if (!css_is_width($width)) {
2237 $width = '0';
2239 $return[] = new css_style_borderwidth('border-top-width', $width);
2240 $return[] = new css_style_borderwidth('border-right-width', $width);
2241 $return[] = new css_style_borderwidth('border-bottom-width', $width);
2242 $return[] = new css_style_borderwidth('border-left-width', $width);
2244 if (count($bits) > 0) {
2245 $style = array_shift($bits);
2246 $return[] = new css_style_borderstyle('border-top-style', $style);
2247 $return[] = new css_style_borderstyle('border-right-style', $style);
2248 $return[] = new css_style_borderstyle('border-bottom-style', $style);
2249 $return[] = new css_style_borderstyle('border-left-style', $style);
2251 if (count($bits) > 0) {
2252 $colour = array_shift($bits);
2253 $return[] = new css_style_bordercolor('border-top-color', $colour);
2254 $return[] = new css_style_bordercolor('border-right-color', $colour);
2255 $return[] = new css_style_bordercolor('border-bottom-color', $colour);
2256 $return[] = new css_style_bordercolor('border-left-color', $colour);
2258 return $return;
2262 * Consolidates all border styles into a single style
2264 * @param array $styles An array of border styles
2265 * @return array An optimised array of border styles
2267 public static function consolidate(array $styles) {
2269 $borderwidths = array('top' => null, 'right' => null, 'bottom' => null, 'left' => null);
2270 $borderstyles = array('top' => null, 'right' => null, 'bottom' => null, 'left' => null);
2271 $bordercolors = array('top' => null, 'right' => null, 'bottom' => null, 'left' => null);
2273 foreach ($styles as $style) {
2274 switch ($style->get_name()) {
2275 case 'border-top-width': $borderwidths['top'] = $style->get_value(); break;
2276 case 'border-right-width': $borderwidths['right'] = $style->get_value(); break;
2277 case 'border-bottom-width': $borderwidths['bottom'] = $style->get_value(); break;
2278 case 'border-left-width': $borderwidths['left'] = $style->get_value(); break;
2280 case 'border-top-style': $borderstyles['top'] = $style->get_value(); break;
2281 case 'border-right-style': $borderstyles['right'] = $style->get_value(); break;
2282 case 'border-bottom-style': $borderstyles['bottom'] = $style->get_value(); break;
2283 case 'border-left-style': $borderstyles['left'] = $style->get_value(); break;
2285 case 'border-top-color': $bordercolors['top'] = $style->get_value(); break;
2286 case 'border-right-color': $bordercolors['right'] = $style->get_value(); break;
2287 case 'border-bottom-color': $bordercolors['bottom'] = $style->get_value(); break;
2288 case 'border-left-color': $bordercolors['left'] = $style->get_value(); break;
2292 $uniquewidths = count(array_unique($borderwidths));
2293 $uniquestyles = count(array_unique($borderstyles));
2294 $uniquecolors = count(array_unique($bordercolors));
2296 $nullwidths = in_array(null, $borderwidths, true);
2297 $nullstyles = in_array(null, $borderstyles, true);
2298 $nullcolors = in_array(null, $bordercolors, true);
2300 $allwidthsthesame = ($uniquewidths === 1)?1:0;
2301 $allstylesthesame = ($uniquestyles === 1)?1:0;
2302 $allcolorsthesame = ($uniquecolors === 1)?1:0;
2304 $allwidthsnull = $allwidthsthesame && $nullwidths;
2305 $allstylesnull = $allstylesthesame && $nullstyles;
2306 $allcolorsnull = $allcolorsthesame && $nullcolors;
2308 $return = array();
2309 if ($allwidthsnull && $allstylesnull && $allcolorsnull) {
2310 // Everything is null still... boo
2311 return array(new css_style_border('border', ''));
2313 } else if ($allwidthsnull && $allstylesnull) {
2315 self::consolidate_styles_by_direction($return, 'css_style_bordercolor', 'border-color', $bordercolors);
2316 return $return;
2318 } else if ($allwidthsnull && $allcolorsnull) {
2320 self::consolidate_styles_by_direction($return, 'css_style_borderstyle', 'border-style', $borderstyles);
2321 return $return;
2323 } else if ($allcolorsnull && $allstylesnull) {
2325 self::consolidate_styles_by_direction($return, 'css_style_borderwidth', 'border-width', $borderwidths);
2326 return $return;
2330 if ($allwidthsthesame + $allstylesthesame + $allcolorsthesame == 3) {
2332 $return[] = new css_style_border('border', $borderwidths['top'].' '.$borderstyles['top'].' '.$bordercolors['top']);
2334 } else if ($allwidthsthesame + $allstylesthesame + $allcolorsthesame == 2) {
2336 if ($allwidthsthesame && $allstylesthesame && !$nullwidths && !$nullstyles) {
2338 $return[] = new css_style_border('border', $borderwidths['top'].' '.$borderstyles['top']);
2339 self::consolidate_styles_by_direction($return, 'css_style_bordercolor', 'border-color', $bordercolors);
2341 } else if ($allwidthsthesame && $allcolorsthesame && !$nullwidths && !$nullcolors) {
2343 $return[] = new css_style_border('border', $borderwidths['top'].' solid '.$bordercolors['top']);
2344 self::consolidate_styles_by_direction($return, 'css_style_borderstyle', 'border-style', $borderstyles);
2346 } else if ($allstylesthesame && $allcolorsthesame && !$nullstyles && !$nullcolors) {
2348 $return[] = new css_style_border('border', '1px '.$borderstyles['top'].' '.$bordercolors['top']);
2349 self::consolidate_styles_by_direction($return, 'css_style_borderwidth', 'border-width', $borderwidths);
2351 } else {
2352 self::consolidate_styles_by_direction($return, 'css_style_borderwidth', 'border-width', $borderwidths);
2353 self::consolidate_styles_by_direction($return, 'css_style_borderstyle', 'border-style', $borderstyles);
2354 self::consolidate_styles_by_direction($return, 'css_style_bordercolor', 'border-color', $bordercolors);
2357 } else if (!$nullwidths && !$nullcolors && !$nullstyles && max(array_count_values($borderwidths)) == 3 && max(array_count_values($borderstyles)) == 3 && max(array_count_values($bordercolors)) == 3) {
2358 $widthkeys = array();
2359 $stylekeys = array();
2360 $colorkeys = array();
2362 foreach ($borderwidths as $key => $value) {
2363 if (!array_key_exists($value, $widthkeys)) {
2364 $widthkeys[$value] = array();
2366 $widthkeys[$value][] = $key;
2368 usort($widthkeys, 'css_sort_by_count');
2369 $widthkeys = array_values($widthkeys);
2371 foreach ($borderstyles as $key => $value) {
2372 if (!array_key_exists($value, $stylekeys)) {
2373 $stylekeys[$value] = array();
2375 $stylekeys[$value][] = $key;
2377 usort($stylekeys, 'css_sort_by_count');
2378 $stylekeys = array_values($stylekeys);
2380 foreach ($bordercolors as $key => $value) {
2381 if (!array_key_exists($value, $colorkeys)) {
2382 $colorkeys[$value] = array();
2384 $colorkeys[$value][] = $key;
2386 usort($colorkeys, 'css_sort_by_count');
2387 $colorkeys = array_values($colorkeys);
2389 if ($widthkeys == $stylekeys && $stylekeys == $colorkeys) {
2390 $key = $widthkeys[0][0];
2391 self::build_style_string($return, 'css_style_border', 'border', $borderwidths[$key], $borderstyles[$key], $bordercolors[$key]);
2392 $key = $widthkeys[1][0];
2393 self::build_style_string($return, 'css_style_border'.$key, 'border-'.$key, $borderwidths[$key], $borderstyles[$key], $bordercolors[$key]);
2394 } else {
2395 self::build_style_string($return, 'css_style_bordertop', 'border-top', $borderwidths['top'], $borderstyles['top'], $bordercolors['top']);
2396 self::build_style_string($return, 'css_style_borderright', 'border-right', $borderwidths['right'], $borderstyles['right'], $bordercolors['right']);
2397 self::build_style_string($return, 'css_style_borderbottom', 'border-bottom', $borderwidths['bottom'], $borderstyles['bottom'], $bordercolors['bottom']);
2398 self::build_style_string($return, 'css_style_borderleft', 'border-left', $borderwidths['left'], $borderstyles['left'], $bordercolors['left']);
2400 } else {
2401 self::build_style_string($return, 'css_style_bordertop', 'border-top', $borderwidths['top'], $borderstyles['top'], $bordercolors['top']);
2402 self::build_style_string($return, 'css_style_borderright', 'border-right', $borderwidths['right'], $borderstyles['right'], $bordercolors['right']);
2403 self::build_style_string($return, 'css_style_borderbottom', 'border-bottom', $borderwidths['bottom'], $borderstyles['bottom'], $bordercolors['bottom']);
2404 self::build_style_string($return, 'css_style_borderleft', 'border-left', $borderwidths['left'], $borderstyles['left'], $bordercolors['left']);
2406 foreach ($return as $key => $style) {
2407 if ($style->get_value() == '') {
2408 unset($return[$key]);
2411 return $return;
2415 * Border styles get consolidated to a single border style.
2417 * @return string
2419 public function consolidate_to() {
2420 return 'border';
2424 * Consolidates a series of border styles into an optimised array of border
2425 * styles by looking at the direction of the border and prioritising that
2426 * during the optimisation.
2428 * @param array $array An array to add styles into during consolidation. Passed by reference.
2429 * @param string $class The class type to initalise
2430 * @param string $style The style to create
2431 * @param string|array $top The top value
2432 * @param string $right The right value
2433 * @param string $bottom The bottom value
2434 * @param string $left The left value
2435 * @return bool
2437 public static function consolidate_styles_by_direction(&$array, $class, $style, $top, $right = null, $bottom = null, $left = null) {
2438 if (is_array($top)) {
2439 $right = $top['right'];
2440 $bottom = $top['bottom'];
2441 $left = $top['left'];
2442 $top = $top['top'];
2445 if ($top == $bottom && $left == $right && $top == $left) {
2446 if (is_null($top)) {
2447 $array[] = new $class($style, '');
2448 } else {
2449 $array[] = new $class($style, $top);
2451 } else if ($top == null || $right == null || $bottom == null || $left == null) {
2452 if ($top !== null) {
2453 $array[] = new $class(str_replace('border-', 'border-top-', $style), $top);
2455 if ($right !== null) {
2456 $array[] = new $class(str_replace('border-', 'border-right-', $style), $right);
2458 if ($bottom !== null) {
2459 $array[] = new $class(str_replace('border-', 'border-bottom-', $style), $bottom);
2461 if ($left !== null) {
2462 $array[] = new $class(str_replace('border-', 'border-left-', $style), $left);
2464 } else if ($top == $bottom && $left == $right) {
2465 $array[] = new $class($style, $top.' '.$right);
2466 } else if ($left == $right) {
2467 $array[] = new $class($style, $top.' '.$right.' '.$bottom);
2468 } else {
2469 $array[] = new $class($style, $top.' '.$right.' '.$bottom.' '.$left);
2471 return true;
2475 * Builds a border style for a set of width, style, and colour values
2477 * @param array $array An array into which the generated style is added
2478 * @param string $class The class type to initialise
2479 * @param string $cssstyle The style to use
2480 * @param string $width The width of the border
2481 * @param string $style The style of the border
2482 * @param string $color The colour of the border
2483 * @return bool
2485 public static function build_style_string(&$array, $class, $cssstyle, $width = null, $style = null, $color = null) {
2486 if (!is_null($width) && !is_null($style) && !is_null($color)) {
2487 $array[] = new $class($cssstyle, $width.' '.$style.' '.$color);
2488 } else if (!is_null($width) && !is_null($style) && is_null($color)) {
2489 $array[] = new $class($cssstyle, $width.' '.$style);
2490 } else if (!is_null($width) && is_null($style) && is_null($color)) {
2491 $array[] = new $class($cssstyle, $width);
2492 } else {
2493 if (!is_null($width)) $array[] = new $class($cssstyle, $width);
2494 if (!is_null($style)) $array[] = new $class($cssstyle, $style);
2495 if (!is_null($color)) $array[] = new $class($cssstyle, $color);
2497 return true;
2502 * A border colour style
2504 * @package core_css
2505 * @category css
2506 * @copyright 2012 Sam Hemelryk
2507 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2509 class css_style_bordercolor extends css_style_color {
2512 * Creates a new border colour style
2514 * Based upon the colour style
2516 * @param mixed $value
2517 * @return Array of css_style_bordercolor
2519 public static function init($value) {
2520 $value = preg_replace('#\s+#', ' ', $value);
2521 $bits = explode(' ', $value, 4);
2523 $top = $right = $bottom = $left = null;
2524 if (count($bits) > 0) {
2525 $top = $right = $bottom = $left = array_shift($bits);
2527 if (count($bits) > 0) {
2528 $right = $left = array_shift($bits);
2530 if (count($bits) > 0) {
2531 $bottom = array_shift($bits);
2533 if (count($bits) > 0) {
2534 $left = array_shift($bits);
2536 return array(
2537 css_style_bordertopcolor::init($top),
2538 css_style_borderrightcolor::init($right),
2539 css_style_borderbottomcolor::init($bottom),
2540 css_style_borderleftcolor::init($left)
2545 * Consolidate this to a single border style
2547 * @return string
2549 public function consolidate_to() {
2550 return 'border';
2554 * Cleans the value
2556 * @param string $value Cleans the provided value optimising it if possible
2557 * @return string
2559 protected function clean_value($value) {
2560 $values = explode(' ', $value);
2561 $values = array_map('parent::clean_value', $values);
2562 return join (' ', $values);
2566 * Outputs this style
2568 * @param string $overridevalue
2569 * @return string
2571 public function out($overridevalue = null) {
2572 if ($overridevalue === null) {
2573 $overridevalue = $this->value;
2575 $values = explode(' ', $overridevalue);
2576 $values = array_map('css_style_color::shrink_value', $values);
2577 return parent::out(join (' ', $values));
2582 * A border left style
2584 * @package core_css
2585 * @category css
2586 * @copyright 2012 Sam Hemelryk
2587 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2589 class css_style_borderleft extends css_style_generic {
2592 * Initialises the border left style into individual components
2594 * @param string $value
2595 * @return array Array of css_style_borderleftwidth|css_style_borderleftstyle|css_style_borderleftcolor
2597 public static function init($value) {
2598 $value = preg_replace('#\s+#', ' ', $value);
2599 $bits = explode(' ', $value, 3);
2601 $return = array();
2602 if (count($bits) > 0) {
2603 $return[] = css_style_borderleftwidth::init(array_shift($bits));
2605 if (count($bits) > 0) {
2606 $return[] = css_style_borderleftstyle::init(array_shift($bits));
2608 if (count($bits) > 0) {
2609 $return[] = css_style_borderleftcolor::init(array_shift($bits));
2611 return $return;
2615 * Consolidate this to a single border style
2617 * @return string
2619 public function consolidate_to() {
2620 return 'border';
2625 * A border right style
2627 * @package core_css
2628 * @category css
2629 * @copyright 2012 Sam Hemelryk
2630 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2632 class css_style_borderright extends css_style_generic {
2635 * Initialises the border right style into individual components
2637 * @param string $value The value of the style
2638 * @return array Array of css_style_borderrightwidth|css_style_borderrightstyle|css_style_borderrightcolor
2640 public static function init($value) {
2641 $value = preg_replace('#\s+#', ' ', $value);
2642 $bits = explode(' ', $value, 3);
2644 $return = array();
2645 if (count($bits) > 0) {
2646 $return[] = css_style_borderrightwidth::init(array_shift($bits));
2648 if (count($bits) > 0) {
2649 $return[] = css_style_borderrightstyle::init(array_shift($bits));
2651 if (count($bits) > 0) {
2652 $return[] = css_style_borderrightcolor::init(array_shift($bits));
2654 return $return;
2658 * Consolidate this to a single border style
2660 * @return string
2662 public function consolidate_to() {
2663 return 'border';
2668 * A border top style
2670 * @package core_css
2671 * @category css
2672 * @copyright 2012 Sam Hemelryk
2673 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2675 class css_style_bordertop extends css_style_generic {
2678 * Initialises the border top style into individual components
2680 * @param string $value The value of the style
2681 * @return array Array of css_style_bordertopwidth|css_style_bordertopstyle|css_style_bordertopcolor
2683 public static function init($value) {
2684 $value = preg_replace('#\s+#', ' ', $value);
2685 $bits = explode(' ', $value, 3);
2687 $return = array();
2688 if (count($bits) > 0) {
2689 $return[] = css_style_bordertopwidth::init(array_shift($bits));
2691 if (count($bits) > 0) {
2692 $return[] = css_style_bordertopstyle::init(array_shift($bits));
2694 if (count($bits) > 0) {
2695 $return[] = css_style_bordertopcolor::init(array_shift($bits));
2697 return $return;
2701 * Consolidate this to a single border style
2703 * @return string
2705 public function consolidate_to() {
2706 return 'border';
2711 * A border bottom style
2713 * @package core_css
2714 * @category css
2715 * @copyright 2012 Sam Hemelryk
2716 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2718 class css_style_borderbottom extends css_style_generic {
2721 * Initialises the border bottom style into individual components
2723 * @param string $value The value of the style
2724 * @return array Array of css_style_borderbottomwidth|css_style_borderbottomstyle|css_style_borderbottomcolor
2726 public static function init($value) {
2727 $value = preg_replace('#\s+#', ' ', $value);
2728 $bits = explode(' ', $value, 3);
2730 $return = array();
2731 if (count($bits) > 0) {
2732 $return[] = css_style_borderbottomwidth::init(array_shift($bits));
2734 if (count($bits) > 0) {
2735 $return[] = css_style_borderbottomstyle::init(array_shift($bits));
2737 if (count($bits) > 0) {
2738 $return[] = css_style_borderbottomcolor::init(array_shift($bits));
2740 return $return;
2744 * Consolidate this to a single border style
2746 * @return string
2748 public function consolidate_to() {
2749 return 'border';
2754 * A border width style
2756 * @package core_css
2757 * @category css
2758 * @copyright 2012 Sam Hemelryk
2759 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2761 class css_style_borderwidth extends css_style_width {
2764 * Creates a new border colour style
2766 * Based upon the colour style
2768 * @param string $value The value of the style
2769 * @return array Array of css_style_border*width
2771 public static function init($value) {
2772 $value = preg_replace('#\s+#', ' ', $value);
2773 $bits = explode(' ', $value, 4);
2775 $top = $right = $bottom = $left = null;
2776 if (count($bits) > 0) {
2777 $top = $right = $bottom = $left = array_shift($bits);
2779 if (count($bits) > 0) {
2780 $right = $left = array_shift($bits);
2782 if (count($bits) > 0) {
2783 $bottom = array_shift($bits);
2785 if (count($bits) > 0) {
2786 $left = array_shift($bits);
2788 return array(
2789 css_style_bordertopwidth::init($top),
2790 css_style_borderrightwidth::init($right),
2791 css_style_borderbottomwidth::init($bottom),
2792 css_style_borderleftwidth::init($left)
2797 * Consolidate this to a single border style
2799 * @return string
2801 public function consolidate_to() {
2802 return 'border';
2807 * A border style style
2809 * @package core_css
2810 * @category css
2811 * @copyright 2012 Sam Hemelryk
2812 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2814 class css_style_borderstyle extends css_style_generic {
2817 * Creates a new border colour style
2819 * Based upon the colour style
2821 * @param string $value The value of the style
2822 * @return array Array of css_style_border*style
2824 public static function init($value) {
2825 $value = preg_replace('#\s+#', ' ', $value);
2826 $bits = explode(' ', $value, 4);
2828 $top = $right = $bottom = $left = null;
2829 if (count($bits) > 0) {
2830 $top = $right = $bottom = $left = array_shift($bits);
2832 if (count($bits) > 0) {
2833 $right = $left = array_shift($bits);
2835 if (count($bits) > 0) {
2836 $bottom = array_shift($bits);
2838 if (count($bits) > 0) {
2839 $left = array_shift($bits);
2841 return array(
2842 css_style_bordertopstyle::init($top),
2843 css_style_borderrightstyle::init($right),
2844 css_style_borderbottomstyle::init($bottom),
2845 css_style_borderleftstyle::init($left)
2850 * Consolidate this to a single border style
2852 * @return string
2854 public function consolidate_to() {
2855 return 'border';
2860 * A border top colour style
2862 * @package core_css
2863 * @category css
2864 * @copyright 2012 Sam Hemelryk
2865 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2867 class css_style_bordertopcolor extends css_style_bordercolor {
2870 * Initialises this style object
2872 * @param string $value The value of the style
2873 * @return css_style_bordertopcolor
2875 public static function init($value) {
2876 return new css_style_bordertopcolor('border-top-color', $value);
2880 * Consolidate this to a single border style
2882 * @return string
2884 public function consolidate_to() {
2885 return 'border';
2890 * A border left colour style
2892 * @package core_css
2893 * @category css
2894 * @copyright 2012 Sam Hemelryk
2895 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2897 class css_style_borderleftcolor extends css_style_bordercolor {
2900 * Initialises this style object
2902 * @param string $value The value of the style
2903 * @return css_style_borderleftcolor
2905 public static function init($value) {
2906 return new css_style_borderleftcolor('border-left-color', $value);
2910 * Consolidate this to a single border style
2912 * @return string
2914 public function consolidate_to() {
2915 return 'border';
2920 * A border right colour style
2922 * @package core_css
2923 * @category css
2924 * @copyright 2012 Sam Hemelryk
2925 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2927 class css_style_borderrightcolor extends css_style_bordercolor {
2930 * Initialises this style object
2932 * @param string $value The value of the style
2933 * @return css_style_borderrightcolor
2935 public static function init($value) {
2936 return new css_style_borderrightcolor('border-right-color', $value);
2940 * Consolidate this to a single border style
2942 * @return string
2944 public function consolidate_to() {
2945 return 'border';
2950 * A border bottom colour style
2952 * @package core_css
2953 * @category css
2954 * @copyright 2012 Sam Hemelryk
2955 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2957 class css_style_borderbottomcolor extends css_style_bordercolor {
2960 * Initialises this style object
2962 * @param string $value The value of the style
2963 * @return css_style_borderbottomcolor
2965 public static function init($value) {
2966 return new css_style_borderbottomcolor('border-bottom-color', $value);
2970 * Consolidate this to a single border style
2972 * @return string
2974 public function consolidate_to() {
2975 return 'border';
2980 * A border width top style
2982 * @package core_css
2983 * @category css
2984 * @copyright 2012 Sam Hemelryk
2985 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2987 class css_style_bordertopwidth extends css_style_borderwidth {
2990 * Initialises this style object
2992 * @param string $value The value of the style
2993 * @return css_style_bordertopwidth
2995 public static function init($value) {
2996 return new css_style_bordertopwidth('border-top-width', $value);
3000 * Consolidate this to a single border style
3002 * @return string
3004 public function consolidate_to() {
3005 return 'border';
3010 * A border width left style
3012 * @package core_css
3013 * @category css
3014 * @copyright 2012 Sam Hemelryk
3015 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3017 class css_style_borderleftwidth extends css_style_borderwidth {
3020 * Initialises this style object
3022 * @param string $value The value of the style
3023 * @return css_style_borderleftwidth
3025 public static function init($value) {
3026 return new css_style_borderleftwidth('border-left-width', $value);
3030 * Consolidate this to a single border style
3032 * @return string
3034 public function consolidate_to() {
3035 return 'border';
3040 * A border width right style
3042 * @package core_css
3043 * @category css
3044 * @copyright 2012 Sam Hemelryk
3045 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3047 class css_style_borderrightwidth extends css_style_borderwidth {
3050 * Initialises this style object
3052 * @param string $value The value of the style
3053 * @return css_style_borderrightwidth
3055 public static function init($value) {
3056 return new css_style_borderrightwidth('border-right-width', $value);
3060 * Consolidate this to a single border style
3062 * @return string
3064 public function consolidate_to() {
3065 return 'border';
3070 * A border width bottom style
3072 * @package core_css
3073 * @category css
3074 * @copyright 2012 Sam Hemelryk
3075 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3077 class css_style_borderbottomwidth extends css_style_borderwidth {
3080 * Initialises this style object
3082 * @param string $value The value of the style
3083 * @return css_style_borderbottomwidth
3085 public static function init($value) {
3086 return new css_style_borderbottomwidth('border-bottom-width', $value);
3090 * Consolidate this to a single border style
3092 * @return string
3094 public function consolidate_to() {
3095 return 'border';
3100 * A border top style
3102 * @package core_css
3103 * @category css
3104 * @copyright 2012 Sam Hemelryk
3105 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3107 class css_style_bordertopstyle extends css_style_borderstyle {
3110 * Initialises this style object
3112 * @param string $value The value of the style
3113 * @return css_style_bordertopstyle
3115 public static function init($value) {
3116 return new css_style_bordertopstyle('border-top-style', $value);
3120 * Consolidate this to a single border style
3122 * @return string
3124 public function consolidate_to() {
3125 return 'border';
3130 * A border left style
3132 * @package core_css
3133 * @category css
3134 * @copyright 2012 Sam Hemelryk
3135 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3137 class css_style_borderleftstyle extends css_style_borderstyle {
3140 * Initialises this style object
3142 * @param string $value The value of the style
3143 * @return css_style_borderleftstyle
3145 public static function init($value) {
3146 return new css_style_borderleftstyle('border-left-style', $value);
3150 * Consolidate this to a single border style
3152 * @return string
3154 public function consolidate_to() {
3155 return 'border';
3160 * A border right style
3162 * @package core_css
3163 * @category css
3164 * @copyright 2012 Sam Hemelryk
3165 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3167 class css_style_borderrightstyle extends css_style_borderstyle {
3170 * Initialises this style object
3172 * @param string $value The value of the style
3173 * @return css_style_borderrightstyle
3175 public static function init($value) {
3176 return new css_style_borderrightstyle('border-right-style', $value);
3180 * Consolidate this to a single border style
3182 * @return string
3184 public function consolidate_to() {
3185 return 'border';
3190 * A border bottom style
3192 * @package core_css
3193 * @category css
3194 * @copyright 2012 Sam Hemelryk
3195 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3197 class css_style_borderbottomstyle extends css_style_borderstyle {
3200 * Initialises this style object
3202 * @return css_style_borderbottomstyle
3204 public static function init($value) {
3205 return new css_style_borderbottomstyle('border-bottom-style', $value);
3209 * Consolidate this to a single border style
3211 * @return string
3213 public function consolidate_to() {
3214 return 'border';
3219 * A background style
3221 * @package core_css
3222 * @category css
3223 * @copyright 2012 Sam Hemelryk
3224 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3226 class css_style_background extends css_style {
3229 * Initialises a background style
3231 * @param type $value The value of the style
3232 * @return array An array of background component.
3234 public static function init($value) {
3235 // colour - image - repeat - attachment - position
3237 $imageurl = null;
3238 if (preg_match('#url\(([^\)]+)\)#', $value, $matches)) {
3239 $imageurl = trim($matches[1]);
3240 $value = str_replace($matches[1], '', $value);
3243 $value = preg_replace('#\s+#', ' ', $value);
3244 $bits = explode(' ', $value);
3246 $repeats = array('repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'inherit');
3247 $attachments = array('scroll' , 'fixed', 'inherit');
3249 $return = array();
3250 $unknownbits = array();
3252 if (count($bits) > 0 && css_is_colour(reset($bits))) {
3253 $return[] = new css_style_backgroundcolor('background-color', array_shift($bits));
3255 if (count($bits) > 0 && preg_match('#(none|inherit|url\(\))#', reset($bits))) {
3256 $image = array_shift($bits);
3257 if ($image == 'url()') {
3258 $image = "url({$imageurl})";
3260 $return[] = new css_style_backgroundimage('background-image', $image);
3262 if (count($bits) > 0 && in_array(reset($bits), $repeats)) {
3263 $return[] = new css_style_backgroundrepeat('background-repeat', array_shift($bits));
3265 if (count($bits) > 0 && in_array(reset($bits), $attachments)) {
3266 // scroll , fixed, inherit
3267 $return[] = new css_style_backgroundattachment('background-attachment', array_shift($bits));
3269 if (count($bits) > 0) {
3270 $widthbits = array();
3271 foreach ($bits as $bit) {
3272 if (in_array($bit, array('top', 'left', 'bottom', 'right', 'center')) || css_is_width($bit)) {
3273 $widthbits[] = $bit;
3274 } else {
3275 $unknownbits[] = $bit;
3278 $return[] = new css_style_backgroundposition('background-position', join(' ',$widthbits));
3280 if (count($unknownbits)) {
3281 foreach ($unknownbits as $bit) {
3282 if (css_is_colour($bit)) {
3283 $return[] = new css_style_backgroundcolor('background-color', $bit);
3284 } else if (in_array($bit, $repeats)) {
3285 $return[] = new css_style_backgroundrepeat('background-repeat', $bit);
3286 } else if (in_array($bit, $attachments)) {
3287 $return[] = new css_style_backgroundattachment('background-attachment', $bit);
3291 return $return;
3295 * Consolidates background styles into a single background style
3297 * @param array $styles Consolidates the provided array of background styles
3298 * @return array Consolidated optimised background styles
3300 public static function consolidate(array $styles) {
3302 if (count($styles) < 1) {
3303 return $styles;
3306 $color = $image = $repeat = $attachment = $position = null;
3307 foreach ($styles as $style) {
3308 switch ($style->get_name()) {
3309 case 'background-color' : $color = css_style_color::shrink_value($style->get_value()); break;
3310 case 'background-image' : $image = $style->get_value(); break;
3311 case 'background-repeat' : $repeat = $style->get_value(); break;
3312 case 'background-attachment' : $attachment = $style->get_value(); break;
3313 case 'background-position' : $position = $style->get_value(); break;
3317 if ((is_null($image) || is_null($position) || is_null($repeat)) && ($image!= null || $position != null || $repeat != null)) {
3318 return $styles;
3321 $value = array();
3322 if (!is_null($color)) $value[] .= $color;
3323 if (!is_null($image)) $value[] .= $image;
3324 if (!is_null($repeat)) $value[] .= $repeat;
3325 if (!is_null($attachment)) $value[] .= $attachment;
3326 if (!is_null($position)) $value[] .= $position;
3327 return array(new css_style_background('background', join(' ', $value)));
3332 * A background colour style.
3334 * Based upon the colour style.
3336 * @package core_css
3337 * @category css
3338 * @copyright 2012 Sam Hemelryk
3339 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3341 class css_style_backgroundcolor extends css_style_color {
3344 * Creates a new background colour style
3346 * @param string $value The value of the style
3347 * @return css_style_backgroundcolor
3349 public static function init($value) {
3350 return new css_style_backgroundcolor('background-color', $value);
3354 * css_style_backgroundcolor consolidates to css_style_background
3356 * @return string
3358 public function consolidate_to() {
3359 return 'background';
3364 * A background image style.
3366 * @package core_css
3367 * @category css
3368 * @copyright 2012 Sam Hemelryk
3369 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3371 class css_style_backgroundimage extends css_style_generic {
3374 * Creates a new background colour style
3376 * @param string $value The value of the style
3377 * @return css_style_backgroundimage
3379 public static function init($value) {
3380 return new css_style_backgroundimage('background-image', $value);
3384 * Consolidates this style into a single background style
3386 * @return string
3388 public function consolidate_to() {
3389 return 'background';
3394 * A background repeat style.
3396 * @package core_css
3397 * @category css
3398 * @copyright 2012 Sam Hemelryk
3399 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3401 class css_style_backgroundrepeat extends css_style_generic {
3404 * Creates a new background colour style
3406 * @param string $value The value of the style
3407 * @return css_style_backgroundrepeat
3409 public static function init($value) {
3410 return new css_style_backgroundrepeat('background-repeat', $value);
3414 * Consolidates this style into a single background style
3416 * @return string
3418 public function consolidate_to() {
3419 return 'background';
3424 * A background attachment style.
3426 * @package core_css
3427 * @category css
3428 * @copyright 2012 Sam Hemelryk
3429 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3431 class css_style_backgroundattachment extends css_style_generic {
3434 * Creates a new background colour style
3436 * @param string $value The value of the style
3437 * @return css_style_backgroundattachment
3439 public static function init($value) {
3440 return new css_style_backgroundattachment('background-attachment', $value);
3444 * Consolidates this style into a single background style
3446 * @return string
3448 public function consolidate_to() {
3449 return 'background';
3454 * A background position style.
3456 * @package core_css
3457 * @category css
3458 * @copyright 2012 Sam Hemelryk
3459 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3461 class css_style_backgroundposition extends css_style_generic {
3464 * Creates a new background colour style
3466 * @param string $value The value of the style
3467 * @return css_style_backgroundposition
3469 public static function init($value) {
3470 return new css_style_backgroundposition('background-position', $value);
3474 * Consolidates this style into a single background style
3476 * @return string
3478 public function consolidate_to() {
3479 return 'background';
3484 * A padding style.
3486 * @package core_css
3487 * @category css
3488 * @copyright 2012 Sam Hemelryk
3489 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3491 class css_style_padding extends css_style_width {
3494 * Initialises this padding style into several individual padding styles
3496 * @param string $value The value fo the style
3497 * @return array An array of padding styles
3499 public static function init($value) {
3500 $important = '';
3501 if (strpos($value, '!important') !== false) {
3502 $important = ' !important';
3503 $value = str_replace('!important', '', $value);
3506 $value = preg_replace('#\s+#', ' ', trim($value));
3507 $bits = explode(' ', $value, 4);
3509 $top = $right = $bottom = $left = null;
3510 if (count($bits) > 0) {
3511 $top = $right = $bottom = $left = array_shift($bits);
3513 if (count($bits) > 0) {
3514 $right = $left = array_shift($bits);
3516 if (count($bits) > 0) {
3517 $bottom = array_shift($bits);
3519 if (count($bits) > 0) {
3520 $left = array_shift($bits);
3522 return array(
3523 new css_style_paddingtop('padding-top', $top.$important),
3524 new css_style_paddingright('padding-right', $right.$important),
3525 new css_style_paddingbottom('padding-bottom', $bottom.$important),
3526 new css_style_paddingleft('padding-left', $left.$important)
3531 * Consolidates several padding styles into a single style.
3533 * @param array $styles Array of padding styles
3534 * @return array Optimised+consolidated array of padding styles
3536 public static function consolidate(array $styles) {
3537 if (count($styles) != 4) {
3538 return $styles;
3540 $top = $right = $bottom = $left = null;
3541 foreach ($styles as $style) {
3542 switch ($style->get_name()) {
3543 case 'padding-top' : $top = $style->get_value();break;
3544 case 'padding-right' : $right = $style->get_value();break;
3545 case 'padding-bottom' : $bottom = $style->get_value();break;
3546 case 'padding-left' : $left = $style->get_value();break;
3549 if ($top == $bottom && $left == $right) {
3550 if ($top == $left) {
3551 return array(new css_style_padding('padding', $top));
3552 } else {
3553 return array(new css_style_padding('padding', "{$top} {$left}"));
3555 } else if ($left == $right) {
3556 return array(new css_style_padding('padding', "{$top} {$right} {$bottom}"));
3557 } else {
3558 return array(new css_style_padding('padding', "{$top} {$right} {$bottom} {$left}"));
3564 * A padding top style.
3566 * @package core_css
3567 * @category css
3568 * @copyright 2012 Sam Hemelryk
3569 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3571 class css_style_paddingtop extends css_style_padding {
3574 * Initialises this style
3576 * @param string $value The value of the style
3577 * @return css_style_paddingtop
3579 public static function init($value) {
3580 return new css_style_paddingtop('padding-top', $value);
3584 * Consolidates this style into a single padding style
3586 * @return string
3588 public function consolidate_to() {
3589 return 'padding';
3594 * A padding right style.
3596 * @package core_css
3597 * @category css
3598 * @copyright 2012 Sam Hemelryk
3599 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3601 class css_style_paddingright extends css_style_padding {
3604 * Initialises this style
3606 * @param string $value The value of the style
3607 * @return css_style_paddingright
3609 public static function init($value) {
3610 return new css_style_paddingright('padding-right', $value);
3614 * Consolidates this style into a single padding style
3616 * @return string
3618 public function consolidate_to() {
3619 return 'padding';
3624 * A padding bottom style.
3626 * @package core_css
3627 * @category css
3628 * @copyright 2012 Sam Hemelryk
3629 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3631 class css_style_paddingbottom extends css_style_padding {
3634 * Initialises this style
3636 * @param string $value The value of the style
3637 * @return css_style_paddingbottom
3639 public static function init($value) {
3640 return new css_style_paddingbottom('padding-bottom', $value);
3644 * Consolidates this style into a single padding style
3646 * @return string
3648 public function consolidate_to() {
3649 return 'padding';
3654 * A padding left style.
3656 * @package core_css
3657 * @category css
3658 * @copyright 2012 Sam Hemelryk
3659 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3661 class css_style_paddingleft extends css_style_padding {
3664 * Initialises this style
3666 * @param string $value The value of the style
3667 * @return css_style_paddingleft
3669 public static function init($value) {
3670 return new css_style_paddingleft('padding-left', $value);
3674 * Consolidates this style into a single padding style
3676 * @return string
3678 public function consolidate_to() {
3679 return 'padding';