mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 05:58:34 +01:00
MDL-33468 css_optimiser: Added support for @keyframes to the CSS optimiser
This commit is contained in:
parent
f2e8d3798c
commit
1121f79b91
194
lib/csslib.php
194
lib/csslib.php
@ -526,6 +526,8 @@ class css_optimiser {
|
||||
);
|
||||
$imports = array();
|
||||
$charset = false;
|
||||
// Keyframes are used for CSS animation they will be processed right at the very end.
|
||||
$keyframes = array();
|
||||
|
||||
$currentprocess = self::PROCESSING_START;
|
||||
$currentrule = css_rule::init();
|
||||
@ -579,6 +581,14 @@ class css_optimiser {
|
||||
$currentmedia = $medias[$mediatypes];
|
||||
$currentprocess = self::PROCESSING_SELECTORS;
|
||||
$buffer = '';
|
||||
} else if ($currentatrule == 'keyframes' && preg_match('#@((\-moz\-|\-webkit\-)?keyframes)\s*([^\s]+)#', $buffer, $matches)) {
|
||||
$keyframefor = $matches[1];
|
||||
$keyframename = $matches[3];
|
||||
$keyframe = new css_keyframe($keyframefor, $keyframename);
|
||||
$keyframes[] = $keyframe;
|
||||
$currentmedia = $keyframe;
|
||||
$currentprocess = self::PROCESSING_SELECTORS;
|
||||
$buffer = '';
|
||||
}
|
||||
// continue 1: The switch processing chars
|
||||
// continue 2: The switch processing the state
|
||||
@ -612,8 +622,8 @@ class css_optimiser {
|
||||
continue 3;
|
||||
}
|
||||
if (!empty($buffer)) {
|
||||
if ($suspectatrule && preg_match('#@(media|import|charset)\s*#', $buffer, $matches)) {
|
||||
$currentatrule = $matches[1];
|
||||
if ($suspectatrule && preg_match('#@(media|import|charset|(\-moz\-|\-webkit\-)?(keyframes))\s*#', $buffer, $matches)) {
|
||||
$currentatrule = (!empty($matches[3]))?$matches[3]:$matches[1];
|
||||
$currentprocess = self::PROCESSING_ATRULE;
|
||||
$buffer .= $char;
|
||||
} else {
|
||||
@ -655,6 +665,10 @@ class css_optimiser {
|
||||
$currentmedia = $medias['all'];
|
||||
$currentatrule = false;
|
||||
$buffer = '';
|
||||
} else if (strpos($currentatrule, 'keyframes') !== false) {
|
||||
$currentmedia = $medias['all'];
|
||||
$currentatrule = false;
|
||||
$buffer = '';
|
||||
}
|
||||
// continue 1: The switch processing chars
|
||||
// continue 2: The switch processing the state
|
||||
@ -760,6 +774,18 @@ class css_optimiser {
|
||||
}
|
||||
$css .= $media->out();
|
||||
}
|
||||
|
||||
if (count($keyframes) > 0) {
|
||||
foreach ($keyframes as $keyframe) {
|
||||
$this->optimisedrules += $keyframe->count_rules();
|
||||
$this->optimisedselectors += $keyframe->count_selectors();
|
||||
if ($keyframe->has_errors()) {
|
||||
$this->errors += $keyframe->get_errors();
|
||||
}
|
||||
$css .= $keyframe->out();
|
||||
}
|
||||
}
|
||||
|
||||
$this->optimisedstrlen = strlen($css);
|
||||
|
||||
$this->timecomplete = microtime(true);
|
||||
@ -1138,6 +1164,17 @@ abstract class css_writer {
|
||||
return $output;
|
||||
}
|
||||
|
||||
public static function keyframe($for, $name, array &$rules) {
|
||||
$nl = self::get_separator();
|
||||
|
||||
$output = $nl."@{$for} {$name} {";
|
||||
foreach ($rules as $rule) {
|
||||
$output .= $rule->out();
|
||||
}
|
||||
$output .= '}';
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns CSS for a rule
|
||||
*
|
||||
@ -1536,44 +1573,23 @@ class css_rule {
|
||||
}
|
||||
}
|
||||
return $css." has the following errors:\n".join("\n", $errors);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class css_rule_collection {
|
||||
/**
|
||||
* A media class to organise rules by the media they apply to.
|
||||
*
|
||||
* @package core_css
|
||||
* @category css
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class css_media {
|
||||
|
||||
/**
|
||||
* An array of the different media types this instance applies to.
|
||||
* @var array
|
||||
*/
|
||||
protected $types = array();
|
||||
|
||||
/**
|
||||
* An array of rules within this media instance
|
||||
* An array of rules within this collection instance
|
||||
* @var array
|
||||
*/
|
||||
protected $rules = array();
|
||||
|
||||
/**
|
||||
* Initalises a new media instance
|
||||
*
|
||||
* @param string $for The media that the contained rules are destined for.
|
||||
* The collection must be able to print itself.
|
||||
*/
|
||||
public function __construct($for = 'all') {
|
||||
$types = explode(',', $for);
|
||||
$this->types = array_map('trim', $types);
|
||||
}
|
||||
abstract public function out();
|
||||
|
||||
/**
|
||||
* Adds a new CSS rule to this media instance
|
||||
* Adds a new CSS rule to this collection instance
|
||||
*
|
||||
* @param css_rule $newrule
|
||||
*/
|
||||
@ -1589,7 +1605,7 @@ class css_media {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rules used by this
|
||||
* Returns the rules used by this collection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@ -1622,7 +1638,7 @@ class css_media {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of rules that exist within this media set
|
||||
* Returns the total number of rules that exist within this collection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@ -1631,7 +1647,7 @@ class css_media {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of selectors that exist within this media set
|
||||
* Returns the total number of selectors that exist within this collection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@ -1643,6 +1659,62 @@ class css_media {
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the collection has any rules that have errors
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has_errors() {
|
||||
foreach ($this->rules as $rule) {
|
||||
if ($rule->has_errors()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any errors that have happened within rules in this collection.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_errors() {
|
||||
$errors = array();
|
||||
foreach ($this->rules as $rule) {
|
||||
if ($rule->has_errors()) {
|
||||
$errors[] = $rule->get_error_string();
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A media class to organise rules by the media they apply to.
|
||||
*
|
||||
* @package core_css
|
||||
* @category css
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class css_media extends css_rule_collection {
|
||||
|
||||
/**
|
||||
* An array of the different media types this instance applies to.
|
||||
* @var array
|
||||
*/
|
||||
protected $types = array();
|
||||
|
||||
/**
|
||||
* Initalises a new media instance
|
||||
*
|
||||
* @param string $for The media that the contained rules are destined for.
|
||||
*/
|
||||
public function __construct($for = 'all') {
|
||||
$types = explode(',', $for);
|
||||
$this->types = array_map('trim', $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSS for this media and all of its rules.
|
||||
*
|
||||
@ -1660,34 +1732,56 @@ class css_media {
|
||||
public function get_types() {
|
||||
return $this->types;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the media has any rules that have errors
|
||||
* A media class to organise rules by the media they apply to.
|
||||
*
|
||||
* @return boolean
|
||||
* @package core_css
|
||||
* @category css
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
public function has_errors() {
|
||||
foreach ($this->rules as $rule) {
|
||||
if ($rule->has_errors()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
class css_keyframe extends css_rule_collection {
|
||||
|
||||
/** @var string $for The directive e.g. keyframes, -moz-keyframes, -webkit-keyframes */
|
||||
protected $for;
|
||||
|
||||
/** @var string $name The name for the keyframes */
|
||||
protected $name;
|
||||
/**
|
||||
* Returns any errors that have happened within rules in this media set.
|
||||
* Constructs a new keyframe
|
||||
*
|
||||
* @param string $for The directive e.g. keyframes, -moz-keyframes, -webkit-keyframes
|
||||
* @param string $name The name for the keyframes
|
||||
*/
|
||||
public function __construct($for, $name) {
|
||||
$this->for = $for;
|
||||
$this->name = $name;
|
||||
}
|
||||
/**
|
||||
* Returns the directive of this keyframe
|
||||
*
|
||||
* e.g. keyframes, -moz-keyframes, -webkit-keyframes
|
||||
* @return string
|
||||
*/
|
||||
public function get_for() {
|
||||
return $this->for;
|
||||
}
|
||||
/**
|
||||
* Returns the name of this keyframe
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return $this->name;
|
||||
}
|
||||
/**
|
||||
* Returns the CSS for this collection of keyframes and all of its rules.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_errors() {
|
||||
$errors = array();
|
||||
foreach ($this->rules as $rule) {
|
||||
if ($rule->has_errors()) {
|
||||
$errors[] = $rule->get_error_string();
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
public function out() {
|
||||
return css_writer::keyframe($this->for, $this->name, $this->rules);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ class css_optimiser_testcase extends advanced_testcase {
|
||||
$this->try_invalid_css_handling($optimiser);
|
||||
$this->try_bulk_processing($optimiser);
|
||||
$this->try_break_things($optimiser);
|
||||
$this->try_advanced_css_animation($optimiser);
|
||||
$this->try_keyframe_css_animation($optimiser);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -807,17 +807,87 @@ CSS;
|
||||
$this->assertEquals($cssout, $optimiser->process($cssin));
|
||||
}
|
||||
|
||||
public function try_advanced_css_animation(css_optimiser $optimiser) {
|
||||
$css = '.dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url(\'[[pix:theme|fp/dnd_arrow]]\') center no-repeat;margin-left:-28px;}';
|
||||
public function try_keyframe_css_animation(css_optimiser $optimiser) {
|
||||
$css = '.dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url(\'[[pix:theme|fp/dnd_arrow]]\') no-repeat center;margin-left:-28px;}';
|
||||
$this->assertEquals($css, $optimiser->process($css));
|
||||
|
||||
$css = '@keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}';
|
||||
$this->assertEquals($css, $optimiser->process($css));
|
||||
|
||||
$css = "@keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}\n";
|
||||
$css .= "@-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}\n";
|
||||
$css .= "@-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}";
|
||||
$this->assertEquals($css, $optimiser->process($css));
|
||||
|
||||
|
||||
$cssin = <<<CSS
|
||||
.test {color:#FFF;}
|
||||
.testtwo {color:#FFF;}
|
||||
@media print {
|
||||
.test {background-color:#FFF;}
|
||||
}
|
||||
.dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;margin-left:-28px;}
|
||||
@media print {
|
||||
.test {background-color:#000;}
|
||||
}
|
||||
@keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
|
||||
@-moz-keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
|
||||
@-webkit-keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
|
||||
@media print {
|
||||
.test {background-color:#333;}
|
||||
}
|
||||
.test {color:#888;}
|
||||
.testtwo {color:#888;}
|
||||
CSS;
|
||||
|
||||
$cssout = <<<CSS
|
||||
.test,
|
||||
.testtwo{color:#888;}
|
||||
.dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;margin-left:-28px;}
|
||||
|
||||
|
||||
@media print {
|
||||
.test{background:#333;}
|
||||
}
|
||||
@keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
|
||||
@-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
|
||||
@-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
|
||||
CSS;
|
||||
$this->assertEquals($cssout, $optimiser->process($cssin));
|
||||
|
||||
$cssin = '@keyframes mymove{0%{top:10px;}12%{top:40px;}30%{top:20px}65%{top:35px;}100%{top:9px;}}';
|
||||
$cssout = '@keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}';
|
||||
|
||||
|
||||
$cssin = <<<CSS
|
||||
.dndupload-target {display:none;}
|
||||
.dndsupported .dndupload-ready .dndupload-target {display:block;}
|
||||
.dndupload-uploadinprogress {display:none;text-align:center;}
|
||||
.dndupload-uploading .dndupload-uploadinprogress {display:block;}
|
||||
.dndupload-arrow {background:url('[[pix:theme|fp/dnd_arrow]]') center no-repeat;width:56px;height:47px;position:absolute;margin-left: -28px;/*right:46%;left:46%;*/animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;}
|
||||
@keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}@-moz-keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}@-webkit-keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
|
||||
|
||||
/*
|
||||
* Select Dialogue (File Manager only)
|
||||
*/
|
||||
.filemanager.fp-select .fp-select-loading {display:none;}
|
||||
.filemanager.fp-select.loading .fp-select-loading {display:block;}
|
||||
.filemanager.fp-select.loading form {display:none;}
|
||||
CSS;
|
||||
|
||||
$cssout = <<<CSS
|
||||
.dndupload-target,
|
||||
.filemanager.fp-select .fp-select-loading,
|
||||
.filemanager.fp-select.loading form{display:none;}
|
||||
.dndsupported .dndupload-ready .dndupload-target,
|
||||
.dndupload-uploading .dndupload-uploadinprogress,
|
||||
.filemanager.fp-select.loading .fp-select-loading{display:block;}
|
||||
.dndupload-uploadinprogress{display:none;text-align:center;}
|
||||
.dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;margin-left:-28px;}
|
||||
|
||||
@keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
|
||||
@-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
|
||||
@-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
|
||||
CSS;
|
||||
$this->assertEquals($cssout, $optimiser->process($cssin));
|
||||
|
||||
$cssin = '@keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}} @-moz-keyframes mymove{0%{top:10px;}12%{top:40px;}30%{top:20px}65%{top:35px;}100%{top:9px;}} @-webkit-keyframes mymove{0%{top:10px;}12%{top:40px;}30%{top:20px}65%{top:35px;}100%{top:9px;}}';
|
||||
$cssout = '@keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}} @-moz-keyframes mymove{0%{top:10px;}12%{top:40px;}30%{top:20px}65%{top:35px;}100%{top:9px;}} @-webkit-keyframes mymove{0%{top:10px;}12%{top:40px;}30%{top:20px}65%{top:35px;}100%{top:9px;}}';
|
||||
$this->assertEquals($cssout, $optimiser->process($cssin));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user