92
87
// the rename() should be more atomic than fwrite().
93
88
ignore_user_abort(true);
97
foreach ($css as $content) {
98
if ($files > 1 && ($count+1) !== $files) {
99
// If there is more than one file and this is not the last file.
100
$filename = preg_replace('#\.css$#', '.'.$count.'.css', $csspath);
90
// First up write out the single file for all those using decent browsers.
91
css_write_file($csspath, $css);
94
// If we need to chunk the CSS for browsers that are sub-par.
95
$css = css_chunk_by_selector_count($css, $chunkurl);
98
foreach ($css as $content) {
99
if ($count === $files) {
100
// If there is more than one file and this IS the last file.
101
$filename = preg_replace('#\.css$#', '.0.css', $csspath);
103
// If there is more than one file and this is not the last file.
104
$filename = preg_replace('#\.css$#', '.'.$count.'.css', $csspath);
103
$filename = $csspath;
105
if ($fp = fopen($filename.'.tmp', 'xb')) {
106
fwrite($fp, $content);
108
rename($filename.'.tmp', $filename);
109
@chmod($filename, $CFG->filepermissions);
110
@unlink($filename.'.tmp'); // just in case anything fails
107
css_write_file($filename, $content);
120
* @param string $filename
121
* @param string $content
123
function css_write_file($filename, $content) {
125
if ($fp = fopen($filename.'.tmp', 'xb')) {
126
fwrite($fp, $content);
128
rename($filename.'.tmp', $filename);
129
@chmod($filename, $CFG->filepermissions);
130
@unlink($filename.'.tmp'); // Just in case anything fails.
121
135
* Takes CSS and chunks it if the number of selectors within it exceeds $maxselectors.
137
* The chunking will not split a group of selectors, or a media query. That means that
138
* if n > $maxselectors and there are n selectors grouped together,
139
* they will not be chunked and you could end up with more selectors than desired.
140
* The same applies for a media query that has more than n selectors.
142
* Also, as we do not split group of selectors or media queries, the chunking might
143
* not be as optimal as it could be, having files with less selectors than it could
144
* potentially contain.
146
* String functions used here are not compliant with unicode characters. But that is
147
* not an issue as the syntax of CSS is using ASCII codes. Even if we have unicode
148
* characters in comments, or in the property 'content: ""', it will behave correcly.
150
* Please note that this strips out the comments if chunking happens.
123
152
* @param string $css The CSS to chunk.
124
153
* @param string $importurl The URL to use for import statements.
125
154
* @param int $maxselectors The number of selectors to limit a chunk to.
126
* @param int $buffer The buffer size to use when chunking. You shouldn't need to reduce this
127
* unless you are lowering the maximum selectors.
155
* @param int $buffer Not used any more.
128
156
* @return array An array of CSS chunks.
130
158
function css_chunk_by_selector_count($css, $importurl, $maxselectors = 4095, $buffer = 50) {
131
160
// Check if we need to chunk this CSS file.
132
161
$count = substr_count($css, ',') + substr_count($css, '{');
133
162
if ($count < $maxselectors) {
135
164
return array($css);
139
// Split the CSS by array, making sure to save the delimiter in the process.
140
$parts = preg_split('#([,\}])#', $css, null, PREG_SPLIT_DELIM_CAPTURE + PREG_SPLIT_NO_EMPTY);
141
// We need to chunk the array. Each delimiter is stored separately so we multiple by 2.
142
// We also subtract 100 to give us a small buffer just in case.
143
$parts = array_chunk($parts, $maxselectors * 2 - $buffer * 2);
145
$partcount = count($parts);
146
foreach ($parts as $key => $chunk) {
147
if (end($chunk) === ',') {
148
// Damn last element was a comma.
149
// Pretty much the only way to deal with this is to take the styles from the end of the
150
// comma separated chain of selectors and apply it to the last selector we have here in place
152
// Unit tests are essential for making sure this works.
155
while ($styles === false && $i < ($partcount - 1)) {
157
$nextpart = $parts[$i];
158
foreach ($nextpart as $style) {
159
if (strpos($style, '{') !== false) {
160
$styles = preg_replace('#^[^\{]+#', '', $style);
165
if ($styles === false) {
166
$styles = '/** Error chunking CSS **/';
171
array_push($chunk, $styles);
173
$css[] = join('', $chunk);
175
// The array $css now contains CSS split into perfect sized chunks.
167
$chunks = array(); // The final chunks.
168
$offsets = array(); // The indexes to chunk at.
169
$offset = 0; // The current offset.
170
$selectorcount = 0; // The number of selectors since the last split.
171
$lastvalidoffset = 0; // The last valid index to split at.
172
$lastvalidoffsetselectorcount = 0; // The number of selectors used at the time were could split.
173
$inrule = 0; // The number of rules we are in, should not be greater than 1.
174
$inmedia = false; // Whether or not we are in a media query.
175
$mediacoming = false; // Whether or not we are expeting a media query.
176
$currentoffseterror = null; // Not null when we have recorded an error for the current split.
177
$offseterrors = array(); // The offsets where we found errors.
179
// Remove the comments. Because it's easier, safer and probably a lot of other good reasons.
180
$css = preg_replace('#/\*(.*?)\*/#s', '', $css);
181
$strlen = strlen($css);
183
// Walk through the CSS content character by character.
184
for ($i = 1; $i <= $strlen; $i++) {
185
$char = $css[$i - 1];
188
// Is that a media query that I see coming towards us?
190
if (!$inmedia && substr($css, $offset, 5) === 'media') {
195
// So we are entering a rule or a media query...
199
$mediacoming = false;
206
// Let's count the number of selectors, but only if we are not in a rule as they
207
// can contain commas too.
208
if (!$inrule && $char === ',') {
212
// We reached the end of something.
214
// Oh, we are in a media query.
217
// This is the end of the media query.
220
// We were in a rule, in the media query.
227
// We are not in a media query, and there is no pending rule, it is safe to split here.
228
if (!$inmedia && !$inrule) {
229
$lastvalidoffset = $offset;
230
$lastvalidoffsetselectorcount = $selectorcount;
234
// Alright, this is splitting time...
235
if ($selectorcount > $maxselectors) {
236
if (!$lastvalidoffset) {
237
// We must have reached more selectors into one set than we were allowed. That means that either
238
// the chunk size value is too small, or that we have a gigantic group of selectors, or that a media
239
// query contains more selectors than the chunk size. We have to ignore this because we do not
240
// support split inside a group of selectors or media query.
241
if ($currentoffseterror === null) {
242
$currentoffseterror = $offset;
243
$offseterrors[] = $currentoffseterror;
246
// We identify the offset to split at and reset the number of selectors found from there.
247
$offsets[] = $lastvalidoffset;
248
$selectorcount = $selectorcount - $lastvalidoffsetselectorcount;
249
$lastvalidoffset = 0;
250
$currentoffseterror = null;
255
// Report offset errors.
256
if (!empty($offseterrors)) {
257
debugging('Could not find a safe place to split at offset(s): ' . implode(', ', $offseterrors) . '. Those were ignored.',
261
// Now that we have got the offets, we can chunk the CSS.
262
$offsetcount = count($offsets);
263
foreach ($offsets as $key => $index) {
266
$start = $offsets[$key - 1];
268
// From somewhere up to the offset.
269
$chunks[] = substr($css, $start, $index - $start);
271
// Add the last chunk (if there is one), from the last offset to the end of the string.
272
if (end($offsets) != $strlen) {
273
$chunks[] = substr($css, end($offsets));
276
// The array $chunks now contains CSS split into perfect sized chunks.
176
277
// Import statements can only appear at the very top of a CSS file.
177
278
// Imported sheets are applied in the the order they are imported and
178
279
// are followed by the contents of the CSS.
183
284
// followed by the contents of the final chunk in the actual sheet.
185
286
$slashargs = strpos($importurl, '.php?') === false;
186
$parts = count($css);
187
for ($i = 0; $i < $parts - 1; $i++) {
287
$parts = count($chunks);
288
for ($i = 1; $i < $parts; $i++) {
188
289
if ($slashargs) {
189
290
$importcss .= "@import url({$importurl}/chunk{$i});\n";
191
292
$importcss .= "@import url({$importurl}&chunk={$i});\n";
194
$importcss .= end($css);
195
$css[key($css)] = $importcss;
201
* Sends IE specific CSS
203
* In writing the CSS parser I have a theory that we could optimise the CSS
204
* then split it based upon the number of selectors to ensure we dont' break IE
205
* and that we include only as many sub-stylesheets as we require.
206
* Of course just a theory but may be fun to code.
208
* @param string $themename The name of the theme we are sending CSS for.
209
* @param string $rev The revision to ensure we utilise the cache.
210
* @param string $etag The revision to ensure we utilise the cache.
211
* @param bool $slasharguments
213
function css_send_ie_css($themename, $rev, $etag, $slasharguments) {
216
$lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
218
$relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot);
220
$css = "/** Unfortunately IE6-9 does not support more than 4096 selectors in one CSS file, which means we have to use some ugly hacks :-( **/";
221
if ($slasharguments) {
222
$css .= "\n@import url($relroot/styles.php/$themename/$rev/plugins);";
223
$css .= "\n@import url($relroot/styles.php/$themename/$rev/parents);";
224
$css .= "\n@import url($relroot/styles.php/$themename/$rev/theme);";
226
$css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=plugins);";
227
$css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=parents);";
228
$css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=theme);";
231
header('Etag: "'.$etag.'"');
232
header('Content-Disposition: inline; filename="styles.php"');
233
header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
234
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
236
header('Cache-Control: public, max-age='.$lifetime);
237
header('Accept-Ranges: none');
238
header('Content-Type: text/css; charset=utf-8');
239
header('Content-Length: '.strlen($css));
295
$importcss .= end($chunks);
296
$chunks[key($chunks)] = $importcss;
331
* Uses the minify library to compress CSS.
333
* This is used if $CFG->enablecssoptimiser has been turned off. This was
334
* the original CSS optimisation library.
335
* It removes whitespace and shrinks things but does no apparent optimisation.
336
* Note the minify library is still being used for JavaScript.
338
* @param array $files An array of files to minify
339
* @return string The minified CSS
341
function css_minify_css($files) {
348
// We do not really want any 304 here!
349
// There does not seem to be any better way to prevent them here.
350
unset($_SERVER['HTTP_IF_NONE_MATCH']);
351
unset($_SERVER['HTTP_IF_MODIFIED_SINCE']);
353
set_include_path($CFG->libdir . '/minify/lib' . PATH_SEPARATOR . get_include_path());
354
require_once('Minify.php');
356
if (0 === stripos(PHP_OS, 'win')) {
357
Minify::setDocRoot(); // IIS may need help
359
// disable all caching, we do it in moodle
360
Minify::setCache(null, false);
363
// JSMin is not GNU GPL compatible, use the plus version instead.
364
'minifiers' => array(Minify::TYPE_JS => array('JSMinPlus', 'minify')),
365
'bubbleCssImports' => false,
366
// Don't gzip content we just want text for storage
367
'encodeOutput' => false,
368
// Maximum age to cache, not used but required
369
'maxAge' => (60*60*24*20),
370
// The files to minify
372
// Turn orr URI rewriting
373
'rewriteCssUris' => false,
374
// This returns the CSS rather than echoing it for display
380
$result = Minify::serve('Files', $options);
381
if ($result['success'] and $result['statusCode'] == 200) {
382
return $result['content'];
384
} catch (Exception $e) {
385
$error = $e->getMessage();
386
$error = str_replace("\r", ' ', $error);
387
$error = str_replace("\n", ' ', $error);
390
// minification failed - try to inform the theme developer and include the non-minified version
393
/* Problem detected during theme CSS minimisation, please review the following code */
394
/* ================================================================================ */
398
foreach ($files as $cssfile) {
399
$css .= file_get_contents($cssfile)."\n";
405
386
* Determines if the given value is a valid CSS colour.
407
388
* A CSS colour can be one of the following:
684
663
$currentatrule = false;
686
// continue 1: The switch processing chars
687
// continue 2: The switch processing the state
688
// continue 3: The for loop
665
// Continue 1: The switch processing chars
666
// Continue 2: The switch processing the state
667
// Continue 3: The for loop.
691
if ($currentatrule == 'media' && preg_match('#\s*@media\s*([a-zA-Z0-9]+(\s*,\s*[a-zA-Z0-9]+)*)\s*{#', $buffer, $matches)) {
692
// Basic media declaration
670
$regexmediabasic = '#\s*@media\s*([a-zA-Z0-9]+(\s*,\s*[a-zA-Z0-9]+)*)\s*{#';
671
$regexadvmedia = '#\s*@media\s*([^{]+)#';
672
$regexkeyframes = '#@((\-moz\-|\-webkit\-|\-ms\-|\-o\-)?keyframes)\s*([^\s]+)#';
674
if ($currentatrule == 'media' && preg_match($regexmediabasic, $buffer, $matches)) {
675
// Basic media declaration.
693
676
$mediatypes = str_replace(' ', '', $matches[1]);
694
677
if (!array_key_exists($mediatypes, $medias)) {
695
678
$medias[$mediatypes] = new css_media($mediatypes);
697
680
$currentmedia = $medias[$mediatypes];
698
681
$currentprocess = self::PROCESSING_SELECTORS;
700
} else if ($currentatrule == 'media' && preg_match('#\s*@media\s*([^{]+)#', $buffer, $matches)) {
701
// Advanced media query declaration http://www.w3.org/TR/css3-mediaqueries/
683
} else if ($currentatrule == 'media' && preg_match($regexadvmedia, $buffer, $matches)) {
684
// Advanced media query declaration http://www.w3.org/TR/css3-mediaqueries/.
702
685
$mediatypes = $matches[1];
703
686
$hash = md5($mediatypes);
704
687
$medias[$hash] = new css_media($mediatypes);
705
688
$currentmedia = $medias[$hash];
706
689
$currentprocess = self::PROCESSING_SELECTORS;
708
} else if ($currentatrule == 'keyframes' && preg_match('#@((\-moz\-|\-webkit\-)?keyframes)\s*([^\s]+)#', $buffer, $matches)) {
691
} else if ($currentatrule == 'keyframes' && preg_match($regexkeyframes, $buffer, $matches)) {
709
692
// Keyframes declaration, we treat it exactly like a @media declaration except we don't allow
710
// them to be overridden to ensure we don't mess anything up. (means we keep everything in order)
693
// them to be overridden to ensure we don't mess anything up. (means we keep everything in order).
711
694
$keyframefor = $matches[1];
712
695
$keyframename = $matches[3];
713
696
$keyframe = new css_keyframe($keyframefor, $keyframename);
716
699
$currentprocess = self::PROCESSING_SELECTORS;
719
// continue 1: The switch processing chars
720
// continue 2: The switch processing the state
721
// continue 3: The for loop
702
// Continue 1: The switch processing chars
703
// Continue 2: The switch processing the state
704
// Continue 3: The for loop.
725
// Start processing selectors
708
// Start processing selectors.
726
709
case self::PROCESSING_START:
727
710
case self::PROCESSING_SELECTORS:
711
$regexatrule = '#@(media|import|charset|(\-moz\-|\-webkit\-|\-ms\-|\-o\-)?(keyframes))\s*#';
731
715
$buffer .= $char;
732
// continue 1: The switch processing chars
733
// continue 2: The switch processing the state
734
// continue 3: The for loop
716
// Continue 1: The switch processing chars
717
// Continue 2: The switch processing the state
718
// Continue 3: The for loop.
738
722
$buffer .= $char;
739
// continue 1: The switch processing chars
740
// continue 2: The switch processing the state
741
// continue 3: The for loop
723
// Continue 1: The switch processing chars
724
// Continue 2: The switch processing the state
725
// Continue 3: The for loop.
744
728
if ($inbrackets) {
745
// continue 1: The switch processing chars
746
// continue 2: The switch processing the state
747
// continue 3: The for loop
729
// Continue 1: The switch processing chars
730
// Continue 2: The switch processing the state
731
// Continue 3: The for loop.
750
734
if (!empty($buffer)) {
751
// Check for known @ rules
752
if ($suspectatrule && preg_match('#@(media|import|charset|(\-moz\-|\-webkit\-)?(keyframes))\s*#', $buffer, $matches)) {
735
// Check for known @ rules.
736
if ($suspectatrule && preg_match($regexatrule, $buffer, $matches)) {
753
737
$currentatrule = (!empty($matches[3]))?$matches[3]:$matches[1];
754
738
$currentprocess = self::PROCESSING_ATRULE;
755
739
$buffer .= $char;
761
745
$suspectatrule = false;
762
// continue 1: The switch processing chars
763
// continue 2: The switch processing the state
764
// continue 3: The for loop
746
// Continue 1: The switch processing chars
747
// Continue 2: The switch processing the state
748
// Continue 3: The for loop.
767
751
if ($inbrackets) {
768
// continue 1: The switch processing chars
769
// continue 2: The switch processing the state
770
// continue 3: The for loop
752
// Continue 1: The switch processing chars
753
// Continue 2: The switch processing the state
754
// Continue 3: The for loop.
757
// Check for known @ rules.
758
if ($suspectatrule && preg_match($regexatrule, $buffer, $matches)) {
759
// Ahh we've been in an @rule, lets rewind one and have the @rule case process this.
760
$currentatrule = (!empty($matches[3]))?$matches[3]:$matches[1];
761
$currentprocess = self::PROCESSING_ATRULE;
763
$suspectatrule = false;
764
// Continue 1: The switch processing chars
765
// Continue 2: The switch processing the state
766
// Continue 3: The for loop.
773
769
if ($buffer !== '') {
798
794
$currentatrule = false;
801
// continue 1: The switch processing chars
802
// continue 2: The switch processing the state
803
// continue 3: The for loop
797
// Continue 1: The switch processing chars
798
// Continue 2: The switch processing the state
799
// Continue 3: The for loop.
806
802
if ($inbrackets) {
807
// continue 1: The switch processing chars
808
// continue 2: The switch processing the state
809
// continue 3: The for loop
803
// Continue 1: The switch processing chars
804
// Continue 2: The switch processing the state
805
// Continue 3: The for loop.
812
808
$currentselector->add($buffer);
813
809
$currentrule->add_selector($currentselector);
814
810
$currentselector = css_selector::init();
816
// continue 1: The switch processing chars
817
// continue 2: The switch processing the state
818
// continue 3: The for loop
812
// Continue 1: The switch processing chars
813
// Continue 2: The switch processing the state
814
// Continue 3: The for loop.
822
// Start processing styles
818
// Start processing styles.
823
819
case self::PROCESSING_STYLES:
824
820
if ($char == '"' || $char == "'") {
825
821
if ($inquotes === false) {
862
858
$inquotes = false;
863
859
$inparenthesis = false;
864
// continue 1: The switch processing chars
865
// continue 2: The switch processing the state
866
// continue 3: The for loop
860
// Continue 1: The switch processing chars
861
// Continue 2: The switch processing the state
862
// Continue 3: The for loop.
869
865
$inparenthesis = true;
870
866
$buffer .= $char;
871
// continue 1: The switch processing chars
872
// continue 2: The switch processing the state
873
// continue 3: The for loop
867
// Continue 1: The switch processing chars
868
// Continue 2: The switch processing the state
869
// Continue 3: The for loop.
876
872
$inparenthesis = false;
877
873
$buffer .= $char;
878
// continue 1: The switch processing chars
879
// continue 2: The switch processing the state
880
// continue 3: The for loop
874
// Continue 1: The switch processing chars
875
// Continue 2: The switch processing the state
876
// Continue 3: The for loop.
2812
2860
if (!css_style_borderwidth::is_border_width($width)) {
2815
$return[] = new css_style_borderwidth('border-top-width', $width);
2816
$return[] = new css_style_borderwidth('border-right-width', $width);
2817
$return[] = new css_style_borderwidth('border-bottom-width', $width);
2818
$return[] = new css_style_borderwidth('border-left-width', $width);
2863
$return[] = css_style_bordertopwidth::init($width);
2864
$return[] = css_style_borderrightwidth::init($width);
2865
$return[] = css_style_borderbottomwidth::init($width);
2866
$return[] = css_style_borderleftwidth::init($width);
2820
2868
if (count($bits) > 0) {
2821
2869
$style = array_shift($bits);
2822
$return[] = new css_style_borderstyle('border-top-style', $style);
2823
$return[] = new css_style_borderstyle('border-right-style', $style);
2824
$return[] = new css_style_borderstyle('border-bottom-style', $style);
2825
$return[] = new css_style_borderstyle('border-left-style', $style);
2870
$return[] = css_style_bordertopstyle::init($style);
2871
$return[] = css_style_borderrightstyle::init($style);
2872
$return[] = css_style_borderbottomstyle::init($style);
2873
$return[] = css_style_borderleftstyle::init($style);
2827
2875
if (count($bits) > 0) {
2828
2876
$colour = array_shift($bits);
2829
$return[] = new css_style_bordercolor('border-top-color', $colour);
2830
$return[] = new css_style_bordercolor('border-right-color', $colour);
2831
$return[] = new css_style_bordercolor('border-bottom-color', $colour);
2832
$return[] = new css_style_bordercolor('border-left-color', $colour);
2877
$return[] = css_style_bordertopcolor::init($colour);
2878
$return[] = css_style_borderrightcolor::init($colour);
2879
$return[] = css_style_borderbottomcolor::init($colour);
2880
$return[] = css_style_borderleftcolor::init($colour);
2834
2882
return $return;
2989
3042
if ($widthkeys == $stylekeys && $stylekeys == $colorkeys) {
2990
3043
$key = $widthkeys[0][0];
2991
self::build_style_string($return, 'css_style_border', 'border', $borderwidths[$key], $borderstyles[$key], $bordercolors[$key]);
3044
self::build_style_string($return, 'css_style_border', 'border',
3045
$borderwidths[$key], $borderstyles[$key], $bordercolors[$key]);
2992
3046
$key = $widthkeys[1][0];
2993
self::build_style_string($return, 'css_style_border'.$key, 'border-'.$key, $borderwidths[$key], $borderstyles[$key], $bordercolors[$key]);
3047
self::build_style_string($return, 'css_style_border'.$key, 'border-'.$key,
3048
$borderwidths[$key], $borderstyles[$key], $bordercolors[$key]);
2995
self::build_style_string($return, 'css_style_bordertop', 'border-top', $borderwidths['top'], $borderstyles['top'], $bordercolors['top']);
2996
self::build_style_string($return, 'css_style_borderright', 'border-right', $borderwidths['right'], $borderstyles['right'], $bordercolors['right']);
2997
self::build_style_string($return, 'css_style_borderbottom', 'border-bottom', $borderwidths['bottom'], $borderstyles['bottom'], $bordercolors['bottom']);
2998
self::build_style_string($return, 'css_style_borderleft', 'border-left', $borderwidths['left'], $borderstyles['left'], $bordercolors['left']);
3050
self::build_style_string($return, 'css_style_bordertop', 'border-top',
3051
$borderwidths['top'], $borderstyles['top'], $bordercolors['top']);
3052
self::build_style_string($return, 'css_style_borderright', 'border-right',
3053
$borderwidths['right'], $borderstyles['right'], $bordercolors['right']);
3054
self::build_style_string($return, 'css_style_borderbottom', 'border-bottom',
3055
$borderwidths['bottom'], $borderstyles['bottom'], $bordercolors['bottom']);
3056
self::build_style_string($return, 'css_style_borderleft', 'border-left',
3057
$borderwidths['left'], $borderstyles['left'], $bordercolors['left']);
3001
self::build_style_string($return, 'css_style_bordertop', 'border-top', $borderwidths['top'], $borderstyles['top'], $bordercolors['top']);
3002
self::build_style_string($return, 'css_style_borderright', 'border-right', $borderwidths['right'], $borderstyles['right'], $bordercolors['right']);
3003
self::build_style_string($return, 'css_style_borderbottom', 'border-bottom', $borderwidths['bottom'], $borderstyles['bottom'], $bordercolors['bottom']);
3004
self::build_style_string($return, 'css_style_borderleft', 'border-left', $borderwidths['left'], $borderstyles['left'], $bordercolors['left']);
3060
self::build_style_string($return, 'css_style_bordertop', 'border-top',
3061
$borderwidths['top'], $borderstyles['top'], $bordercolors['top']);
3062
self::build_style_string($return, 'css_style_borderright', 'border-right',
3063
$borderwidths['right'], $borderstyles['right'], $bordercolors['right']);
3064
self::build_style_string($return, 'css_style_borderbottom', 'border-bottom',
3065
$borderwidths['bottom'], $borderstyles['bottom'], $bordercolors['bottom']);
3066
self::build_style_string($return, 'css_style_borderleft', 'border-left',
3067
$borderwidths['left'], $borderstyles['left'], $bordercolors['left']);
3006
3069
foreach ($return as $key => $style) {
3007
3070
if ($style->get_value() == '') {
3974
if ($color === self::NULL_VALUE && $image === self::NULL_VALUE && $repeat === self::NULL_VALUE && $attachment === self::NULL_VALUE && $position === self::NULL_VALUE) {
4038
if ($color === self::NULL_VALUE &&
4039
$image === self::NULL_VALUE &&
4040
$repeat === self::NULL_VALUE && $attachment === self::NULL_VALUE &&
4041
$position === self::NULL_VALUE) {
3975
4042
// All primaries are null, return without doing anything else. There may be advanced madness there.
3976
4043
return $return;
3979
$return[] = new css_style_backgroundcolor('background-color', $color);
3980
$return[] = new css_style_backgroundimage('background-image', $image);
3981
$return[] = new css_style_backgroundrepeat('background-repeat', $repeat);
3982
$return[] = new css_style_backgroundattachment('background-attachment', $attachment);
3983
$return[] = new css_style_backgroundposition('background-position', $position);
4046
$return[] = css_style_backgroundcolor::init($color);
4047
$return[] = css_style_backgroundimage::init($image);
4048
$return[] = css_style_backgroundrepeat::init($repeat);
4049
$return[] = css_style_backgroundattachment::init($attachment);
4050
$return[] = css_style_backgroundposition::init($position);
3985
4052
if ($important) {
3986
4053
foreach ($return as $style) {
4121
4192
foreach ($styles as $style) {
4123
4194
switch ($style->get_name()) {
4124
case 'background-color' : $value = $color; break;
4125
case 'background-image' : $value = $image; break;
4126
case 'background-repeat' : $value = $repeat; break;
4127
case 'background-attachment' : $value = $attachment; break;
4128
case 'background-position' : $value = $position; break;
4129
case 'background-clip' : $value = $clip; break;
4130
case 'background-origin' : $value = $origin; break;
4131
case 'background-size' : $value = $size; break;
4195
case 'background-color' :
4198
case 'background-image' :
4201
case 'background-repeat' :
4204
case 'background-attachment' :
4205
$value = $attachment;
4207
case 'background-position' :
4210
case 'background-clip' :
4213
case 'background-origin':
4216
case 'background-size':
4133
4220
if (!is_null($value)) {
4134
4221
$return[] = $style;
4822
4917
* @return string
4824
4919
protected function clean_value($value) {
4825
// Allowed values for the cursor style
4920
// Allowed values for the cursor style.
4826
4921
$allowed = array('auto', 'crosshair', 'default', 'e-resize', 'help', 'move', 'n-resize', 'ne-resize', 'nw-resize',
4827
4922
'pointer', 'progress', 's-resize', 'se-resize', 'sw-resize', 'text', 'w-resize', 'wait', 'inherit');
4828
// Has to be one of the allowed values of an image to use. Loosely match the image... doesn't need to be thorough
4923
// Has to be one of the allowed values of an image to use. Loosely match the image... doesn't need to be thorough.
4829
4924
if (!in_array($value, $allowed) && !preg_match('#\.[a-zA-Z0-9_\-]{1,5}$#', $value)) {
4830
4925
$this->set_error('Invalid or unexpected cursor value specified: '.$value);